1/*	$NetBSD: rump_cygwin_compat.c,v 1.3 2019/01/27 02:08:49 pgoyette Exp $	*/
2
3/*
4 * Copyright (c) 2013 Antti Kantee.  All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: rump_cygwin_compat.c,v 1.3 2019/01/27 02:08:49 pgoyette Exp $");
30
31#include <sys/param.h>
32#include <sys/dirent.h>
33#include <sys/fcntl.h>
34#include <sys/file.h>
35#include <sys/filedesc.h>
36#include <sys/malloc.h>
37#include <sys/namei.h>
38#include <sys/stat.h>
39#include <sys/syscallargs.h>
40#include <sys/vnode.h>
41#include <sys/vfs_syscalls.h>
42
43#include <compat/sys/time_types.h>
44
45#include "rump_cygwin_syscallargs.h"
46
47struct cygwin_stat {
48        int		st_dev;
49        int64_t		st_ino;
50        int		st_mode;
51        unsigned short	st_nlink;
52        int		st_uid;
53        int		st_gid;
54        int		st_rdev;
55        off_t		st_size;
56
57        struct timespec50 st_atim;
58        struct timespec50 st_mtim;
59        struct timespec50 st_ctim;
60
61        long		st_blksize;
62        uint64_t	st_blocks;
63
64        struct timespec50 st_btim;
65};
66
67#define PARCOPY(a) ssb->a = sb->a
68static void
69bsd_to_cygwin_stat(const struct stat *sb, struct cygwin_stat *ssb)
70{
71
72	memset(ssb, 0, sizeof(*ssb));
73	PARCOPY(st_dev);
74	PARCOPY(st_ino);
75	PARCOPY(st_mode);
76	PARCOPY(st_nlink);
77	PARCOPY(st_uid);
78	PARCOPY(st_gid);
79	PARCOPY(st_rdev);
80	PARCOPY(st_size);
81	PARCOPY(st_blksize);
82	PARCOPY(st_blocks);
83
84	timespec_to_timespec50(&sb->st_atimespec, &ssb->st_atim);
85	timespec_to_timespec50(&sb->st_mtimespec, &ssb->st_mtim);
86	timespec_to_timespec50(&sb->st_ctimespec, &ssb->st_ctim);
87	timespec_to_timespec50(&sb->st_birthtimespec, &ssb->st_btim);
88}
89
90int
91rump_cygwin_sys_stat(struct lwp *l, const struct rump_cygwin_sys_stat_args *uap,
92	register_t *retval)
93{
94	struct cygwin_stat ssb;
95	struct stat sb;
96	int error;
97
98	error = do_sys_stat(SCARG(uap, path), FOLLOW, &sb);
99	if (error)
100		return error;
101
102	bsd_to_cygwin_stat(&sb, &ssb);
103
104	return copyout(&ssb, SCARG(uap, sp), sizeof(ssb));
105}
106
107int
108rump_cygwin_sys_fstat(struct lwp *l, const struct rump_cygwin_sys_fstat_args *uap,
109	register_t *retval)
110{
111	struct cygwin_stat ssb;
112	struct stat sb;
113	int error;
114
115	error = do_sys_fstat(SCARG(uap, fd), &sb);
116	if (error)
117		return error;
118
119	bsd_to_cygwin_stat(&sb, &ssb);
120
121	return copyout(&ssb, SCARG(uap, sp), sizeof(ssb));
122}
123
124int
125rump_cygwin_sys_lstat(struct lwp *l, const struct rump_cygwin_sys_lstat_args *uap,
126	register_t *retval)
127{
128	struct cygwin_stat ssb;
129	struct stat sb;
130	int error;
131
132	error = do_sys_stat(SCARG(uap, path), NOFOLLOW, &sb);
133	if (error)
134		return error;
135
136	bsd_to_cygwin_stat(&sb, &ssb);
137
138	return copyout(&ssb, SCARG(uap, sp), sizeof(ssb));
139}
140
141int
142rump_cygwin_sys_open(struct lwp *l, const struct rump_cygwin_sys_open_args *uap,
143	register_t *retval)
144{
145	/* {
146		syscallarg(const char *) path;
147		syscallarg(int) flags;
148		syscallarg(int) mode;
149	} */
150	struct sys_open_args ua;
151	int sflags, flags;
152
153	sflags = SCARG(uap, flags);
154	flags = sflags & (3 | O_APPEND | O_ASYNC | O_CREAT | O_TRUNC | O_EXCL);
155
156	SCARG(&ua, path) = SCARG(uap, path);
157	SCARG(&ua, flags) = flags;
158	SCARG(&ua, mode) = SCARG(uap, mode);
159
160	return sys_open(l, &ua, retval);
161}
162
163#define CYGWIN_NAME_MAX 255
164struct cygwin_dirent {
165	long		d_version;
166	int64_t		d_ino;
167	unsigned char	d_type;
168	unsigned char	d_unused[3];
169	uint32_t	d_internal;
170	char		d_name[CYGWIN_NAME_MAX + 1];
171} __packed;
172
173#define CYGWIN_NAMEOFF(dp) 	   offsetof(struct cygwin_dirent,d_name)
174#define CYGWIN_RECLEN(dp, namlen)  ((CYGWIN_NAMEOFF(dp) + (namlen) + 1))
175
176int
177rump_cygwin_sys_getdents(struct lwp *l,
178	const struct rump_cygwin_sys_getdents_args *uap, register_t *retval)
179{
180	struct file *fp;
181	struct dirent *bdp;
182	struct cygwin_dirent idb;
183	char *buf, *inp, *outp;
184	size_t resid, buflen, nbytes;
185	size_t reclen, cygwin_reclen;
186	int error, done;
187
188	if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
189		return (error);
190
191	/*
192	 * Sneaky, but avoids having "rewind" f_offset due to the
193	 * conversions not fitting from our intermediate kernel buffer
194	 * into the user buffer
195	 */
196	nbytes = SCARG(uap, nbytes);
197	buflen = min(MAXBSIZE, (nbytes*8)/10);
198	buf = kmem_alloc(buflen, KM_SLEEP);
199
200	if ((fp->f_flag & FREAD) == 0) {
201		error = EBADF;
202		goto out;
203	}
204
205	resid = nbytes;
206	outp = SCARG(uap, buf);
207
208 again:
209	if ((error = vn_readdir(fp, buf, UIO_SYSSPACE, buflen, &done,
210	    l, NULL, NULL)) != 0)
211		goto out;
212	if (done == 0)
213		goto eof;
214
215	for (inp = buf; done > 0; done -= reclen) {
216		bdp = (struct dirent *)inp;
217		reclen = bdp->d_reclen;
218
219		/* skip empty entries */
220		if (bdp->d_fileno == 0) {
221			inp += reclen;
222			continue;
223		}
224
225		cygwin_reclen = CYGWIN_RECLEN(&idb, bdp->d_namlen);
226		if (resid < cygwin_reclen) {
227			panic("impossible shortage of resid");
228		}
229
230		memset(&idb, 0, sizeof(idb));
231		idb.d_ino = bdp->d_fileno;
232		idb.d_type = bdp->d_type;
233		strlcpy(idb.d_name, bdp->d_name, sizeof(idb.d_name));
234		if ((error = copyout(&idb, outp, cygwin_reclen)) != 0)
235			goto out;
236
237		inp += reclen;
238		outp += cygwin_reclen;
239		resid -= cygwin_reclen;
240	}
241
242	/* if we squished out the whole block, try again */
243	if (outp == SCARG(uap, buf)) {
244		goto again;
245	}
246
247 eof:
248	*retval = nbytes - resid;
249 out:
250	kmem_free(buf, buflen);
251 	fd_putfile(SCARG(uap, fd));
252
253	return (error);
254}
255