Deleted Added
full compact
linux_getcwd.c (112342) linux_getcwd.c (112430)
1/* $FreeBSD: head/sys/compat/linux/linux_getcwd.c 112342 2003-03-17 12:21:08Z phk $ */
1/* $FreeBSD: head/sys/compat/linux/linux_getcwd.c 112430 2003-03-20 10:40:45Z phk $ */
2/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */
3/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
4
5/*-
6 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation

--- 22 unchanged lines hidden (view full) ---

32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
2/* $OpenBSD: linux_getcwd.c,v 1.2 2001/05/16 12:50:21 ho Exp $ */
3/* $NetBSD: vfs_getcwd.c,v 1.3.2.3 1999/07/11 10:24:09 sommerfeld Exp $ */
4
5/*-
6 * Copyright (c) 1999 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation

--- 22 unchanged lines hidden (view full) ---

32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
40#include "opt_compat.h"
41#include "opt_mac.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/namei.h>
46#include <sys/filedesc.h>
47#include <sys/kernel.h>
48#include <sys/file.h>
49#include <sys/stat.h>
43#include <sys/syscallsubr.h>
50#include <sys/syscallsubr.h>
51#include <sys/vnode.h>
52#include <sys/mount.h>
44#include <sys/proc.h>
53#include <sys/proc.h>
54#include <sys/uio.h>
55#include <sys/mac.h>
56#include <sys/malloc.h>
57#include <sys/dirent.h>
58#include <ufs/ufs/dir.h> /* XXX only for DIRBLKSIZ */
45
46#include <machine/../linux/linux.h>
47#include <machine/../linux/linux_proto.h>
59
60#include <machine/../linux/linux.h>
61#include <machine/../linux/linux_proto.h>
62#include <compat/linux/linux_util.h>
48
63
64static int
65linux_getcwd_scandir(struct vnode **, struct vnode **,
66 char **, char *, struct thread *);
67static int
68linux_getcwd_common(struct vnode *, struct vnode *,
69 char **, char *, int, int, struct thread *);
70
71#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
72
49/*
73/*
74 * Vnode variable naming conventions in this file:
75 *
76 * rvp: the current root we're aiming towards.
77 * lvp, *lvpp: the "lower" vnode
78 * uvp, *uvpp: the "upper" vnode.
79 *
80 * Since all the vnodes we're dealing with are directories, and the
81 * lookups are going *up* in the filesystem rather than *down*, the
82 * usual "pvp" (parent) or "dvp" (directory) naming conventions are
83 * too confusing.
84 */
85
86/*
87 * XXX Will infinite loop in certain cases if a directory read reliably
88 * returns EINVAL on last block.
89 * XXX is EINVAL the right thing to return if a directory is malformed?
90 */
91
92/*
93 * XXX Untested vs. mount -o union; probably does the wrong thing.
94 */
95
96/*
97 * Find parent vnode of *lvpp, return in *uvpp
98 *
99 * If we care about the name, scan it looking for name of directory
100 * entry pointing at lvp.
101 *
102 * Place the name in the buffer which starts at bufp, immediately
103 * before *bpp, and move bpp backwards to point at the start of it.
104 *
105 * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
106 * On exit, *uvpp is either NULL or is a locked vnode reference.
107 */
108static int
109linux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td)
110 struct vnode **lvpp;
111 struct vnode **uvpp;
112 char **bpp;
113 char *bufp;
114 struct thread *td;
115{
116 int error = 0;
117 int eofflag;
118 off_t off;
119 int tries;
120 struct uio uio;
121 struct iovec iov;
122 char *dirbuf = NULL;
123 int dirbuflen;
124 ino_t fileno;
125 struct vattr va;
126 struct vnode *uvp = NULL;
127 struct vnode *lvp = *lvpp;
128 struct componentname cn;
129 int len, reclen;
130 tries = 0;
131
132 /*
133 * If we want the filename, get some info we need while the
134 * current directory is still locked.
135 */
136 if (bufp != NULL) {
137 error = VOP_GETATTR(lvp, &va, td->td_ucred, td);
138 if (error) {
139 vput(lvp);
140 *lvpp = NULL;
141 *uvpp = NULL;
142 return error;
143 }
144 }
145
146 /*
147 * Ok, we have to do it the hard way..
148 * Next, get parent vnode using lookup of ..
149 */
150 cn.cn_nameiop = LOOKUP;
151 cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
152 cn.cn_thread = td;
153 cn.cn_cred = td->td_ucred;
154 cn.cn_pnbuf = NULL;
155 cn.cn_nameptr = "..";
156 cn.cn_namelen = 2;
157 cn.cn_consume = 0;
158
159 /*
160 * At this point, lvp is locked and will be unlocked by the lookup.
161 * On successful return, *uvpp will be locked
162 */
163 error = VOP_LOOKUP(lvp, uvpp, &cn);
164 if (error) {
165 vput(lvp);
166 *lvpp = NULL;
167 *uvpp = NULL;
168 return error;
169 }
170 uvp = *uvpp;
171
172 /* If we don't care about the pathname, we're done */
173 if (bufp == NULL) {
174 vrele(lvp);
175 *lvpp = NULL;
176 return 0;
177 }
178
179 fileno = va.va_fileid;
180
181 dirbuflen = DIRBLKSIZ;
182 if (dirbuflen < va.va_blocksize)
183 dirbuflen = va.va_blocksize;
184 dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
185
186#if 0
187unionread:
188#endif
189 off = 0;
190 do {
191 /* call VOP_READDIR of parent */
192 iov.iov_base = dirbuf;
193 iov.iov_len = dirbuflen;
194
195 uio.uio_iov = &iov;
196 uio.uio_iovcnt = 1;
197 uio.uio_offset = off;
198 uio.uio_resid = dirbuflen;
199 uio.uio_segflg = UIO_SYSSPACE;
200 uio.uio_rw = UIO_READ;
201 uio.uio_td = td;
202
203 eofflag = 0;
204
205#ifdef MAC
206 error = mac_check_vnode_readdir(td->td_ucred, uvp);
207 if (error == 0)
208#endif /* MAC */
209 error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag,
210 0, 0);
211
212 off = uio.uio_offset;
213
214 /*
215 * Try again if NFS tosses its cookies.
216 * XXX this can still loop forever if the directory is busted
217 * such that the second or subsequent page of it always
218 * returns EINVAL
219 */
220 if ((error == EINVAL) && (tries < 3)) {
221 off = 0;
222 tries++;
223 continue; /* once more, with feeling */
224 }
225
226 if (!error) {
227 char *cpos;
228 struct dirent *dp;
229
230 cpos = dirbuf;
231 tries = 0;
232
233 /* scan directory page looking for matching vnode */
234 for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) {
235 dp = (struct dirent *) cpos;
236 reclen = dp->d_reclen;
237
238 /* check for malformed directory.. */
239 if (reclen < DIRENT_MINSIZE) {
240 error = EINVAL;
241 goto out;
242 }
243 /*
244 * XXX should perhaps do VOP_LOOKUP to
245 * check that we got back to the right place,
246 * but getting the locking games for that
247 * right would be heinous.
248 */
249 if ((dp->d_type != DT_WHT) &&
250 (dp->d_fileno == fileno)) {
251 char *bp = *bpp;
252 bp -= dp->d_namlen;
253
254 if (bp <= bufp) {
255 error = ERANGE;
256 goto out;
257 }
258 bcopy(dp->d_name, bp, dp->d_namlen);
259 error = 0;
260 *bpp = bp;
261 goto out;
262 }
263 cpos += reclen;
264 }
265 }
266 } while (!eofflag);
267 error = ENOENT;
268
269out:
270 vrele(lvp);
271 *lvpp = NULL;
272 free(dirbuf, M_TEMP);
273 return error;
274}
275
276
277/*
278 * common routine shared by sys___getcwd() and linux_vn_isunder()
279 */
280
281#define GETCWD_CHECK_ACCESS 0x0001
282
283static int
284linux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td)
285 struct vnode *lvp;
286 struct vnode *rvp;
287 char **bpp;
288 char *bufp;
289 int limit;
290 int flags;
291 struct thread *td;
292{
293 struct filedesc *fdp = td->td_proc->p_fd;
294 struct vnode *uvp = NULL;
295 char *bp = NULL;
296 int error;
297 int perms = VEXEC;
298
299 if (rvp == NULL) {
300 rvp = fdp->fd_rdir;
301 if (rvp == NULL)
302 rvp = rootvnode;
303 }
304
305 VREF(rvp);
306 VREF(lvp);
307
308 /*
309 * Error handling invariant:
310 * Before a `goto out':
311 * lvp is either NULL, or locked and held.
312 * uvp is either NULL, or locked and held.
313 */
314
315 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
316 if (error) {
317 vrele(lvp);
318 lvp = NULL;
319 goto out;
320 }
321 if (bufp)
322 bp = *bpp;
323 /*
324 * this loop will terminate when one of the following happens:
325 * - we hit the root
326 * - getdirentries or lookup fails
327 * - we run out of space in the buffer.
328 */
329 if (lvp == rvp) {
330 if (bp)
331 *(--bp) = '/';
332 goto out;
333 }
334 do {
335 if (lvp->v_type != VDIR) {
336 error = ENOTDIR;
337 goto out;
338 }
339
340 /*
341 * access check here is optional, depending on
342 * whether or not caller cares.
343 */
344 if (flags & GETCWD_CHECK_ACCESS) {
345 error = VOP_ACCESS(lvp, perms, td->td_ucred, td);
346 if (error)
347 goto out;
348 perms = VEXEC|VREAD;
349 }
350
351 /*
352 * step up if we're a covered vnode..
353 */
354 while (lvp->v_vflag & VV_ROOT) {
355 struct vnode *tvp;
356
357 if (lvp == rvp)
358 goto out;
359
360 tvp = lvp;
361 lvp = lvp->v_mount->mnt_vnodecovered;
362 vput(tvp);
363 /*
364 * hodie natus est radici frater
365 */
366 if (lvp == NULL) {
367 error = ENOENT;
368 goto out;
369 }
370 VREF(lvp);
371 error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
372 if (error != 0) {
373 vrele(lvp);
374 lvp = NULL;
375 goto out;
376 }
377 }
378 error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td);
379 if (error)
380 goto out;
381#if DIAGNOSTIC
382 if (lvp != NULL)
383 panic("getcwd: oops, forgot to null lvp");
384 if (bufp && (bp <= bufp)) {
385 panic("getcwd: oops, went back too far");
386 }
387#endif
388 if (bp)
389 *(--bp) = '/';
390 lvp = uvp;
391 uvp = NULL;
392 limit--;
393 } while ((lvp != rvp) && (limit > 0));
394
395out:
396 if (bpp)
397 *bpp = bp;
398 if (uvp)
399 vput(uvp);
400 if (lvp)
401 vput(lvp);
402 vrele(rvp);
403 return error;
404}
405
406
407/*
50 * Find pathname of process's current directory.
51 *
52 * Use vfs vnode-to-name reverse cache; if that fails, fall back
408 * Find pathname of process's current directory.
409 *
410 * Use vfs vnode-to-name reverse cache; if that fails, fall back
53 * to reading directory contents. Use the common routines in vfs_cache.c.
411 * to reading directory contents.
54 */
55
56int
57linux_getcwd(struct thread *td, struct linux_getcwd_args *args)
58{
412 */
413
414int
415linux_getcwd(struct thread *td, struct linux_getcwd_args *args)
416{
59 return (kern___getcwd (td, args->buf, UIO_USERSPACE, args->bufsize));
417 caddr_t bp, bend, path;
418 int error, len, lenused;
419
420#ifdef DEBUG
421 printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid,
422 args->buf, (long)args->bufsize);
423#endif
424
425 len = args->bufsize;
426
427 if (len > MAXPATHLEN*4)
428 len = MAXPATHLEN*4;
429 else if (len < 2)
430 return ERANGE;
431
432 path = (char *)malloc(len, M_TEMP, M_WAITOK);
433
434 error = kern___getcwd(td, path, UIO_SYSSPACE, len);
435 if (!error) {
436 lenused = strlen(path) + 1;
437 if (lenused <= args->bufsize) {
438 td->td_retval[0] = lenused;
439 error = copyout(path, args->buf, lenused);
440 }
441 else
442 error = ERANGE;
443 } else {
444 bp = &path[len];
445 bend = bp;
446 *(--bp) = '\0';
447
448 /*
449 * 5th argument here is "max number of vnodes to traverse".
450 * Since each entry takes up at least 2 bytes in the output buffer,
451 * limit it to N/2 vnodes for an N byte buffer.
452 */
453
454 error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL,
455 &bp, path, len/2, GETCWD_CHECK_ACCESS, td);
456
457 if (error)
458 goto out;
459 lenused = bend - bp;
460 td->td_retval[0] = lenused;
461 /* put the result into user buffer */
462 error = copyout(bp, args->buf, lenused);
463 }
464out:
465 free(path, M_TEMP);
466 return (error);
60}
61
467}
468