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