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