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