Annotation of sys/kern/vfs_getcwd.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: vfs_getcwd.c,v 1.12 2007/08/07 07:41:59 thib Exp $ */
2: /* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
3:
4: /*
5: * Copyright (c) 1999 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Bill Sommerfeld.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: #include <sys/param.h>
41: #include <sys/systm.h>
42: #include <sys/namei.h>
43: #include <sys/filedesc.h>
44: #include <sys/kernel.h>
45: #include <sys/file.h>
46: #include <sys/stat.h>
47: #include <sys/vnode.h>
48: #include <sys/mount.h>
49: #include <sys/proc.h>
50: #include <sys/uio.h>
51: #include <sys/malloc.h>
52: #include <sys/dirent.h>
53: #include <ufs/ufs/dir.h> /* only for DIRBLKSIZ */
54:
55: #include <sys/syscallargs.h>
56:
57: #define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN + 1) + 4)
58:
59: /* Find parent vnode of *lvpp, return in *uvpp */
60: int
61: vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
62: char *bufp, struct proc *p)
63: {
64: int eofflag, tries, dirbuflen, len, reclen, error = 0;
65: off_t off;
66: struct uio uio;
67: struct iovec iov;
68: char *dirbuf = NULL;
69: ino_t fileno;
70: struct vattr va;
71: struct vnode *uvp = NULL;
72: struct vnode *lvp = *lvpp;
73: struct componentname cn;
74:
75: tries = 0;
76:
77: /*
78: * If we want the filename, get some info we need while the
79: * current directory is still locked.
80: */
81: if (bufp != NULL) {
82: error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
83: if (error) {
84: vput(lvp);
85: *lvpp = NULL;
86: *uvpp = NULL;
87: return (error);
88: }
89: }
90:
91: cn.cn_nameiop = LOOKUP;
92: cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
93: cn.cn_proc = p;
94: cn.cn_cred = p->p_ucred;
95: cn.cn_pnbuf = NULL;
96: cn.cn_nameptr = "..";
97: cn.cn_namelen = 2;
98: cn.cn_hash = 0;
99: cn.cn_consume = 0;
100:
101: /* Get parent vnode using lookup of '..' */
102: error = VOP_LOOKUP(lvp, uvpp, &cn);
103: if (error) {
104: vput(lvp);
105: *lvpp = NULL;
106: *uvpp = NULL;
107: return (error);
108: }
109:
110: uvp = *uvpp;
111:
112: /* If we don't care about the pathname, we're done */
113: if (bufp == NULL) {
114: vrele(lvp);
115: *lvpp = NULL;
116: return (0);
117: }
118:
119: fileno = va.va_fileid;
120:
121: dirbuflen = DIRBLKSIZ;
122:
123: if (dirbuflen < va.va_blocksize)
124: dirbuflen = va.va_blocksize;
125:
126: dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);
127:
128: off = 0;
129:
130: do {
131: char *cpos;
132: struct dirent *dp;
133:
134: iov.iov_base = dirbuf;
135: iov.iov_len = dirbuflen;
136:
137: uio.uio_iov = &iov;
138: uio.uio_iovcnt = 1;
139: uio.uio_offset = off;
140: uio.uio_resid = dirbuflen;
141: uio.uio_segflg = UIO_SYSSPACE;
142: uio.uio_rw = UIO_READ;
143: uio.uio_procp = p;
144:
145: eofflag = 0;
146:
147: /* Call VOP_READDIR of parent */
148: error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag, 0, 0);
149:
150: off = uio.uio_offset;
151:
152: /* Try again if NFS tosses its cookies */
153: if (error == EINVAL && tries < 3) {
154: tries++;
155: off = 0;
156: continue;
157: } else if (error) {
158: goto out; /* Old userland getcwd() behaviour */
159: }
160:
161: cpos = dirbuf;
162: tries = 0;
163:
164: /* Scan directory page looking for matching vnode */
165: for (len = (dirbuflen - uio.uio_resid); len > 0;
166: len -= reclen) {
167: dp = (struct dirent *)cpos;
168: reclen = dp->d_reclen;
169:
170: /* Check for malformed directory */
171: if (reclen < DIRENT_MINSIZE) {
172: error = EINVAL;
173: goto out;
174: }
175:
176: if (dp->d_fileno == fileno) {
177: char *bp = *bpp;
178: bp -= dp->d_namlen;
179:
180: if (bp <= bufp) {
181: error = ERANGE;
182: goto out;
183: }
184:
185: bcopy(dp->d_name, bp, dp->d_namlen);
186: error = 0;
187: *bpp = bp;
188:
189: goto out;
190: }
191:
192: cpos += reclen;
193: }
194:
195: } while (!eofflag);
196:
197: error = ENOENT;
198:
199: out:
200:
201: vrele(lvp);
202: *lvpp = NULL;
203:
204: free(dirbuf, M_TEMP);
205:
206: return (error);
207: }
208:
209: /* Do a lookup in the vnode-to-name reverse */
210: int
211: vfs_getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
212: char *bufp)
213: {
214: struct vnode *lvp, *uvp = NULL;
215: struct proc *p = curproc;
216: char *obp;
217: int error, vpid;
218:
219: lvp = *lvpp;
220: obp = *bpp; /* Save orginal position to restore to on error */
221:
222: error = cache_revlookup(lvp, uvpp, bpp, bufp);
223: if (error) {
224: if (error != -1) {
225: vput(lvp);
226: *lvpp = NULL;
227: *uvpp = NULL;
228: }
229:
230: return (error);
231: }
232:
233: uvp = *uvpp;
234: vpid = uvp->v_id;
235:
236:
237: /* Release current lock before acquiring the parent lock */
238: VOP_UNLOCK(lvp, 0, p);
239:
240: error = vget(uvp, LK_EXCLUSIVE | LK_RETRY, p);
241: if (error)
242: *uvpp = NULL;
243:
244: /*
245: * Verify that vget() succeeded, and check that vnode capability
246: * didn't change while we were waiting for the lock.
247: */
248: if (error || (vpid != uvp->v_id)) {
249: /*
250: * Try to get our lock back. If that works, tell the caller to
251: * try things the hard way, otherwise give up.
252: */
253: if (!error)
254: vput(uvp);
255:
256: *uvpp = NULL;
257:
258: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
259: if (!error) {
260: *bpp = obp; /* restore the buffer */
261: return (-1);
262: }
263: }
264:
265: vrele(lvp);
266: *lvpp = NULL;
267:
268: return (error);
269: }
270:
271: #define GETCWD_CHECK_ACCESS 0x0001
272:
273: /* Common routine shared by sys___getcwd() and vn_isunder() */
274: int
275: vfs_getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
276: int limit, int flags, struct proc *p)
277: {
278: struct filedesc *fdp = p->p_fd;
279: struct vnode *uvp = NULL;
280: char *bp = NULL;
281: int error, perms = VEXEC;
282:
283: if (rvp == NULL) {
284: rvp = fdp->fd_rdir;
285: if (rvp == NULL)
286: rvp = rootvnode;
287: }
288:
289: VREF(rvp);
290: VREF(lvp);
291:
292: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
293: if (error) {
294: vrele(lvp);
295: lvp = NULL;
296: goto out;
297: }
298:
299: if (bufp)
300: bp = *bpp;
301:
302: if (lvp == rvp) {
303: if (bp)
304: *(--bp) = '/';
305: goto out;
306: }
307:
308: /*
309: * This loop will terminate when we hit the root, VOP_READDIR() or
310: * VOP_LOOKUP() fails, or we run out of space in the user buffer.
311: */
312: do {
313: if (lvp->v_type != VDIR) {
314: error = ENOTDIR;
315: goto out;
316: }
317:
318: /* Check for access if caller cares */
319: if (flags & GETCWD_CHECK_ACCESS) {
320: error = VOP_ACCESS(lvp, perms, p->p_ucred, p);
321: if (error)
322: goto out;
323: perms = VEXEC|VREAD;
324: }
325:
326: /* Step up if we're a covered vnode */
327: while (lvp->v_flag & VROOT) {
328: struct vnode *tvp;
329:
330: if (lvp == rvp)
331: goto out;
332:
333: tvp = lvp;
334: lvp = lvp->v_mount->mnt_vnodecovered;
335:
336: vput(tvp);
337:
338: if (lvp == NULL) {
339: error = ENOENT;
340: goto out;
341: }
342:
343: VREF(lvp);
344:
345: error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
346: if (error) {
347: vrele(lvp);
348: lvp = NULL;
349: goto out;
350: }
351: }
352:
353: /* Look in the name cache */
354: error = vfs_getcwd_getcache(&lvp, &uvp, &bp, bufp);
355:
356: if (error == -1) {
357: /* If that fails, look in the directory */
358: error = vfs_getcwd_scandir(&lvp, &uvp, &bp, bufp, p);
359: }
360:
361: if (error)
362: goto out;
363:
364: #ifdef DIAGNOSTIC
365: if (lvp != NULL)
366: panic("getcwd: oops, forgot to null lvp");
367: if (bufp && (bp <= bufp)) {
368: panic("getcwd: oops, went back too far");
369: }
370: #endif
371:
372: if (bp)
373: *(--bp) = '/';
374:
375: lvp = uvp;
376: uvp = NULL;
377: limit--;
378:
379: } while ((lvp != rvp) && (limit > 0));
380:
381: out:
382:
383: if (bpp)
384: *bpp = bp;
385:
386: if (uvp)
387: vput(uvp);
388:
389: if (lvp)
390: vput(lvp);
391:
392: vrele(rvp);
393:
394: return (error);
395: }
396:
397: /* True if p1's root directory is equal to or under p2's root directory */
398: int
399: proc_isunder(struct proc *p1, struct proc *p2)
400: {
401: struct vnode *r1 = p1->p_fd->fd_rdir;
402: struct vnode *r2 = p2->p_fd->fd_rdir;
403:
404: if (r1 == NULL)
405: return (r2 == NULL);
406:
407: if (r2 == NULL)
408: return (1);
409:
410: return (vn_isunder(r1, r2, p2));
411: }
412:
413: /* Find pathname of a process's current directory */
414: int
415: sys___getcwd(struct proc *p, void *v, register_t *retval)
416: {
417: struct sys___getcwd_args *uap = v;
418: int error, lenused, len = SCARG(uap, len);
419: char *path, *bp, *bend;
420:
421: if (len > MAXPATHLEN * 4)
422: len = MAXPATHLEN * 4;
423: else if (len < 2)
424: return (ERANGE);
425:
426: path = malloc(len, M_TEMP, M_WAITOK);
427:
428: bp = &path[len];
429: bend = bp;
430: *(--bp) = '\0';
431:
432: /*
433: * 5th argument here is "max number of vnodes to traverse".
434: * Since each entry takes up at least 2 bytes in the output
435: * buffer, limit it to N/2 vnodes for an N byte buffer.
436: */
437: error = vfs_getcwd_common(p->p_fd->fd_cdir, NULL, &bp, path, len/2,
438: GETCWD_CHECK_ACCESS, p);
439:
440: if (error)
441: goto out;
442:
443: lenused = bend - bp;
444: *retval = lenused;
445:
446: /* Put the result into user buffer */
447: error = copyout(bp, SCARG(uap, buf), lenused);
448:
449: out:
450: free(path, M_TEMP);
451:
452: return (error);
453: }
CVSweb