1 /* $NetBSD: linux_file64.c,v 1.48 2008/06/24 11:18:15 ad Exp $ */
4 * Copyright (c) 1995, 1998, 2000, 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Frank van der Linden and Eric Haszlakiewicz.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
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
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
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
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
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Linux 64bit filesystem calls. Used on 32bit archs, not used on 64bit ones.
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: linux_file64.c,v 1.48 2008/06/24 11:18:15 ad Exp $");
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/namei.h>
43 #include <sys/dirent.h>
46 #include <sys/filedesc.h>
47 #include <sys/ioctl.h>
48 #include <sys/kernel.h>
49 #include <sys/mount.h>
50 #include <sys/malloc.h>
51 #include <sys/namei.h>
52 #include <sys/vfs_syscalls.h>
53 #include <sys/vnode.h>
57 #include <sys/syscallargs.h>
59 #include <compat/linux/common/linux_types.h>
60 #include <compat/linux/common/linux_signal.h>
61 #include <compat/linux/common/linux_fcntl.h>
62 #include <compat/linux/common/linux_util.h>
63 #include <compat/linux/common/linux_machdep.h>
64 #include <compat/linux/common/linux_dirent.h>
65 #include <compat/linux/common/linux_ipc.h>
66 #include <compat/linux/common/linux_sem.h>
68 #include <compat/linux/linux_syscallargs.h>
72 static void bsd_to_linux_stat(struct stat
*, struct linux_stat64
*);
75 * Convert a NetBSD stat structure to a Linux stat structure.
76 * Only the order of the fields and the padding in the structure
77 * is different. linux_fakedev is a machine-dependent function
78 * which optionally converts device driver major/minor numbers
79 * (XXX horrible, but what can you do against code that compares
80 * things against constant major device numbers? sigh)
83 bsd_to_linux_stat(struct stat
*bsp
, struct linux_stat64
*lsp
)
85 lsp
->lst_dev
= linux_fakedev(bsp
->st_dev
, 0);
86 lsp
->lst_ino
= bsp
->st_ino
;
87 lsp
->lst_mode
= (linux_mode_t
)bsp
->st_mode
;
88 if (bsp
->st_nlink
>= (1 << 15))
89 lsp
->lst_nlink
= (1 << 15) - 1;
91 lsp
->lst_nlink
= (linux_nlink_t
)bsp
->st_nlink
;
92 lsp
->lst_uid
= bsp
->st_uid
;
93 lsp
->lst_gid
= bsp
->st_gid
;
94 lsp
->lst_rdev
= linux_fakedev(bsp
->st_rdev
, 1);
95 lsp
->lst_size
= bsp
->st_size
;
96 lsp
->lst_blksize
= bsp
->st_blksize
;
97 lsp
->lst_blocks
= bsp
->st_blocks
;
98 lsp
->lst_atime
= bsp
->st_atime
;
99 lsp
->lst_mtime
= bsp
->st_mtime
;
100 lsp
->lst_ctime
= bsp
->st_ctime
;
101 # ifdef LINUX_STAT64_HAS_NSEC
102 lsp
->lst_atime_nsec
= bsp
->st_atimensec
;
103 lsp
->lst_mtime_nsec
= bsp
->st_mtimensec
;
104 lsp
->lst_ctime_nsec
= bsp
->st_ctimensec
;
106 # if LINUX_STAT64_HAS_BROKEN_ST_INO
107 lsp
->__lst_ino
= (linux_ino_t
) bsp
->st_ino
;
112 * The stat functions below are plain sailing. stat and lstat are handled
113 * by one function to avoid code duplication.
116 linux_sys_fstat64(struct lwp
*l
, const struct linux_sys_fstat64_args
*uap
, register_t
*retval
)
120 syscallarg(struct linux_stat64 *) sp;
122 struct linux_stat64 tmplst
;
126 error
= do_sys_fstat(SCARG(uap
, fd
), &tmpst
);
130 bsd_to_linux_stat(&tmpst
, &tmplst
);
132 return copyout(&tmplst
, SCARG(uap
, sp
), sizeof tmplst
);
136 linux_do_stat64(struct lwp
*l
, const struct linux_sys_stat64_args
*uap
, register_t
*retval
, int flags
)
138 struct linux_stat64 tmplst
;
142 error
= do_sys_stat(SCARG(uap
, path
), flags
, &tmpst
);
146 bsd_to_linux_stat(&tmpst
, &tmplst
);
148 return copyout(&tmplst
, SCARG(uap
, sp
), sizeof tmplst
);
152 linux_sys_stat64(struct lwp
*l
, const struct linux_sys_stat64_args
*uap
, register_t
*retval
)
155 syscallarg(const char *) path;
156 syscallarg(struct linux_stat64 *) sp;
159 return linux_do_stat64(l
, uap
, retval
, FOLLOW
);
163 linux_sys_lstat64(struct lwp
*l
, const struct linux_sys_lstat64_args
*uap
, register_t
*retval
)
166 syscallarg(const char *) path;
167 syscallarg(struct linux_stat64 *) sp;
170 return linux_do_stat64(l
, (const void *)uap
, retval
, NOFOLLOW
);
174 linux_sys_truncate64(struct lwp
*l
, const struct linux_sys_truncate64_args
*uap
, register_t
*retval
)
177 syscallarg(const char *) path;
178 syscallarg(off_t) length;
180 struct sys_truncate_args ta
;
182 /* Linux doesn't have the 'pad' pseudo-parameter */
183 SCARG(&ta
, path
) = SCARG(uap
, path
);
185 SCARG(&ta
, length
) = SCARG(uap
, length
);
187 return sys_truncate(l
, &ta
, retval
);
191 linux_sys_ftruncate64(struct lwp
*l
, const struct linux_sys_ftruncate64_args
*uap
, register_t
*retval
)
194 syscallarg(unsigned int) fd;
195 syscallarg(off_t) length;
197 struct sys_ftruncate_args ta
;
199 /* Linux doesn't have the 'pad' pseudo-parameter */
200 SCARG(&ta
, fd
) = SCARG(uap
, fd
);
202 SCARG(&ta
, length
) = SCARG(uap
, length
);
204 return sys_ftruncate(l
, &ta
, retval
);
209 * Linux 'readdir' call. This code is mostly taken from the
210 * SunOS getdents call (see compat/sunos/sunos_misc.c), though
211 * an attempt has been made to keep it a little cleaner.
213 * The d_off field contains the offset of the next valid entry,
214 * unless the older Linux getdents(2), which used to have it set
215 * to the offset of the entry itself. This function also doesn't
216 * need to deal with the old count == 1 glibc problem.
218 * Read in BSD-style entries, convert them, and copy them out.
220 * Note that this doesn't handle union-mounted filesystems.
223 linux_sys_getdents64(struct lwp
*l
, const struct linux_sys_getdents64_args
*uap
, register_t
*retval
)
227 syscallarg(struct linux_dirent64 *) dent;
228 syscallarg(unsigned int) count;
232 char *inp
, *tbuf
; /* BSD-format */
233 int len
, reclen
; /* BSD-format */
234 char *outp
; /* Linux-format */
235 int resid
, linux_reclen
= 0; /* Linux-format */
239 struct linux_dirent64 idb
;
240 off_t off
; /* true file offset */
241 int buflen
, error
, eofflag
, nbytes
;
243 off_t
*cookiebuf
= NULL
, *cookie
;
246 /* fd_getvnode() will use the descriptor for us */
247 if ((error
= fd_getvnode(SCARG(uap
, fd
), &fp
)) != 0)
250 if ((fp
->f_flag
& FREAD
) == 0) {
255 vp
= (struct vnode
*)fp
->f_data
;
256 if (vp
->v_type
!= VDIR
) {
261 if ((error
= VOP_GETATTR(vp
, &va
, l
->l_cred
)))
264 nbytes
= SCARG(uap
, count
);
265 buflen
= min(MAXBSIZE
, nbytes
);
266 if (buflen
< va
.va_blocksize
)
267 buflen
= va
.va_blocksize
;
268 tbuf
= malloc(buflen
, M_TEMP
, M_WAITOK
);
270 vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
273 aiov
.iov_base
= tbuf
;
274 aiov
.iov_len
= buflen
;
275 auio
.uio_iov
= &aiov
;
277 auio
.uio_rw
= UIO_READ
;
278 auio
.uio_resid
= buflen
;
279 auio
.uio_offset
= off
;
280 UIO_SETUP_SYSSPACE(&auio
);
282 * First we read into the malloc'ed buffer, then
283 * we massage it into user space, one record at a time.
285 error
= VOP_READDIR(vp
, &auio
, fp
->f_cred
, &eofflag
, &cookiebuf
,
291 outp
= (void *)SCARG(uap
, dent
);
293 if ((len
= buflen
- auio
.uio_resid
) == 0)
296 for (cookie
= cookiebuf
; len
> 0; len
-= reclen
) {
297 bdp
= (struct dirent
*)inp
;
298 reclen
= bdp
->d_reclen
;
300 panic("linux_readdir");
301 if (bdp
->d_fileno
== 0) {
302 inp
+= reclen
; /* it is a hole; squish it out */
309 linux_reclen
= LINUX_RECLEN(&idb
, bdp
->d_namlen
);
310 if (reclen
> len
|| resid
< linux_reclen
) {
311 /* entry too big for buffer, so just stop */
316 off
= *cookie
++; /* each entry points to next */
320 * Massage in place to make a Linux-shaped dirent (otherwise
321 * we have to worry about touching user memory outside of
322 * the copyout() call).
324 idb
.d_ino
= bdp
->d_fileno
;
325 idb
.d_type
= bdp
->d_type
;
327 idb
.d_reclen
= (u_short
)linux_reclen
;
328 strcpy(idb
.d_name
, bdp
->d_name
);
329 if ((error
= copyout((void *)&idb
, outp
, linux_reclen
)))
331 /* advance past this real entry */
333 /* advance output past Linux-shaped entry */
334 outp
+= linux_reclen
;
335 resid
-= linux_reclen
;
338 /* if we squished out the whole block, try again */
339 if (outp
== (void *)SCARG(uap
, dent
))
341 fp
->f_offset
= off
; /* update the vnode offset */
344 *retval
= nbytes
- resid
;
348 free(cookiebuf
, M_TEMP
);
351 fd_putfile(SCARG(uap
, fd
));