Sat Aug 19 04:17:11 2017 UTC ()
Pull up following revision(s) (requested by mrg in ticket #1482):
	sys/kern/vfs_getcwd.c: revision 1.52
Don't walk off the end of the dirent buffer.
From Ilja Van Sprundel.


(snj)
diff -r1.47 -r1.47.14.1 src/sys/kern/vfs_getcwd.c

cvs diff -r1.47 -r1.47.14.1 src/sys/kern/vfs_getcwd.c (switch to unified diff)

--- src/sys/kern/vfs_getcwd.c 2010/11/30 10:30:02 1.47
+++ src/sys/kern/vfs_getcwd.c 2017/08/19 04:17:11 1.47.14.1
@@ -1,589 +1,590 @@ @@ -1,589 +1,590 @@
1/* $NetBSD: vfs_getcwd.c,v 1.47 2010/11/30 10:30:02 dholland Exp $ */ 1/* $NetBSD: vfs_getcwd.c,v 1.47.14.1 2017/08/19 04:17:11 snj Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to The NetBSD Foundation 7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Bill Sommerfeld. 8 * by Bill Sommerfeld.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright 15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the 16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution. 17 * documentation and/or other materials provided with the distribution.
18 * 18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE. 29 * POSSIBILITY OF SUCH DAMAGE.
30 */ 30 */
31 31
32#include <sys/cdefs.h> 32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.47 2010/11/30 10:30:02 dholland Exp $"); 33__KERNEL_RCSID(0, "$NetBSD: vfs_getcwd.c,v 1.47.14.1 2017/08/19 04:17:11 snj Exp $");
34 34
35#include <sys/param.h> 35#include <sys/param.h>
36#include <sys/systm.h> 36#include <sys/systm.h>
37#include <sys/namei.h> 37#include <sys/namei.h>
38#include <sys/filedesc.h> 38#include <sys/filedesc.h>
39#include <sys/kernel.h> 39#include <sys/kernel.h>
40#include <sys/file.h> 40#include <sys/file.h>
41#include <sys/stat.h> 41#include <sys/stat.h>
42#include <sys/vnode.h> 42#include <sys/vnode.h>
43#include <sys/mount.h> 43#include <sys/mount.h>
44#include <sys/proc.h> 44#include <sys/proc.h>
45#include <sys/uio.h> 45#include <sys/uio.h>
46#include <sys/kmem.h> 46#include <sys/kmem.h>
47#include <sys/dirent.h> 47#include <sys/dirent.h>
48#include <sys/kauth.h> 48#include <sys/kauth.h>
49 49
50#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */ 50#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */
51 51
52#include <sys/syscallargs.h> 52#include <sys/syscallargs.h>
53 53
54/* 54/*
55 * Vnode variable naming conventions in this file: 55 * Vnode variable naming conventions in this file:
56 * 56 *
57 * rvp: the current root we're aiming towards. 57 * rvp: the current root we're aiming towards.
58 * lvp, *lvpp: the "lower" vnode 58 * lvp, *lvpp: the "lower" vnode
59 * uvp, *uvpp: the "upper" vnode. 59 * uvp, *uvpp: the "upper" vnode.
60 * 60 *
61 * Since all the vnodes we're dealing with are directories, and the 61 * Since all the vnodes we're dealing with are directories, and the
62 * lookups are going *up* in the filesystem rather than *down*, the 62 * lookups are going *up* in the filesystem rather than *down*, the
63 * usual "pvp" (parent) or "dvp" (directory) naming conventions are 63 * usual "pvp" (parent) or "dvp" (directory) naming conventions are
64 * too confusing. 64 * too confusing.
65 */ 65 */
66 66
67/* 67/*
68 * XXX Will infinite loop in certain cases if a directory read reliably 68 * XXX Will infinite loop in certain cases if a directory read reliably
69 * returns EINVAL on last block. 69 * returns EINVAL on last block.
70 * XXX is EINVAL the right thing to return if a directory is malformed? 70 * XXX is EINVAL the right thing to return if a directory is malformed?
71 */ 71 */
72 72
73/* 73/*
74 * XXX Untested vs. mount -o union; probably does the wrong thing. 74 * XXX Untested vs. mount -o union; probably does the wrong thing.
75 */ 75 */
76 76
77/* 77/*
78 * Find parent vnode of *lvpp, return in *uvpp 78 * Find parent vnode of *lvpp, return in *uvpp
79 * 79 *
80 * If we care about the name, scan it looking for name of directory 80 * If we care about the name, scan it looking for name of directory
81 * entry pointing at lvp. 81 * entry pointing at lvp.
82 * 82 *
83 * Place the name in the buffer which starts at bufp, immediately 83 * Place the name in the buffer which starts at bufp, immediately
84 * before *bpp, and move bpp backwards to point at the start of it. 84 * before *bpp, and move bpp backwards to point at the start of it.
85 * 85 *
86 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed 86 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
87 * On exit, *uvpp is either NULL or is a locked vnode reference. 87 * On exit, *uvpp is either NULL or is a locked vnode reference.
88 */ 88 */
89static int 89static int
90getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp, 90getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
91 char *bufp, struct lwp *l) 91 char *bufp, struct lwp *l)
92{ 92{
93 int error = 0; 93 int error = 0;
94 int eofflag; 94 int eofflag;
95 off_t off; 95 off_t off;
96 int tries; 96 int tries;
97 struct uio uio; 97 struct uio uio;
98 struct iovec iov; 98 struct iovec iov;
99 char *dirbuf = NULL; 99 char *dirbuf = NULL;
100 int dirbuflen; 100 int dirbuflen;
101 ino_t fileno; 101 ino_t fileno;
102 struct vattr va; 102 struct vattr va;
103 struct vnode *uvp = NULL; 103 struct vnode *uvp = NULL;
104 struct vnode *lvp = *lvpp; 104 struct vnode *lvp = *lvpp;
105 kauth_cred_t cred = l->l_cred; 105 kauth_cred_t cred = l->l_cred;
106 struct componentname cn; 106 struct componentname cn;
107 int len, reclen; 107 int len, reclen;
108 tries = 0; 108 tries = 0;
109 109
110 /* 110 /*
111 * If we want the filename, get some info we need while the 111 * If we want the filename, get some info we need while the
112 * current directory is still locked. 112 * current directory is still locked.
113 */ 113 */
114 if (bufp != NULL) { 114 if (bufp != NULL) {
115 error = VOP_GETATTR(lvp, &va, cred); 115 error = VOP_GETATTR(lvp, &va, cred);
116 if (error) { 116 if (error) {
117 vput(lvp); 117 vput(lvp);
118 *lvpp = NULL; 118 *lvpp = NULL;
119 *uvpp = NULL; 119 *uvpp = NULL;
120 return error; 120 return error;
121 } 121 }
122 } 122 }
123 123
124 /* 124 /*
125 * Ok, we have to do it the hard way.. 125 * Ok, we have to do it the hard way..
126 * Next, get parent vnode using lookup of .. 126 * Next, get parent vnode using lookup of ..
127 */ 127 */
128 cn.cn_nameiop = LOOKUP; 128 cn.cn_nameiop = LOOKUP;
129 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY; 129 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
130 cn.cn_cred = cred; 130 cn.cn_cred = cred;
131 cn.cn_nameptr = ".."; 131 cn.cn_nameptr = "..";
132 cn.cn_namelen = 2; 132 cn.cn_namelen = 2;
133 cn.cn_hash = 0; 133 cn.cn_hash = 0;
134 cn.cn_consume = 0; 134 cn.cn_consume = 0;
135 135
136 /* 136 /*
137 * At this point, lvp is locked. 137 * At this point, lvp is locked.
138 * On successful return, *uvpp will be locked 138 * On successful return, *uvpp will be locked
139 */ 139 */
140 error = VOP_LOOKUP(lvp, uvpp, &cn); 140 error = VOP_LOOKUP(lvp, uvpp, &cn);
141 vput(lvp); 141 vput(lvp);
142 if (error) { 142 if (error) {
143 *lvpp = NULL; 143 *lvpp = NULL;
144 *uvpp = NULL; 144 *uvpp = NULL;
145 return error; 145 return error;
146 } 146 }
147 uvp = *uvpp; 147 uvp = *uvpp;
148 148
149 /* If we don't care about the pathname, we're done */ 149 /* If we don't care about the pathname, we're done */
150 if (bufp == NULL) { 150 if (bufp == NULL) {
151 *lvpp = NULL; 151 *lvpp = NULL;
152 return 0; 152 return 0;
153 } 153 }
154 154
155 fileno = va.va_fileid; 155 fileno = va.va_fileid;
156 156
157 dirbuflen = DIRBLKSIZ; 157 dirbuflen = DIRBLKSIZ;
158 if (dirbuflen < va.va_blocksize) 158 if (dirbuflen < va.va_blocksize)
159 dirbuflen = va.va_blocksize; 159 dirbuflen = va.va_blocksize;
160 dirbuf = kmem_alloc(dirbuflen, KM_SLEEP); 160 dirbuf = kmem_alloc(dirbuflen, KM_SLEEP);
161 161
162#if 0 162#if 0
163unionread: 163unionread:
164#endif 164#endif
165 off = 0; 165 off = 0;
166 do { 166 do {
167 /* call VOP_READDIR of parent */ 167 /* call VOP_READDIR of parent */
168 iov.iov_base = dirbuf; 168 iov.iov_base = dirbuf;
169 iov.iov_len = dirbuflen; 169 iov.iov_len = dirbuflen;
170 170
171 uio.uio_iov = &iov; 171 uio.uio_iov = &iov;
172 uio.uio_iovcnt = 1; 172 uio.uio_iovcnt = 1;
173 uio.uio_offset = off; 173 uio.uio_offset = off;
174 uio.uio_resid = dirbuflen; 174 uio.uio_resid = dirbuflen;
175 uio.uio_rw = UIO_READ; 175 uio.uio_rw = UIO_READ;
176 UIO_SETUP_SYSSPACE(&uio); 176 UIO_SETUP_SYSSPACE(&uio);
177 177
178 eofflag = 0; 178 eofflag = 0;
179 179
180 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0); 180 error = VOP_READDIR(uvp, &uio, cred, &eofflag, 0, 0);
181 181
182 off = uio.uio_offset; 182 off = uio.uio_offset;
183 183
184 /* 184 /*
185 * Try again if NFS tosses its cookies. 185 * Try again if NFS tosses its cookies.
186 * XXX this can still loop forever if the directory is busted 186 * XXX this can still loop forever if the directory is busted
187 * such that the second or subsequent page of it always 187 * such that the second or subsequent page of it always
188 * returns EINVAL 188 * returns EINVAL
189 */ 189 */
190 if ((error == EINVAL) && (tries < 3)) { 190 if ((error == EINVAL) && (tries < 3)) {
191 off = 0; 191 off = 0;
192 tries++; 192 tries++;
193 continue; /* once more, with feeling */ 193 continue; /* once more, with feeling */
194 } 194 }
195 195
196 if (!error) { 196 if (!error) {
197 char *cpos; 197 char *cpos;
198 struct dirent *dp; 198 struct dirent *dp;
199 199
200 cpos = dirbuf; 200 cpos = dirbuf;
201 tries = 0; 201 tries = 0;
202 202
203 /* scan directory page looking for matching vnode */ 203 /* scan directory page looking for matching vnode */
204 for (len = (dirbuflen - uio.uio_resid); len > 0; 204 for (len = (dirbuflen - uio.uio_resid); len > 0;
205 len -= reclen) { 205 len -= reclen) {
206 dp = (struct dirent *) cpos; 206 dp = (struct dirent *) cpos;
207 reclen = dp->d_reclen; 207 reclen = dp->d_reclen;
208 208
209 /* check for malformed directory.. */ 209 /* check for malformed directory.. */
210 if (reclen < _DIRENT_MINSIZE(dp)) { 210 if (reclen < _DIRENT_MINSIZE(dp) ||
 211 reclen > len) {
211 error = EINVAL; 212 error = EINVAL;
212 goto out; 213 goto out;
213 } 214 }
214 /* 215 /*
215 * XXX should perhaps do VOP_LOOKUP to 216 * XXX should perhaps do VOP_LOOKUP to
216 * check that we got back to the right place, 217 * check that we got back to the right place,
217 * but getting the locking games for that 218 * but getting the locking games for that
218 * right would be heinous. 219 * right would be heinous.
219 */ 220 */
220 if ((dp->d_type != DT_WHT) && 221 if ((dp->d_type != DT_WHT) &&
221 (dp->d_fileno == fileno)) { 222 (dp->d_fileno == fileno)) {
222 char *bp = *bpp; 223 char *bp = *bpp;
223 224
224 bp -= dp->d_namlen; 225 bp -= dp->d_namlen;
225 if (bp <= bufp) { 226 if (bp <= bufp) {
226 error = ERANGE; 227 error = ERANGE;
227 goto out; 228 goto out;
228 } 229 }
229 memcpy(bp, dp->d_name, dp->d_namlen); 230 memcpy(bp, dp->d_name, dp->d_namlen);
230 error = 0; 231 error = 0;
231 *bpp = bp; 232 *bpp = bp;
232 goto out; 233 goto out;
233 } 234 }
234 cpos += reclen; 235 cpos += reclen;
235 } 236 }
236 } else 237 } else
237 goto out; 238 goto out;
238 } while (!eofflag); 239 } while (!eofflag);
239#if 0 240#if 0
240 /* 241 /*
241 * Deal with mount -o union, which unions only the 242 * Deal with mount -o union, which unions only the
242 * root directory of the mount. 243 * root directory of the mount.
243 */ 244 */
244 if ((uvp->v_vflag & VV_ROOT) && 245 if ((uvp->v_vflag & VV_ROOT) &&
245 (uvp->v_mount->mnt_flag & MNT_UNION)) { 246 (uvp->v_mount->mnt_flag & MNT_UNION)) {
246 struct vnode *tvp = uvp; 247 struct vnode *tvp = uvp;
247 248
248 uvp = uvp->v_mount->mnt_vnodecovered; 249 uvp = uvp->v_mount->mnt_vnodecovered;
249 vput(tvp); 250 vput(tvp);
250 vref(uvp); 251 vref(uvp);
251 *uvpp = uvp; 252 *uvpp = uvp;
252 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY); 253 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY);
253 goto unionread; 254 goto unionread;
254 } 255 }
255#endif 256#endif
256 error = ENOENT; 257 error = ENOENT;
257 258
258out: 259out:
259 *lvpp = NULL; 260 *lvpp = NULL;
260 kmem_free(dirbuf, dirbuflen); 261 kmem_free(dirbuf, dirbuflen);
261 return error; 262 return error;
262} 263}
263 264
264/* 265/*
265 * Look in the vnode-to-name reverse cache to see if 266 * Look in the vnode-to-name reverse cache to see if
266 * we can find things the easy way. 267 * we can find things the easy way.
267 * 268 *
268 * XXX vget failure path is untested. 269 * XXX vget failure path is untested.
269 * 270 *
270 * On entry, *lvpp is a locked vnode reference. 271 * On entry, *lvpp is a locked vnode reference.
271 * On exit, one of the following is the case: 272 * On exit, one of the following is the case:
272 * 0) Both *lvpp and *uvpp are NULL and failure is returned. 273 * 0) Both *lvpp and *uvpp are NULL and failure is returned.
273 * 1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss) 274 * 1) *uvpp is NULL, *lvpp remains locked and -1 is returned (cache miss)
274 * 2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed 275 * 2) *uvpp is a locked vnode reference, *lvpp is vput and NULL'ed
275 * and 0 is returned (cache hit) 276 * and 0 is returned (cache hit)
276 */ 277 */
277 278
278static int 279static int
279getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp, 280getcwd_getcache(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
280 char *bufp) 281 char *bufp)
281{ 282{
282 struct vnode *lvp, *uvp = NULL; 283 struct vnode *lvp, *uvp = NULL;
283 int error; 284 int error;
284 285
285 lvp = *lvpp; 286 lvp = *lvpp;
286 287
287 /* 288 /*
288 * This returns 0 on a cache hit, -1 on a clean cache miss, 289 * This returns 0 on a cache hit, -1 on a clean cache miss,
289 * or an errno on other failure. 290 * or an errno on other failure.
290 */ 291 */
291 error = cache_revlookup(lvp, uvpp, bpp, bufp); 292 error = cache_revlookup(lvp, uvpp, bpp, bufp);
292 if (error) { 293 if (error) {
293 if (error != -1) { 294 if (error != -1) {
294 vput(lvp); 295 vput(lvp);
295 *lvpp = NULL; 296 *lvpp = NULL;
296 *uvpp = NULL; 297 *uvpp = NULL;
297 } 298 }
298 return error; 299 return error;
299 } 300 }
300 uvp = *uvpp; 301 uvp = *uvpp;
301 302
302 /* 303 /*
303 * Since we're going up, we have to release the current lock 304 * Since we're going up, we have to release the current lock
304 * before we take the parent lock. 305 * before we take the parent lock.
305 */ 306 */
306 307
307 VOP_UNLOCK(lvp); 308 VOP_UNLOCK(lvp);
308 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY); 309 vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY);
309 vrele(lvp); 310 vrele(lvp);
310 *lvpp = NULL; 311 *lvpp = NULL;
311 312
312 return error; 313 return error;
313} 314}
314 315
315/* 316/*
316 * common routine shared by sys___getcwd() and vn_isunder() 317 * common routine shared by sys___getcwd() and vn_isunder()
317 */ 318 */
318 319
319int 320int
320getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp, 321getcwd_common(struct vnode *lvp, struct vnode *rvp, char **bpp, char *bufp,
321 int limit, int flags, struct lwp *l) 322 int limit, int flags, struct lwp *l)
322{ 323{
323 struct cwdinfo *cwdi = l->l_proc->p_cwdi; 324 struct cwdinfo *cwdi = l->l_proc->p_cwdi;
324 kauth_cred_t cred = l->l_cred; 325 kauth_cred_t cred = l->l_cred;
325 struct vnode *uvp = NULL; 326 struct vnode *uvp = NULL;
326 char *bp = NULL; 327 char *bp = NULL;
327 int error; 328 int error;
328 int perms = VEXEC; 329 int perms = VEXEC;
329 330
330 error = 0; 331 error = 0;
331 if (rvp == NULL) { 332 if (rvp == NULL) {
332 rvp = cwdi->cwdi_rdir; 333 rvp = cwdi->cwdi_rdir;
333 if (rvp == NULL) 334 if (rvp == NULL)
334 rvp = rootvnode; 335 rvp = rootvnode;
335 } 336 }
336 337
337 vref(rvp); 338 vref(rvp);
338 vref(lvp); 339 vref(lvp);
339 340
340 /* 341 /*
341 * Error handling invariant: 342 * Error handling invariant:
342 * Before a `goto out': 343 * Before a `goto out':
343 * lvp is either NULL, or locked and held. 344 * lvp is either NULL, or locked and held.
344 * uvp is either NULL, or locked and held. 345 * uvp is either NULL, or locked and held.
345 */ 346 */
346 347
347 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 348 vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
348 if (bufp) 349 if (bufp)
349 bp = *bpp; 350 bp = *bpp;
350 351
351 /* 352 /*
352 * this loop will terminate when one of the following happens: 353 * this loop will terminate when one of the following happens:
353 * - we hit the root 354 * - we hit the root
354 * - getdirentries or lookup fails 355 * - getdirentries or lookup fails
355 * - we run out of space in the buffer. 356 * - we run out of space in the buffer.
356 */ 357 */
357 if (lvp == rvp) { 358 if (lvp == rvp) {
358 if (bp) 359 if (bp)
359 *(--bp) = '/'; 360 *(--bp) = '/';
360 goto out; 361 goto out;
361 } 362 }
362 do { 363 do {
363 /* 364 /*
364 * access check here is optional, depending on 365 * access check here is optional, depending on
365 * whether or not caller cares. 366 * whether or not caller cares.
366 */ 367 */
367 if (flags & GETCWD_CHECK_ACCESS) { 368 if (flags & GETCWD_CHECK_ACCESS) {
368 error = VOP_ACCESS(lvp, perms, cred); 369 error = VOP_ACCESS(lvp, perms, cred);
369 if (error) 370 if (error)
370 goto out; 371 goto out;
371 perms = VEXEC|VREAD; 372 perms = VEXEC|VREAD;
372 } 373 }
373 374
374 /* 375 /*
375 * step up if we're a covered vnode.. 376 * step up if we're a covered vnode..
376 */ 377 */
377 while (lvp->v_vflag & VV_ROOT) { 378 while (lvp->v_vflag & VV_ROOT) {
378 struct vnode *tvp; 379 struct vnode *tvp;
379 380
380 if (lvp == rvp) 381 if (lvp == rvp)
381 goto out; 382 goto out;
382 383
383 tvp = lvp; 384 tvp = lvp;
384 lvp = lvp->v_mount->mnt_vnodecovered; 385 lvp = lvp->v_mount->mnt_vnodecovered;
385 vput(tvp); 386 vput(tvp);
386 /* 387 /*
387 * hodie natus est radici frater 388 * hodie natus est radici frater
388 */ 389 */
389 if (lvp == NULL) { 390 if (lvp == NULL) {
390 error = ENOENT; 391 error = ENOENT;
391 goto out; 392 goto out;
392 } 393 }
393 vref(lvp); 394 vref(lvp);
394 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY); 395 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY);
395 if (error != 0) { 396 if (error != 0) {
396 vrele(lvp); 397 vrele(lvp);
397 lvp = NULL; 398 lvp = NULL;
398 goto out; 399 goto out;
399 } 400 }
400 } 401 }
401 /* 402 /*
402 * Look in the name cache; if that fails, look in the 403 * Look in the name cache; if that fails, look in the
403 * directory.. 404 * directory..
404 */ 405 */
405 error = getcwd_getcache(&lvp, &uvp, &bp, bufp); 406 error = getcwd_getcache(&lvp, &uvp, &bp, bufp);
406 if (error == -1) { 407 if (error == -1) {
407 if (lvp->v_type != VDIR) { 408 if (lvp->v_type != VDIR) {
408 error = ENOTDIR; 409 error = ENOTDIR;
409 goto out; 410 goto out;
410 } 411 }
411 error = getcwd_scandir(&lvp, &uvp, &bp, bufp, l); 412 error = getcwd_scandir(&lvp, &uvp, &bp, bufp, l);
412 } 413 }
413 if (error) 414 if (error)
414 goto out; 415 goto out;
415#if DIAGNOSTIC 416#if DIAGNOSTIC
416 if (lvp != NULL) 417 if (lvp != NULL)
417 panic("getcwd: oops, forgot to null lvp"); 418 panic("getcwd: oops, forgot to null lvp");
418 if (bufp && (bp <= bufp)) { 419 if (bufp && (bp <= bufp)) {
419 panic("getcwd: oops, went back too far"); 420 panic("getcwd: oops, went back too far");
420 } 421 }
421#endif 422#endif
422 if (bp) 423 if (bp)
423 *(--bp) = '/'; 424 *(--bp) = '/';
424 lvp = uvp; 425 lvp = uvp;
425 uvp = NULL; 426 uvp = NULL;
426 limit--; 427 limit--;
427 } while ((lvp != rvp) && (limit > 0)); 428 } while ((lvp != rvp) && (limit > 0));
428 429
429out: 430out:
430 if (bpp) 431 if (bpp)
431 *bpp = bp; 432 *bpp = bp;
432 if (uvp) 433 if (uvp)
433 vput(uvp); 434 vput(uvp);
434 if (lvp) 435 if (lvp)
435 vput(lvp); 436 vput(lvp);
436 vrele(rvp); 437 vrele(rvp);
437 return error; 438 return error;
438} 439}
439 440
440/* 441/*
441 * Check if one directory can be found inside another in the directory 442 * Check if one directory can be found inside another in the directory
442 * hierarchy. 443 * hierarchy.
443 * 444 *
444 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that 445 * Intended to be used in chroot, chdir, fchdir, etc., to ensure that
445 * chroot() actually means something. 446 * chroot() actually means something.
446 */ 447 */
447int 448int
448vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l) 449vn_isunder(struct vnode *lvp, struct vnode *rvp, struct lwp *l)
449{ 450{
450 int error; 451 int error;
451 452
452 error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l); 453 error = getcwd_common(lvp, rvp, NULL, NULL, MAXPATHLEN / 2, 0, l);
453 454
454 if (!error) 455 if (!error)
455 return 1; 456 return 1;
456 else 457 else
457 return 0; 458 return 0;
458} 459}
459 460
460/* 461/*
461 * Returns true if proc p1's root directory equal to or under p2's 462 * Returns true if proc p1's root directory equal to or under p2's
462 * root directory. 463 * root directory.
463 * 464 *
464 * Intended to be used from ptrace/procfs sorts of things. 465 * Intended to be used from ptrace/procfs sorts of things.
465 */ 466 */
466 467
467int 468int
468proc_isunder(struct proc *p1, struct lwp *l2) 469proc_isunder(struct proc *p1, struct lwp *l2)
469{ 470{
470 struct vnode *r1 = p1->p_cwdi->cwdi_rdir; 471 struct vnode *r1 = p1->p_cwdi->cwdi_rdir;
471 struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir; 472 struct vnode *r2 = l2->l_proc->p_cwdi->cwdi_rdir;
472 473
473 if (r1 == NULL) 474 if (r1 == NULL)
474 return (r2 == NULL); 475 return (r2 == NULL);
475 else if (r2 == NULL) 476 else if (r2 == NULL)
476 return 1; 477 return 1;
477 else 478 else
478 return vn_isunder(r1, r2, l2); 479 return vn_isunder(r1, r2, l2);
479} 480}
480 481
481/* 482/*
482 * Find pathname of process's current directory. 483 * Find pathname of process's current directory.
483 * 484 *
484 * Use vfs vnode-to-name reverse cache; if that fails, fall back 485 * Use vfs vnode-to-name reverse cache; if that fails, fall back
485 * to reading directory contents. 486 * to reading directory contents.
486 */ 487 */
487 488
488int 489int
489sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, register_t *retval) 490sys___getcwd(struct lwp *l, const struct sys___getcwd_args *uap, register_t *retval)
490{ 491{
491 /* { 492 /* {
492 syscallarg(char *) bufp; 493 syscallarg(char *) bufp;
493 syscallarg(size_t) length; 494 syscallarg(size_t) length;
494 } */ 495 } */
495 496
496 int error; 497 int error;
497 char *path; 498 char *path;
498 char *bp, *bend; 499 char *bp, *bend;
499 int len = SCARG(uap, length); 500 int len = SCARG(uap, length);
500 int lenused; 501 int lenused;
501 struct cwdinfo *cwdi; 502 struct cwdinfo *cwdi;
502 503
503 if (len > MAXPATHLEN * 4) 504 if (len > MAXPATHLEN * 4)
504 len = MAXPATHLEN * 4; 505 len = MAXPATHLEN * 4;
505 else if (len < 2) 506 else if (len < 2)
506 return ERANGE; 507 return ERANGE;
507 508
508 path = kmem_alloc(len, KM_SLEEP); 509 path = kmem_alloc(len, KM_SLEEP);
509 if (!path) 510 if (!path)
510 return ENOMEM; 511 return ENOMEM;
511 512
512 bp = &path[len]; 513 bp = &path[len];
513 bend = bp; 514 bend = bp;
514 *(--bp) = '\0'; 515 *(--bp) = '\0';
515 516
516 /* 517 /*
517 * 5th argument here is "max number of vnodes to traverse". 518 * 5th argument here is "max number of vnodes to traverse".
518 * Since each entry takes up at least 2 bytes in the output buffer, 519 * Since each entry takes up at least 2 bytes in the output buffer,
519 * limit it to N/2 vnodes for an N byte buffer. 520 * limit it to N/2 vnodes for an N byte buffer.
520 */ 521 */
521 cwdi = l->l_proc->p_cwdi; 522 cwdi = l->l_proc->p_cwdi;
522 rw_enter(&cwdi->cwdi_lock, RW_READER); 523 rw_enter(&cwdi->cwdi_lock, RW_READER);
523 error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path,  524 error = getcwd_common(cwdi->cwdi_cdir, NULL, &bp, path,
524 len/2, GETCWD_CHECK_ACCESS, l); 525 len/2, GETCWD_CHECK_ACCESS, l);
525 rw_exit(&cwdi->cwdi_lock); 526 rw_exit(&cwdi->cwdi_lock);
526 527
527 if (error) 528 if (error)
528 goto out; 529 goto out;
529 lenused = bend - bp; 530 lenused = bend - bp;
530 *retval = lenused; 531 *retval = lenused;
531 /* put the result into user buffer */ 532 /* put the result into user buffer */
532 error = copyout(bp, SCARG(uap, bufp), lenused); 533 error = copyout(bp, SCARG(uap, bufp), lenused);
533 534
534out: 535out:
535 kmem_free(path, len); 536 kmem_free(path, len);
536 return error; 537 return error;
537} 538}
538 539
539/* 540/*
540 * Try to find a pathname for a vnode. Since there is no mapping 541 * Try to find a pathname for a vnode. Since there is no mapping
541 * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE 542 * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE
542 * option to work (to make cache_revlookup succeed). Caller holds a 543 * option to work (to make cache_revlookup succeed). Caller holds a
543 * reference to the vnode. 544 * reference to the vnode.
544 */ 545 */
545int 546int
546vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl, 547vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl,
547 struct proc *p) 548 struct proc *p)
548{ 549{
549 struct proc *curp = curl->l_proc; 550 struct proc *curp = curl->l_proc;
550 int error, lenused, elen; 551 int error, lenused, elen;
551 char *bp, *bend; 552 char *bp, *bend;
552 struct vnode *dvp; 553 struct vnode *dvp;
553 554
554 KASSERT(vp->v_usecount > 0); 555 KASSERT(vp->v_usecount > 0);
555 556
556 bp = bend = &path[len]; 557 bp = bend = &path[len];
557 *(--bp) = '\0'; 558 *(--bp) = '\0';
558 559
559 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 560 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
560 if (error != 0) 561 if (error != 0)
561 return error; 562 return error;
562 error = cache_revlookup(vp, &dvp, &bp, path); 563 error = cache_revlookup(vp, &dvp, &bp, path);
563 VOP_UNLOCK(vp); 564 VOP_UNLOCK(vp);
564 if (error != 0) 565 if (error != 0)
565 return (error == -1 ? ENOENT : error); 566 return (error == -1 ? ENOENT : error);
566 567
567 *(--bp) = '/'; 568 *(--bp) = '/';
568 error = getcwd_common(dvp, NULL, &bp, path, len / 2, 569 error = getcwd_common(dvp, NULL, &bp, path, len / 2,
569 GETCWD_CHECK_ACCESS, curl); 570 GETCWD_CHECK_ACCESS, curl);
570 vrele(dvp); 571 vrele(dvp);
571 572
572 /* 573 /*
573 * Strip off emulation path for emulated processes looking at 574 * Strip off emulation path for emulated processes looking at
574 * the maps file of a process of the same emulation. (Won't 575 * the maps file of a process of the same emulation. (Won't
575 * work if /emul/xxx is a symlink..) 576 * work if /emul/xxx is a symlink..)
576 */ 577 */
577 if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) { 578 if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
578 elen = strlen(curp->p_emul->e_path); 579 elen = strlen(curp->p_emul->e_path);
579 if (!strncmp(bp, curp->p_emul->e_path, elen)) 580 if (!strncmp(bp, curp->p_emul->e_path, elen))
580 bp = &bp[elen]; 581 bp = &bp[elen];
581 } 582 }
582 583
583 lenused = bend - bp; 584 lenused = bend - bp;
584 585
585 memcpy(path, bp, lenused); 586 memcpy(path, bp, lenused);
586 path[lenused] = 0; 587 path[lenused] = 0;
587 588
588 return 0; 589 return 0;
589} 590}