1 /* This file contains the code for performing four system calls relating to 2 * status and directories. 3 * 4 * The entry points into this file are 5 * do_chdir: perform the CHDIR system call 6 * do_chroot: perform the CHROOT system call 7 * do_lstat: perform the LSTAT system call 8 * do_stat: perform the STAT system call 9 * do_fstat: perform the FSTAT system call 10 * do_statvfs: perform the STATVFS1 system call 11 * do_fstatvfs: perform the FSTATVFS1 system call 12 * do_getvfsstat: perform the GETVFSSTAT system call 13 */ 14 15 #include "fs.h" 16 #include <sys/stat.h> 17 #include <minix/com.h> 18 #include <minix/u64.h> 19 #include <string.h> 20 #include "file.h" 21 #include "path.h" 22 #include <minix/vfsif.h> 23 #include <minix/callnr.h> 24 #include "vnode.h" 25 #include "vmnt.h" 26 27 static int change_into(struct vnode **iip, struct vnode *vp); 28 29 /*===========================================================================* 30 * do_fchdir * 31 *===========================================================================*/ 32 int do_fchdir(void) 33 { 34 /* Change directory on already-opened fd. */ 35 struct filp *rfilp; 36 int r, rfd; 37 38 rfd = job_m_in.m_lc_vfs_fchdir.fd; 39 40 /* Is the file descriptor valid? */ 41 if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); 42 r = change_into(&fp->fp_wd, rfilp->filp_vno); 43 unlock_filp(rfilp); 44 return(r); 45 } 46 47 /*===========================================================================* 48 * do_chdir * 49 *===========================================================================*/ 50 int do_chdir(void) 51 { 52 /* Perform the chdir(name) system call. 53 * syscall might provide 'name' embedded in the message. 54 */ 55 56 int r; 57 struct vnode *vp; 58 struct vmnt *vmp; 59 char fullpath[PATH_MAX]; 60 struct lookup resolve; 61 62 if (copy_path(fullpath, sizeof(fullpath)) != OK) 63 return(err_code); 64 65 /* Try to open the directory */ 66 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 67 resolve.l_vmnt_lock = VMNT_READ; 68 resolve.l_vnode_lock = VNODE_READ; 69 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 70 71 r = change_into(&fp->fp_wd, vp); 72 73 unlock_vnode(vp); 74 unlock_vmnt(vmp); 75 put_vnode(vp); 76 77 return(r); 78 } 79 80 /*===========================================================================* 81 * do_chroot * 82 *===========================================================================*/ 83 int do_chroot(void) 84 { 85 /* Perform the chroot(name) system call. 86 * syscall might provide 'name' embedded in the message. 87 */ 88 int r; 89 struct vnode *vp; 90 struct vmnt *vmp; 91 char fullpath[PATH_MAX]; 92 struct lookup resolve; 93 94 if (!super_user) return(EPERM); /* only su may chroot() */ 95 96 if (copy_path(fullpath, sizeof(fullpath)) != OK) 97 return(err_code); 98 99 /* Try to open the directory */ 100 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 101 resolve.l_vmnt_lock = VMNT_READ; 102 resolve.l_vnode_lock = VNODE_READ; 103 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 104 105 r = change_into(&fp->fp_rd, vp); 106 107 unlock_vnode(vp); 108 unlock_vmnt(vmp); 109 put_vnode(vp); 110 111 return(r); 112 } 113 114 /*===========================================================================* 115 * change_into * 116 *===========================================================================*/ 117 static int change_into(struct vnode **result, struct vnode *vp) 118 { 119 int r; 120 121 if (*result == vp) return(OK); /* Nothing to do */ 122 123 /* It must be a directory and also be searchable */ 124 if (!S_ISDIR(vp->v_mode)) 125 r = ENOTDIR; 126 else 127 r = forbidden(fp, vp, X_BIT); /* Check if dir is searchable*/ 128 if (r != OK) return(r); 129 130 /* Everything is OK. Make the change. */ 131 put_vnode(*result); /* release the old directory */ 132 dup_vnode(vp); 133 *result = vp; /* acquire the new one */ 134 return(OK); 135 } 136 137 /*===========================================================================* 138 * do_stat * 139 *===========================================================================*/ 140 int do_stat(void) 141 { 142 /* Perform the stat(name, buf) system call. */ 143 int r; 144 struct vnode *vp; 145 struct vmnt *vmp; 146 char fullpath[PATH_MAX]; 147 struct lookup resolve; 148 vir_bytes vname1, statbuf; 149 size_t vname1_length; 150 151 vname1 = job_m_in.m_lc_vfs_stat.name; 152 vname1_length = job_m_in.m_lc_vfs_stat.len; 153 statbuf = job_m_in.m_lc_vfs_stat.buf; 154 155 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 156 resolve.l_vmnt_lock = VMNT_READ; 157 resolve.l_vnode_lock = VNODE_READ; 158 159 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code); 160 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 161 r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf); 162 163 unlock_vnode(vp); 164 unlock_vmnt(vmp); 165 166 put_vnode(vp); 167 return r; 168 } 169 170 /*===========================================================================* 171 * do_fstat * 172 *===========================================================================*/ 173 int do_fstat(void) 174 { 175 /* Perform the fstat(fd, buf) system call. */ 176 register struct filp *rfilp; 177 int r, rfd; 178 vir_bytes statbuf; 179 180 statbuf = job_m_in.m_lc_vfs_fstat.buf; 181 rfd = job_m_in.m_lc_vfs_fstat.fd; 182 183 /* Is the file descriptor valid? */ 184 if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); 185 186 r = req_stat(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, 187 who_e, statbuf); 188 189 unlock_filp(rfilp); 190 191 return(r); 192 } 193 194 /*===========================================================================* 195 * update_statvfs * 196 *===========================================================================*/ 197 int update_statvfs(struct vmnt *vmp, struct statvfs *buf) 198 { 199 /* Get statistics from a file system, and cache part of the results. */ 200 int r; 201 202 if ((r = req_statvfs(vmp->m_fs_e, buf)) != OK) 203 return r; 204 205 vmp->m_stats.f_flag = buf->f_flag; 206 vmp->m_stats.f_bsize = buf->f_bsize; 207 vmp->m_stats.f_frsize = buf->f_frsize; 208 vmp->m_stats.f_iosize = buf->f_iosize; 209 210 vmp->m_stats.f_blocks = buf->f_blocks; 211 vmp->m_stats.f_bfree = buf->f_bfree; 212 vmp->m_stats.f_bavail = buf->f_bavail; 213 vmp->m_stats.f_bresvd = buf->f_bresvd; 214 215 vmp->m_stats.f_files = buf->f_files; 216 vmp->m_stats.f_ffree = buf->f_ffree; 217 vmp->m_stats.f_favail = buf->f_favail; 218 vmp->m_stats.f_fresvd = buf->f_fresvd; 219 220 vmp->m_stats.f_syncreads = buf->f_syncreads; 221 vmp->m_stats.f_syncwrites = buf->f_syncwrites; 222 223 vmp->m_stats.f_asyncreads = buf->f_asyncreads; 224 vmp->m_stats.f_asyncwrites = buf->f_asyncwrites; 225 226 vmp->m_stats.f_namemax = buf->f_namemax; 227 228 return OK; 229 } 230 231 /*===========================================================================* 232 * fill_statvfs * 233 *===========================================================================*/ 234 static int fill_statvfs(struct vmnt *vmp, endpoint_t endpt, vir_bytes buf_addr, 235 int flags) 236 { 237 /* Fill a statvfs structure in a userspace process. First let the target file 238 * server fill in most fields, or use the cached copy if ST_NOWAIT is given. 239 * Then fill in some remaining fields with local information. Finally, copy 240 * the result to user space. 241 */ 242 struct statvfs buf; 243 244 if (!(flags & ST_NOWAIT)) { 245 /* Get fresh statistics from the file system. */ 246 if (update_statvfs(vmp, &buf) != OK) 247 return EIO; 248 } else { 249 /* Use the cached statistics. */ 250 memset(&buf, 0, sizeof(buf)); 251 252 buf.f_flag = vmp->m_stats.f_flag; 253 buf.f_bsize = vmp->m_stats.f_bsize; 254 buf.f_frsize = vmp->m_stats.f_frsize; 255 buf.f_iosize = vmp->m_stats.f_iosize; 256 257 buf.f_blocks = vmp->m_stats.f_blocks; 258 buf.f_bfree = vmp->m_stats.f_bfree; 259 buf.f_bavail = vmp->m_stats.f_bavail; 260 buf.f_bresvd = vmp->m_stats.f_bresvd; 261 262 buf.f_files = vmp->m_stats.f_files; 263 buf.f_ffree = vmp->m_stats.f_ffree; 264 buf.f_favail = vmp->m_stats.f_favail; 265 buf.f_fresvd = vmp->m_stats.f_fresvd; 266 267 buf.f_syncreads = vmp->m_stats.f_syncreads; 268 buf.f_syncwrites = vmp->m_stats.f_syncwrites; 269 270 buf.f_asyncreads = vmp->m_stats.f_asyncreads; 271 buf.f_asyncwrites = vmp->m_stats.f_asyncwrites; 272 273 buf.f_namemax = vmp->m_stats.f_namemax; 274 } 275 276 if (vmp->m_flags & VMNT_READONLY) 277 buf.f_flag |= ST_RDONLY; 278 279 buf.f_fsid = (unsigned long)vmp->m_dev; 280 buf.f_fsidx.__fsid_val[0] = (long)vmp->m_dev; /* This is what is done on NetBSD */ 281 buf.f_fsidx.__fsid_val[1] = 0; /* Here they convert the FS type name into a number. */ 282 283 strlcpy(buf.f_fstypename, vmp->m_fstype, sizeof(buf.f_fstypename)); 284 strlcpy(buf.f_mntonname, vmp->m_mount_path, sizeof(buf.f_mntonname)); 285 strlcpy(buf.f_mntfromname, vmp->m_mount_dev, sizeof(buf.f_mntfromname)); 286 287 return sys_datacopy_wrapper(SELF, (vir_bytes) &buf, 288 endpt, buf_addr, sizeof(buf)); 289 } 290 291 /*===========================================================================* 292 * do_statvfs * 293 *===========================================================================*/ 294 int do_statvfs(void) 295 { 296 /* Perform the statvfs1(name, buf, flags) system call. */ 297 int r, flags; 298 struct vnode *vp; 299 struct vmnt *vmp; 300 char fullpath[PATH_MAX]; 301 struct lookup resolve; 302 vir_bytes vname1, statbuf; 303 size_t vname1_length; 304 305 vname1 = job_m_in.m_lc_vfs_statvfs1.name; 306 vname1_length = job_m_in.m_lc_vfs_statvfs1.len; 307 statbuf = job_m_in.m_lc_vfs_statvfs1.buf; 308 flags = job_m_in.m_lc_vfs_statvfs1.flags; 309 310 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 311 resolve.l_vmnt_lock = VMNT_READ; 312 resolve.l_vnode_lock = VNODE_READ; 313 314 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code); 315 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 316 r = fill_statvfs(vp->v_vmnt, who_e, statbuf, flags); 317 318 unlock_vnode(vp); 319 unlock_vmnt(vmp); 320 321 put_vnode(vp); 322 return r; 323 } 324 325 /*===========================================================================* 326 * do_fstatvfs * 327 *===========================================================================*/ 328 int do_fstatvfs(void) 329 { 330 /* Perform the fstatvfs1(fd, buf, flags) system call. */ 331 register struct filp *rfilp; 332 int r, rfd, flags; 333 vir_bytes statbuf; 334 335 rfd = job_m_in.m_lc_vfs_statvfs1.fd; 336 statbuf = job_m_in.m_lc_vfs_statvfs1.buf; 337 flags = job_m_in.m_lc_vfs_statvfs1.flags; 338 339 /* Is the file descriptor valid? */ 340 if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); 341 r = fill_statvfs(rfilp->filp_vno->v_vmnt, who_e, statbuf, flags); 342 343 unlock_filp(rfilp); 344 345 return(r); 346 } 347 348 /*===========================================================================* 349 * do_getvfsstat * 350 *===========================================================================*/ 351 int do_getvfsstat(void) 352 { 353 /* Perform the getvfsstat(buf, bufsize, flags) system call. */ 354 struct vmnt *vmp; 355 vir_bytes buf; 356 size_t bufsize; 357 int r, flags, count, do_lock; 358 359 buf = job_m_in.m_lc_vfs_getvfsstat.buf; 360 bufsize = job_m_in.m_lc_vfs_getvfsstat.len; 361 flags = job_m_in.m_lc_vfs_getvfsstat.flags; 362 363 count = 0; 364 365 if (buf != 0) { 366 /* We only need to lock target file systems if we are going to query 367 * them. This will only happen if ST_NOWAIT is not given. If we do 368 * not lock, we rely on the VMNT_CANSTAT flag to protect us from 369 * concurrent (un)mount operations. Note that procfs relies on 370 * ST_NOWAIT calls being lock free, as it is a file system itself. 371 */ 372 do_lock = !(flags & ST_NOWAIT); 373 374 for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) { 375 /* If there is no more space, return the count so far. */ 376 if (bufsize < sizeof(struct statvfs)) 377 break; 378 379 /* Lock the file system before checking any fields. */ 380 if (do_lock && (r = lock_vmnt(vmp, VMNT_READ)) != OK) 381 return r; 382 383 /* Obtain information for this file system, if it is in use and 384 * can be reported. File systems that are being (un)mounted 385 * are skipped, as is PFS. The fill call will block only if 386 * ST_NOWAIT was not given. 387 */ 388 if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT)) { 389 if ((r = fill_statvfs(vmp, who_e, buf, flags)) != OK) { 390 if (do_lock) 391 unlock_vmnt(vmp); 392 393 return r; 394 } 395 396 count++; 397 buf += sizeof(struct statvfs); 398 bufsize -= sizeof(struct statvfs); 399 } 400 401 if (do_lock) 402 unlock_vmnt(vmp); 403 } 404 } else { 405 /* Just report a file system count. No need to lock, as above. */ 406 for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) { 407 if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT)) 408 count++; 409 } 410 } 411 412 return count; 413 } 414 415 /*===========================================================================* 416 * do_lstat * 417 *===========================================================================*/ 418 int do_lstat(void) 419 { 420 /* Perform the lstat(name, buf) system call. */ 421 struct vnode *vp; 422 struct vmnt *vmp; 423 int r; 424 char fullpath[PATH_MAX]; 425 struct lookup resolve; 426 vir_bytes vname1, statbuf; 427 size_t vname1_length; 428 429 vname1 = job_m_in.m_lc_vfs_stat.name; 430 vname1_length = job_m_in.m_lc_vfs_stat.len; 431 statbuf = job_m_in.m_lc_vfs_stat.buf; 432 433 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp); 434 resolve.l_vmnt_lock = VMNT_READ; 435 resolve.l_vnode_lock = VNODE_READ; 436 437 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code); 438 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 439 r = req_stat(vp->v_fs_e, vp->v_inode_nr, who_e, statbuf); 440 441 unlock_vnode(vp); 442 unlock_vmnt(vmp); 443 444 put_vnode(vp); 445 return(r); 446 } 447