1/* $NetBSD: vfs_syscalls_30.c,v 1.36 2014/10/20 11:58:01 christos Exp $ */
2
3/*-
4 * Copyright (c) 2005, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
18 *
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.
30 */
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls_30.c,v 1.36 2014/10/20 11:58:01 christos Exp $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/namei.h>
37#include <sys/filedesc.h>
38#include <sys/kernel.h>
39#include <sys/file.h>
40#include <sys/stat.h>
41#include <sys/socketvar.h>
42#include <sys/vnode.h>
43#include <sys/mount.h>
44#include <sys/proc.h>
45#include <sys/uio.h>
46#include <sys/dirent.h>
47#include <sys/malloc.h>
48#include <sys/kauth.h>
49#include <sys/vfs_syscalls.h>
50
51#include <sys/syscallargs.h>
52
53#include <compat/common/compat_util.h>
54#include <compat/sys/stat.h>
55#include <compat/sys/dirent.h>
56#include <compat/sys/mount.h>
57
58static void cvtstat(struct stat13 *, const struct stat *);
59
60/*
61 * Convert from a new to an old stat structure.
62 */
63static void
64cvtstat(struct stat13 *ost, const struct stat *st)
65{
66
67 ost->st_dev = st->st_dev;
68 ost->st_ino = (uint32_t)st->st_ino;
69 ost->st_mode = st->st_mode;
70 ost->st_nlink = st->st_nlink;
71 ost->st_uid = st->st_uid;
72 ost->st_gid = st->st_gid;
73 ost->st_rdev = st->st_rdev;
74 timespec_to_timespec50(&st->st_atimespec, &ost->st_atimespec);
75 timespec_to_timespec50(&st->st_mtimespec, &ost->st_mtimespec);
76 timespec_to_timespec50(&st->st_ctimespec, &ost->st_ctimespec);
77 timespec_to_timespec50(&st->st_birthtimespec, &ost->st_birthtimespec);
78 ost->st_size = st->st_size;
79 ost->st_blocks = st->st_blocks;
80 ost->st_blksize = st->st_blksize;
81 ost->st_flags = st->st_flags;
82 ost->st_gen = st->st_gen;
83}
84
85/*
86 * Get file status; this version follows links.
87 */
88/* ARGSUSED */
89int
90compat_30_sys___stat13(struct lwp *l, const struct compat_30_sys___stat13_args *uap, register_t *retval)
91{
92 /* {
93 syscallarg(const char *) path;
94 syscallarg(struct stat13 *) ub;
95 } */
96 struct stat sb;
97 struct stat13 osb;
98 int error;
99
100 error = do_sys_stat(SCARG(uap, path), FOLLOW, &sb);
101 if (error)
102 return error;
103 cvtstat(&osb, &sb);
104 error = copyout(&osb, SCARG(uap, ub), sizeof (osb));
105 return error;
106}
107
108
109/*
110 * Get file status; this version does not follow links.
111 */
112/* ARGSUSED */
113int
114compat_30_sys___lstat13(struct lwp *l, const struct compat_30_sys___lstat13_args *uap, register_t *retval)
115{
116 /* {
117 syscallarg(const char *) path;
118 syscallarg(struct stat13 *) ub;
119 } */
120 struct stat sb;
121 struct stat13 osb;
122 int error;
123
124 error = do_sys_stat(SCARG(uap, path), NOFOLLOW, &sb);
125 if (error)
126 return error;
127 cvtstat(&osb, &sb);
128 error = copyout(&osb, SCARG(uap, ub), sizeof (osb));
129 return error;
130}
131
132/* ARGSUSED */
133int
134compat_30_sys_fhstat(struct lwp *l, const struct compat_30_sys_fhstat_args *uap, register_t *retval)
135{
136 /* {
137 syscallarg(const struct compat_30_fhandle *) fhp;
138 syscallarg(struct stat13 *) sb;
139 } */
140 struct stat sb;
141 struct stat13 osb;
142 int error;
143 struct compat_30_fhandle fh;
144 struct mount *mp;
145 struct vnode *vp;
146
147 /*
148 * Must be super user
149 */
150 if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE,
151 0, NULL, NULL, NULL)))
152 return (error);
153
154 if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fh))) != 0)
155 return (error);
156
157 if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL)
158 return (ESTALE);
159 if (mp->mnt_op->vfs_fhtovp == NULL)
160 return EOPNOTSUPP;
161 if ((error = VFS_FHTOVP(mp, (struct fid*)&fh.fh_fid, &vp)))
162 return (error);
163 error = vn_stat(vp, &sb);
164 vput(vp);
165 if (error)
166 return (error);
167 cvtstat(&osb, &sb);
168 error = copyout(&osb, SCARG(uap, sb), sizeof(sb));
169 return (error);
170}
171
172/*
173 * Return status information about a file descriptor.
174 */
175/* ARGSUSED */
176int
177compat_30_sys___fstat13(struct lwp *l, const struct compat_30_sys___fstat13_args *uap, register_t *retval)
178{
179 /* {
180 syscallarg(int) fd;
181 syscallarg(struct stat13 *) sb;
182 } */
183 struct stat sb;
184 struct stat13 osb;
185 int error;
186
187 error = do_sys_fstat(SCARG(uap, fd), &sb);
188 if (error)
189 return error;
190 cvtstat(&osb, &sb);
191 error = copyout(&osb, SCARG(uap, sb), sizeof (osb));
192 return error;
193}
194
195/*
196 * Read a block of directory entries in a file system independent format.
197 */
198int
199compat_30_sys_getdents(struct lwp *l, const struct compat_30_sys_getdents_args *uap, register_t *retval)
200{
201 /* {
202 syscallarg(int) fd;
203 syscallarg(char *) buf;
204 syscallarg(size_t) count;
205 } */
206 struct dirent *bdp;
207 struct vnode *vp;
208 char *inp, *tbuf; /* BSD-format */
209 int len, reclen; /* BSD-format */
210 char *outp; /* NetBSD-3.0-format */
211 int resid;
212 struct file *fp;
213 struct uio auio;
214 struct iovec aiov;
215 struct dirent12 idb;
216 off_t off; /* true file offset */
217 int buflen, error, eofflag;
218 off_t *cookiebuf = NULL, *cookie;
219 int ncookies;
220
221 /* fd_getvnode() will use the descriptor for us */
222 if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
223 return error;
224
225 if ((fp->f_flag & FREAD) == 0) {
226 error = EBADF;
227 goto out1;
228 }
229
230 vp = fp->f_vnode;
231 if (vp->v_type != VDIR) {
232 error = EINVAL;
233 goto out1;
234 }
235
236 buflen = min(MAXBSIZE, SCARG(uap, count));
237 tbuf = malloc(buflen, M_TEMP, M_WAITOK);
238 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
239 off = fp->f_offset;
240again:
241 aiov.iov_base = tbuf;
242 aiov.iov_len = buflen;
243 auio.uio_iov = &aiov;
244 auio.uio_iovcnt = 1;
245 auio.uio_rw = UIO_READ;
246 auio.uio_resid = buflen;
247 auio.uio_offset = off;
248 UIO_SETUP_SYSSPACE(&auio);
249 /*
250 * First we read into the malloc'ed buffer, then
251 * we massage it into user space, one record at a time.
252 */
253 error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf,
254 &ncookies);
255 if (error)
256 goto out;
257
258 inp = tbuf;
259 outp = SCARG(uap, buf);
260 resid = SCARG(uap, count);
261 if ((len = buflen - auio.uio_resid) == 0)
262 goto eof;
263
264 for (cookie = cookiebuf; len > 0; len -= reclen) {
265 bdp = (struct dirent *)inp;
266 reclen = bdp->d_reclen;
267 if (reclen & _DIRENT_ALIGN(bdp))
268 panic("netbsd30_getdents: bad reclen %d", reclen);
269 if (cookie)
270 off = *cookie++; /* each entry points to the next */
271 else
272 off += reclen;
273 if ((off >> 32) != 0) {
274 compat_offseterr(vp, "netbsd30_getdents");
275 error = EINVAL;
276 goto out;
277 }
278 if (bdp->d_namlen >= sizeof(idb.d_name))
279 idb.d_namlen = sizeof(idb.d_name) - 1;
280 else
281 idb.d_namlen = bdp->d_namlen;
282 idb.d_reclen = _DIRENT_SIZE(&idb);
283 if (reclen > len || resid < idb.d_reclen) {
284 /* entry too big for buffer, so just stop */
285 outp++;
286 break;
287 }
288 /*
289 * Massage in place to make a NetBSD-3.0-shaped dirent
290 * (otherwise we have to worry about touching user memory
291 * outside of the copyout() call).
292 */
293 idb.d_fileno = (u_int32_t)bdp->d_fileno;
294 idb.d_type = bdp->d_type;
295 (void)memcpy(idb.d_name, bdp->d_name, idb.d_namlen);
296 memset(idb.d_name + idb.d_namlen, 0,
297 idb.d_reclen - _DIRENT_NAMEOFF(&idb) - idb.d_namlen);
298 if ((error = copyout(&idb, outp, idb.d_reclen)) != 0)
299 goto out;
300 /* advance past this real entry */
301 inp += reclen;
302 /* advance output past NetBSD-3.0-shaped entry */
303 outp += idb.d_reclen;
304 resid -= idb.d_reclen;
305 }
306
307 /* if we squished out the whole block, try again */
308 if (outp == SCARG(uap, buf)) {
309 if (cookiebuf)
310 free(cookiebuf, M_TEMP);
311 cookiebuf = NULL;
312 goto again;
313 }
314 fp->f_offset = off; /* update the vnode offset */
315
316eof:
317 *retval = SCARG(uap, count) - resid;
318out:
319 VOP_UNLOCK(vp);
320 if (cookiebuf)
321 free(cookiebuf, M_TEMP);
322 free(tbuf, M_TEMP);
323out1:
324 fd_putfile(SCARG(uap, fd));
325 return error;
326}
327
328/*
329 * Get file handle system call
330 */
331int
332compat_30_sys_getfh(struct lwp *l, const struct compat_30_sys_getfh_args *uap, register_t *retval)
333{
334 /* {
335 syscallarg(char *) fname;
336 syscallarg(struct compat_30_fhandle *) fhp;
337 } */
338 struct vnode *vp;
339 struct compat_30_fhandle fh;
340 int error;
341 struct pathbuf *pb;
342 struct nameidata nd;
343 size_t sz;
344
345 /*
346 * Must be super user
347 */
348 error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE,
349 0, NULL, NULL, NULL);
350 if (error)
351 return (error);
352
353 error = pathbuf_copyin(SCARG(uap, fname), &pb);
354 if (error) {
355 return error;
356 }
357 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb);
358 error = namei(&nd);
359 pathbuf_destroy(pb);
360 if (error)
361 return error;
362 vp = nd.ni_vp;
363
364 sz = sizeof(struct compat_30_fhandle);
365 error = vfs_composefh(vp, (void *)&fh, &sz);
366 vput(vp);
367 if (sz != FHANDLE_SIZE_COMPAT) {
368 error = EINVAL;
369 }
370 if (error)
371 return (error);
372 error = copyout(&fh, SCARG(uap, fhp), sizeof(struct compat_30_fhandle));
373 return (error);
374}
375
376/*
377 * Open a file given a file handle.
378 *
379 * Check permissions, allocate an open file structure,
380 * and call the device open routine if any.
381 */
382int
383compat_30_sys_fhopen(struct lwp *l, const struct compat_30_sys_fhopen_args *uap, register_t *retval)
384{
385 /* {
386 syscallarg(const fhandle_t *) fhp;
387 syscallarg(int) flags;
388 } */
389
390 return dofhopen(l, SCARG(uap, fhp), FHANDLE_SIZE_COMPAT,
391 SCARG(uap, flags), retval);
392}
393
394/* ARGSUSED */
395int
396compat_30_sys___fhstat30(struct lwp *l, const struct compat_30_sys___fhstat30_args *uap_30, register_t *retval)
397{
398 /* {
399 syscallarg(const fhandle_t *) fhp;
400 syscallarg(struct stat30 *) sb;
401 } */
402 struct stat sb;
403 struct stat13 osb;
404 int error;
405
406 error = do_fhstat(l, SCARG(uap_30, fhp), FHANDLE_SIZE_COMPAT, &sb);
407 if (error)
408 return error;
409 cvtstat(&osb, &sb);
410 error = copyout(&osb, SCARG(uap_30, sb), sizeof (osb));
411 return error;
412}
413
414/* ARGSUSED */
415int
416compat_30_sys_fhstatvfs1(struct lwp *l, const struct compat_30_sys_fhstatvfs1_args *uap_30, register_t *retval)
417{
418 /* {
419 syscallarg(const fhandle_t *) fhp;
420 syscallarg(struct statvfs *) buf;
421 syscallarg(int) flags;
422 } */
423 struct sys___fhstatvfs140_args uap;
424
425 SCARG(&uap, fhp) = SCARG(uap_30, fhp);
426 SCARG(&uap, fh_size) = FHANDLE_SIZE_COMPAT;
427 SCARG(&uap, buf) = SCARG(uap_30, buf);
428 SCARG(&uap, flags) = SCARG(uap_30, flags);
429
430 return sys___fhstatvfs140(l, &uap, retval);
431}
432