1 | /* $NetBSD: vfs_syscalls_43.c,v 1.58 2016/08/20 00:34:42 mrg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1989, 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * (c) UNIX System Laboratories, Inc. |
7 | * All or some portions of this file are derived from material licensed |
8 | * to the University of California by American Telephone and Telegraph |
9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
10 | * the permission of UNIX System Laboratories, Inc. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * 3. Neither the name of the University nor the names of its contributors |
21 | * may be used to endorse or promote products derived from this software |
22 | * without specific prior written permission. |
23 | * |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
34 | * SUCH DAMAGE. |
35 | * |
36 | * @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94 |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: vfs_syscalls_43.c,v 1.58 2016/08/20 00:34:42 mrg Exp $" ); |
41 | |
42 | #if defined(_KERNEL_OPT) |
43 | #include "opt_compat_netbsd.h" |
44 | #endif |
45 | |
46 | #include <sys/param.h> |
47 | #include <sys/systm.h> |
48 | #include <sys/filedesc.h> |
49 | #include <sys/kernel.h> |
50 | #include <sys/proc.h> |
51 | #include <sys/file.h> |
52 | #include <sys/vnode.h> |
53 | #include <sys/namei.h> |
54 | #include <sys/dirent.h> |
55 | #include <sys/socket.h> |
56 | #include <sys/socketvar.h> |
57 | #include <sys/stat.h> |
58 | #include <sys/malloc.h> |
59 | #include <sys/ioctl.h> |
60 | #include <sys/fcntl.h> |
61 | #include <sys/sysctl.h> |
62 | #include <sys/syslog.h> |
63 | #include <sys/unistd.h> |
64 | #include <sys/resourcevar.h> |
65 | #include <sys/sysctl.h> |
66 | |
67 | #include <sys/mount.h> |
68 | #include <sys/syscallargs.h> |
69 | #include <sys/vfs_syscalls.h> |
70 | |
71 | #include <compat/sys/stat.h> |
72 | #include <compat/sys/mount.h> |
73 | #include <compat/sys/dirent.h> |
74 | |
75 | #include <compat/common/compat_util.h> |
76 | #include <compat/common/compat_mod.h> |
77 | |
78 | static void cvttimespec(struct timespec *, struct timespec50 *); |
79 | static void cvtstat(struct stat *, struct stat43 *); |
80 | |
81 | /* |
82 | * Convert from an old to a new timespec structure. |
83 | */ |
84 | static void |
85 | cvttimespec(struct timespec *ts, struct timespec50 *ots) |
86 | { |
87 | |
88 | if (ts->tv_sec > INT_MAX) { |
89 | #if defined(DEBUG) || 1 |
90 | static bool first = true; |
91 | |
92 | if (first) { |
93 | first = false; |
94 | printf("%s[%s:%d]: time_t does not fit\n" , |
95 | __func__, curlwp->l_proc->p_comm, |
96 | curlwp->l_lid); |
97 | } |
98 | #endif |
99 | ots->tv_sec = INT_MAX; |
100 | } else |
101 | ots->tv_sec = ts->tv_sec; |
102 | ots->tv_nsec = ts->tv_nsec; |
103 | } |
104 | |
105 | /* |
106 | * Convert from an old to a new stat structure. |
107 | */ |
108 | static void |
109 | cvtstat(struct stat *st, struct stat43 *ost) |
110 | { |
111 | |
112 | /* Handle any padding. */ |
113 | memset(ost, 0, sizeof *ost); |
114 | ost->st_dev = st->st_dev; |
115 | ost->st_ino = st->st_ino; |
116 | ost->st_mode = st->st_mode & 0xffff; |
117 | ost->st_nlink = st->st_nlink; |
118 | ost->st_uid = st->st_uid; |
119 | ost->st_gid = st->st_gid; |
120 | ost->st_rdev = st->st_rdev; |
121 | if (st->st_size < (quad_t)1 << 32) |
122 | ost->st_size = st->st_size; |
123 | else |
124 | ost->st_size = -2; |
125 | cvttimespec(&st->st_atimespec, &ost->st_atimespec); |
126 | cvttimespec(&st->st_mtimespec, &ost->st_mtimespec); |
127 | cvttimespec(&st->st_ctimespec, &ost->st_ctimespec); |
128 | ost->st_blksize = st->st_blksize; |
129 | ost->st_blocks = st->st_blocks; |
130 | ost->st_flags = st->st_flags; |
131 | ost->st_gen = st->st_gen; |
132 | } |
133 | |
134 | /* |
135 | * Get file status; this version follows links. |
136 | */ |
137 | /* ARGSUSED */ |
138 | int |
139 | compat_43_sys_stat(struct lwp *l, const struct compat_43_sys_stat_args *uap, register_t *retval) |
140 | { |
141 | /* { |
142 | syscallarg(char *) path; |
143 | syscallarg(struct stat43 *) ub; |
144 | } */ |
145 | struct stat sb; |
146 | struct stat43 osb; |
147 | int error; |
148 | |
149 | error = do_sys_stat(SCARG(uap, path), FOLLOW, &sb); |
150 | if (error) |
151 | return (error); |
152 | cvtstat(&sb, &osb); |
153 | error = copyout((void *)&osb, (void *)SCARG(uap, ub), sizeof (osb)); |
154 | return (error); |
155 | } |
156 | |
157 | /* |
158 | * Get file status; this version does not follow links. |
159 | */ |
160 | /* ARGSUSED */ |
161 | int |
162 | compat_43_sys_lstat(struct lwp *l, const struct compat_43_sys_lstat_args *uap, register_t *retval) |
163 | { |
164 | /* { |
165 | syscallarg(char *) path; |
166 | syscallarg(struct ostat *) ub; |
167 | } */ |
168 | struct vnode *vp, *dvp; |
169 | struct stat sb, sb1; |
170 | struct stat43 osb; |
171 | int error; |
172 | struct pathbuf *pb; |
173 | struct nameidata nd; |
174 | int ndflags; |
175 | |
176 | error = pathbuf_copyin(SCARG(uap, path), &pb); |
177 | if (error) { |
178 | return error; |
179 | } |
180 | |
181 | ndflags = NOFOLLOW | LOCKLEAF | LOCKPARENT | TRYEMULROOT; |
182 | again: |
183 | NDINIT(&nd, LOOKUP, ndflags, pb); |
184 | if ((error = namei(&nd))) { |
185 | if (error == EISDIR && (ndflags & LOCKPARENT) != 0) { |
186 | /* |
187 | * Should only happen on '/'. Retry without LOCKPARENT; |
188 | * this is safe since the vnode won't be a VLNK. |
189 | */ |
190 | ndflags &= ~LOCKPARENT; |
191 | goto again; |
192 | } |
193 | pathbuf_destroy(pb); |
194 | return (error); |
195 | } |
196 | /* |
197 | * For symbolic links, always return the attributes of its |
198 | * containing directory, except for mode, size, and links. |
199 | */ |
200 | vp = nd.ni_vp; |
201 | dvp = nd.ni_dvp; |
202 | pathbuf_destroy(pb); |
203 | if (vp->v_type != VLNK) { |
204 | if ((ndflags & LOCKPARENT) != 0) { |
205 | if (dvp == vp) |
206 | vrele(dvp); |
207 | else |
208 | vput(dvp); |
209 | } |
210 | error = vn_stat(vp, &sb); |
211 | vput(vp); |
212 | if (error) |
213 | return (error); |
214 | } else { |
215 | error = vn_stat(dvp, &sb); |
216 | vput(dvp); |
217 | if (error) { |
218 | vput(vp); |
219 | return (error); |
220 | } |
221 | error = vn_stat(vp, &sb1); |
222 | vput(vp); |
223 | if (error) |
224 | return (error); |
225 | sb.st_mode &= ~S_IFDIR; |
226 | sb.st_mode |= S_IFLNK; |
227 | sb.st_nlink = sb1.st_nlink; |
228 | sb.st_size = sb1.st_size; |
229 | sb.st_blocks = sb1.st_blocks; |
230 | } |
231 | cvtstat(&sb, &osb); |
232 | error = copyout((void *)&osb, (void *)SCARG(uap, ub), sizeof (osb)); |
233 | return (error); |
234 | } |
235 | |
236 | /* |
237 | * Return status information about a file descriptor. |
238 | */ |
239 | /* ARGSUSED */ |
240 | int |
241 | compat_43_sys_fstat(struct lwp *l, const struct compat_43_sys_fstat_args *uap, register_t *retval) |
242 | { |
243 | /* { |
244 | syscallarg(int) fd; |
245 | syscallarg(struct stat43 *) sb; |
246 | } */ |
247 | struct stat ub; |
248 | struct stat43 oub; |
249 | int error; |
250 | |
251 | error = do_sys_fstat(SCARG(uap, fd), &ub); |
252 | if (error == 0) { |
253 | cvtstat(&ub, &oub); |
254 | error = copyout((void *)&oub, (void *)SCARG(uap, sb), |
255 | sizeof (oub)); |
256 | } |
257 | |
258 | return (error); |
259 | } |
260 | |
261 | |
262 | /* |
263 | * Truncate a file given a file descriptor. |
264 | */ |
265 | /* ARGSUSED */ |
266 | int |
267 | compat_43_sys_ftruncate(struct lwp *l, const struct compat_43_sys_ftruncate_args *uap, register_t *retval) |
268 | { |
269 | /* { |
270 | syscallarg(int) fd; |
271 | syscallarg(long) length; |
272 | } */ |
273 | struct sys_ftruncate_args /* { |
274 | syscallarg(int) fd; |
275 | syscallarg(int) pad; |
276 | syscallarg(off_t) length; |
277 | } */ nuap; |
278 | |
279 | SCARG(&nuap, fd) = SCARG(uap, fd); |
280 | SCARG(&nuap, length) = SCARG(uap, length); |
281 | return (sys_ftruncate(l, &nuap, retval)); |
282 | } |
283 | |
284 | /* |
285 | * Truncate a file given its path name. |
286 | */ |
287 | /* ARGSUSED */ |
288 | int |
289 | compat_43_sys_truncate(struct lwp *l, const struct compat_43_sys_truncate_args *uap, register_t *retval) |
290 | { |
291 | /* { |
292 | syscallarg(char *) path; |
293 | syscallarg(long) length; |
294 | } */ |
295 | struct sys_truncate_args /* { |
296 | syscallarg(char *) path; |
297 | syscallarg(int) pad; |
298 | syscallarg(off_t) length; |
299 | } */ nuap; |
300 | |
301 | SCARG(&nuap, path) = SCARG(uap, path); |
302 | SCARG(&nuap, length) = SCARG(uap, length); |
303 | return (sys_truncate(l, &nuap, retval)); |
304 | } |
305 | |
306 | |
307 | /* |
308 | * Reposition read/write file offset. |
309 | */ |
310 | int |
311 | compat_43_sys_lseek(struct lwp *l, const struct compat_43_sys_lseek_args *uap, register_t *retval) |
312 | { |
313 | /* { |
314 | syscallarg(int) fd; |
315 | syscallarg(long) offset; |
316 | syscallarg(int) whence; |
317 | } */ |
318 | struct sys_lseek_args /* { |
319 | syscallarg(int) fd; |
320 | syscallarg(int) pad; |
321 | syscallarg(off_t) offset; |
322 | syscallarg(int) whence; |
323 | } */ nuap; |
324 | off_t qret; |
325 | int error; |
326 | |
327 | SCARG(&nuap, fd) = SCARG(uap, fd); |
328 | SCARG(&nuap, offset) = SCARG(uap, offset); |
329 | SCARG(&nuap, whence) = SCARG(uap, whence); |
330 | error = sys_lseek(l, &nuap, (register_t *)&qret); |
331 | *(long *)retval = qret; |
332 | return (error); |
333 | } |
334 | |
335 | |
336 | /* |
337 | * Create a file. |
338 | */ |
339 | int |
340 | compat_43_sys_creat(struct lwp *l, const struct compat_43_sys_creat_args *uap, register_t *retval) |
341 | { |
342 | /* { |
343 | syscallarg(char *) path; |
344 | syscallarg(int) mode; |
345 | } */ |
346 | struct sys_open_args /* { |
347 | syscallarg(char *) path; |
348 | syscallarg(int) flags; |
349 | syscallarg(int) mode; |
350 | } */ nuap; |
351 | |
352 | SCARG(&nuap, path) = SCARG(uap, path); |
353 | SCARG(&nuap, mode) = SCARG(uap, mode); |
354 | SCARG(&nuap, flags) = O_WRONLY | O_CREAT | O_TRUNC; |
355 | return (sys_open(l, &nuap, retval)); |
356 | } |
357 | |
358 | /*ARGSUSED*/ |
359 | int |
360 | compat_43_sys_quota(struct lwp *l, const void *v, register_t *retval) |
361 | { |
362 | |
363 | return (ENOSYS); |
364 | } |
365 | |
366 | |
367 | /* |
368 | * Read a block of directory entries in a file system independent format. |
369 | */ |
370 | int |
371 | compat_43_sys_getdirentries(struct lwp *l, const struct compat_43_sys_getdirentries_args *uap, register_t *retval) |
372 | { |
373 | /* { |
374 | syscallarg(int) fd; |
375 | syscallarg(char *) buf; |
376 | syscallarg(u_int) count; |
377 | syscallarg(long *) basep; |
378 | } */ |
379 | struct dirent *bdp; |
380 | struct vnode *vp; |
381 | void *tbuf; /* Current-format */ |
382 | char *inp; /* Current-format */ |
383 | int len, reclen; /* Current-format */ |
384 | char *outp; /* Dirent12-format */ |
385 | int resid, old_reclen = 0; /* Dirent12-format */ |
386 | struct file *fp; |
387 | struct uio auio; |
388 | struct iovec aiov; |
389 | struct dirent43 idb; |
390 | off_t off; /* true file offset */ |
391 | int buflen, error, eofflag, nbytes; |
392 | struct vattr va; |
393 | off_t *cookiebuf = NULL, *cookie; |
394 | int ncookies; |
395 | long loff; |
396 | |
397 | /* fd_getvnode() will use the descriptor for us */ |
398 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
399 | return (error); |
400 | |
401 | if ((fp->f_flag & FREAD) == 0) { |
402 | error = EBADF; |
403 | goto out1; |
404 | } |
405 | |
406 | vp = fp->f_vnode; |
407 | if (vp->v_type != VDIR) { |
408 | error = ENOTDIR; |
409 | goto out1; |
410 | } |
411 | |
412 | vn_lock(vp, LK_SHARED | LK_RETRY); |
413 | error = VOP_GETATTR(vp, &va, l->l_cred); |
414 | VOP_UNLOCK(vp); |
415 | if (error) |
416 | goto out1; |
417 | |
418 | loff = fp->f_offset; |
419 | nbytes = SCARG(uap, count); |
420 | buflen = min(MAXBSIZE, nbytes); |
421 | if (buflen < va.va_blocksize) |
422 | buflen = va.va_blocksize; |
423 | tbuf = malloc(buflen, M_TEMP, M_WAITOK); |
424 | |
425 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
426 | off = fp->f_offset; |
427 | again: |
428 | aiov.iov_base = tbuf; |
429 | aiov.iov_len = buflen; |
430 | auio.uio_iov = &aiov; |
431 | auio.uio_iovcnt = 1; |
432 | auio.uio_rw = UIO_READ; |
433 | auio.uio_resid = buflen; |
434 | auio.uio_offset = off; |
435 | UIO_SETUP_SYSSPACE(&auio); |
436 | /* |
437 | * First we read into the malloc'ed buffer, then |
438 | * we massage it into user space, one record at a time. |
439 | */ |
440 | error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, |
441 | &ncookies); |
442 | if (error) |
443 | goto out; |
444 | |
445 | inp = (char *)tbuf; |
446 | outp = SCARG(uap, buf); |
447 | resid = nbytes; |
448 | if ((len = buflen - auio.uio_resid) == 0) |
449 | goto eof; |
450 | |
451 | for (cookie = cookiebuf; len > 0; len -= reclen) { |
452 | bdp = (struct dirent *)inp; |
453 | reclen = bdp->d_reclen; |
454 | if (reclen & 3) |
455 | panic(__func__); |
456 | if (bdp->d_fileno == 0) { |
457 | inp += reclen; /* it is a hole; squish it out */ |
458 | if (cookie) |
459 | off = *cookie++; |
460 | else |
461 | off += reclen; |
462 | continue; |
463 | } |
464 | old_reclen = _DIRENT_RECLEN(&idb, bdp->d_namlen); |
465 | if (reclen > len || resid < old_reclen) { |
466 | /* entry too big for buffer, so just stop */ |
467 | outp++; |
468 | break; |
469 | } |
470 | /* |
471 | * Massage in place to make a Dirent12-shaped dirent (otherwise |
472 | * we have to worry about touching user memory outside of |
473 | * the copyout() call). |
474 | */ |
475 | idb.d_fileno = (uint32_t)bdp->d_fileno; |
476 | idb.d_reclen = (uint16_t)old_reclen; |
477 | idb.d_namlen = (uint16_t)bdp->d_namlen; |
478 | strcpy(idb.d_name, bdp->d_name); |
479 | if ((error = copyout(&idb, outp, old_reclen))) |
480 | goto out; |
481 | /* advance past this real entry */ |
482 | inp += reclen; |
483 | if (cookie) |
484 | off = *cookie++; /* each entry points to itself */ |
485 | else |
486 | off += reclen; |
487 | /* advance output past Dirent12-shaped entry */ |
488 | outp += old_reclen; |
489 | resid -= old_reclen; |
490 | } |
491 | |
492 | /* if we squished out the whole block, try again */ |
493 | if (outp == SCARG(uap, buf)) { |
494 | if (cookiebuf) |
495 | free(cookiebuf, M_TEMP); |
496 | cookiebuf = NULL; |
497 | goto again; |
498 | } |
499 | fp->f_offset = off; /* update the vnode offset */ |
500 | |
501 | eof: |
502 | *retval = nbytes - resid; |
503 | out: |
504 | VOP_UNLOCK(vp); |
505 | if (cookiebuf) |
506 | free(cookiebuf, M_TEMP); |
507 | free(tbuf, M_TEMP); |
508 | out1: |
509 | fd_putfile(SCARG(uap, fd)); |
510 | if (error) |
511 | return error; |
512 | return copyout(&loff, SCARG(uap, basep), sizeof(long)); |
513 | } |
514 | |
515 | /* |
516 | * sysctl helper routine for vfs.generic.conf lookups. |
517 | */ |
518 | #if defined(COMPAT_09) || defined(COMPAT_43) || defined(COMPAT_44) |
519 | |
520 | static int |
521 | sysctl_vfs_generic_conf(SYSCTLFN_ARGS) |
522 | { |
523 | struct vfsconf vfc; |
524 | extern const char * const mountcompatnames[]; |
525 | extern int nmountcompatnames; |
526 | struct sysctlnode node; |
527 | struct vfsops *vfsp; |
528 | u_int vfsnum; |
529 | |
530 | if (namelen != 1) |
531 | return (ENOTDIR); |
532 | vfsnum = name[0]; |
533 | if (vfsnum >= nmountcompatnames || |
534 | mountcompatnames[vfsnum] == NULL) |
535 | return (EOPNOTSUPP); |
536 | vfsp = vfs_getopsbyname(mountcompatnames[vfsnum]); |
537 | if (vfsp == NULL) |
538 | return (EOPNOTSUPP); |
539 | |
540 | vfc.vfc_vfsops = vfsp; |
541 | strncpy(vfc.vfc_name, vfsp->vfs_name, sizeof(vfc.vfc_name)); |
542 | vfc.vfc_typenum = vfsnum; |
543 | vfc.vfc_refcount = vfsp->vfs_refcount; |
544 | vfc.vfc_flags = 0; |
545 | vfc.vfc_mountroot = vfsp->vfs_mountroot; |
546 | vfc.vfc_next = NULL; |
547 | vfs_delref(vfsp); |
548 | |
549 | node = *rnode; |
550 | node.sysctl_data = &vfc; |
551 | return (sysctl_lookup(SYSCTLFN_CALL(&node))); |
552 | } |
553 | |
554 | /* |
555 | * Top level filesystem related information gathering. |
556 | */ |
557 | void |
558 | compat_sysctl_vfs(struct sysctllog **clog) |
559 | { |
560 | extern int nmountcompatnames; |
561 | |
562 | sysctl_createv(clog, 0, NULL, NULL, |
563 | CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, |
564 | CTLTYPE_INT, "maxtypenum" , |
565 | SYSCTL_DESCR("Highest valid filesystem type number" ), |
566 | NULL, nmountcompatnames, NULL, 0, |
567 | CTL_VFS, VFS_GENERIC, VFS_MAXTYPENUM, CTL_EOL); |
568 | sysctl_createv(clog, 0, NULL, NULL, |
569 | CTLFLAG_PERMANENT, |
570 | CTLTYPE_STRUCT, "conf" , |
571 | SYSCTL_DESCR("Filesystem configuration information" ), |
572 | sysctl_vfs_generic_conf, 0, NULL, |
573 | sizeof(struct vfsconf), |
574 | CTL_VFS, VFS_GENERIC, VFS_CONF, CTL_EOL); |
575 | } |
576 | #endif |
577 | |