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