|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1982, 1986, 1989 Regents of the University of California. ! 3: * All rights reserved. ! 4: * ! 5: * Redistribution is only permitted until one year after the first shipment ! 6: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and ! 7: * binary forms are permitted provided that: (1) source distributions retain ! 8: * this entire copyright notice and comment, and (2) distributions including ! 9: * binaries display the following acknowledgement: This product includes ! 10: * software developed by the University of California, Berkeley and its ! 11: * contributors'' in the documentation or other materials provided with the ! 12: * distribution and in all advertising materials mentioning features or use ! 13: * of this software. Neither the name of the University nor the names of ! 14: * its contributors may be used to endorse or promote products derived from ! 15: * this software without specific prior written permission. ! 16: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 19: * ! 20: * @(#)vfs_lookup.c 7.22 (Berkeley) 6/28/90 ! 21: */ ! 22: ! 23: #include "param.h" ! 24: #include "time.h" ! 25: #include "namei.h" ! 26: #include "vnode.h" ! 27: #include "mount.h" ! 28: #include "errno.h" ! 29: #include "malloc.h" ! 30: ! 31: #ifdef KTRACE ! 32: #include "user.h" ! 33: #include "proc.h" ! 34: #include "ktrace.h" ! 35: #endif ! 36: ! 37: /* ! 38: * Convert a pathname into a pointer to a locked inode. ! 39: * This is a very central and rather complicated routine. ! 40: * ! 41: * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on ! 42: * whether the name is to be looked up, created, renamed, or deleted. ! 43: * When CREATE, RENAME, or DELETE is specified, information usable in ! 44: * creating, renaming, or deleting a directory entry may be calculated. ! 45: * If flag has LOCKPARENT or'ed into it and the target of the pathname ! 46: * exists, namei returns both the target and its parent directory locked. ! 47: * When creating or renaming and LOCKPARENT is specified, the target may not ! 48: * be ".". When deleting and LOCKPARENT is specified, the target may be ".". ! 49: * ! 50: * The FOLLOW flag is set when symbolic links are to be followed ! 51: * when they occur at the end of the name translation process. ! 52: * Symbolic links are always followed for all other pathname ! 53: * components other than the last. ! 54: * ! 55: * The segflg defines whether the name is to be copied from user ! 56: * space or kernel space. ! 57: * ! 58: * Overall outline of namei: ! 59: * ! 60: * copy in name ! 61: * get starting directory ! 62: * dirloop: ! 63: * copy next component of name to ndp->ni_dent ! 64: * handle degenerate case where name is null string ! 65: * if .. and on mounted filesys, find parent ! 66: * call lookup routine for next component name ! 67: * directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set ! 68: * component vnode returned in ni_vp (if it exists), locked. ! 69: * if symbolic link, massage name in buffer and continue at dirloop ! 70: * if result inode is mounted on, find mounted on vnode ! 71: * if more components of name, do next level at dirloop ! 72: * return the answer in ni_vp as locked vnode; ! 73: * if LOCKPARENT set, return locked parent in ni_dvp ! 74: * ! 75: * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked. ! 76: */ ! 77: namei(ndp) ! 78: register struct nameidata *ndp; ! 79: { ! 80: register char *cp; /* pointer into pathname argument */ ! 81: register struct vnode *dp = 0; /* the directory we are searching */ ! 82: register int i; /* Temp counter */ ! 83: struct vnode *tdp; /* saved dp */ ! 84: struct mount *mp; /* mount table entry */ ! 85: int docache; /* == 0 do not cache last component */ ! 86: int flag; /* LOOKUP, CREATE, RENAME or DELETE */ ! 87: int wantparent; /* 1 => wantparent or lockparent flag */ ! 88: int lockparent; /* 1 => lockparent flag */ ! 89: int getbuf; /* 1 => Malloc a pathname buffer */ ! 90: int rdonly; /* mounted read-only flag bit(s) */ ! 91: int error = 0; ! 92: ! 93: /* ! 94: * Setup: break out flag bits into variables. ! 95: */ ! 96: ndp->ni_dvp = NULL; ! 97: flag = ndp->ni_nameiop & OPFLAG; ! 98: wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT); ! 99: lockparent = ndp->ni_nameiop & LOCKPARENT; ! 100: docache = (ndp->ni_nameiop & NOCACHE) ^ NOCACHE; ! 101: getbuf = (ndp->ni_nameiop & HASBUF) ^ HASBUF; ! 102: if (flag == DELETE || wantparent) ! 103: docache = 0; ! 104: rdonly = MNT_RDONLY; ! 105: if (ndp->ni_nameiop & REMOTE) ! 106: rdonly |= MNT_EXRDONLY; ! 107: /* ! 108: * Get a buffer for the name to be translated, and copy the ! 109: * name into the buffer. ! 110: */ ! 111: if (getbuf) { ! 112: MALLOC(ndp->ni_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); ! 113: if (ndp->ni_segflg == UIO_SYSSPACE) ! 114: error = copystr(ndp->ni_dirp, ndp->ni_pnbuf, ! 115: MAXPATHLEN, &ndp->ni_pathlen); ! 116: else ! 117: error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf, ! 118: MAXPATHLEN, &ndp->ni_pathlen); ! 119: if (error) { ! 120: free(ndp->ni_pnbuf, M_NAMEI); ! 121: ndp->ni_vp = NULL; ! 122: return (error); ! 123: } ! 124: ndp->ni_ptr = ndp->ni_pnbuf; ! 125: } ! 126: ndp->ni_loopcnt = 0; ! 127: dp = ndp->ni_cdir; ! 128: VREF(dp); ! 129: #ifdef KTRACE ! 130: if (KTRPOINT(u.u_procp, KTR_NAMEI)) ! 131: ktrnamei(u.u_procp->p_tracep, ndp->ni_pnbuf); ! 132: #endif ! 133: ! 134: start: ! 135: /* ! 136: * Get starting directory. ! 137: * Done at start of translation and after symbolic link. ! 138: */ ! 139: if (*ndp->ni_ptr == '/') { ! 140: vrele(dp); ! 141: while (*ndp->ni_ptr == '/') { ! 142: ndp->ni_ptr++; ! 143: ndp->ni_pathlen--; ! 144: } ! 145: if ((dp = ndp->ni_rdir) == NULL) ! 146: dp = rootdir; ! 147: VREF(dp); ! 148: } ! 149: VOP_LOCK(dp); ! 150: ndp->ni_endoff = 0; ! 151: ! 152: /* ! 153: * We come to dirloop to search a new directory. ! 154: */ ! 155: dirloop: ! 156: /* ! 157: * Copy next component of name to ndp->ni_dent. ! 158: * XXX kern_exec looks at d_name ! 159: * ??? The ni_hash value may be useful for vfs_cache ! 160: * XXX There must be the last component of the filename left ! 161: * somewhere accessible via. ndp for NFS (and any other stateless file ! 162: * systems) in case they are doing a CREATE. The "Towards a..." noted ! 163: * that ni_ptr would be left pointing to the last component, but since ! 164: * the ni_pnbuf gets free'd, that is not a good idea. ! 165: */ ! 166: if (getbuf) { ! 167: ndp->ni_hash = 0; ! 168: for (cp = ndp->ni_ptr, i = 0; *cp != 0 && *cp != '/'; cp++) { ! 169: if (i >= MAXNAMLEN) { ! 170: error = ENAMETOOLONG; ! 171: goto bad; ! 172: } ! 173: if (*cp & 0200) ! 174: if ((*cp&0377) == ('/'|0200) || ! 175: flag != DELETE) { ! 176: error = EINVAL; ! 177: goto bad; ! 178: } ! 179: ndp->ni_dent.d_name[i++] = *cp; ! 180: ndp->ni_hash += (unsigned char)*cp * i; ! 181: } ! 182: ndp->ni_namelen = i; ! 183: ndp->ni_dent.d_namlen = i; ! 184: ndp->ni_dent.d_name[i] = '\0'; ! 185: ndp->ni_pathlen -= i; ! 186: ndp->ni_next = cp; ! 187: #ifdef NAMEI_DIAGNOSTIC ! 188: printf("{%s}: ", ndp->ni_dent.d_name); ! 189: #endif ! 190: } ! 191: cp = ndp->ni_next; ! 192: ndp->ni_makeentry = 1; ! 193: if (*cp == '\0' && docache == 0) ! 194: ndp->ni_makeentry = 0; ! 195: ndp->ni_isdotdot = (ndp->ni_namelen == 2 && ! 196: ndp->ni_dent.d_name[1] == '.' && ndp->ni_dent.d_name[0] == '.'); ! 197: ! 198: /* ! 199: * Check for degenerate name (e.g. / or "") ! 200: * which is a way of talking about a directory, ! 201: * e.g. like "/." or ".". ! 202: */ ! 203: if (ndp->ni_ptr[0] == '\0') { ! 204: if (flag != LOOKUP || wantparent) { ! 205: error = EISDIR; ! 206: goto bad; ! 207: } ! 208: if (getbuf) ! 209: free(ndp->ni_pnbuf, M_NAMEI); ! 210: if (!(ndp->ni_nameiop & LOCKLEAF)) ! 211: VOP_UNLOCK(dp); ! 212: ndp->ni_vp = dp; ! 213: return (0); ! 214: } ! 215: ! 216: /* ! 217: * Handle "..": two special cases. ! 218: * 1. If at root directory (e.g. after chroot) ! 219: * then ignore it so can't get out. ! 220: * 2. If this vnode is the root of a mounted ! 221: * file system, then replace it with the ! 222: * vnode which was mounted on so we take the ! 223: * .. in the other file system. ! 224: */ ! 225: if (ndp->ni_isdotdot) { ! 226: for (;;) { ! 227: if (dp == ndp->ni_rdir || dp == rootdir) { ! 228: ndp->ni_dvp = dp; ! 229: ndp->ni_vp = dp; ! 230: VREF(dp); ! 231: goto nextname; ! 232: } ! 233: if ((dp->v_flag & VROOT) == 0 || ! 234: (ndp->ni_nameiop & NOCROSSMOUNT)) ! 235: break; ! 236: tdp = dp; ! 237: dp = dp->v_mount->mnt_vnodecovered; ! 238: vput(tdp); ! 239: VREF(dp); ! 240: VOP_LOCK(dp); ! 241: } ! 242: } ! 243: ! 244: /* ! 245: * We now have a segment name to search for, and a directory to search. ! 246: */ ! 247: if (error = VOP_LOOKUP(dp, ndp)) { ! 248: if (ndp->ni_vp != NULL) ! 249: panic("leaf should be empty"); ! 250: #ifdef NAMEI_DIAGNOSTIC ! 251: printf("not found\n"); ! 252: #endif ! 253: if (flag == LOOKUP || flag == DELETE || ! 254: error != ENOENT || *cp != 0) ! 255: goto bad; ! 256: /* ! 257: * If creating and at end of pathname, then can consider ! 258: * allowing file to be created. ! 259: */ ! 260: if (ndp->ni_dvp->v_mount->mnt_flag & rdonly) { ! 261: error = EROFS; ! 262: goto bad; ! 263: } ! 264: /* ! 265: * We return with ni_vp NULL to indicate that the entry ! 266: * doesn't currently exist, leaving a pointer to the ! 267: * (possibly locked) directory inode in ndp->ni_dvp. ! 268: */ ! 269: if (getbuf) ! 270: FREE(ndp->ni_pnbuf, M_NAMEI); ! 271: return (0); /* should this be ENOENT? */ ! 272: } ! 273: #ifdef NAMEI_DIAGNOSTIC ! 274: printf("found\n"); ! 275: #endif ! 276: ! 277: /* ! 278: * Check for symbolic link ! 279: */ ! 280: dp = ndp->ni_vp; ! 281: if ((dp->v_type == VLNK) && ! 282: ((ndp->ni_nameiop & FOLLOW) || *ndp->ni_next == '/')) { ! 283: struct iovec aiov; ! 284: struct uio auio; ! 285: int linklen; ! 286: ! 287: if (!getbuf) ! 288: panic("namei: unexpected symlink"); ! 289: if (++ndp->ni_loopcnt > MAXSYMLINKS) { ! 290: error = ELOOP; ! 291: goto bad2; ! 292: } ! 293: if (ndp->ni_pathlen > 1) ! 294: MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); ! 295: else ! 296: cp = ndp->ni_pnbuf; ! 297: aiov.iov_base = cp; ! 298: aiov.iov_len = MAXPATHLEN; ! 299: auio.uio_iov = &aiov; ! 300: auio.uio_iovcnt = 1; ! 301: auio.uio_offset = 0; ! 302: auio.uio_rw = UIO_READ; ! 303: auio.uio_segflg = UIO_SYSSPACE; ! 304: auio.uio_resid = MAXPATHLEN; ! 305: if (error = VOP_READLINK(dp, &auio, ndp->ni_cred)) { ! 306: if (ndp->ni_pathlen > 1) ! 307: free(cp, M_NAMEI); ! 308: goto bad2; ! 309: } ! 310: linklen = MAXPATHLEN - auio.uio_resid; ! 311: if (linklen + ndp->ni_pathlen >= MAXPATHLEN) { ! 312: if (ndp->ni_pathlen > 1) ! 313: free(cp, M_NAMEI); ! 314: error = ENAMETOOLONG; ! 315: goto bad2; ! 316: } ! 317: if (ndp->ni_pathlen > 1) { ! 318: bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen); ! 319: FREE(ndp->ni_pnbuf, M_NAMEI); ! 320: ndp->ni_pnbuf = cp; ! 321: } else ! 322: ndp->ni_pnbuf[linklen] = '\0'; ! 323: ndp->ni_ptr = cp; ! 324: vput(dp); ! 325: dp = ndp->ni_dvp; ! 326: if (lockparent && ndp->ni_pathlen == 1) ! 327: VOP_UNLOCK(dp); ! 328: ndp->ni_pathlen += linklen; ! 329: goto start; ! 330: } ! 331: ! 332: /* ! 333: * Check to see if the vnode has been mounted on; ! 334: * if so find the root of the mounted file system. ! 335: */ ! 336: mntloop: ! 337: while (dp->v_type == VDIR && (mp = dp->v_mountedhere) && ! 338: (ndp->ni_nameiop & NOCROSSMOUNT) == 0) { ! 339: while(mp->mnt_flag & MNT_MLOCK) { ! 340: mp->mnt_flag |= MNT_MWAIT; ! 341: sleep((caddr_t)mp, PVFS); ! 342: goto mntloop; ! 343: } ! 344: error = VFS_ROOT(dp->v_mountedhere, &tdp); ! 345: if (error) ! 346: goto bad2; ! 347: vput(dp); ! 348: ndp->ni_vp = dp = tdp; ! 349: } ! 350: ! 351: nextname: ! 352: /* ! 353: * Not a symbolic link. If more pathname, ! 354: * continue at next component, else return. ! 355: */ ! 356: ndp->ni_ptr = ndp->ni_next; ! 357: if (*ndp->ni_ptr == '/') { ! 358: while (*ndp->ni_ptr == '/') { ! 359: ndp->ni_ptr++; ! 360: ndp->ni_pathlen--; ! 361: } ! 362: vrele(ndp->ni_dvp); ! 363: goto dirloop; ! 364: } ! 365: /* ! 366: * Check for read-only file systems. ! 367: */ ! 368: if (flag == DELETE || flag == RENAME) { ! 369: /* ! 370: * Disallow directory write attempts on read-only ! 371: * file systems. ! 372: */ ! 373: if ((dp->v_mount->mnt_flag & rdonly) || ! 374: (wantparent && (ndp->ni_dvp->v_mount->mnt_flag & rdonly))) { ! 375: error = EROFS; ! 376: goto bad2; ! 377: } ! 378: } ! 379: if (!wantparent) ! 380: vrele(ndp->ni_dvp); ! 381: if ((ndp->ni_nameiop & LOCKLEAF) == 0) ! 382: VOP_UNLOCK(dp); ! 383: if (getbuf) ! 384: FREE(ndp->ni_pnbuf, M_NAMEI); ! 385: return (0); ! 386: ! 387: bad2: ! 388: if (lockparent && *ndp->ni_next == '\0') ! 389: VOP_UNLOCK(ndp->ni_dvp); ! 390: vrele(ndp->ni_dvp); ! 391: bad: ! 392: vput(dp); ! 393: ndp->ni_vp = NULL; ! 394: if (getbuf) ! 395: FREE(ndp->ni_pnbuf, M_NAMEI); ! 396: return (error); ! 397: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.