xref: /netbsd-src/sys/compat/linux/common/linux_file64.c (revision a0a4eb1d2ef812bd289da9273c2bd475b6f3e30c)
1*a0a4eb1dSchristos /*	$NetBSD: linux_file64.c,v 1.68 2023/07/29 15:04:29 christos Exp $	*/
2daff00c2Sjdolecek 
3daff00c2Sjdolecek /*-
4a9ca7a37Sad  * Copyright (c) 1995, 1998, 2000, 2008 The NetBSD Foundation, Inc.
5daff00c2Sjdolecek  * All rights reserved.
6daff00c2Sjdolecek  *
7daff00c2Sjdolecek  * This code is derived from software contributed to The NetBSD Foundation
8daff00c2Sjdolecek  * by Frank van der Linden and Eric Haszlakiewicz.
9daff00c2Sjdolecek  *
10daff00c2Sjdolecek  * Redistribution and use in source and binary forms, with or without
11daff00c2Sjdolecek  * modification, are permitted provided that the following conditions
12daff00c2Sjdolecek  * are met:
13daff00c2Sjdolecek  * 1. Redistributions of source code must retain the above copyright
14daff00c2Sjdolecek  *    notice, this list of conditions and the following disclaimer.
15daff00c2Sjdolecek  * 2. Redistributions in binary form must reproduce the above copyright
16daff00c2Sjdolecek  *    notice, this list of conditions and the following disclaimer in the
17daff00c2Sjdolecek  *    documentation and/or other materials provided with the distribution.
18daff00c2Sjdolecek  *
19daff00c2Sjdolecek  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20daff00c2Sjdolecek  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21daff00c2Sjdolecek  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22daff00c2Sjdolecek  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23daff00c2Sjdolecek  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24daff00c2Sjdolecek  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25daff00c2Sjdolecek  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26daff00c2Sjdolecek  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27daff00c2Sjdolecek  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28daff00c2Sjdolecek  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29daff00c2Sjdolecek  * POSSIBILITY OF SUCH DAMAGE.
30daff00c2Sjdolecek  */
31daff00c2Sjdolecek 
32daff00c2Sjdolecek /*
33daff00c2Sjdolecek  * Linux 64bit filesystem calls. Used on 32bit archs, not used on 64bit ones.
34daff00c2Sjdolecek  */
35daff00c2Sjdolecek 
36dab6ef8bSlukem #include <sys/cdefs.h>
37*a0a4eb1dSchristos __KERNEL_RCSID(0, "$NetBSD: linux_file64.c,v 1.68 2023/07/29 15:04:29 christos Exp $");
38dab6ef8bSlukem 
39daff00c2Sjdolecek #include <sys/param.h>
40daff00c2Sjdolecek #include <sys/systm.h>
41daff00c2Sjdolecek #include <sys/namei.h>
42daff00c2Sjdolecek #include <sys/proc.h>
43add83289Stron #include <sys/dirent.h>
44daff00c2Sjdolecek #include <sys/file.h>
45daff00c2Sjdolecek #include <sys/stat.h>
46daff00c2Sjdolecek #include <sys/filedesc.h>
47daff00c2Sjdolecek #include <sys/ioctl.h>
48daff00c2Sjdolecek #include <sys/kernel.h>
49daff00c2Sjdolecek #include <sys/mount.h>
50daff00c2Sjdolecek #include <sys/malloc.h>
51893faeaeSdsl #include <sys/namei.h>
52893faeaeSdsl #include <sys/vfs_syscalls.h>
53daff00c2Sjdolecek #include <sys/vnode.h>
54daff00c2Sjdolecek #include <sys/tty.h>
55daff00c2Sjdolecek #include <sys/conf.h>
56daff00c2Sjdolecek 
57daff00c2Sjdolecek #include <sys/syscallargs.h>
58daff00c2Sjdolecek 
59daff00c2Sjdolecek #include <compat/linux/common/linux_types.h>
60daff00c2Sjdolecek #include <compat/linux/common/linux_signal.h>
61daff00c2Sjdolecek #include <compat/linux/common/linux_fcntl.h>
62daff00c2Sjdolecek #include <compat/linux/common/linux_util.h>
63daff00c2Sjdolecek #include <compat/linux/common/linux_machdep.h>
64add83289Stron #include <compat/linux/common/linux_dirent.h>
65a478f23bSnjoly #include <compat/linux/common/linux_ipc.h>
66a478f23bSnjoly #include <compat/linux/common/linux_sem.h>
67daff00c2Sjdolecek 
685fe0554cSryo #include <compat/linux/linux_syscall.h>
69daff00c2Sjdolecek #include <compat/linux/linux_syscallargs.h>
70daff00c2Sjdolecek 
714cd291c6Sryo static void bsd_to_linux_stat64(struct stat *, struct linux_stat64 *);
72daff00c2Sjdolecek 
73daff00c2Sjdolecek /*
74daff00c2Sjdolecek  * Convert a NetBSD stat structure to a Linux stat structure.
75daff00c2Sjdolecek  * Only the order of the fields and the padding in the structure
76daff00c2Sjdolecek  * is different. linux_fakedev is a machine-dependent function
77daff00c2Sjdolecek  * which optionally converts device driver major/minor numbers
78daff00c2Sjdolecek  * (XXX horrible, but what can you do against code that compares
79daff00c2Sjdolecek  * things against constant major device numbers? sigh)
80daff00c2Sjdolecek  */
81daff00c2Sjdolecek static void
bsd_to_linux_stat64(struct stat * bsp,struct linux_stat64 * lsp)824cd291c6Sryo bsd_to_linux_stat64(struct stat *bsp, struct linux_stat64 *lsp)
83daff00c2Sjdolecek {
840289089dSmaxv 	memset(lsp, 0, sizeof(*lsp));
8528debea3Schristos 	lsp->lst_dev     = linux_fakedev(bsp->st_dev, 0);
86daff00c2Sjdolecek 	lsp->lst_ino     = bsp->st_ino;
87daff00c2Sjdolecek 	lsp->lst_mode    = (linux_mode_t)bsp->st_mode;
88daff00c2Sjdolecek 	if (bsp->st_nlink >= (1 << 15))
89daff00c2Sjdolecek 		lsp->lst_nlink = (1 << 15) - 1;
90daff00c2Sjdolecek 	else
91daff00c2Sjdolecek 		lsp->lst_nlink = (linux_nlink_t)bsp->st_nlink;
92daff00c2Sjdolecek 	lsp->lst_uid     = bsp->st_uid;
93daff00c2Sjdolecek 	lsp->lst_gid     = bsp->st_gid;
9428debea3Schristos 	lsp->lst_rdev    = linux_fakedev(bsp->st_rdev, 1);
95daff00c2Sjdolecek 	lsp->lst_size    = bsp->st_size;
96daff00c2Sjdolecek 	lsp->lst_blksize = bsp->st_blksize;
97daff00c2Sjdolecek 	lsp->lst_blocks  = bsp->st_blocks;
98daff00c2Sjdolecek 	lsp->lst_atime   = bsp->st_atime;
99daff00c2Sjdolecek 	lsp->lst_mtime   = bsp->st_mtime;
100daff00c2Sjdolecek 	lsp->lst_ctime   = bsp->st_ctime;
10147e40fceSchristos #  ifdef LINUX_STAT64_HAS_NSEC
10247e40fceSchristos 	lsp->lst_atime_nsec   = bsp->st_atimensec;
10347e40fceSchristos 	lsp->lst_mtime_nsec   = bsp->st_mtimensec;
10447e40fceSchristos 	lsp->lst_ctime_nsec   = bsp->st_ctimensec;
10547e40fceSchristos #  endif
106bfbe5061Sjdolecek #  if LINUX_STAT64_HAS_BROKEN_ST_INO
107bfbe5061Sjdolecek 	lsp->__lst_ino   = (linux_ino_t) bsp->st_ino;
108bfbe5061Sjdolecek #  endif
109daff00c2Sjdolecek }
110daff00c2Sjdolecek 
1115fe0554cSryo int
bsd_to_linux_statx(struct stat * st,struct linux_statx * stx,unsigned int mask)1125fe0554cSryo bsd_to_linux_statx(struct stat *st, struct linux_statx *stx,
1135fe0554cSryo     unsigned int mask)
1145fe0554cSryo {
1155fe0554cSryo 	if (mask & STATX__RESERVED)
1165fe0554cSryo 		return EINVAL;
1175fe0554cSryo 
1185fe0554cSryo 	/* XXX: STATX_MNT_ID is not supported */
1195fe0554cSryo 	unsigned int rmask = STATX_TYPE | STATX_MODE | STATX_NLINK |
1205fe0554cSryo 	    STATX_UID | STATX_GID | STATX_ATIME | STATX_MTIME | STATX_CTIME |
1215fe0554cSryo 	    STATX_INO | STATX_SIZE | STATX_BLOCKS | STATX_BTIME;
1225fe0554cSryo 
1235fe0554cSryo 	memset(stx, 0, sizeof(*stx));
1245fe0554cSryo 
1255fe0554cSryo 	if ((st->st_flags & UF_NODUMP) != 0)
1265fe0554cSryo 		stx->stx_attributes |= STATX_ATTR_NODUMP;
1275fe0554cSryo 	if ((st->st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) != 0)
1285fe0554cSryo 		stx->stx_attributes |= STATX_ATTR_IMMUTABLE;
1295fe0554cSryo 	if ((st->st_flags & (UF_APPEND|SF_APPEND)) != 0)
1305fe0554cSryo 		stx->stx_attributes |= STATX_ATTR_APPEND;
1315fe0554cSryo 
1325fe0554cSryo 	stx->stx_attributes_mask =
1335fe0554cSryo 	    STATX_ATTR_NODUMP | STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND;
1345fe0554cSryo 
1355fe0554cSryo 	stx->stx_blksize = st->st_blksize;
1365fe0554cSryo 
1375fe0554cSryo 	stx->stx_nlink = st->st_nlink;
1385fe0554cSryo 	stx->stx_uid = st->st_uid;
1395fe0554cSryo 	stx->stx_gid = st->st_gid;
1405fe0554cSryo 	stx->stx_mode |= st->st_mode & S_IFMT;
1415fe0554cSryo 	stx->stx_mode |= st->st_mode & ~S_IFMT;
1425fe0554cSryo 	stx->stx_ino = st->st_ino;
1435fe0554cSryo 	stx->stx_size = st->st_size;
1445fe0554cSryo 	stx->stx_blocks = st->st_blocks;
1455fe0554cSryo 
1465fe0554cSryo 	stx->stx_atime.tv_sec = st->st_atime;
1475fe0554cSryo 	stx->stx_atime.tv_nsec = st->st_atimensec;
1485fe0554cSryo 
1495fe0554cSryo 	/* some filesystem has no birthtime returns 0 or -1 */
1505fe0554cSryo 	if ((st->st_birthtime == 0 && st->st_birthtimensec == 0) ||
1515fe0554cSryo 	    (st->st_birthtime == (time_t)-1 &&
1525fe0554cSryo 	    st->st_birthtimensec == (long)-1)) {
1535fe0554cSryo 		rmask &= ~STATX_BTIME;
1545fe0554cSryo 	} else {
1555fe0554cSryo 		stx->stx_btime.tv_sec = st->st_birthtime;
1565fe0554cSryo 		stx->stx_btime.tv_nsec = st->st_birthtimensec;
1575fe0554cSryo 	}
1585fe0554cSryo 
1595fe0554cSryo 	stx->stx_ctime.tv_sec = st->st_ctime;
1605fe0554cSryo 	stx->stx_ctime.tv_nsec = st->st_ctimensec;
1615fe0554cSryo 
1625fe0554cSryo 	stx->stx_mtime.tv_sec = st->st_mtime;
1635fe0554cSryo 	stx->stx_mtime.tv_nsec = st->st_mtimensec;
1645fe0554cSryo 
1655fe0554cSryo 	if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
1665fe0554cSryo 		stx->stx_rdev_major = major(st->st_rdev);
1675fe0554cSryo 		stx->stx_rdev_minor = minor(st->st_rdev);
1685fe0554cSryo 	} else {
1695fe0554cSryo 		stx->stx_dev_major = major(st->st_rdev);
1705fe0554cSryo 		stx->stx_dev_minor = minor(st->st_rdev);
1715fe0554cSryo 	}
1725fe0554cSryo 
1735fe0554cSryo 	stx->stx_mask = rmask;
1745fe0554cSryo 
1755fe0554cSryo 	return 0;
1765fe0554cSryo }
1775fe0554cSryo 
178daff00c2Sjdolecek /*
179daff00c2Sjdolecek  * The stat functions below are plain sailing. stat and lstat are handled
180daff00c2Sjdolecek  * by one function to avoid code duplication.
181daff00c2Sjdolecek  */
182daff00c2Sjdolecek int
linux_sys_fstat64(struct lwp * l,const struct linux_sys_fstat64_args * uap,register_t * retval)1837e2790cfSdsl linux_sys_fstat64(struct lwp *l, const struct linux_sys_fstat64_args *uap, register_t *retval)
184daff00c2Sjdolecek {
1857e2790cfSdsl 	/* {
186daff00c2Sjdolecek 		syscallarg(int) fd;
187abcf0c84Smanu 		syscallarg(struct linux_stat64 *) sp;
1887e2790cfSdsl 	} */
189daff00c2Sjdolecek 	struct linux_stat64 tmplst;
190893faeaeSdsl 	struct stat tmpst;
191daff00c2Sjdolecek 	int error;
192daff00c2Sjdolecek 
193a9ca7a37Sad 	error = do_sys_fstat(SCARG(uap, fd), &tmpst);
194893faeaeSdsl 	if (error != 0)
195daff00c2Sjdolecek 		return error;
196daff00c2Sjdolecek 
1974cd291c6Sryo 	bsd_to_linux_stat64(&tmpst, &tmplst);
198daff00c2Sjdolecek 
199893faeaeSdsl 	return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst);
200daff00c2Sjdolecek }
201daff00c2Sjdolecek 
202abbc8a65Sryo #if !defined(__aarch64__)
203daff00c2Sjdolecek static int
linux_do_stat64(struct lwp * l,const struct linux_sys_stat64_args * uap,register_t * retval,int flags)2047e2790cfSdsl linux_do_stat64(struct lwp *l, const struct linux_sys_stat64_args *uap, register_t *retval, int flags)
205daff00c2Sjdolecek {
206daff00c2Sjdolecek 	struct linux_stat64 tmplst;
207893faeaeSdsl 	struct stat tmpst;
208daff00c2Sjdolecek 	int error;
209daff00c2Sjdolecek 
210a9ca7a37Sad 	error = do_sys_stat(SCARG(uap, path), flags, &tmpst);
211893faeaeSdsl 	if (error != 0)
212daff00c2Sjdolecek 		return error;
213daff00c2Sjdolecek 
2144cd291c6Sryo 	bsd_to_linux_stat64(&tmpst, &tmplst);
215daff00c2Sjdolecek 
216893faeaeSdsl 	return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst);
217daff00c2Sjdolecek }
218daff00c2Sjdolecek 
219daff00c2Sjdolecek int
linux_sys_stat64(struct lwp * l,const struct linux_sys_stat64_args * uap,register_t * retval)2207e2790cfSdsl linux_sys_stat64(struct lwp *l, const struct linux_sys_stat64_args *uap, register_t *retval)
221daff00c2Sjdolecek {
2227e2790cfSdsl 	/* {
223daff00c2Sjdolecek 		syscallarg(const char *) path;
224daff00c2Sjdolecek 		syscallarg(struct linux_stat64 *) sp;
2257e2790cfSdsl 	} */
226daff00c2Sjdolecek 
227893faeaeSdsl 	return linux_do_stat64(l, uap, retval, FOLLOW);
228daff00c2Sjdolecek }
229daff00c2Sjdolecek 
230daff00c2Sjdolecek int
linux_sys_lstat64(struct lwp * l,const struct linux_sys_lstat64_args * uap,register_t * retval)2317e2790cfSdsl linux_sys_lstat64(struct lwp *l, const struct linux_sys_lstat64_args *uap, register_t *retval)
232daff00c2Sjdolecek {
2337e2790cfSdsl 	/* {
234daff00c2Sjdolecek 		syscallarg(const char *) path;
235daff00c2Sjdolecek 		syscallarg(struct linux_stat64 *) sp;
2367e2790cfSdsl 	} */
237daff00c2Sjdolecek 
2387e2790cfSdsl 	return linux_do_stat64(l, (const void *)uap, retval, NOFOLLOW);
239daff00c2Sjdolecek }
240abbc8a65Sryo #endif
241638d2777Sjdolecek 
2425fe0554cSryo /*
2435fe0554cSryo  * This is an internal function for the *statat() variant of linux,
2445fe0554cSryo  * which returns struct stat, but flags and other handling are
2455fe0554cSryo  * the same as in linux.
2465fe0554cSryo  */
2475fe0554cSryo int
linux_statat(struct lwp * l,int fd,const char * path,int lflag,struct stat * st)2485fe0554cSryo linux_statat(struct lwp *l, int fd, const char *path, int lflag,
2495fe0554cSryo     struct stat *st)
2505fe0554cSryo {
2515fe0554cSryo 	struct vnode *vp;
2525fe0554cSryo 	int error, nd_flag;
2535fe0554cSryo 	uint8_t c;
2545fe0554cSryo 
255*a0a4eb1dSchristos 	if (lflag & ~(LINUX_AT_EMPTY_PATH|LINUX_AT_NO_AUTOMOUNT
256*a0a4eb1dSchristos             |LINUX_AT_SYMLINK_NOFOLLOW))
257*a0a4eb1dSchristos 		return EINVAL;
258*a0a4eb1dSchristos 
2595fe0554cSryo 	if (lflag & LINUX_AT_EMPTY_PATH) {
2605fe0554cSryo 		/*
2615fe0554cSryo 		 * If path is null string:
2625fe0554cSryo 		 */
2635fe0554cSryo 		error = ufetch_8(path, &c);
2645fe0554cSryo 		if (error != 0)
2655fe0554cSryo 			return error;
2665fe0554cSryo 		if (c == '\0') {
2675fe0554cSryo 			if (fd == LINUX_AT_FDCWD) {
2685fe0554cSryo 				/*
2695fe0554cSryo 				 * operate on current directory
2705fe0554cSryo 				 */
2715fe0554cSryo 				vp = l->l_proc->p_cwdi->cwdi_cdir;
2725fe0554cSryo 				vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
2735fe0554cSryo 				error = vn_stat(vp, st);
2745fe0554cSryo 				VOP_UNLOCK(vp);
2755fe0554cSryo 			} else {
2765fe0554cSryo 				/*
2775fe0554cSryo 				 * operate on fd
2785fe0554cSryo 				 */
2795fe0554cSryo 				error = do_sys_fstat(fd, st);
2805fe0554cSryo 			}
2815fe0554cSryo 			return error;
2825fe0554cSryo 		}
2835fe0554cSryo 	}
2845fe0554cSryo 
2855fe0554cSryo 	if (lflag & LINUX_AT_SYMLINK_NOFOLLOW)
2865fe0554cSryo 		nd_flag = NOFOLLOW;
2875fe0554cSryo 	else
2885fe0554cSryo 		nd_flag = FOLLOW;
2895fe0554cSryo 
2905fe0554cSryo 	return do_sys_statat(l, fd, path, nd_flag, st);
2915fe0554cSryo }
2925fe0554cSryo 
293638d2777Sjdolecek int
linux_sys_fstatat64(struct lwp * l,const struct linux_sys_fstatat64_args * uap,register_t * retval)29429a841b3Schs linux_sys_fstatat64(struct lwp *l, const struct linux_sys_fstatat64_args *uap, register_t *retval)
29529a841b3Schs {
29629a841b3Schs 	/* {
29729a841b3Schs 		syscallarg(int) fd;
29829a841b3Schs 		syscallarg(const char *) path;
29929a841b3Schs 		syscallarg(struct linux_stat64 *) sp;
30029a841b3Schs 		syscallarg(int) flag;
30129a841b3Schs 	} */
30229a841b3Schs 	struct linux_stat64 tmplst;
30329a841b3Schs 	struct stat tmpst;
3045fe0554cSryo 	int error;
30548ab0cf3Srin 
3065fe0554cSryo 	error = linux_statat(l, SCARG(uap, fd), SCARG(uap, path),
3075fe0554cSryo 	    SCARG(uap, flag), &tmpst);
30829a841b3Schs 	if (error != 0)
30929a841b3Schs 		return error;
31029a841b3Schs 
3114cd291c6Sryo 	bsd_to_linux_stat64(&tmpst, &tmplst);
31229a841b3Schs 
31329a841b3Schs 	return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst);
31429a841b3Schs }
31529a841b3Schs 
3165fe0554cSryo #ifdef LINUX_SYS_statx
3175fe0554cSryo int
linux_sys_statx(struct lwp * l,const struct linux_sys_statx_args * uap,register_t * retval)3185fe0554cSryo linux_sys_statx(struct lwp *l, const struct linux_sys_statx_args *uap,
3195fe0554cSryo     register_t *retval)
3205fe0554cSryo {
3215fe0554cSryo 	/* {
3225fe0554cSryo 		syscallarg(int) fd;
3235fe0554cSryo 		syscallarg(const char *) path;
3245fe0554cSryo 		syscallarg(int) flag;
3255fe0554cSryo 		syscallarg(unsigned int) mask;
3265fe0554cSryo 		syscallarg(struct linux_statx *) sp;
3275fe0554cSryo 	} */
3285fe0554cSryo 	struct linux_statx stx;
3295fe0554cSryo 	struct stat st;
3305fe0554cSryo 	int error;
3315fe0554cSryo 
3325fe0554cSryo 	error = linux_statat(l, SCARG(uap, fd), SCARG(uap, path),
3335fe0554cSryo 	    SCARG(uap, flag), &st);
3345fe0554cSryo 	if (error != 0)
3355fe0554cSryo 		return error;
3365fe0554cSryo 
3375fe0554cSryo 	error = bsd_to_linux_statx(&st, &stx, SCARG(uap, mask));
3385fe0554cSryo 	if (error != 0)
3395fe0554cSryo 		return error;
3405fe0554cSryo 
3415fe0554cSryo 	return copyout(&stx, SCARG(uap, sp), sizeof stx);
3425fe0554cSryo }
3435fe0554cSryo #endif /* LINUX_SYS_statx */
3445fe0554cSryo 
34587ffdd52Snjoly #ifndef __alpha__
34629a841b3Schs int
linux_sys_truncate64(struct lwp * l,const struct linux_sys_truncate64_args * uap,register_t * retval)3477e2790cfSdsl linux_sys_truncate64(struct lwp *l, const struct linux_sys_truncate64_args *uap, register_t *retval)
348638d2777Sjdolecek {
3497e2790cfSdsl 	/* {
350638d2777Sjdolecek 		syscallarg(const char *) path;
351638d2777Sjdolecek 		syscallarg(off_t) length;
3527e2790cfSdsl 	} */
35394b15227Sjdolecek 	struct sys_truncate_args ta;
354638d2777Sjdolecek 
35594b15227Sjdolecek 	/* Linux doesn't have the 'pad' pseudo-parameter */
35694b15227Sjdolecek 	SCARG(&ta, path) = SCARG(uap, path);
357dfb36ab4Spooka 	SCARG(&ta, PAD) = 0;
35894b15227Sjdolecek 	SCARG(&ta, length) = SCARG(uap, length);
35994b15227Sjdolecek 
36094b15227Sjdolecek 	return sys_truncate(l, &ta, retval);
36194b15227Sjdolecek }
36294b15227Sjdolecek 
36394b15227Sjdolecek int
linux_sys_ftruncate64(struct lwp * l,const struct linux_sys_ftruncate64_args * uap,register_t * retval)3647e2790cfSdsl linux_sys_ftruncate64(struct lwp *l, const struct linux_sys_ftruncate64_args *uap, register_t *retval)
36594b15227Sjdolecek {
3667e2790cfSdsl 	/* {
36794b15227Sjdolecek 		syscallarg(unsigned int) fd;
36894b15227Sjdolecek 		syscallarg(off_t) length;
3697e2790cfSdsl 	} */
37094b15227Sjdolecek 	struct sys_ftruncate_args ta;
37194b15227Sjdolecek 
37294b15227Sjdolecek 	/* Linux doesn't have the 'pad' pseudo-parameter */
37394b15227Sjdolecek 	SCARG(&ta, fd) = SCARG(uap, fd);
374dfb36ab4Spooka 	SCARG(&ta, PAD) = 0;
37594b15227Sjdolecek 	SCARG(&ta, length) = SCARG(uap, length);
37694b15227Sjdolecek 
37794b15227Sjdolecek 	return sys_ftruncate(l, &ta, retval);
378638d2777Sjdolecek }
37987ffdd52Snjoly #endif /* __alpha__ */
3806456f8faSmatt 
381add83289Stron /*
382add83289Stron  * Linux 'readdir' call. This code is mostly taken from the
383add83289Stron  * SunOS getdents call (see compat/sunos/sunos_misc.c), though
384ee7012d1Sjdolecek  * an attempt has been made to keep it a little cleaner.
385add83289Stron  *
386ee7012d1Sjdolecek  * The d_off field contains the offset of the next valid entry,
387ee7012d1Sjdolecek  * unless the older Linux getdents(2), which used to have it set
388ee7012d1Sjdolecek  * to the offset of the entry itself. This function also doesn't
389ee7012d1Sjdolecek  * need to deal with the old count == 1 glibc problem.
390add83289Stron  *
391add83289Stron  * Read in BSD-style entries, convert them, and copy them out.
392add83289Stron  *
393add83289Stron  * Note that this doesn't handle union-mounted filesystems.
394add83289Stron  */
395add83289Stron int
linux_sys_getdents64(struct lwp * l,const struct linux_sys_getdents64_args * uap,register_t * retval)3967e2790cfSdsl linux_sys_getdents64(struct lwp *l, const struct linux_sys_getdents64_args *uap, register_t *retval)
397add83289Stron {
3987e2790cfSdsl 	/* {
399add83289Stron 		syscallarg(int) fd;
400add83289Stron 		syscallarg(struct linux_dirent64 *) dent;
401add83289Stron 		syscallarg(unsigned int) count;
4027e2790cfSdsl 	} */
403add83289Stron 	struct dirent *bdp;
404add83289Stron 	struct vnode *vp;
40553524e44Schristos 	char *inp, *tbuf;		/* BSD-format */
406add83289Stron 	int len, reclen;		/* BSD-format */
40753524e44Schristos 	char *outp;			/* Linux-format */
408add83289Stron 	int resid, linux_reclen = 0;	/* Linux-format */
409a9ca7a37Sad 	file_t *fp;
410add83289Stron 	struct uio auio;
411add83289Stron 	struct iovec aiov;
412add83289Stron 	struct linux_dirent64 idb;
413add83289Stron 	off_t off;		/* true file offset */
414ee7012d1Sjdolecek 	int buflen, error, eofflag, nbytes;
415add83289Stron 	struct vattr va;
416add83289Stron 	off_t *cookiebuf = NULL, *cookie;
417add83289Stron 	int ncookies;
418add83289Stron 
419a00bd89dSad 	/* fd_getvnode() will use the descriptor for us */
420a00bd89dSad 	if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
421add83289Stron 		return (error);
422add83289Stron 
423add83289Stron 	if ((fp->f_flag & FREAD) == 0) {
424add83289Stron 		error = EBADF;
425add83289Stron 		goto out1;
426add83289Stron 	}
427add83289Stron 
428add83289Stron 	vp = (struct vnode *)fp->f_data;
429add83289Stron 	if (vp->v_type != VDIR) {
430e6b8e35eSnjoly 		error = ENOTDIR;
431add83289Stron 		goto out1;
432add83289Stron 	}
433add83289Stron 
4342cc7a01fShannken 	vn_lock(vp, LK_SHARED | LK_RETRY);
4352cc7a01fShannken 	error = VOP_GETATTR(vp, &va, l->l_cred);
4362cc7a01fShannken 	VOP_UNLOCK(vp);
4372cc7a01fShannken 	if (error)
438add83289Stron 		goto out1;
439add83289Stron 
440add83289Stron 	nbytes = SCARG(uap, count);
441d1579b2dSriastradh 	buflen = uimin(MAXBSIZE, nbytes);
442add83289Stron 	if (buflen < va.va_blocksize)
443add83289Stron 		buflen = va.va_blocksize;
444fb4b40b7Schristos 	tbuf = malloc(buflen, M_TEMP, M_WAITOK);
445add83289Stron 
446add83289Stron 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
447add83289Stron 	off = fp->f_offset;
448add83289Stron again:
449fb4b40b7Schristos 	aiov.iov_base = tbuf;
450add83289Stron 	aiov.iov_len = buflen;
451add83289Stron 	auio.uio_iov = &aiov;
452add83289Stron 	auio.uio_iovcnt = 1;
453add83289Stron 	auio.uio_rw = UIO_READ;
454add83289Stron 	auio.uio_resid = buflen;
455add83289Stron 	auio.uio_offset = off;
456ec5a9318Syamt 	UIO_SETUP_SYSSPACE(&auio);
457add83289Stron 	/*
458add83289Stron          * First we read into the malloc'ed buffer, then
459add83289Stron          * we massage it into user space, one record at a time.
460add83289Stron          */
461add83289Stron 	error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf,
462add83289Stron 	    &ncookies);
463add83289Stron 	if (error)
464add83289Stron 		goto out;
465add83289Stron 
466fb4b40b7Schristos 	inp = tbuf;
46753524e44Schristos 	outp = (void *)SCARG(uap, dent);
468add83289Stron 	resid = nbytes;
469add83289Stron 	if ((len = buflen - auio.uio_resid) == 0)
470add83289Stron 		goto eof;
471add83289Stron 
472add83289Stron 	for (cookie = cookiebuf; len > 0; len -= reclen) {
473add83289Stron 		bdp = (struct dirent *)inp;
474add83289Stron 		reclen = bdp->d_reclen;
47539f1e868Sriastradh 		if (reclen & 3) {
47639f1e868Sriastradh 			error = EIO;
47739f1e868Sriastradh 			goto out;
47839f1e868Sriastradh 		}
479add83289Stron 		if (bdp->d_fileno == 0) {
480add83289Stron 			inp += reclen;	/* it is a hole; squish it out */
481a5fb891bSchristos 			if (cookie)
482add83289Stron 				off = *cookie++;
483a5fb891bSchristos 			else
484a5fb891bSchristos 				off += reclen;
485add83289Stron 			continue;
486add83289Stron 		}
487add83289Stron 		linux_reclen = LINUX_RECLEN(&idb, bdp->d_namlen);
488add83289Stron 		if (reclen > len || resid < linux_reclen) {
489add83289Stron 			/* entry too big for buffer, so just stop */
490add83289Stron 			outp++;
491add83289Stron 			break;
492add83289Stron 		}
493a5fb891bSchristos 		if (cookie)
494c118cb14Sjdolecek 			off = *cookie++;	/* each entry points to next */
495a5fb891bSchristos 		else
496a5fb891bSchristos 			off += reclen;
497add83289Stron 		/*
498add83289Stron 		 * Massage in place to make a Linux-shaped dirent (otherwise
499add83289Stron 		 * we have to worry about touching user memory outside of
500add83289Stron 		 * the copyout() call).
501add83289Stron 		 */
5022a6e05d8Smaxv 		memset(&idb, 0, sizeof(idb));
503add83289Stron 		idb.d_ino = bdp->d_fileno;
504add83289Stron 		idb.d_type = bdp->d_type;
505add83289Stron 		idb.d_off = off;
506add83289Stron 		idb.d_reclen = (u_short)linux_reclen;
507968eee5dSchristos 		memcpy(idb.d_name, bdp->d_name, MIN(sizeof(idb.d_name),
5086c8a9019Schristos 		   bdp->d_namlen + 1));
50953524e44Schristos 		if ((error = copyout((void *)&idb, outp, linux_reclen)))
510add83289Stron 			goto out;
511add83289Stron 		/* advance past this real entry */
512add83289Stron 		inp += reclen;
513add83289Stron 		/* advance output past Linux-shaped entry */
514add83289Stron 		outp += linux_reclen;
515add83289Stron 		resid -= linux_reclen;
516add83289Stron 	}
517add83289Stron 
518add83289Stron 	/* if we squished out the whole block, try again */
5197a305442She 	if (outp == (void *)SCARG(uap, dent)) {
5207a305442She 		if (cookiebuf)
5217a305442She 			free(cookiebuf, M_TEMP);
5227a305442She 		cookiebuf = NULL;
523add83289Stron 		goto again;
5247a305442She 	}
525add83289Stron 	fp->f_offset = off;	/* update the vnode offset */
526add83289Stron 
527add83289Stron eof:
528add83289Stron 	*retval = nbytes - resid;
529add83289Stron out:
5301423e65bShannken 	VOP_UNLOCK(vp);
531add83289Stron 	if (cookiebuf)
532add83289Stron 		free(cookiebuf, M_TEMP);
533fb4b40b7Schristos 	free(tbuf, M_TEMP);
534add83289Stron out1:
535a9ca7a37Sad 	fd_putfile(SCARG(uap, fd));
536add83289Stron 	return error;
537add83289Stron }
538