1 /* $NetBSD: linux32_dirent.c,v 1.8 2008/12/29 14:33:40 njoly Exp $ */
4 * Copyright (c) 2006 Emmanuel Dreyfus, all rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Emmanuel Dreyfus
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior written
21 * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: linux32_dirent.c,v 1.8 2008/12/29 14:33:40 njoly Exp $");
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/fstypes.h>
41 #include <sys/signal.h>
42 #include <sys/dirent.h>
43 #include <sys/kernel.h>
44 #include <sys/fcntl.h>
46 #include <sys/filedesc.h>
47 #include <sys/malloc.h>
48 #include <sys/select.h>
50 #include <sys/ucred.h>
51 #include <sys/vnode.h>
54 #include <machine/types.h>
56 #include <sys/syscallargs.h>
58 #include <compat/netbsd32/netbsd32.h>
59 #include <compat/netbsd32/netbsd32_conv.h>
60 #include <compat/netbsd32/netbsd32_syscallargs.h>
62 #include <compat/linux/common/linux_types.h>
63 #include <compat/linux/common/linux_signal.h>
64 #include <compat/linux/common/linux_machdep.h>
65 #include <compat/linux/common/linux_misc.h>
66 #include <compat/linux/common/linux_oldolduname.h>
67 #include <compat/linux/common/linux_dirent.h>
68 #include <compat/linux/common/linux_ipc.h>
69 #include <compat/linux/common/linux_sem.h>
70 #include <compat/linux/linux_syscallargs.h>
72 #include <compat/linux32/common/linux32_types.h>
73 #include <compat/linux32/common/linux32_signal.h>
74 #include <compat/linux32/common/linux32_machdep.h>
75 #include <compat/linux32/common/linux32_sysctl.h>
76 #include <compat/linux32/common/linux32_socketcall.h>
77 #include <compat/linux32/linux32_syscallargs.h>
80 * Linux 'readdir' call. This code is mostly taken from the
81 * SunOS getdents call (see compat/sunos/sunos_misc.c), though
82 * an attempt has been made to keep it a little cleaner (failing
83 * miserably, because of the cruft needed if count 1 is passed).
85 * The d_off field should contain the offset of the next valid entry,
86 * but in Linux it has the offset of the entry itself. We emulate
89 * Read in BSD-style entries, convert them, and copy them out.
91 * Note that this doesn't handle union-mounted filesystems.
95 linux32_sys_getdents(struct lwp
*l
, const struct linux32_sys_getdents_args
*uap
, register_t
*retval
)
99 syscallarg(linux32_direntp_t) dent;
100 syscallarg(unsigned int) count;
104 char *inp
, *tbuf
; /* BSD-format */
105 int len
, reclen
; /* BSD-format */
106 char *outp
; /* Linux-format */
107 int resid
, linux32_reclen
= 0; /* Linux-format */
111 struct linux32_dirent idb
;
112 off_t off
; /* true file offset */
113 int buflen
, error
, eofflag
, nbytes
, oldcall
;
115 off_t
*cookiebuf
= NULL
, *cookie
;
118 /* fd_getvnode() will use the descriptor for us */
119 if ((error
= fd_getvnode(SCARG(uap
, fd
), &fp
)) != 0)
122 if ((fp
->f_flag
& FREAD
) == 0) {
127 vp
= (struct vnode
*)fp
->f_data
;
128 if (vp
->v_type
!= VDIR
) {
133 if ((error
= VOP_GETATTR(vp
, &va
, l
->l_cred
)))
136 nbytes
= SCARG(uap
, count
);
137 if (nbytes
== 1) { /* emulating old, broken behaviour */
138 nbytes
= sizeof (idb
);
139 buflen
= max(va
.va_blocksize
, nbytes
);
142 buflen
= min(MAXBSIZE
, nbytes
);
143 if (buflen
< va
.va_blocksize
)
144 buflen
= va
.va_blocksize
;
147 tbuf
= malloc(buflen
, M_TEMP
, M_WAITOK
);
149 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
152 aiov
.iov_base
= tbuf
;
153 aiov
.iov_len
= buflen
;
154 auio
.uio_iov
= &aiov
;
156 auio
.uio_rw
= UIO_READ
;
157 auio
.uio_resid
= buflen
;
158 auio
.uio_offset
= off
;
159 UIO_SETUP_SYSSPACE(&auio
);
161 * First we read into the malloc'ed buffer, then
162 * we massage it into user space, one record at a time.
164 error
= VOP_READDIR(vp
, &auio
, fp
->f_cred
, &eofflag
, &cookiebuf
,
170 outp
= (void *)SCARG_P32(uap
, dent
);
172 if ((len
= buflen
- auio
.uio_resid
) == 0)
175 for (cookie
= cookiebuf
; len
> 0; len
-= reclen
) {
176 bdp
= (struct dirent
*)inp
;
177 reclen
= bdp
->d_reclen
;
179 panic("linux32_readdir");
180 if (bdp
->d_fileno
== 0) {
181 inp
+= reclen
; /* it is a hole; squish it out */
188 linux32_reclen
= LINUX_RECLEN(&idb
, bdp
->d_namlen
);
189 if (reclen
> len
|| resid
< linux32_reclen
) {
190 /* entry too big for buffer, so just stop */
195 * Massage in place to make a Linux-shaped dirent (otherwise
196 * we have to worry about touching user memory outside of
197 * the copyout() call).
199 idb
.d_ino
= bdp
->d_fileno
;
201 * The old readdir() call misuses the offset and reclen fields.
204 idb
.d_off
= (linux32_off_t
)linux32_reclen
;
205 idb
.d_reclen
= (u_short
)bdp
->d_namlen
;
207 if (sizeof (idb
.d_off
) <= 4 && (off
>> 32) != 0) {
208 compat_offseterr(vp
, "linux32_getdents");
212 idb
.d_off
= (linux32_off_t
)off
;
213 idb
.d_reclen
= (u_short
)linux32_reclen
;
215 strcpy(idb
.d_name
, bdp
->d_name
);
216 if ((error
= copyout((void *)&idb
, outp
, linux32_reclen
)))
218 /* advance past this real entry */
221 off
= *cookie
++; /* each entry points to itself */
224 /* advance output past Linux-shaped entry */
225 outp
+= linux32_reclen
;
226 resid
-= linux32_reclen
;
231 /* if we squished out the whole block, try again */
232 if (outp
== (void *)SCARG_P32(uap
, dent
))
234 fp
->f_offset
= off
; /* update the vnode offset */
237 nbytes
= resid
+ linux32_reclen
;
240 *retval
= nbytes
- resid
;
244 free(cookiebuf
, M_TEMP
);
247 fd_putfile(SCARG(uap
, fd
));
252 linux32_sys_getdents64(struct lwp
*l
, const struct linux32_sys_getdents64_args
*uap
, register_t
*retval
)
256 syscallarg(linux32_dirent64p_t) dent;
257 syscallarg(unsigned int) count;
259 struct linux_sys_getdents64_args ua
;
261 NETBSD32TO64_UAP(fd
);
262 NETBSD32TOP_UAP(dent
, struct linux_dirent64
);
263 NETBSD32TO64_UAP(count
);
265 return linux_sys_getdents64(l
, &ua
, retval
);
269 linux32_sys_readdir(struct lwp
*l
, const struct linux32_sys_readdir_args
*uap
, register_t
*retval
)
273 syscallarg(struct linux32_direntp_t) dent;
274 syscallarg(unsigned int) count;
277 struct linux32_sys_getdents_args da
;
279 SCARG(&da
, fd
) = SCARG(uap
, fd
);
280 SCARG(&da
, dent
) = SCARG(uap
, dent
);
281 SCARG(&da
, count
) = 1;
283 error
= linux32_sys_getdents(l
, &da
, retval
);
284 if (error
== 0 && *retval
> 1)