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 | |
58 | static void cvtstat(struct stat13 *, const struct stat *); |
59 | |
60 | /* |
61 | * Convert from a new to an old stat structure. |
62 | */ |
63 | static void |
64 | cvtstat(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 */ |
89 | int |
90 | compat_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 */ |
113 | int |
114 | compat_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 */ |
133 | int |
134 | compat_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 */ |
176 | int |
177 | compat_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 | */ |
198 | int |
199 | compat_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; |
240 | again: |
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 | |
316 | eof: |
317 | *retval = SCARG(uap, count) - resid; |
318 | out: |
319 | VOP_UNLOCK(vp); |
320 | if (cookiebuf) |
321 | free(cookiebuf, M_TEMP); |
322 | free(tbuf, M_TEMP); |
323 | out1: |
324 | fd_putfile(SCARG(uap, fd)); |
325 | return error; |
326 | } |
327 | |
328 | /* |
329 | * Get file handle system call |
330 | */ |
331 | int |
332 | compat_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 | */ |
382 | int |
383 | compat_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 */ |
395 | int |
396 | compat_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 */ |
415 | int |
416 | compat_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 | |