1 /* lookup() is the main routine that controls the path name lookup. It 2 * handles mountpoints and symbolic links. The actual lookup requests 3 * are sent through the req_lookup wrapper function. 4 */ 5 6 #include "fs.h" 7 #include <string.h> 8 #include <minix/callnr.h> 9 #include <minix/com.h> 10 #include <minix/const.h> 11 #include <minix/endpoint.h> 12 #include <stddef.h> 13 #include <unistd.h> 14 #include <assert.h> 15 #include <minix/vfsif.h> 16 #include <sys/param.h> 17 #include <sys/stat.h> 18 #include <sys/un.h> 19 #include <sys/dirent.h> 20 #include "vmnt.h" 21 #include "vnode.h" 22 #include "path.h" 23 24 /* Set to following define to 1 if you really want to use the POSIX definition 25 * (IEEE Std 1003.1, 2004) of pathname resolution. POSIX requires pathnames 26 * with a traling slash (and that do not entirely consist of slash characters) 27 * to be treated as if a single dot is appended. This means that for example 28 * mkdir("dir/", ...) and rmdir("dir/") will fail because the call tries to 29 * create or remove the directory '.'. Historically, Unix systems just ignore 30 * trailing slashes. 31 */ 32 #define DO_POSIX_PATHNAME_RES 0 33 34 static int lookup(struct vnode *dirp, struct lookup *resolve, 35 node_details_t *node, struct fproc *rfp); 36 static int check_perms(endpoint_t ep, cp_grant_id_t io_gr, size_t 37 pathlen); 38 39 /*===========================================================================* 40 * advance * 41 *===========================================================================*/ 42 struct vnode *advance(dirp, resolve, rfp) 43 struct vnode *dirp; 44 struct lookup *resolve; 45 struct fproc *rfp; 46 { 47 /* Resolve a path name starting at dirp to a vnode. */ 48 int r; 49 int do_downgrade = 1; 50 struct vnode *new_vp, *vp; 51 struct vmnt *vmp; 52 struct node_details res = {0,0,0,0,0,0,0}; 53 tll_access_t initial_locktype; 54 55 assert(dirp); 56 assert(resolve->l_vnode_lock != TLL_NONE); 57 assert(resolve->l_vmnt_lock != TLL_NONE); 58 59 if (resolve->l_vnode_lock == VNODE_READ) 60 initial_locktype = VNODE_OPCL; 61 else 62 initial_locktype = resolve->l_vnode_lock; 63 64 /* Get a free vnode and lock it */ 65 if ((new_vp = get_free_vnode()) == NULL) return(NULL); 66 lock_vnode(new_vp, initial_locktype); 67 68 /* Lookup vnode belonging to the file. */ 69 if ((r = lookup(dirp, resolve, &res, rfp)) != OK) { 70 err_code = r; 71 unlock_vnode(new_vp); 72 return(NULL); 73 } 74 75 /* Check whether we already have a vnode for that file */ 76 if ((vp = find_vnode(res.fs_e, res.inode_nr)) != NULL) { 77 unlock_vnode(new_vp); /* Don't need this anymore */ 78 do_downgrade = (lock_vnode(vp, initial_locktype) != EBUSY); 79 80 /* Unfortunately, by the time we get the lock, another thread might've 81 * rid of the vnode (e.g., find_vnode found the vnode while a 82 * req_putnode was being processed). */ 83 if (vp->v_ref_count == 0) { /* vnode vanished! */ 84 /* As the lookup before increased the usage counters in the FS, 85 * we can simply set the usage counters to 1 and proceed as 86 * normal, because the putnode resulted in a use count of 1 in 87 * the FS. Other data is still valid, because the vnode was 88 * marked as pending lock, so get_free_vnode hasn't 89 * reinitialized the vnode yet. */ 90 vp->v_fs_count = 1; 91 if (vp->v_mapfs_e != NONE) vp->v_mapfs_count = 1; 92 } else { 93 vp->v_fs_count++; /* We got a reference from the FS */ 94 } 95 96 } else { 97 /* Vnode not found, fill in the free vnode's fields */ 98 99 new_vp->v_fs_e = res.fs_e; 100 new_vp->v_inode_nr = res.inode_nr; 101 new_vp->v_mode = res.fmode; 102 new_vp->v_size = res.fsize; 103 new_vp->v_uid = res.uid; 104 new_vp->v_gid = res.gid; 105 new_vp->v_sdev = res.dev; 106 107 if( (vmp = find_vmnt(new_vp->v_fs_e)) == NULL) 108 panic("advance: vmnt not found"); 109 110 new_vp->v_vmnt = vmp; 111 new_vp->v_dev = vmp->m_dev; 112 new_vp->v_fs_count = 1; 113 114 vp = new_vp; 115 } 116 117 dup_vnode(vp); 118 if (do_downgrade) { 119 /* Only downgrade a lock if we managed to lock it in the first place */ 120 *(resolve->l_vnode) = vp; 121 122 if (initial_locktype != resolve->l_vnode_lock) 123 tll_downgrade(&vp->v_lock); 124 125 #if LOCK_DEBUG 126 if (resolve->l_vnode_lock == VNODE_READ) 127 fp->fp_vp_rdlocks++; 128 #endif 129 } 130 131 return(vp); 132 } 133 134 /*===========================================================================* 135 * eat_path * 136 *===========================================================================*/ 137 struct vnode *eat_path(resolve, rfp) 138 struct lookup *resolve; 139 struct fproc *rfp; 140 { 141 /* Resolve path to a vnode. advance does the actual work. */ 142 struct vnode *start_dir; 143 144 start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd : rfp->fp_wd); 145 return advance(start_dir, resolve, rfp); 146 } 147 148 /*===========================================================================* 149 * last_dir * 150 *===========================================================================*/ 151 struct vnode *last_dir(resolve, rfp) 152 struct lookup *resolve; 153 struct fproc *rfp; 154 { 155 /* Parse a path, as far as the last directory, fetch the vnode 156 * for the last directory into the vnode table, and return a pointer to the 157 * vnode. In addition, return the final component of the path in 'string'. If 158 * the last directory can't be opened, return NULL and the reason for 159 * failure in 'err_code'. We can't parse component by component as that would 160 * be too expensive. Alternatively, we cut off the last component of the path, 161 * and parse the path up to the penultimate component. 162 */ 163 164 size_t len; 165 char *cp; 166 char dir_entry[NAME_MAX+1]; 167 struct vnode *start_dir, *res_vp, *sym_vp, *sym_vp_l, *loop_start; 168 struct vmnt *sym_vmp = NULL; 169 int r, symloop = 0, ret_on_symlink = 0; 170 struct lookup symlink; 171 172 *resolve->l_vnode = NULL; 173 *resolve->l_vmp = NULL; 174 loop_start = NULL; 175 sym_vp = NULL; 176 177 ret_on_symlink = !!(resolve->l_flags & PATH_RET_SYMLINK); 178 179 do { 180 /* Is the path absolute or relative? Initialize 'start_dir' 181 * accordingly. Use loop_start in case we're looping. 182 */ 183 if (loop_start != NULL) 184 start_dir = loop_start; 185 else 186 start_dir = (resolve->l_path[0] == '/' ? rfp->fp_rd:rfp->fp_wd); 187 188 len = strlen(resolve->l_path); 189 190 /* If path is empty, return ENOENT. */ 191 if (len == 0) { 192 err_code = ENOENT; 193 res_vp = NULL; 194 break; 195 } 196 197 #if !DO_POSIX_PATHNAME_RES 198 /* Remove trailing slashes */ 199 while (len > 1 && resolve->l_path[len-1] == '/') { 200 len--; 201 resolve->l_path[len]= '\0'; 202 } 203 #endif 204 205 cp = strrchr(resolve->l_path, '/'); 206 if (cp == NULL) { 207 /* Just an entry in the current working directory. Prepend 208 * "./" in front of the path and resolve it. 209 */ 210 if (strlcpy(dir_entry, resolve->l_path, NAME_MAX+1) >= NAME_MAX + 1) { 211 err_code = ENAMETOOLONG; 212 res_vp = NULL; 213 break; 214 } 215 dir_entry[NAME_MAX] = '\0'; 216 resolve->l_path[0] = '.'; 217 resolve->l_path[1] = '\0'; 218 } else if (cp[1] == '\0') { 219 /* Path ends in a slash. The directory entry is '.' */ 220 strlcpy(dir_entry, ".", NAME_MAX+1); 221 } else { 222 /* A path name for the directory and a directory entry */ 223 if (strlcpy(dir_entry, cp+1, NAME_MAX+1) >= NAME_MAX + 1) { 224 err_code = ENAMETOOLONG; 225 res_vp = NULL; 226 break; 227 } 228 cp[1] = '\0'; 229 dir_entry[NAME_MAX] = '\0'; 230 } 231 232 /* Remove trailing slashes */ 233 while (cp > resolve->l_path && cp[0] == '/') { 234 cp[0]= '\0'; 235 cp--; 236 } 237 238 /* Resolve up to and including the last directory of the path. Turn off 239 * PATH_RET_SYMLINK, because we do want to follow the symlink in this 240 * case. That is, the flag is meant for the actual filename of the path, 241 * not the last directory. 242 */ 243 resolve->l_flags &= ~PATH_RET_SYMLINK; 244 if ((res_vp = advance(start_dir, resolve, rfp)) == NULL) { 245 break; 246 } 247 248 /* If the directory entry is not a symlink we're done now. If it is a 249 * symlink, then we're not at the last directory, yet. */ 250 251 /* Copy the directory entry back to user_fullpath */ 252 strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1); 253 254 /* Look up the directory entry, but do not follow the symlink when it 255 * is one. Note: depending on the previous advance, we might not be 256 * able to lock the resulting vnode. For example, when we look up "./." 257 * and request a VNODE_WRITE lock on the result, then the previous 258 * advance has "./" locked. The next advance to "." will try to lock 259 * the same vnode with a VNODE_READ lock, and fail. When that happens, 260 * sym_vp_l will be NULL and we must not unlock the vnode. If we would 261 * unlock, we actually unlock the vnode locked by the previous advance. 262 */ 263 lookup_init(&symlink, resolve->l_path, 264 resolve->l_flags|PATH_RET_SYMLINK, &sym_vmp, &sym_vp_l); 265 symlink.l_vmnt_lock = VMNT_READ; 266 symlink.l_vnode_lock = VNODE_READ; 267 sym_vp = advance(res_vp, &symlink, rfp); 268 269 if (sym_vp == NULL) break; 270 271 if (S_ISLNK(sym_vp->v_mode)) { 272 /* Last component is a symlink, but if we've been asked to not 273 * resolve it, return now. 274 */ 275 if (ret_on_symlink) { 276 break; 277 } 278 279 r = req_rdlink(sym_vp->v_fs_e, sym_vp->v_inode_nr, NONE, 280 (vir_bytes) resolve->l_path, PATH_MAX - 1, 1); 281 282 if (r < 0) { 283 /* Failed to read link */ 284 err_code = r; 285 unlock_vnode(res_vp); 286 unlock_vmnt(*resolve->l_vmp); 287 put_vnode(res_vp); 288 *resolve->l_vmp = NULL; 289 *resolve->l_vnode = NULL; 290 res_vp = NULL; 291 break; 292 } 293 resolve->l_path[r] = '\0'; 294 295 if (strrchr(resolve->l_path, '/') != NULL) { 296 if (sym_vp_l != NULL) 297 unlock_vnode(sym_vp); 298 unlock_vmnt(*resolve->l_vmp); 299 if (sym_vmp != NULL) 300 unlock_vmnt(sym_vmp); 301 *resolve->l_vmp = NULL; 302 put_vnode(sym_vp); 303 sym_vp = NULL; 304 305 symloop++; 306 307 /* Relative symlinks are relative to res_vp, not cwd */ 308 if (resolve->l_path[0] != '/') { 309 loop_start = res_vp; 310 } else { 311 /* Absolute symlink, forget about res_vp */ 312 unlock_vnode(res_vp); 313 put_vnode(res_vp); 314 } 315 316 continue; 317 } 318 } else { 319 symloop = 0; /* Not a symlink, so restart counting */ 320 321 /* If we're crossing a mount point, return root node of mount 322 * point on which the file resides. That's the 'real' last 323 * dir that holds the file we're looking for. 324 */ 325 if (sym_vp->v_fs_e != res_vp->v_fs_e) { 326 assert(sym_vmp != NULL); 327 328 /* Unlock final file, it might have wrong lock types */ 329 if (sym_vp_l != NULL) 330 unlock_vnode(sym_vp); 331 unlock_vmnt(sym_vmp); 332 put_vnode(sym_vp); 333 sym_vp = NULL; 334 335 /* Also unlock and release erroneous result */ 336 unlock_vnode(*resolve->l_vnode); 337 unlock_vmnt(*resolve->l_vmp); 338 put_vnode(res_vp); 339 340 /* Relock vmnt and vnode with correct lock types */ 341 lock_vmnt(sym_vmp, resolve->l_vmnt_lock); 342 lock_vnode(sym_vmp->m_root_node, resolve->l_vnode_lock); 343 res_vp = sym_vmp->m_root_node; 344 dup_vnode(res_vp); 345 *resolve->l_vnode = res_vp; 346 *resolve->l_vmp = sym_vmp; 347 348 /* We've effectively resolved the final component, so 349 * change it to current directory to prevent future 350 * 'advances' of returning erroneous results. 351 */ 352 strlcpy(dir_entry, ".", NAME_MAX+1); 353 } 354 } 355 break; 356 } while (symloop < _POSIX_SYMLOOP_MAX); 357 358 if (symloop >= _POSIX_SYMLOOP_MAX) { 359 err_code = ELOOP; 360 res_vp = NULL; 361 } 362 363 if (sym_vp != NULL) { 364 if (sym_vp_l != NULL) { 365 unlock_vnode(sym_vp); 366 } 367 if (sym_vmp != NULL) { 368 unlock_vmnt(sym_vmp); 369 } 370 put_vnode(sym_vp); 371 } 372 373 if (loop_start != NULL) { 374 unlock_vnode(loop_start); 375 put_vnode(loop_start); 376 } 377 378 /* Copy the directory entry back to user_fullpath */ 379 strlcpy(resolve->l_path, dir_entry, NAME_MAX + 1); 380 381 /* Turn PATH_RET_SYMLINK flag back on if it was on */ 382 if (ret_on_symlink) resolve->l_flags |= PATH_RET_SYMLINK; 383 384 return(res_vp); 385 } 386 387 /*===========================================================================* 388 * lookup * 389 *===========================================================================*/ 390 static int lookup(start_node, resolve, result_node, rfp) 391 struct vnode *start_node; 392 struct lookup *resolve; 393 node_details_t *result_node; 394 struct fproc *rfp; 395 { 396 /* Resolve a path name relative to start_node. */ 397 398 int r, symloop; 399 endpoint_t fs_e; 400 size_t path_off, path_left_len; 401 ino_t dir_ino, root_ino; 402 uid_t uid; 403 gid_t gid; 404 struct vnode *dir_vp; 405 struct vmnt *vmp, *vmpres; 406 struct lookup_res res; 407 tll_access_t mnt_lock_type; 408 409 assert(resolve->l_vmp); 410 assert(resolve->l_vnode); 411 412 *(resolve->l_vmp) = vmpres = NULL; /* No vmnt found nor locked yet */ 413 414 /* Empty (start) path? */ 415 if (resolve->l_path[0] == '\0') { 416 result_node->inode_nr = 0; 417 return(ENOENT); 418 } 419 420 if (!rfp->fp_rd || !rfp->fp_wd) { 421 printf("VFS: lookup %d: no rd/wd\n", rfp->fp_endpoint); 422 return(ENOENT); 423 } 424 425 fs_e = start_node->v_fs_e; 426 dir_ino = start_node->v_inode_nr; 427 vmpres = find_vmnt(fs_e); 428 429 if (vmpres == NULL) return(EIO); /* mountpoint vanished? */ 430 431 /* Is the process' root directory on the same partition?, 432 * if so, set the chroot directory too. */ 433 if (rfp->fp_rd->v_dev == rfp->fp_wd->v_dev) 434 root_ino = rfp->fp_rd->v_inode_nr; 435 else 436 root_ino = 0; 437 438 /* Set user and group ids according to the system call */ 439 uid = (job_call_nr == VFS_ACCESS ? rfp->fp_realuid : rfp->fp_effuid); 440 gid = (job_call_nr == VFS_ACCESS ? rfp->fp_realgid : rfp->fp_effgid); 441 442 symloop = 0; /* Number of symlinks seen so far */ 443 444 /* Lock vmnt */ 445 if (resolve->l_vmnt_lock == VMNT_READ) 446 mnt_lock_type = VMNT_WRITE; 447 else 448 mnt_lock_type = resolve->l_vmnt_lock; 449 450 if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) { 451 if (r == EBUSY) /* vmnt already locked */ 452 vmpres = NULL; 453 else 454 return(r); 455 } 456 *(resolve->l_vmp) = vmpres; 457 458 /* Issue the request */ 459 r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp); 460 461 if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) { 462 if (vmpres) unlock_vmnt(vmpres); 463 *(resolve->l_vmp) = NULL; 464 return(r); /* i.e., an error occured */ 465 } 466 467 /* While the response is related to mount control set the 468 * new requests respectively */ 469 while (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) { 470 /* Update user_fullpath to reflect what's left to be parsed. */ 471 path_off = res.char_processed; 472 path_left_len = strlen(&resolve->l_path[path_off]); 473 memmove(resolve->l_path, &resolve->l_path[path_off], path_left_len); 474 resolve->l_path[path_left_len] = '\0'; /* terminate string */ 475 476 /* Update the current value of the symloop counter */ 477 symloop += res.symloop; 478 if (symloop > _POSIX_SYMLOOP_MAX) { 479 if (vmpres) unlock_vmnt(vmpres); 480 *(resolve->l_vmp) = NULL; 481 return(ELOOP); 482 } 483 484 /* Symlink encountered with absolute path */ 485 if (r == ESYMLINK) { 486 dir_vp = rfp->fp_rd; 487 vmp = NULL; 488 } else if (r == EENTERMOUNT) { 489 /* Entering a new partition */ 490 dir_vp = NULL; 491 /* Start node is now the mounted partition's root node */ 492 for (vmp = &vmnt[0]; vmp != &vmnt[NR_MNTS]; ++vmp) { 493 if (vmp->m_dev != NO_DEV && vmp->m_mounted_on) { 494 if (vmp->m_mounted_on->v_inode_nr == res.inode_nr && 495 vmp->m_mounted_on->v_fs_e == res.fs_e) { 496 dir_vp = vmp->m_root_node; 497 break; 498 } 499 } 500 } 501 if (dir_vp == NULL) { 502 printf("VFS: path lookup error; root node not found\n"); 503 if (vmpres) unlock_vmnt(vmpres); 504 *(resolve->l_vmp) = NULL; 505 return(EIO); 506 } 507 } else { 508 /* Climbing up mount */ 509 /* Find the vmnt that represents the partition on 510 * which we "climb up". */ 511 if ((vmp = find_vmnt(res.fs_e)) == NULL) { 512 panic("VFS lookup: can't find parent vmnt"); 513 } 514 515 /* Make sure that the child FS does not feed a bogus path 516 * to the parent FS. That is, when we climb up the tree, we 517 * must've encountered ".." in the path, and that is exactly 518 * what we're going to feed to the parent */ 519 if(strncmp(resolve->l_path, "..", 2) != 0 || 520 (resolve->l_path[2] != '\0' && resolve->l_path[2] != '/')) { 521 printf("VFS: bogus path: %s\n", resolve->l_path); 522 if (vmpres) unlock_vmnt(vmpres); 523 *(resolve->l_vmp) = NULL; 524 return(ENOENT); 525 } 526 527 /* Start node is the vnode on which the partition is 528 * mounted */ 529 dir_vp = vmp->m_mounted_on; 530 } 531 532 /* Set the starting directories inode number and FS endpoint */ 533 fs_e = dir_vp->v_fs_e; 534 dir_ino = dir_vp->v_inode_nr; 535 536 /* Is the process' root directory on the same partition?, 537 * if so, set the chroot directory too. */ 538 if (dir_vp->v_dev == rfp->fp_rd->v_dev) 539 root_ino = rfp->fp_rd->v_inode_nr; 540 else 541 root_ino = 0; 542 543 /* Unlock a previously locked vmnt if locked and lock new vmnt */ 544 if (vmpres) unlock_vmnt(vmpres); 545 vmpres = find_vmnt(fs_e); 546 if (vmpres == NULL) return(EIO); /* mount point vanished? */ 547 if ((r = lock_vmnt(vmpres, mnt_lock_type)) != OK) { 548 if (r == EBUSY) 549 vmpres = NULL; /* Already locked */ 550 else 551 return(r); 552 } 553 *(resolve->l_vmp) = vmpres; 554 555 r = req_lookup(fs_e, dir_ino, root_ino, uid, gid, resolve, &res, rfp); 556 557 if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK) { 558 if (vmpres) unlock_vmnt(vmpres); 559 *(resolve->l_vmp) = NULL; 560 return(r); 561 } 562 } 563 564 if (*(resolve->l_vmp) != NULL && resolve->l_vmnt_lock != mnt_lock_type) { 565 /* downgrade VMNT_WRITE to VMNT_READ */ 566 downgrade_vmnt_lock(*(resolve->l_vmp)); 567 } 568 569 /* Fill in response fields */ 570 result_node->inode_nr = res.inode_nr; 571 result_node->fmode = res.fmode; 572 result_node->fsize = res.fsize; 573 result_node->dev = res.dev; 574 result_node->fs_e = res.fs_e; 575 result_node->uid = res.uid; 576 result_node->gid = res.gid; 577 578 return(r); 579 } 580 581 /*===========================================================================* 582 * lookup_init * 583 *===========================================================================*/ 584 void lookup_init(resolve, path, flags, vmp, vp) 585 struct lookup *resolve; 586 char *path; 587 int flags; 588 struct vmnt **vmp; 589 struct vnode **vp; 590 { 591 assert(vmp != NULL); 592 assert(vp != NULL); 593 594 resolve->l_path = path; 595 resolve->l_flags = flags; 596 resolve->l_vmp = vmp; 597 resolve->l_vnode = vp; 598 resolve->l_vmnt_lock = TLL_NONE; 599 resolve->l_vnode_lock = TLL_NONE; 600 *vmp = NULL; /* Initialize lookup result to NULL */ 601 *vp = NULL; 602 } 603 604 /*===========================================================================* 605 * get_name * 606 *===========================================================================*/ 607 int get_name(dirp, entry, ename) 608 struct vnode *dirp; 609 struct vnode *entry; 610 char ename[NAME_MAX + 1]; 611 { 612 #define DIR_ENTRIES 8 613 #define DIR_ENTRY_SIZE (sizeof(struct dirent) + NAME_MAX) 614 off_t pos, new_pos; 615 int r, consumed, totalbytes, name_len; 616 char buf[DIR_ENTRY_SIZE * DIR_ENTRIES]; 617 struct dirent *cur; 618 619 pos = 0; 620 621 if (!S_ISDIR(dirp->v_mode)) return(EBADF); 622 623 do { 624 r = req_getdents(dirp->v_fs_e, dirp->v_inode_nr, pos, (vir_bytes)buf, 625 sizeof(buf), &new_pos, 1); 626 627 if (r == 0) { 628 return(ENOENT); /* end of entries -- matching inode !found */ 629 } else if (r < 0) { 630 return(r); /* error */ 631 } 632 633 consumed = 0; /* bytes consumed */ 634 totalbytes = r; /* number of bytes to consume */ 635 636 do { 637 cur = (struct dirent *) (buf + consumed); 638 name_len = cur->d_reclen - offsetof(struct dirent, d_name) - 1; 639 640 if(cur->d_name + name_len+1 > &buf[sizeof(buf)]) 641 return(EINVAL); /* Rubbish in dir entry */ 642 if (entry->v_inode_nr == cur->d_fileno) { 643 /* found the entry we were looking for */ 644 int copylen = MIN(name_len + 1, NAME_MAX + 1); 645 if (strlcpy(ename, cur->d_name, copylen) >= copylen) { 646 return(ENAMETOOLONG); 647 } 648 ename[NAME_MAX] = '\0'; 649 return(OK); 650 } 651 652 /* not a match -- move on to the next dirent */ 653 consumed += cur->d_reclen; 654 } while (consumed < totalbytes); 655 656 pos = new_pos; 657 } while (1); 658 } 659 660 /*===========================================================================* 661 * canonical_path * 662 *===========================================================================*/ 663 int canonical_path(orig_path, rfp) 664 char orig_path[PATH_MAX]; 665 struct fproc *rfp; 666 { 667 /* Find canonical path of a given path */ 668 int len = 0; 669 int r, symloop = 0; 670 struct vnode *dir_vp, *parent_dir; 671 struct vmnt *dir_vmp, *parent_vmp; 672 char component[NAME_MAX+1]; /* NAME_MAX does /not/ include '\0' */ 673 char temp_path[PATH_MAX]; 674 struct lookup resolve; 675 676 parent_dir = dir_vp = NULL; 677 parent_vmp = dir_vmp = NULL; 678 strlcpy(temp_path, orig_path, PATH_MAX); 679 temp_path[PATH_MAX - 1] = '\0'; 680 681 /* First resolve path to the last directory holding the file */ 682 do { 683 if (dir_vp) { 684 unlock_vnode(dir_vp); 685 unlock_vmnt(dir_vmp); 686 put_vnode(dir_vp); 687 } 688 689 lookup_init(&resolve, temp_path, PATH_NOFLAGS, &dir_vmp, &dir_vp); 690 resolve.l_vmnt_lock = VMNT_READ; 691 resolve.l_vnode_lock = VNODE_READ; 692 if ((dir_vp = last_dir(&resolve, rfp)) == NULL) return(err_code); 693 694 /* dir_vp points to dir and resolve path now contains only the 695 * filename. 696 */ 697 strlcpy(orig_path, temp_path, NAME_MAX+1); /* Store file name */ 698 699 /* If we're just crossing a mount point, our name has changed to '.' */ 700 if (!strcmp(orig_path, ".")) orig_path[0] = '\0'; 701 702 /* check if the file is a symlink, if so resolve it */ 703 r = rdlink_direct(orig_path, temp_path, rfp); 704 705 if (r <= 0) 706 break; 707 708 /* encountered a symlink -- loop again */ 709 strlcpy(orig_path, temp_path, PATH_MAX); 710 symloop++; 711 } while (symloop < _POSIX_SYMLOOP_MAX); 712 713 if (symloop >= _POSIX_SYMLOOP_MAX) { 714 if (dir_vp) { 715 unlock_vnode(dir_vp); 716 unlock_vmnt(dir_vmp); 717 put_vnode(dir_vp); 718 } 719 return(ELOOP); 720 } 721 722 /* We've got the filename and the actual directory holding the file. From 723 * here we start building up the canonical path by climbing up the tree */ 724 while (dir_vp != rfp->fp_rd) { 725 726 strlcpy(temp_path, "..", NAME_MAX+1); 727 728 /* check if we're at the root node of the file system */ 729 if (dir_vp->v_vmnt->m_root_node == dir_vp) { 730 if (dir_vp->v_vmnt->m_mounted_on == NULL) { 731 /* Bail out, we can't go any higher */ 732 break; 733 } 734 unlock_vnode(dir_vp); 735 unlock_vmnt(dir_vmp); 736 put_vnode(dir_vp); 737 dir_vp = dir_vp->v_vmnt->m_mounted_on; 738 dir_vmp = dir_vp->v_vmnt; 739 if (lock_vmnt(dir_vmp, VMNT_READ) != OK) 740 panic("failed to lock vmnt"); 741 if (lock_vnode(dir_vp, VNODE_READ) != OK) 742 panic("failed to lock vnode"); 743 dup_vnode(dir_vp); 744 } 745 746 lookup_init(&resolve, temp_path, PATH_NOFLAGS, &parent_vmp, 747 &parent_dir); 748 resolve.l_vmnt_lock = VMNT_READ; 749 resolve.l_vnode_lock = VNODE_READ; 750 751 if ((parent_dir = advance(dir_vp, &resolve, rfp)) == NULL) { 752 unlock_vnode(dir_vp); 753 unlock_vmnt(dir_vmp); 754 put_vnode(dir_vp); 755 return(err_code); 756 } 757 758 /* now we have to retrieve the name of the parent directory */ 759 if ((r = get_name(parent_dir, dir_vp, component)) != OK) { 760 unlock_vnode(parent_dir); 761 unlock_vmnt(parent_vmp); 762 unlock_vnode(dir_vp); 763 unlock_vmnt(dir_vmp); 764 put_vnode(parent_dir); 765 put_vnode(dir_vp); 766 return(r); 767 } 768 769 len += strlen(component) + 1; 770 if (len >= PATH_MAX) { 771 /* adding the component to orig_path would exceed PATH_MAX */ 772 unlock_vnode(parent_dir); 773 unlock_vmnt(parent_vmp); 774 unlock_vnode(dir_vp); 775 unlock_vmnt(dir_vmp); 776 put_vnode(parent_dir); 777 put_vnode(dir_vp); 778 return(ENOMEM); 779 } 780 781 /* Store result of component in orig_path. First make space by moving 782 * the contents of orig_path to the right. Move strlen + 1 bytes to 783 * include the terminating '\0'. Move to strlen + 1 bytes to reserve 784 * space for the slash. 785 */ 786 memmove(orig_path+strlen(component)+1, orig_path, strlen(orig_path)+1); 787 /* Copy component into canon_path */ 788 memmove(orig_path, component, strlen(component)); 789 /* Put slash into place */ 790 orig_path[strlen(component)] = '/'; 791 792 /* Store parent_dir result, and continue the loop once more */ 793 unlock_vnode(dir_vp); 794 unlock_vmnt(dir_vmp); 795 put_vnode(dir_vp); 796 dir_vp = parent_dir; 797 dir_vmp = parent_vmp; 798 parent_vmp = NULL; 799 } 800 801 unlock_vmnt(dir_vmp); 802 unlock_vnode(dir_vp); 803 put_vnode(dir_vp); 804 805 /* add the leading slash */ 806 len = strlen(orig_path); 807 if (strlen(orig_path) >= PATH_MAX) return(ENAMETOOLONG); 808 memmove(orig_path+1, orig_path, len + 1 /* include terminating nul */); 809 orig_path[0] = '/'; 810 811 /* remove trailing slash if there is any */ 812 if (len > 1 && orig_path[len] == '/') orig_path[len] = '\0'; 813 814 return(OK); 815 } 816 817 /*===========================================================================* 818 * check_perms * 819 *===========================================================================*/ 820 static int check_perms(ep, io_gr, pathlen) 821 endpoint_t ep; 822 cp_grant_id_t io_gr; 823 size_t pathlen; 824 { 825 int r, slot; 826 struct vnode *vp; 827 struct vmnt *vmp; 828 struct fproc *rfp; 829 char canon_path[PATH_MAX]; 830 struct lookup resolve; 831 struct sockaddr_un sun; 832 833 if (isokendpt(ep, &slot) != OK) return(EINVAL); 834 if (pathlen < sizeof(sun.sun_path) || pathlen >= PATH_MAX) return(EINVAL); 835 836 rfp = &(fproc[slot]); 837 r = sys_safecopyfrom(who_e, io_gr, (vir_bytes) 0, (vir_bytes) canon_path, 838 pathlen); 839 if (r != OK) return(r); 840 canon_path[pathlen] = '\0'; 841 842 /* Turn path into canonical path to the socket file */ 843 if ((r = canonical_path(canon_path, rfp)) != OK) return(r); 844 if (strlen(canon_path) >= pathlen) return(ENAMETOOLONG); 845 846 /* copy canon_path back to the caller */ 847 r = sys_safecopyto(who_e, (cp_grant_id_t) io_gr, (vir_bytes) 0, 848 (vir_bytes) canon_path, pathlen); 849 if (r != OK) return(r); 850 851 /* Now do permissions checking */ 852 lookup_init(&resolve, canon_path, PATH_NOFLAGS, &vmp, &vp); 853 resolve.l_vmnt_lock = VMNT_READ; 854 resolve.l_vnode_lock = VNODE_READ; 855 if ((vp = eat_path(&resolve, rfp)) == NULL) return(err_code); 856 857 /* check permissions */ 858 r = forbidden(rfp, vp, (R_BIT | W_BIT)); 859 860 unlock_vnode(vp); 861 unlock_vmnt(vmp); 862 863 put_vnode(vp); 864 return(r); 865 } 866 867 /*===========================================================================* 868 * do_checkperms * 869 *===========================================================================*/ 870 int do_checkperms(void) 871 { 872 /* This should be replaced by an ACL check. */ 873 if (!super_user) return EPERM; 874 875 return check_perms(job_m_in.m_lsys_vfs_checkperms.endpt, 876 job_m_in.m_lsys_vfs_checkperms.grant, 877 job_m_in.m_lsys_vfs_checkperms.count); 878 } 879