1 /* This file handles the LINK and UNLINK system calls. It also deals with 2 * deallocating the storage used by a file when the last UNLINK is done to a 3 * file and the blocks must be returned to the free block pool. 4 * 5 * The entry points into this file are 6 * do_link: perform the LINK system call 7 * do_unlink: perform the UNLINK and RMDIR system calls 8 * do_rename: perform the RENAME system call 9 * do_truncate: perform the TRUNCATE system call 10 * do_ftruncate: perform the FTRUNCATE system call 11 * do_rdlink: perform the RDLNK system call 12 */ 13 14 #include "fs.h" 15 #include <sys/stat.h> 16 #include <string.h> 17 #include <minix/com.h> 18 #include <minix/callnr.h> 19 #include <minix/vfsif.h> 20 #include <sys/dirent.h> 21 #include <assert.h> 22 #include "file.h" 23 #include "path.h" 24 #include "vnode.h" 25 #include "scratchpad.h" 26 27 /*===========================================================================* 28 * do_link * 29 *===========================================================================*/ 30 int do_link(void) 31 { 32 /* Perform the link(name1, name2) system call. */ 33 int r = OK; 34 struct vnode *vp = NULL, *dirp = NULL; 35 struct vmnt *vmp1 = NULL, *vmp2 = NULL; 36 char fullpath[PATH_MAX]; 37 struct lookup resolve; 38 vir_bytes vname1, vname2; 39 size_t vname1_length, vname2_length; 40 41 vname1 = job_m_in.m_lc_vfs_link.name1; 42 vname1_length = job_m_in.m_lc_vfs_link.len1; 43 vname2 = job_m_in.m_lc_vfs_link.name2; 44 vname2_length = job_m_in.m_lc_vfs_link.len2; 45 46 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp1, &vp); 47 resolve.l_vmnt_lock = VMNT_WRITE; 48 resolve.l_vnode_lock = VNODE_READ; 49 50 /* See if 'name1' (file to be linked to) exists. */ 51 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code); 52 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 53 54 /* Does the final directory of 'name2' exist? */ 55 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp2, &dirp); 56 resolve.l_vmnt_lock = VMNT_READ; 57 resolve.l_vnode_lock = VNODE_WRITE; 58 if (fetch_name(vname2, vname2_length, fullpath) != OK) 59 r = err_code; 60 else if ((dirp = last_dir(&resolve, fp)) == NULL) 61 r = err_code; 62 63 if (r != OK) { 64 unlock_vnode(vp); 65 unlock_vmnt(vmp1); 66 put_vnode(vp); 67 return(r); 68 } 69 70 /* Check for links across devices. */ 71 if (vp->v_fs_e != dirp->v_fs_e) 72 r = EXDEV; 73 else 74 r = forbidden(fp, dirp, W_BIT | X_BIT); 75 76 if (r == OK) 77 r = req_link(vp->v_fs_e, dirp->v_inode_nr, fullpath, 78 vp->v_inode_nr); 79 80 unlock_vnode(vp); 81 unlock_vnode(dirp); 82 if (vmp2 != NULL) unlock_vmnt(vmp2); 83 unlock_vmnt(vmp1); 84 put_vnode(vp); 85 put_vnode(dirp); 86 return(r); 87 } 88 89 /*===========================================================================* 90 * do_unlink * 91 *===========================================================================*/ 92 int do_unlink(void) 93 { 94 /* Perform the unlink(name) or rmdir(name) system call. The code for these two 95 * is almost the same. They differ only in some condition testing. Unlink() 96 * may be used by the superuser to do dangerous things; rmdir() may not. 97 * The syscall might provide 'name' embedded in the message. 98 */ 99 struct vnode *dirp, *dirp_l, *vp; 100 struct vmnt *vmp, *vmp2; 101 int r; 102 char fullpath[PATH_MAX]; 103 struct lookup resolve, stickycheck; 104 105 if (copy_path(fullpath, sizeof(fullpath)) != OK) 106 return(err_code); 107 108 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &dirp_l); 109 resolve.l_vmnt_lock = VMNT_WRITE; 110 resolve.l_vnode_lock = VNODE_WRITE; 111 112 /* Get the last directory in the path. */ 113 if ((dirp = last_dir(&resolve, fp)) == NULL) return(err_code); 114 115 /* Make sure that the object is a directory */ 116 if (!S_ISDIR(dirp->v_mode)) { 117 unlock_vnode(dirp); 118 unlock_vmnt(vmp); 119 put_vnode(dirp); 120 return(ENOTDIR); 121 } 122 123 /* The caller must have both search and execute permission */ 124 if ((r = forbidden(fp, dirp, X_BIT | W_BIT)) != OK) { 125 unlock_vnode(dirp); 126 unlock_vmnt(vmp); 127 put_vnode(dirp); 128 return(r); 129 } 130 131 /* Also, if the sticky bit is set, only the owner of the file or a privileged 132 user is allowed to unlink */ 133 if ((dirp->v_mode & S_ISVTX) == S_ISVTX) { 134 /* Look up inode of file to unlink to retrieve owner */ 135 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp); 136 stickycheck.l_vmnt_lock = VMNT_READ; 137 stickycheck.l_vnode_lock = VNODE_READ; 138 vp = advance(dirp, &stickycheck, fp); 139 assert(vmp2 == NULL); 140 if (vp != NULL) { 141 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) 142 r = EPERM; 143 unlock_vnode(vp); 144 put_vnode(vp); 145 } else 146 r = err_code; 147 if (r != OK) { 148 unlock_vnode(dirp); 149 unlock_vmnt(vmp); 150 put_vnode(dirp); 151 return(r); 152 } 153 } 154 155 upgrade_vmnt_lock(vmp); 156 157 if (job_call_nr == VFS_UNLINK) 158 r = req_unlink(dirp->v_fs_e, dirp->v_inode_nr, fullpath); 159 else 160 r = req_rmdir(dirp->v_fs_e, dirp->v_inode_nr, fullpath); 161 unlock_vnode(dirp); 162 unlock_vmnt(vmp); 163 put_vnode(dirp); 164 return(r); 165 } 166 167 /*===========================================================================* 168 * do_rename * 169 *===========================================================================*/ 170 int do_rename(void) 171 { 172 /* Perform the rename(name1, name2) system call. */ 173 int r = OK, r1; 174 struct vnode *old_dirp = NULL, *new_dirp = NULL, *new_dirp_l = NULL, *vp; 175 struct vmnt *oldvmp, *newvmp, *vmp2; 176 char old_name[PATH_MAX]; 177 char fullpath[PATH_MAX]; 178 struct lookup resolve, stickycheck; 179 vir_bytes vname1, vname2; 180 size_t vname1_length, vname2_length; 181 182 vname1 = job_m_in.m_lc_vfs_link.name1; 183 vname1_length = job_m_in.m_lc_vfs_link.len1; 184 vname2 = job_m_in.m_lc_vfs_link.name2; 185 vname2_length = job_m_in.m_lc_vfs_link.len2; 186 187 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &oldvmp, &old_dirp); 188 /* Do not yet request exclusive lock on vmnt to prevent deadlocks later on */ 189 resolve.l_vmnt_lock = VMNT_WRITE; 190 resolve.l_vnode_lock = VNODE_WRITE; 191 192 /* See if 'name1' (existing file) exists. Get dir and file inodes. */ 193 if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code); 194 if ((old_dirp = last_dir(&resolve, fp)) == NULL) return(err_code); 195 196 /* If the sticky bit is set, only the owner of the file or a privileged 197 user is allowed to rename */ 198 if ((old_dirp->v_mode & S_ISVTX) == S_ISVTX) { 199 /* Look up inode of file to unlink to retrieve owner */ 200 lookup_init(&stickycheck, resolve.l_path, PATH_RET_SYMLINK, &vmp2, &vp); 201 stickycheck.l_vmnt_lock = VMNT_READ; 202 stickycheck.l_vnode_lock = VNODE_READ; 203 vp = advance(old_dirp, &stickycheck, fp); 204 assert(vmp2 == NULL); 205 if (vp != NULL) { 206 if(vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) 207 r = EPERM; 208 unlock_vnode(vp); 209 put_vnode(vp); 210 } else 211 r = err_code; 212 if (r != OK) { 213 unlock_vnode(old_dirp); 214 unlock_vmnt(oldvmp); 215 put_vnode(old_dirp); 216 return(r); 217 } 218 } 219 220 /* Save the last component of the old name */ 221 if (strlen(fullpath) >= sizeof(old_name)) { 222 unlock_vnode(old_dirp); 223 unlock_vmnt(oldvmp); 224 put_vnode(old_dirp); 225 return(ENAMETOOLONG); 226 } 227 strlcpy(old_name, fullpath, PATH_MAX); 228 229 /* See if 'name2' (new name) exists. Get dir inode */ 230 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &newvmp, &new_dirp_l); 231 resolve.l_vmnt_lock = VMNT_READ; 232 resolve.l_vnode_lock = VNODE_WRITE; 233 if (fetch_name(vname2, vname2_length, fullpath) != OK) r = err_code; 234 else if ((new_dirp = last_dir(&resolve, fp)) == NULL) r = err_code; 235 236 /* We used a separate vnode pointer to see whether we obtained a lock on the 237 * new_dirp vnode. If the new directory and old directory are the same, then 238 * the VNODE_WRITE lock on new_dirp will fail. In that case, new_dirp_l will 239 * be NULL, but new_dirp will not. 240 */ 241 if (new_dirp == old_dirp) assert(new_dirp_l == NULL); 242 243 if (r != OK) { 244 unlock_vnode(old_dirp); 245 unlock_vmnt(oldvmp); 246 put_vnode(old_dirp); 247 return(r); 248 } 249 250 /* Both parent directories must be on the same device. */ 251 if (old_dirp->v_fs_e != new_dirp->v_fs_e) r = EXDEV; 252 253 /* Parent dirs must be writable, searchable and on a writable device */ 254 if ((r1 = forbidden(fp, old_dirp, W_BIT|X_BIT)) != OK || 255 (r1 = forbidden(fp, new_dirp, W_BIT|X_BIT)) != OK) r = r1; 256 257 if (r == OK) { 258 upgrade_vmnt_lock(oldvmp); /* Upgrade to exclusive access */ 259 r = req_rename(old_dirp->v_fs_e, old_dirp->v_inode_nr, old_name, 260 new_dirp->v_inode_nr, fullpath); 261 } 262 263 unlock_vnode(old_dirp); 264 unlock_vmnt(oldvmp); 265 if (new_dirp_l) unlock_vnode(new_dirp_l); 266 if (newvmp) unlock_vmnt(newvmp); 267 268 put_vnode(old_dirp); 269 put_vnode(new_dirp); 270 271 return(r); 272 } 273 274 /*===========================================================================* 275 * do_truncate * 276 *===========================================================================*/ 277 int do_truncate(void) 278 { 279 /* truncate_vnode() does the actual work of do_truncate() and do_ftruncate(). 280 * do_truncate() and do_ftruncate() have to get hold of the inode, either 281 * by name or fd, do checks on it, and call truncate_inode() to do the 282 * work. 283 */ 284 struct vnode *vp; 285 struct vmnt *vmp; 286 int r; 287 char fullpath[PATH_MAX]; 288 struct lookup resolve; 289 off_t length; 290 vir_bytes vname; 291 size_t vname_length; 292 293 vname = job_m_in.m_lc_vfs_truncate.name; 294 vname_length = job_m_in.m_lc_vfs_truncate.len; 295 296 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 297 resolve.l_vmnt_lock = VMNT_READ; 298 resolve.l_vnode_lock = VNODE_WRITE; 299 300 length = job_m_in.m_lc_vfs_truncate.offset; 301 if (length < 0) return(EINVAL); 302 303 /* Temporarily open file */ 304 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code); 305 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 306 307 /* Ask FS to truncate the file */ 308 if ((r = forbidden(fp, vp, W_BIT)) == OK) { 309 /* If the file size does not change, do not make the actual call. This 310 * ensures that the file times are retained when the file size remains 311 * the same, which is a POSIX requirement. 312 */ 313 if (S_ISREG(vp->v_mode) && vp->v_size == length) 314 r = OK; 315 else 316 r = truncate_vnode(vp, length); 317 } 318 319 unlock_vnode(vp); 320 unlock_vmnt(vmp); 321 put_vnode(vp); 322 return(r); 323 } 324 325 /*===========================================================================* 326 * do_ftruncate * 327 *===========================================================================*/ 328 int do_ftruncate(void) 329 { 330 /* As with do_truncate(), truncate_vnode() does the actual work. */ 331 struct filp *rfilp; 332 struct vnode *vp; 333 int r; 334 off_t length; 335 336 scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_truncate.fd; 337 338 length = job_m_in.m_lc_vfs_truncate.offset; 339 if (length < 0) return(EINVAL); 340 341 /* File is already opened; get a vnode pointer from filp */ 342 if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_WRITE)) == NULL) 343 return(err_code); 344 345 vp = rfilp->filp_vno; 346 347 if (!(rfilp->filp_mode & W_BIT)) 348 r = EBADF; 349 else if (S_ISREG(vp->v_mode) && vp->v_size == length) 350 /* If the file size does not change, do not make the actual call. This 351 * ensures that the file times are retained when the file size remains 352 * the same, which is a POSIX requirement. 353 */ 354 r = OK; 355 else 356 r = truncate_vnode(vp, length); 357 358 unlock_filp(rfilp); 359 return(r); 360 } 361 362 363 /*===========================================================================* 364 * truncate_vnode * 365 *===========================================================================*/ 366 int truncate_vnode(vp, newsize) 367 struct vnode *vp; 368 off_t newsize; 369 { 370 /* Truncate a regular file or a pipe */ 371 int r; 372 373 assert(tll_locked_by_me(&vp->v_lock)); 374 if (!S_ISREG(vp->v_mode) && !S_ISFIFO(vp->v_mode)) return(EINVAL); 375 376 /* We must not compare the old and the new size here: this function may be 377 * called for open(2), which requires an update to the file times if O_TRUNC 378 * is given, even if the file size remains the same. 379 */ 380 if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) == OK) 381 vp->v_size = newsize; 382 return(r); 383 } 384 385 386 /*===========================================================================* 387 * do_slink * 388 *===========================================================================*/ 389 int do_slink(void) 390 { 391 /* Perform the symlink(name1, name2) system call. */ 392 int r; 393 struct vnode *vp; 394 struct vmnt *vmp; 395 char fullpath[PATH_MAX]; 396 struct lookup resolve; 397 vir_bytes vname1, vname2; 398 size_t vname1_length, vname2_length; 399 400 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 401 resolve.l_vmnt_lock = VMNT_WRITE; 402 resolve.l_vnode_lock = VNODE_WRITE; 403 404 vname1 = job_m_in.m_lc_vfs_link.name1; 405 vname1_length = job_m_in.m_lc_vfs_link.len1; 406 vname2 = job_m_in.m_lc_vfs_link.name2; 407 vname2_length = job_m_in.m_lc_vfs_link.len2; 408 409 if (vname1_length <= 1) return(ENOENT); 410 if (vname1_length >= _POSIX_SYMLINK_MAX) return(ENAMETOOLONG); 411 412 /* Get dir inode of 'name2' */ 413 if (fetch_name(vname2, vname2_length, fullpath) != OK) return(err_code); 414 if ((vp = last_dir(&resolve, fp)) == NULL) return(err_code); 415 if ((r = forbidden(fp, vp, W_BIT|X_BIT)) == OK) { 416 r = req_slink(vp->v_fs_e, vp->v_inode_nr, fullpath, who_e, 417 vname1, vname1_length - 1, fp->fp_effuid, 418 fp->fp_effgid); 419 } 420 421 unlock_vnode(vp); 422 unlock_vmnt(vmp); 423 put_vnode(vp); 424 425 return(r); 426 } 427 428 /*===========================================================================* 429 * rdlink_direct * 430 *===========================================================================*/ 431 int rdlink_direct(orig_path, link_path, rfp) 432 char *orig_path; 433 char link_path[PATH_MAX]; /* should have length PATH_MAX */ 434 struct fproc *rfp; 435 { 436 /* Perform a readlink()-like call from within the VFS */ 437 int r; 438 struct vnode *vp; 439 struct vmnt *vmp; 440 struct lookup resolve; 441 442 lookup_init(&resolve, link_path, PATH_RET_SYMLINK, &vmp, &vp); 443 resolve.l_vmnt_lock = VMNT_READ; 444 resolve.l_vnode_lock = VNODE_READ; 445 446 /* Temporarily open the file containing the symbolic link. Use link_path 447 * for temporary storage to keep orig_path untouched. */ 448 strncpy(link_path, orig_path, PATH_MAX); /* PATH_MAX includes '\0' */ 449 link_path[PATH_MAX - 1] = '\0'; 450 if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code); 451 452 /* Make sure this is a symbolic link */ 453 if (!S_ISLNK(vp->v_mode)) 454 r = EINVAL; 455 else 456 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, NONE, (vir_bytes) link_path, 457 PATH_MAX - 1, 1); 458 459 if (r > 0) link_path[r] = '\0'; /* Terminate string when succesful */ 460 461 unlock_vnode(vp); 462 unlock_vmnt(vmp); 463 put_vnode(vp); 464 465 return r; 466 } 467 468 /*===========================================================================* 469 * do_rdlink * 470 *===========================================================================*/ 471 int do_rdlink(void) 472 { 473 /* Perform the readlink(name, buf, bufsize) system call. */ 474 int r; 475 struct vnode *vp; 476 struct vmnt *vmp; 477 char fullpath[PATH_MAX]; 478 struct lookup resolve; 479 vir_bytes vname; 480 size_t vname_length, buf_size; 481 vir_bytes buf; 482 483 vname = job_m_in.m_lc_vfs_readlink.name; 484 vname_length = job_m_in.m_lc_vfs_readlink.namelen; 485 buf = job_m_in.m_lc_vfs_readlink.buf; 486 buf_size = job_m_in.m_lc_vfs_readlink.bufsize; 487 if (buf_size > SSIZE_MAX) return(EINVAL); 488 489 lookup_init(&resolve, fullpath, PATH_RET_SYMLINK, &vmp, &vp); 490 resolve.l_vmnt_lock = VMNT_READ; 491 resolve.l_vnode_lock = VNODE_READ; 492 493 /* Temporarily open the file containing the symbolic link */ 494 if (fetch_name(vname, vname_length, fullpath) != OK) return(err_code); 495 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 496 497 /* Make sure this is a symbolic link */ 498 if (!S_ISLNK(vp->v_mode)) 499 r = EINVAL; 500 else 501 r = req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, buf, buf_size, 0); 502 503 unlock_vnode(vp); 504 unlock_vmnt(vmp); 505 put_vnode(vp); 506 507 return(r); 508 } 509