1 | /* $NetBSD: vfs_syscalls.c,v 1.533 2019/07/06 14:37:24 maxv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. |
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 | |
32 | /* |
33 | * Copyright (c) 1989, 1993 |
34 | * The Regents of the University of California. All rights reserved. |
35 | * (c) UNIX System Laboratories, Inc. |
36 | * All or some portions of this file are derived from material licensed |
37 | * to the University of California by American Telephone and Telegraph |
38 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
39 | * the permission of UNIX System Laboratories, Inc. |
40 | * |
41 | * Redistribution and use in source and binary forms, with or without |
42 | * modification, are permitted provided that the following conditions |
43 | * are met: |
44 | * 1. Redistributions of source code must retain the above copyright |
45 | * notice, this list of conditions and the following disclaimer. |
46 | * 2. Redistributions in binary form must reproduce the above copyright |
47 | * notice, this list of conditions and the following disclaimer in the |
48 | * documentation and/or other materials provided with the distribution. |
49 | * 3. Neither the name of the University nor the names of its contributors |
50 | * may be used to endorse or promote products derived from this software |
51 | * without specific prior written permission. |
52 | * |
53 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
54 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
55 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
56 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
57 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
58 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
59 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
60 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
61 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
62 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
63 | * SUCH DAMAGE. |
64 | * |
65 | * @(#)vfs_syscalls.c 8.42 (Berkeley) 7/31/95 |
66 | */ |
67 | |
68 | /* |
69 | * Virtual File System System Calls |
70 | */ |
71 | |
72 | #include <sys/cdefs.h> |
73 | __KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.533 2019/07/06 14:37:24 maxv Exp $" ); |
74 | |
75 | #ifdef _KERNEL_OPT |
76 | #include "opt_fileassoc.h" |
77 | #include "veriexec.h" |
78 | #endif |
79 | |
80 | #include <sys/param.h> |
81 | #include <sys/systm.h> |
82 | #include <sys/namei.h> |
83 | #include <sys/filedesc.h> |
84 | #include <sys/kernel.h> |
85 | #include <sys/file.h> |
86 | #include <sys/fcntl.h> |
87 | #include <sys/stat.h> |
88 | #include <sys/vnode.h> |
89 | #include <sys/mount.h> |
90 | #include <sys/fstrans.h> |
91 | #include <sys/proc.h> |
92 | #include <sys/uio.h> |
93 | #include <sys/kmem.h> |
94 | #include <sys/dirent.h> |
95 | #include <sys/sysctl.h> |
96 | #include <sys/syscallargs.h> |
97 | #include <sys/vfs_syscalls.h> |
98 | #include <sys/quota.h> |
99 | #include <sys/quotactl.h> |
100 | #include <sys/ktrace.h> |
101 | #ifdef FILEASSOC |
102 | #include <sys/fileassoc.h> |
103 | #endif /* FILEASSOC */ |
104 | #include <sys/extattr.h> |
105 | #include <sys/verified_exec.h> |
106 | #include <sys/kauth.h> |
107 | #include <sys/atomic.h> |
108 | #include <sys/module.h> |
109 | #include <sys/buf.h> |
110 | #include <sys/event.h> |
111 | #include <sys/compat_stub.h> |
112 | |
113 | #include <miscfs/genfs/genfs.h> |
114 | #include <miscfs/specfs/specdev.h> |
115 | |
116 | #include <nfs/rpcv2.h> |
117 | #include <nfs/nfsproto.h> |
118 | #include <nfs/nfs.h> |
119 | #include <nfs/nfs_var.h> |
120 | |
121 | /* XXX this shouldn't be here */ |
122 | #ifndef OFF_T_MAX |
123 | #define OFF_T_MAX __type_max(off_t) |
124 | #endif |
125 | |
126 | static int change_flags(struct vnode *, u_long, struct lwp *); |
127 | static int change_mode(struct vnode *, int, struct lwp *); |
128 | static int change_owner(struct vnode *, uid_t, gid_t, struct lwp *, int); |
129 | static int do_sys_openat(lwp_t *, int, const char *, int, int, int *); |
130 | static int do_sys_mkdirat(struct lwp *l, int, const char *, mode_t, |
131 | enum uio_seg); |
132 | static int do_sys_mkfifoat(struct lwp *, int, const char *, mode_t); |
133 | static int do_sys_symlinkat(struct lwp *, const char *, int, const char *, |
134 | enum uio_seg); |
135 | static int do_sys_renameat(struct lwp *l, int, const char *, int, const char *, |
136 | enum uio_seg, int); |
137 | static int do_sys_readlinkat(struct lwp *, int, const char *, char *, |
138 | size_t, register_t *); |
139 | static int do_sys_unlinkat(struct lwp *, int, const char *, int, enum uio_seg); |
140 | |
141 | static int fd_nameiat(struct lwp *, int, struct nameidata *); |
142 | static int fd_nameiat_simple_user(struct lwp *, int, const char *, |
143 | namei_simple_flags_t, struct vnode **); |
144 | |
145 | /* |
146 | * This table is used to maintain compatibility with 4.3BSD |
147 | * and NetBSD 0.9 mount syscalls - and possibly other systems. |
148 | * Note, the order is important! |
149 | * |
150 | * Do not modify this table. It should only contain filesystems |
151 | * supported by NetBSD 0.9 and 4.3BSD. |
152 | */ |
153 | const char * const mountcompatnames[] = { |
154 | NULL, /* 0 = MOUNT_NONE */ |
155 | MOUNT_FFS, /* 1 = MOUNT_UFS */ |
156 | MOUNT_NFS, /* 2 */ |
157 | MOUNT_MFS, /* 3 */ |
158 | MOUNT_MSDOS, /* 4 */ |
159 | MOUNT_CD9660, /* 5 = MOUNT_ISOFS */ |
160 | MOUNT_FDESC, /* 6 */ |
161 | MOUNT_KERNFS, /* 7 */ |
162 | NULL, /* 8 = MOUNT_DEVFS */ |
163 | MOUNT_AFS, /* 9 */ |
164 | }; |
165 | |
166 | const int nmountcompatnames = __arraycount(mountcompatnames); |
167 | |
168 | static int |
169 | fd_nameiat(struct lwp *l, int fdat, struct nameidata *ndp) |
170 | { |
171 | file_t *dfp; |
172 | int error; |
173 | |
174 | if (fdat != AT_FDCWD) { |
175 | if ((error = fd_getvnode(fdat, &dfp)) != 0) |
176 | goto out; |
177 | |
178 | NDAT(ndp, dfp->f_vnode); |
179 | } |
180 | |
181 | error = namei(ndp); |
182 | |
183 | if (fdat != AT_FDCWD) |
184 | fd_putfile(fdat); |
185 | out: |
186 | return error; |
187 | } |
188 | |
189 | static int |
190 | fd_nameiat_simple_user(struct lwp *l, int fdat, const char *path, |
191 | namei_simple_flags_t sflags, struct vnode **vp_ret) |
192 | { |
193 | file_t *dfp; |
194 | struct vnode *dvp; |
195 | int error; |
196 | |
197 | if (fdat != AT_FDCWD) { |
198 | if ((error = fd_getvnode(fdat, &dfp)) != 0) |
199 | goto out; |
200 | |
201 | dvp = dfp->f_vnode; |
202 | } else { |
203 | dvp = NULL; |
204 | } |
205 | |
206 | error = nameiat_simple_user(dvp, path, sflags, vp_ret); |
207 | |
208 | if (fdat != AT_FDCWD) |
209 | fd_putfile(fdat); |
210 | out: |
211 | return error; |
212 | } |
213 | |
214 | static int |
215 | open_setfp(struct lwp *l, file_t *fp, struct vnode *vp, int indx, int flags) |
216 | { |
217 | int error; |
218 | |
219 | fp->f_flag = flags & FMASK; |
220 | fp->f_type = DTYPE_VNODE; |
221 | fp->f_ops = &vnops; |
222 | fp->f_vnode = vp; |
223 | |
224 | if (flags & (O_EXLOCK | O_SHLOCK)) { |
225 | struct flock lf; |
226 | int type; |
227 | |
228 | lf.l_whence = SEEK_SET; |
229 | lf.l_start = 0; |
230 | lf.l_len = 0; |
231 | if (flags & O_EXLOCK) |
232 | lf.l_type = F_WRLCK; |
233 | else |
234 | lf.l_type = F_RDLCK; |
235 | type = F_FLOCK; |
236 | if ((flags & FNONBLOCK) == 0) |
237 | type |= F_WAIT; |
238 | VOP_UNLOCK(vp); |
239 | error = VOP_ADVLOCK(vp, fp, F_SETLK, &lf, type); |
240 | if (error) { |
241 | (void) vn_close(vp, fp->f_flag, fp->f_cred); |
242 | fd_abort(l->l_proc, fp, indx); |
243 | return error; |
244 | } |
245 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
246 | atomic_or_uint(&fp->f_flag, FHASLOCK); |
247 | } |
248 | if (flags & O_CLOEXEC) |
249 | fd_set_exclose(l, indx, true); |
250 | return 0; |
251 | } |
252 | |
253 | static int |
254 | mount_update(struct lwp *l, struct vnode *vp, const char *path, int flags, |
255 | void *data, size_t *data_len) |
256 | { |
257 | struct mount *mp; |
258 | int error = 0, saved_flags; |
259 | |
260 | mp = vp->v_mount; |
261 | saved_flags = mp->mnt_flag; |
262 | |
263 | /* We can operate only on VV_ROOT nodes. */ |
264 | if ((vp->v_vflag & VV_ROOT) == 0) { |
265 | error = EINVAL; |
266 | goto out; |
267 | } |
268 | |
269 | /* |
270 | * We only allow the filesystem to be reloaded if it |
271 | * is currently mounted read-only. Additionally, we |
272 | * prevent read-write to read-only downgrades. |
273 | */ |
274 | if ((flags & (MNT_RELOAD | MNT_RDONLY)) != 0 && |
275 | (mp->mnt_flag & MNT_RDONLY) == 0 && |
276 | (mp->mnt_iflag & IMNT_CAN_RWTORO) == 0) { |
277 | error = EOPNOTSUPP; /* Needs translation */ |
278 | goto out; |
279 | } |
280 | |
281 | /* |
282 | * Enabling MNT_UNION requires a covered mountpoint and |
283 | * must not happen on the root mount. |
284 | */ |
285 | if ((flags & MNT_UNION) != 0 && mp->mnt_vnodecovered == NULLVP) { |
286 | error = EOPNOTSUPP; |
287 | goto out; |
288 | } |
289 | |
290 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, |
291 | KAUTH_REQ_SYSTEM_MOUNT_UPDATE, mp, KAUTH_ARG(flags), data); |
292 | if (error) |
293 | goto out; |
294 | |
295 | error = vfs_suspend(mp, 0); |
296 | if (error) |
297 | goto out; |
298 | |
299 | mutex_enter(&mp->mnt_updating); |
300 | |
301 | mp->mnt_flag &= ~MNT_OP_FLAGS; |
302 | mp->mnt_flag |= flags & MNT_OP_FLAGS; |
303 | |
304 | /* |
305 | * Set the mount level flags. |
306 | */ |
307 | if ((flags & MNT_RDONLY) != (mp->mnt_flag & MNT_RDONLY)) { |
308 | if ((flags & MNT_RDONLY)) |
309 | mp->mnt_iflag |= IMNT_WANTRDONLY; |
310 | else |
311 | mp->mnt_iflag |= IMNT_WANTRDWR; |
312 | } |
313 | mp->mnt_flag &= ~MNT_BASIC_FLAGS; |
314 | mp->mnt_flag |= flags & MNT_BASIC_FLAGS; |
315 | if ((mp->mnt_iflag & IMNT_WANTRDONLY)) |
316 | mp->mnt_flag &= ~MNT_RDONLY; |
317 | |
318 | error = VFS_MOUNT(mp, path, data, data_len); |
319 | |
320 | if (error && data != NULL) { |
321 | int error2; |
322 | |
323 | /* |
324 | * Update failed; let's try and see if it was an |
325 | * export request. For compat with 3.0 and earlier. |
326 | */ |
327 | error2 = vfs_hooks_reexport(mp, path, data); |
328 | |
329 | /* |
330 | * Only update error code if the export request was |
331 | * understood but some problem occurred while |
332 | * processing it. |
333 | */ |
334 | if (error2 != EJUSTRETURN) |
335 | error = error2; |
336 | } |
337 | |
338 | if (error == 0 && (mp->mnt_iflag & IMNT_WANTRDONLY)) |
339 | mp->mnt_flag |= MNT_RDONLY; |
340 | if (error) |
341 | mp->mnt_flag = saved_flags; |
342 | mp->mnt_flag &= ~MNT_OP_FLAGS; |
343 | mp->mnt_iflag &= ~(IMNT_WANTRDONLY | IMNT_WANTRDWR); |
344 | if ((mp->mnt_flag & (MNT_RDONLY | MNT_ASYNC)) == 0) { |
345 | if ((mp->mnt_iflag & IMNT_ONWORKLIST) == 0) |
346 | vfs_syncer_add_to_worklist(mp); |
347 | } else { |
348 | if ((mp->mnt_iflag & IMNT_ONWORKLIST) != 0) |
349 | vfs_syncer_remove_from_worklist(mp); |
350 | } |
351 | mutex_exit(&mp->mnt_updating); |
352 | vfs_resume(mp); |
353 | |
354 | if ((error == 0) && !(saved_flags & MNT_EXTATTR) && |
355 | (flags & MNT_EXTATTR)) { |
356 | if (VFS_EXTATTRCTL(mp, EXTATTR_CMD_START, |
357 | NULL, 0, NULL) != 0) { |
358 | printf("%s: failed to start extattr, error = %d" , |
359 | mp->mnt_stat.f_mntonname, error); |
360 | mp->mnt_flag &= ~MNT_EXTATTR; |
361 | } |
362 | } |
363 | |
364 | if ((error == 0) && (saved_flags & MNT_EXTATTR) && |
365 | !(flags & MNT_EXTATTR)) { |
366 | if (VFS_EXTATTRCTL(mp, EXTATTR_CMD_STOP, |
367 | NULL, 0, NULL) != 0) { |
368 | printf("%s: failed to stop extattr, error = %d" , |
369 | mp->mnt_stat.f_mntonname, error); |
370 | mp->mnt_flag |= MNT_RDONLY; |
371 | } |
372 | } |
373 | out: |
374 | return (error); |
375 | } |
376 | |
377 | static int |
378 | mount_get_vfsops(const char *fstype, enum uio_seg type_seg, |
379 | struct vfsops **vfsops) |
380 | { |
381 | char fstypename[sizeof(((struct statvfs *)NULL)->f_fstypename)]; |
382 | int error; |
383 | |
384 | if (type_seg == UIO_USERSPACE) { |
385 | /* Copy file-system type from userspace. */ |
386 | error = copyinstr(fstype, fstypename, sizeof(fstypename), NULL); |
387 | } else { |
388 | error = copystr(fstype, fstypename, sizeof(fstypename), NULL); |
389 | KASSERT(error == 0); |
390 | } |
391 | |
392 | if (error) { |
393 | /* |
394 | * Historically, filesystem types were identified by numbers. |
395 | * If we get an integer for the filesystem type instead of a |
396 | * string, we check to see if it matches one of the historic |
397 | * filesystem types. |
398 | */ |
399 | u_long fsindex = (u_long)fstype; |
400 | if (fsindex >= nmountcompatnames || |
401 | mountcompatnames[fsindex] == NULL) |
402 | return ENODEV; |
403 | strlcpy(fstypename, mountcompatnames[fsindex], |
404 | sizeof(fstypename)); |
405 | } |
406 | |
407 | /* Accept `ufs' as an alias for `ffs', for compatibility. */ |
408 | if (strcmp(fstypename, "ufs" ) == 0) |
409 | fstypename[0] = 'f'; |
410 | |
411 | if ((*vfsops = vfs_getopsbyname(fstypename)) != NULL) |
412 | return 0; |
413 | |
414 | /* If we can autoload a vfs module, try again */ |
415 | (void)module_autoload(fstypename, MODULE_CLASS_VFS); |
416 | |
417 | if ((*vfsops = vfs_getopsbyname(fstypename)) != NULL) |
418 | return 0; |
419 | |
420 | return ENODEV; |
421 | } |
422 | |
423 | static int |
424 | mount_getargs(struct lwp *l, struct vnode *vp, const char *path, int flags, |
425 | void *data, size_t *data_len) |
426 | { |
427 | struct mount *mp; |
428 | int error; |
429 | |
430 | /* If MNT_GETARGS is specified, it should be the only flag. */ |
431 | if (flags & ~MNT_GETARGS) |
432 | return EINVAL; |
433 | |
434 | mp = vp->v_mount; |
435 | |
436 | /* XXX: probably some notion of "can see" here if we want isolation. */ |
437 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, |
438 | KAUTH_REQ_SYSTEM_MOUNT_GET, mp, data, NULL); |
439 | if (error) |
440 | return error; |
441 | |
442 | if ((vp->v_vflag & VV_ROOT) == 0) |
443 | return EINVAL; |
444 | |
445 | if (vfs_busy(mp)) |
446 | return EPERM; |
447 | |
448 | mutex_enter(&mp->mnt_updating); |
449 | mp->mnt_flag &= ~MNT_OP_FLAGS; |
450 | mp->mnt_flag |= MNT_GETARGS; |
451 | error = VFS_MOUNT(mp, path, data, data_len); |
452 | mp->mnt_flag &= ~MNT_OP_FLAGS; |
453 | mutex_exit(&mp->mnt_updating); |
454 | |
455 | vfs_unbusy(mp); |
456 | return (error); |
457 | } |
458 | |
459 | int |
460 | sys___mount50(struct lwp *l, const struct sys___mount50_args *uap, register_t *retval) |
461 | { |
462 | /* { |
463 | syscallarg(const char *) type; |
464 | syscallarg(const char *) path; |
465 | syscallarg(int) flags; |
466 | syscallarg(void *) data; |
467 | syscallarg(size_t) data_len; |
468 | } */ |
469 | |
470 | return do_sys_mount(l, SCARG(uap, type), UIO_USERSPACE, SCARG(uap, path), |
471 | SCARG(uap, flags), SCARG(uap, data), UIO_USERSPACE, |
472 | SCARG(uap, data_len), retval); |
473 | } |
474 | |
475 | int |
476 | do_sys_mount(struct lwp *l, const char *type, enum uio_seg type_seg, |
477 | const char *path, int flags, void *data, enum uio_seg data_seg, |
478 | size_t data_len, register_t *retval) |
479 | { |
480 | struct vfsops *vfsops = NULL; /* XXX gcc4.8 */ |
481 | struct vnode *vp; |
482 | void *data_buf = data; |
483 | bool vfsopsrele = false; |
484 | size_t alloc_sz = 0; |
485 | int error; |
486 | |
487 | /* |
488 | * Get vnode to be covered |
489 | */ |
490 | error = namei_simple_user(path, NSM_FOLLOW_TRYEMULROOT, &vp); |
491 | if (error != 0) { |
492 | vp = NULL; |
493 | goto done; |
494 | } |
495 | |
496 | if (flags & (MNT_GETARGS | MNT_UPDATE)) { |
497 | vfsops = vp->v_mount->mnt_op; |
498 | } else { |
499 | /* 'type' is userspace */ |
500 | error = mount_get_vfsops(type, type_seg, &vfsops); |
501 | if (error != 0) |
502 | goto done; |
503 | vfsopsrele = true; |
504 | } |
505 | |
506 | /* |
507 | * We allow data to be NULL, even for userspace. Some fs's don't need |
508 | * it. The others will handle NULL. |
509 | */ |
510 | if (data != NULL && data_seg == UIO_USERSPACE) { |
511 | if (data_len == 0) { |
512 | /* No length supplied, use default for filesystem */ |
513 | data_len = vfsops->vfs_min_mount_data; |
514 | |
515 | /* |
516 | * Hopefully a longer buffer won't make copyin() fail. |
517 | * For compatibility with 3.0 and earlier. |
518 | */ |
519 | if (flags & MNT_UPDATE |
520 | && data_len < sizeof (struct mnt_export_args30)) |
521 | data_len = sizeof (struct mnt_export_args30); |
522 | } |
523 | if ((data_len == 0) || (data_len > VFS_MAX_MOUNT_DATA)) { |
524 | error = EINVAL; |
525 | goto done; |
526 | } |
527 | alloc_sz = data_len; |
528 | data_buf = kmem_alloc(alloc_sz, KM_SLEEP); |
529 | |
530 | /* NFS needs the buffer even for mnt_getargs .... */ |
531 | error = copyin(data, data_buf, data_len); |
532 | if (error != 0) |
533 | goto done; |
534 | } |
535 | |
536 | if (flags & MNT_GETARGS) { |
537 | if (data_len == 0) { |
538 | error = EINVAL; |
539 | goto done; |
540 | } |
541 | error = mount_getargs(l, vp, path, flags, data_buf, &data_len); |
542 | if (error != 0) |
543 | goto done; |
544 | if (data_seg == UIO_USERSPACE) |
545 | error = copyout(data_buf, data, data_len); |
546 | *retval = data_len; |
547 | } else if (flags & MNT_UPDATE) { |
548 | error = mount_update(l, vp, path, flags, data_buf, &data_len); |
549 | } else { |
550 | /* Locking is handled internally in mount_domount(). */ |
551 | KASSERT(vfsopsrele == true); |
552 | error = mount_domount(l, &vp, vfsops, path, flags, data_buf, |
553 | &data_len); |
554 | vfsopsrele = false; |
555 | } |
556 | if (!error) |
557 | KNOTE(&fs_klist, VQ_MOUNT); |
558 | |
559 | done: |
560 | if (vfsopsrele) |
561 | vfs_delref(vfsops); |
562 | if (vp != NULL) { |
563 | vrele(vp); |
564 | } |
565 | if (data_buf != data) |
566 | kmem_free(data_buf, alloc_sz); |
567 | return (error); |
568 | } |
569 | |
570 | /* |
571 | * Unmount a file system. |
572 | * |
573 | * Note: unmount takes a path to the vnode mounted on as argument, |
574 | * not special file (as before). |
575 | */ |
576 | /* ARGSUSED */ |
577 | int |
578 | sys_unmount(struct lwp *l, const struct sys_unmount_args *uap, register_t *retval) |
579 | { |
580 | /* { |
581 | syscallarg(const char *) path; |
582 | syscallarg(int) flags; |
583 | } */ |
584 | struct vnode *vp; |
585 | struct mount *mp; |
586 | int error; |
587 | struct pathbuf *pb; |
588 | struct nameidata nd; |
589 | |
590 | error = pathbuf_copyin(SCARG(uap, path), &pb); |
591 | if (error) { |
592 | return error; |
593 | } |
594 | |
595 | NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
596 | if ((error = namei(&nd)) != 0) { |
597 | pathbuf_destroy(pb); |
598 | return error; |
599 | } |
600 | vp = nd.ni_vp; |
601 | pathbuf_destroy(pb); |
602 | |
603 | mp = vp->v_mount; |
604 | vfs_ref(mp); |
605 | VOP_UNLOCK(vp); |
606 | |
607 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, |
608 | KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT, mp, NULL, NULL); |
609 | if (error) { |
610 | vrele(vp); |
611 | vfs_rele(mp); |
612 | return (error); |
613 | } |
614 | |
615 | /* |
616 | * Don't allow unmounting the root file system. |
617 | */ |
618 | if (mp->mnt_flag & MNT_ROOTFS) { |
619 | vrele(vp); |
620 | vfs_rele(mp); |
621 | return (EINVAL); |
622 | } |
623 | |
624 | /* |
625 | * Must be the root of the filesystem |
626 | */ |
627 | if ((vp->v_vflag & VV_ROOT) == 0) { |
628 | vrele(vp); |
629 | vfs_rele(mp); |
630 | return (EINVAL); |
631 | } |
632 | |
633 | vrele(vp); |
634 | error = dounmount(mp, SCARG(uap, flags), l); |
635 | vfs_rele(mp); |
636 | if (!error) |
637 | KNOTE(&fs_klist, VQ_UNMOUNT); |
638 | return error; |
639 | } |
640 | |
641 | /* |
642 | * Sync each mounted filesystem. |
643 | */ |
644 | #ifdef DEBUG |
645 | int syncprt = 0; |
646 | struct ctldebug debug0 = { "syncprt" , &syncprt }; |
647 | #endif |
648 | |
649 | void |
650 | do_sys_sync(struct lwp *l) |
651 | { |
652 | mount_iterator_t *iter; |
653 | struct mount *mp; |
654 | int asyncflag; |
655 | |
656 | mountlist_iterator_init(&iter); |
657 | while ((mp = mountlist_iterator_next(iter)) != NULL) { |
658 | mutex_enter(&mp->mnt_updating); |
659 | if ((mp->mnt_flag & MNT_RDONLY) == 0) { |
660 | asyncflag = mp->mnt_flag & MNT_ASYNC; |
661 | mp->mnt_flag &= ~MNT_ASYNC; |
662 | VFS_SYNC(mp, MNT_NOWAIT, l->l_cred); |
663 | if (asyncflag) |
664 | mp->mnt_flag |= MNT_ASYNC; |
665 | } |
666 | mutex_exit(&mp->mnt_updating); |
667 | } |
668 | mountlist_iterator_destroy(iter); |
669 | #ifdef DEBUG |
670 | if (syncprt) |
671 | vfs_bufstats(); |
672 | #endif /* DEBUG */ |
673 | } |
674 | |
675 | /* ARGSUSED */ |
676 | int |
677 | sys_sync(struct lwp *l, const void *v, register_t *retval) |
678 | { |
679 | do_sys_sync(l); |
680 | return (0); |
681 | } |
682 | |
683 | |
684 | /* |
685 | * Access or change filesystem quotas. |
686 | * |
687 | * (this is really 14 different calls bundled into one) |
688 | */ |
689 | |
690 | static int |
691 | do_sys_quotactl_stat(struct mount *mp, struct quotastat *info_u) |
692 | { |
693 | struct quotastat info_k; |
694 | int error; |
695 | |
696 | /* ensure any padding bytes are cleared */ |
697 | memset(&info_k, 0, sizeof(info_k)); |
698 | |
699 | error = vfs_quotactl_stat(mp, &info_k); |
700 | if (error) { |
701 | return error; |
702 | } |
703 | |
704 | return copyout(&info_k, info_u, sizeof(info_k)); |
705 | } |
706 | |
707 | static int |
708 | do_sys_quotactl_idtypestat(struct mount *mp, int idtype, |
709 | struct quotaidtypestat *info_u) |
710 | { |
711 | struct quotaidtypestat info_k; |
712 | int error; |
713 | |
714 | /* ensure any padding bytes are cleared */ |
715 | memset(&info_k, 0, sizeof(info_k)); |
716 | |
717 | error = vfs_quotactl_idtypestat(mp, idtype, &info_k); |
718 | if (error) { |
719 | return error; |
720 | } |
721 | |
722 | return copyout(&info_k, info_u, sizeof(info_k)); |
723 | } |
724 | |
725 | static int |
726 | do_sys_quotactl_objtypestat(struct mount *mp, int objtype, |
727 | struct quotaobjtypestat *info_u) |
728 | { |
729 | struct quotaobjtypestat info_k; |
730 | int error; |
731 | |
732 | /* ensure any padding bytes are cleared */ |
733 | memset(&info_k, 0, sizeof(info_k)); |
734 | |
735 | error = vfs_quotactl_objtypestat(mp, objtype, &info_k); |
736 | if (error) { |
737 | return error; |
738 | } |
739 | |
740 | return copyout(&info_k, info_u, sizeof(info_k)); |
741 | } |
742 | |
743 | static int |
744 | do_sys_quotactl_get(struct mount *mp, const struct quotakey *key_u, |
745 | struct quotaval *val_u) |
746 | { |
747 | struct quotakey key_k; |
748 | struct quotaval val_k; |
749 | int error; |
750 | |
751 | /* ensure any padding bytes are cleared */ |
752 | memset(&val_k, 0, sizeof(val_k)); |
753 | |
754 | error = copyin(key_u, &key_k, sizeof(key_k)); |
755 | if (error) { |
756 | return error; |
757 | } |
758 | |
759 | error = vfs_quotactl_get(mp, &key_k, &val_k); |
760 | if (error) { |
761 | return error; |
762 | } |
763 | |
764 | return copyout(&val_k, val_u, sizeof(val_k)); |
765 | } |
766 | |
767 | static int |
768 | do_sys_quotactl_put(struct mount *mp, const struct quotakey *key_u, |
769 | const struct quotaval *val_u) |
770 | { |
771 | struct quotakey key_k; |
772 | struct quotaval val_k; |
773 | int error; |
774 | |
775 | error = copyin(key_u, &key_k, sizeof(key_k)); |
776 | if (error) { |
777 | return error; |
778 | } |
779 | |
780 | error = copyin(val_u, &val_k, sizeof(val_k)); |
781 | if (error) { |
782 | return error; |
783 | } |
784 | |
785 | return vfs_quotactl_put(mp, &key_k, &val_k); |
786 | } |
787 | |
788 | static int |
789 | do_sys_quotactl_del(struct mount *mp, const struct quotakey *key_u) |
790 | { |
791 | struct quotakey key_k; |
792 | int error; |
793 | |
794 | error = copyin(key_u, &key_k, sizeof(key_k)); |
795 | if (error) { |
796 | return error; |
797 | } |
798 | |
799 | return vfs_quotactl_del(mp, &key_k); |
800 | } |
801 | |
802 | static int |
803 | do_sys_quotactl_cursoropen(struct mount *mp, struct quotakcursor *cursor_u) |
804 | { |
805 | struct quotakcursor cursor_k; |
806 | int error; |
807 | |
808 | /* ensure any padding bytes are cleared */ |
809 | memset(&cursor_k, 0, sizeof(cursor_k)); |
810 | |
811 | error = vfs_quotactl_cursoropen(mp, &cursor_k); |
812 | if (error) { |
813 | return error; |
814 | } |
815 | |
816 | return copyout(&cursor_k, cursor_u, sizeof(cursor_k)); |
817 | } |
818 | |
819 | static int |
820 | do_sys_quotactl_cursorclose(struct mount *mp, struct quotakcursor *cursor_u) |
821 | { |
822 | struct quotakcursor cursor_k; |
823 | int error; |
824 | |
825 | error = copyin(cursor_u, &cursor_k, sizeof(cursor_k)); |
826 | if (error) { |
827 | return error; |
828 | } |
829 | |
830 | return vfs_quotactl_cursorclose(mp, &cursor_k); |
831 | } |
832 | |
833 | static int |
834 | do_sys_quotactl_cursorskipidtype(struct mount *mp, |
835 | struct quotakcursor *cursor_u, int idtype) |
836 | { |
837 | struct quotakcursor cursor_k; |
838 | int error; |
839 | |
840 | error = copyin(cursor_u, &cursor_k, sizeof(cursor_k)); |
841 | if (error) { |
842 | return error; |
843 | } |
844 | |
845 | error = vfs_quotactl_cursorskipidtype(mp, &cursor_k, idtype); |
846 | if (error) { |
847 | return error; |
848 | } |
849 | |
850 | return copyout(&cursor_k, cursor_u, sizeof(cursor_k)); |
851 | } |
852 | |
853 | static int |
854 | do_sys_quotactl_cursorget(struct mount *mp, struct quotakcursor *cursor_u, |
855 | struct quotakey *keys_u, struct quotaval *vals_u, unsigned maxnum, |
856 | unsigned *ret_u) |
857 | { |
858 | #define CGET_STACK_MAX 8 |
859 | struct quotakcursor cursor_k; |
860 | struct quotakey stackkeys[CGET_STACK_MAX]; |
861 | struct quotaval stackvals[CGET_STACK_MAX]; |
862 | struct quotakey *keys_k; |
863 | struct quotaval *vals_k; |
864 | unsigned ret_k; |
865 | int error; |
866 | |
867 | if (maxnum > 128) { |
868 | maxnum = 128; |
869 | } |
870 | |
871 | error = copyin(cursor_u, &cursor_k, sizeof(cursor_k)); |
872 | if (error) { |
873 | return error; |
874 | } |
875 | |
876 | if (maxnum <= CGET_STACK_MAX) { |
877 | keys_k = stackkeys; |
878 | vals_k = stackvals; |
879 | /* ensure any padding bytes are cleared */ |
880 | memset(keys_k, 0, maxnum * sizeof(keys_k[0])); |
881 | memset(vals_k, 0, maxnum * sizeof(vals_k[0])); |
882 | } else { |
883 | keys_k = kmem_zalloc(maxnum * sizeof(keys_k[0]), KM_SLEEP); |
884 | vals_k = kmem_zalloc(maxnum * sizeof(vals_k[0]), KM_SLEEP); |
885 | } |
886 | |
887 | error = vfs_quotactl_cursorget(mp, &cursor_k, keys_k, vals_k, maxnum, |
888 | &ret_k); |
889 | if (error) { |
890 | goto fail; |
891 | } |
892 | |
893 | error = copyout(keys_k, keys_u, ret_k * sizeof(keys_k[0])); |
894 | if (error) { |
895 | goto fail; |
896 | } |
897 | |
898 | error = copyout(vals_k, vals_u, ret_k * sizeof(vals_k[0])); |
899 | if (error) { |
900 | goto fail; |
901 | } |
902 | |
903 | error = copyout(&ret_k, ret_u, sizeof(ret_k)); |
904 | if (error) { |
905 | goto fail; |
906 | } |
907 | |
908 | /* do last to maximize the chance of being able to recover a failure */ |
909 | error = copyout(&cursor_k, cursor_u, sizeof(cursor_k)); |
910 | |
911 | fail: |
912 | if (keys_k != stackkeys) { |
913 | kmem_free(keys_k, maxnum * sizeof(keys_k[0])); |
914 | } |
915 | if (vals_k != stackvals) { |
916 | kmem_free(vals_k, maxnum * sizeof(vals_k[0])); |
917 | } |
918 | return error; |
919 | } |
920 | |
921 | static int |
922 | do_sys_quotactl_cursoratend(struct mount *mp, struct quotakcursor *cursor_u, |
923 | int *ret_u) |
924 | { |
925 | struct quotakcursor cursor_k; |
926 | int ret_k; |
927 | int error; |
928 | |
929 | error = copyin(cursor_u, &cursor_k, sizeof(cursor_k)); |
930 | if (error) { |
931 | return error; |
932 | } |
933 | |
934 | error = vfs_quotactl_cursoratend(mp, &cursor_k, &ret_k); |
935 | if (error) { |
936 | return error; |
937 | } |
938 | |
939 | error = copyout(&ret_k, ret_u, sizeof(ret_k)); |
940 | if (error) { |
941 | return error; |
942 | } |
943 | |
944 | return copyout(&cursor_k, cursor_u, sizeof(cursor_k)); |
945 | } |
946 | |
947 | static int |
948 | do_sys_quotactl_cursorrewind(struct mount *mp, struct quotakcursor *cursor_u) |
949 | { |
950 | struct quotakcursor cursor_k; |
951 | int error; |
952 | |
953 | error = copyin(cursor_u, &cursor_k, sizeof(cursor_k)); |
954 | if (error) { |
955 | return error; |
956 | } |
957 | |
958 | error = vfs_quotactl_cursorrewind(mp, &cursor_k); |
959 | if (error) { |
960 | return error; |
961 | } |
962 | |
963 | return copyout(&cursor_k, cursor_u, sizeof(cursor_k)); |
964 | } |
965 | |
966 | static int |
967 | do_sys_quotactl_quotaon(struct mount *mp, int idtype, const char *path_u) |
968 | { |
969 | char *path_k; |
970 | int error; |
971 | |
972 | /* XXX this should probably be a struct pathbuf */ |
973 | path_k = PNBUF_GET(); |
974 | error = copyin(path_u, path_k, PATH_MAX); |
975 | if (error) { |
976 | PNBUF_PUT(path_k); |
977 | return error; |
978 | } |
979 | |
980 | error = vfs_quotactl_quotaon(mp, idtype, path_k); |
981 | |
982 | PNBUF_PUT(path_k); |
983 | return error; |
984 | } |
985 | |
986 | static int |
987 | do_sys_quotactl_quotaoff(struct mount *mp, int idtype) |
988 | { |
989 | return vfs_quotactl_quotaoff(mp, idtype); |
990 | } |
991 | |
992 | int |
993 | do_sys_quotactl(const char *path_u, const struct quotactl_args *args) |
994 | { |
995 | struct mount *mp; |
996 | struct vnode *vp; |
997 | int error; |
998 | |
999 | error = namei_simple_user(path_u, NSM_FOLLOW_TRYEMULROOT, &vp); |
1000 | if (error != 0) |
1001 | return (error); |
1002 | mp = vp->v_mount; |
1003 | |
1004 | switch (args->qc_op) { |
1005 | case QUOTACTL_STAT: |
1006 | error = do_sys_quotactl_stat(mp, args->u.stat.qc_info); |
1007 | break; |
1008 | case QUOTACTL_IDTYPESTAT: |
1009 | error = do_sys_quotactl_idtypestat(mp, |
1010 | args->u.idtypestat.qc_idtype, |
1011 | args->u.idtypestat.qc_info); |
1012 | break; |
1013 | case QUOTACTL_OBJTYPESTAT: |
1014 | error = do_sys_quotactl_objtypestat(mp, |
1015 | args->u.objtypestat.qc_objtype, |
1016 | args->u.objtypestat.qc_info); |
1017 | break; |
1018 | case QUOTACTL_GET: |
1019 | error = do_sys_quotactl_get(mp, |
1020 | args->u.get.qc_key, |
1021 | args->u.get.qc_val); |
1022 | break; |
1023 | case QUOTACTL_PUT: |
1024 | error = do_sys_quotactl_put(mp, |
1025 | args->u.put.qc_key, |
1026 | args->u.put.qc_val); |
1027 | break; |
1028 | case QUOTACTL_DEL: |
1029 | error = do_sys_quotactl_del(mp, args->u.del.qc_key); |
1030 | break; |
1031 | case QUOTACTL_CURSOROPEN: |
1032 | error = do_sys_quotactl_cursoropen(mp, |
1033 | args->u.cursoropen.qc_cursor); |
1034 | break; |
1035 | case QUOTACTL_CURSORCLOSE: |
1036 | error = do_sys_quotactl_cursorclose(mp, |
1037 | args->u.cursorclose.qc_cursor); |
1038 | break; |
1039 | case QUOTACTL_CURSORSKIPIDTYPE: |
1040 | error = do_sys_quotactl_cursorskipidtype(mp, |
1041 | args->u.cursorskipidtype.qc_cursor, |
1042 | args->u.cursorskipidtype.qc_idtype); |
1043 | break; |
1044 | case QUOTACTL_CURSORGET: |
1045 | error = do_sys_quotactl_cursorget(mp, |
1046 | args->u.cursorget.qc_cursor, |
1047 | args->u.cursorget.qc_keys, |
1048 | args->u.cursorget.qc_vals, |
1049 | args->u.cursorget.qc_maxnum, |
1050 | args->u.cursorget.qc_ret); |
1051 | break; |
1052 | case QUOTACTL_CURSORATEND: |
1053 | error = do_sys_quotactl_cursoratend(mp, |
1054 | args->u.cursoratend.qc_cursor, |
1055 | args->u.cursoratend.qc_ret); |
1056 | break; |
1057 | case QUOTACTL_CURSORREWIND: |
1058 | error = do_sys_quotactl_cursorrewind(mp, |
1059 | args->u.cursorrewind.qc_cursor); |
1060 | break; |
1061 | case QUOTACTL_QUOTAON: |
1062 | error = do_sys_quotactl_quotaon(mp, |
1063 | args->u.quotaon.qc_idtype, |
1064 | args->u.quotaon.qc_quotafile); |
1065 | break; |
1066 | case QUOTACTL_QUOTAOFF: |
1067 | error = do_sys_quotactl_quotaoff(mp, |
1068 | args->u.quotaoff.qc_idtype); |
1069 | break; |
1070 | default: |
1071 | error = EINVAL; |
1072 | break; |
1073 | } |
1074 | |
1075 | vrele(vp); |
1076 | return error; |
1077 | } |
1078 | |
1079 | /* ARGSUSED */ |
1080 | int |
1081 | sys___quotactl(struct lwp *l, const struct sys___quotactl_args *uap, |
1082 | register_t *retval) |
1083 | { |
1084 | /* { |
1085 | syscallarg(const char *) path; |
1086 | syscallarg(struct quotactl_args *) args; |
1087 | } */ |
1088 | struct quotactl_args args; |
1089 | int error; |
1090 | |
1091 | error = copyin(SCARG(uap, args), &args, sizeof(args)); |
1092 | if (error) { |
1093 | return error; |
1094 | } |
1095 | |
1096 | return do_sys_quotactl(SCARG(uap, path), &args); |
1097 | } |
1098 | |
1099 | int |
1100 | dostatvfs(struct mount *mp, struct statvfs *sp, struct lwp *l, int flags, |
1101 | int root) |
1102 | { |
1103 | struct cwdinfo *cwdi = l->l_proc->p_cwdi; |
1104 | int error = 0; |
1105 | |
1106 | /* |
1107 | * If MNT_NOWAIT or MNT_LAZY is specified, do not |
1108 | * refresh the fsstat cache. MNT_WAIT or MNT_LAZY |
1109 | * overrides MNT_NOWAIT. |
1110 | */ |
1111 | if (flags == MNT_NOWAIT || flags == MNT_LAZY || |
1112 | (flags != MNT_WAIT && flags != 0)) { |
1113 | memcpy(sp, &mp->mnt_stat, sizeof(*sp)); |
1114 | goto done; |
1115 | } |
1116 | |
1117 | /* Get the filesystem stats now */ |
1118 | memset(sp, 0, sizeof(*sp)); |
1119 | if ((error = VFS_STATVFS(mp, sp)) != 0) { |
1120 | return error; |
1121 | } |
1122 | |
1123 | if (cwdi->cwdi_rdir == NULL) |
1124 | (void)memcpy(&mp->mnt_stat, sp, sizeof(mp->mnt_stat)); |
1125 | done: |
1126 | if (cwdi->cwdi_rdir != NULL) { |
1127 | size_t len; |
1128 | char *bp; |
1129 | char c; |
1130 | char *path = PNBUF_GET(); |
1131 | |
1132 | bp = path + MAXPATHLEN; |
1133 | *--bp = '\0'; |
1134 | rw_enter(&cwdi->cwdi_lock, RW_READER); |
1135 | error = getcwd_common(cwdi->cwdi_rdir, rootvnode, &bp, path, |
1136 | MAXPATHLEN / 2, 0, l); |
1137 | rw_exit(&cwdi->cwdi_lock); |
1138 | if (error) { |
1139 | PNBUF_PUT(path); |
1140 | return error; |
1141 | } |
1142 | len = strlen(bp); |
1143 | if (len != 1) { |
1144 | /* |
1145 | * for mount points that are below our root, we can see |
1146 | * them, so we fix up the pathname and return them. The |
1147 | * rest we cannot see, so we don't allow viewing the |
1148 | * data. |
1149 | */ |
1150 | if (strncmp(bp, sp->f_mntonname, len) == 0 && |
1151 | ((c = sp->f_mntonname[len]) == '/' || c == '\0')) { |
1152 | (void)strlcpy(sp->f_mntonname, |
1153 | c == '\0' ? "/" : &sp->f_mntonname[len], |
1154 | sizeof(sp->f_mntonname)); |
1155 | } else { |
1156 | if (root) |
1157 | (void)strlcpy(sp->f_mntonname, "/" , |
1158 | sizeof(sp->f_mntonname)); |
1159 | else |
1160 | error = EPERM; |
1161 | } |
1162 | } |
1163 | PNBUF_PUT(path); |
1164 | } |
1165 | sp->f_flag = mp->mnt_flag & MNT_VISFLAGMASK; |
1166 | return error; |
1167 | } |
1168 | |
1169 | /* |
1170 | * Get filesystem statistics by path. |
1171 | */ |
1172 | int |
1173 | do_sys_pstatvfs(struct lwp *l, const char *path, int flags, struct statvfs *sb) |
1174 | { |
1175 | struct mount *mp; |
1176 | int error; |
1177 | struct vnode *vp; |
1178 | |
1179 | error = namei_simple_user(path, NSM_FOLLOW_TRYEMULROOT, &vp); |
1180 | if (error != 0) |
1181 | return error; |
1182 | mp = vp->v_mount; |
1183 | error = dostatvfs(mp, sb, l, flags, 1); |
1184 | vrele(vp); |
1185 | return error; |
1186 | } |
1187 | |
1188 | /* ARGSUSED */ |
1189 | int |
1190 | sys_statvfs1(struct lwp *l, const struct sys_statvfs1_args *uap, register_t *retval) |
1191 | { |
1192 | /* { |
1193 | syscallarg(const char *) path; |
1194 | syscallarg(struct statvfs *) buf; |
1195 | syscallarg(int) flags; |
1196 | } */ |
1197 | struct statvfs *sb; |
1198 | int error; |
1199 | |
1200 | sb = STATVFSBUF_GET(); |
1201 | error = do_sys_pstatvfs(l, SCARG(uap, path), SCARG(uap, flags), sb); |
1202 | if (error == 0) |
1203 | error = copyout(sb, SCARG(uap, buf), sizeof(*sb)); |
1204 | STATVFSBUF_PUT(sb); |
1205 | return error; |
1206 | } |
1207 | |
1208 | /* |
1209 | * Get filesystem statistics by fd. |
1210 | */ |
1211 | int |
1212 | do_sys_fstatvfs(struct lwp *l, int fd, int flags, struct statvfs *sb) |
1213 | { |
1214 | file_t *fp; |
1215 | struct mount *mp; |
1216 | int error; |
1217 | |
1218 | /* fd_getvnode() will use the descriptor for us */ |
1219 | if ((error = fd_getvnode(fd, &fp)) != 0) |
1220 | return (error); |
1221 | mp = fp->f_vnode->v_mount; |
1222 | error = dostatvfs(mp, sb, curlwp, flags, 1); |
1223 | fd_putfile(fd); |
1224 | return error; |
1225 | } |
1226 | |
1227 | /* ARGSUSED */ |
1228 | int |
1229 | sys_fstatvfs1(struct lwp *l, const struct sys_fstatvfs1_args *uap, register_t *retval) |
1230 | { |
1231 | /* { |
1232 | syscallarg(int) fd; |
1233 | syscallarg(struct statvfs *) buf; |
1234 | syscallarg(int) flags; |
1235 | } */ |
1236 | struct statvfs *sb; |
1237 | int error; |
1238 | |
1239 | sb = STATVFSBUF_GET(); |
1240 | error = do_sys_fstatvfs(l, SCARG(uap, fd), SCARG(uap, flags), sb); |
1241 | if (error == 0) |
1242 | error = copyout(sb, SCARG(uap, buf), sizeof(*sb)); |
1243 | STATVFSBUF_PUT(sb); |
1244 | return error; |
1245 | } |
1246 | |
1247 | |
1248 | /* |
1249 | * Get statistics on all filesystems. |
1250 | */ |
1251 | int |
1252 | do_sys_getvfsstat(struct lwp *l, void *sfsp, size_t bufsize, int flags, |
1253 | int (*copyfn)(const void *, void *, size_t), size_t entry_sz, |
1254 | register_t *retval) |
1255 | { |
1256 | int root = 0; |
1257 | mount_iterator_t *iter; |
1258 | struct proc *p = l->l_proc; |
1259 | struct mount *mp; |
1260 | struct statvfs *sb; |
1261 | size_t count, maxcount; |
1262 | int error = 0; |
1263 | |
1264 | sb = STATVFSBUF_GET(); |
1265 | maxcount = bufsize / entry_sz; |
1266 | count = 0; |
1267 | mountlist_iterator_init(&iter); |
1268 | while ((mp = mountlist_iterator_next(iter)) != NULL) { |
1269 | if (sfsp && count < maxcount) { |
1270 | error = dostatvfs(mp, sb, l, flags, 0); |
1271 | if (error) { |
1272 | error = 0; |
1273 | continue; |
1274 | } |
1275 | error = copyfn(sb, sfsp, entry_sz); |
1276 | if (error) |
1277 | goto out; |
1278 | sfsp = (char *)sfsp + entry_sz; |
1279 | root |= strcmp(sb->f_mntonname, "/" ) == 0; |
1280 | } |
1281 | count++; |
1282 | } |
1283 | |
1284 | if (root == 0 && p->p_cwdi->cwdi_rdir) { |
1285 | /* |
1286 | * fake a root entry |
1287 | */ |
1288 | error = dostatvfs(p->p_cwdi->cwdi_rdir->v_mount, |
1289 | sb, l, flags, 1); |
1290 | if (error != 0) |
1291 | goto out; |
1292 | if (sfsp) { |
1293 | error = copyfn(sb, sfsp, entry_sz); |
1294 | if (error != 0) |
1295 | goto out; |
1296 | } |
1297 | count++; |
1298 | } |
1299 | if (sfsp && count > maxcount) |
1300 | *retval = maxcount; |
1301 | else |
1302 | *retval = count; |
1303 | out: |
1304 | mountlist_iterator_destroy(iter); |
1305 | STATVFSBUF_PUT(sb); |
1306 | return error; |
1307 | } |
1308 | |
1309 | int |
1310 | sys_getvfsstat(struct lwp *l, const struct sys_getvfsstat_args *uap, register_t *retval) |
1311 | { |
1312 | /* { |
1313 | syscallarg(struct statvfs *) buf; |
1314 | syscallarg(size_t) bufsize; |
1315 | syscallarg(int) flags; |
1316 | } */ |
1317 | |
1318 | return do_sys_getvfsstat(l, SCARG(uap, buf), SCARG(uap, bufsize), |
1319 | SCARG(uap, flags), copyout, sizeof (struct statvfs), retval); |
1320 | } |
1321 | |
1322 | /* |
1323 | * Change current working directory to a given file descriptor. |
1324 | */ |
1325 | /* ARGSUSED */ |
1326 | int |
1327 | sys_fchdir(struct lwp *l, const struct sys_fchdir_args *uap, register_t *retval) |
1328 | { |
1329 | /* { |
1330 | syscallarg(int) fd; |
1331 | } */ |
1332 | struct proc *p = l->l_proc; |
1333 | struct cwdinfo *cwdi; |
1334 | struct vnode *vp, *tdp; |
1335 | struct mount *mp; |
1336 | file_t *fp; |
1337 | int error, fd; |
1338 | |
1339 | /* fd_getvnode() will use the descriptor for us */ |
1340 | fd = SCARG(uap, fd); |
1341 | if ((error = fd_getvnode(fd, &fp)) != 0) |
1342 | return (error); |
1343 | vp = fp->f_vnode; |
1344 | |
1345 | vref(vp); |
1346 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
1347 | if (vp->v_type != VDIR) |
1348 | error = ENOTDIR; |
1349 | else |
1350 | error = VOP_ACCESS(vp, VEXEC, l->l_cred); |
1351 | if (error) { |
1352 | vput(vp); |
1353 | goto out; |
1354 | } |
1355 | while ((mp = vp->v_mountedhere) != NULL) { |
1356 | error = vfs_busy(mp); |
1357 | vput(vp); |
1358 | if (error != 0) |
1359 | goto out; |
1360 | error = VFS_ROOT(mp, &tdp); |
1361 | vfs_unbusy(mp); |
1362 | if (error) |
1363 | goto out; |
1364 | vp = tdp; |
1365 | } |
1366 | VOP_UNLOCK(vp); |
1367 | |
1368 | /* |
1369 | * Disallow changing to a directory not under the process's |
1370 | * current root directory (if there is one). |
1371 | */ |
1372 | cwdi = p->p_cwdi; |
1373 | rw_enter(&cwdi->cwdi_lock, RW_WRITER); |
1374 | if (cwdi->cwdi_rdir && !vn_isunder(vp, NULL, l)) { |
1375 | vrele(vp); |
1376 | error = EPERM; /* operation not permitted */ |
1377 | } else { |
1378 | vrele(cwdi->cwdi_cdir); |
1379 | cwdi->cwdi_cdir = vp; |
1380 | } |
1381 | rw_exit(&cwdi->cwdi_lock); |
1382 | |
1383 | out: |
1384 | fd_putfile(fd); |
1385 | return (error); |
1386 | } |
1387 | |
1388 | /* |
1389 | * Change this process's notion of the root directory to a given file |
1390 | * descriptor. |
1391 | */ |
1392 | int |
1393 | sys_fchroot(struct lwp *l, const struct sys_fchroot_args *uap, register_t *retval) |
1394 | { |
1395 | struct proc *p = l->l_proc; |
1396 | struct vnode *vp; |
1397 | file_t *fp; |
1398 | int error, fd = SCARG(uap, fd); |
1399 | |
1400 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CHROOT, |
1401 | KAUTH_REQ_SYSTEM_CHROOT_FCHROOT, NULL, NULL, NULL)) != 0) |
1402 | return error; |
1403 | /* fd_getvnode() will use the descriptor for us */ |
1404 | if ((error = fd_getvnode(fd, &fp)) != 0) |
1405 | return error; |
1406 | vp = fp->f_vnode; |
1407 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
1408 | if (vp->v_type != VDIR) |
1409 | error = ENOTDIR; |
1410 | else |
1411 | error = VOP_ACCESS(vp, VEXEC, l->l_cred); |
1412 | VOP_UNLOCK(vp); |
1413 | if (error) |
1414 | goto out; |
1415 | vref(vp); |
1416 | |
1417 | change_root(p->p_cwdi, vp, l); |
1418 | |
1419 | out: |
1420 | fd_putfile(fd); |
1421 | return (error); |
1422 | } |
1423 | |
1424 | /* |
1425 | * Change current working directory (``.''). |
1426 | */ |
1427 | /* ARGSUSED */ |
1428 | int |
1429 | sys_chdir(struct lwp *l, const struct sys_chdir_args *uap, register_t *retval) |
1430 | { |
1431 | /* { |
1432 | syscallarg(const char *) path; |
1433 | } */ |
1434 | struct proc *p = l->l_proc; |
1435 | struct cwdinfo *cwdi; |
1436 | int error; |
1437 | struct vnode *vp; |
1438 | |
1439 | if ((error = chdir_lookup(SCARG(uap, path), UIO_USERSPACE, |
1440 | &vp, l)) != 0) |
1441 | return (error); |
1442 | cwdi = p->p_cwdi; |
1443 | rw_enter(&cwdi->cwdi_lock, RW_WRITER); |
1444 | vrele(cwdi->cwdi_cdir); |
1445 | cwdi->cwdi_cdir = vp; |
1446 | rw_exit(&cwdi->cwdi_lock); |
1447 | return (0); |
1448 | } |
1449 | |
1450 | /* |
1451 | * Change notion of root (``/'') directory. |
1452 | */ |
1453 | /* ARGSUSED */ |
1454 | int |
1455 | sys_chroot(struct lwp *l, const struct sys_chroot_args *uap, register_t *retval) |
1456 | { |
1457 | /* { |
1458 | syscallarg(const char *) path; |
1459 | } */ |
1460 | struct proc *p = l->l_proc; |
1461 | int error; |
1462 | struct vnode *vp; |
1463 | |
1464 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_CHROOT, |
1465 | KAUTH_REQ_SYSTEM_CHROOT_CHROOT, NULL, NULL, NULL)) != 0) |
1466 | return (error); |
1467 | if ((error = chdir_lookup(SCARG(uap, path), UIO_USERSPACE, |
1468 | &vp, l)) != 0) |
1469 | return (error); |
1470 | |
1471 | change_root(p->p_cwdi, vp, l); |
1472 | |
1473 | return (0); |
1474 | } |
1475 | |
1476 | /* |
1477 | * Common routine for chroot and fchroot. |
1478 | * NB: callers need to properly authorize the change root operation. |
1479 | */ |
1480 | void |
1481 | change_root(struct cwdinfo *cwdi, struct vnode *vp, struct lwp *l) |
1482 | { |
1483 | struct proc *p = l->l_proc; |
1484 | kauth_cred_t ncred; |
1485 | |
1486 | ncred = kauth_cred_alloc(); |
1487 | |
1488 | rw_enter(&cwdi->cwdi_lock, RW_WRITER); |
1489 | if (cwdi->cwdi_rdir != NULL) |
1490 | vrele(cwdi->cwdi_rdir); |
1491 | cwdi->cwdi_rdir = vp; |
1492 | |
1493 | /* |
1494 | * Prevent escaping from chroot by putting the root under |
1495 | * the working directory. Silently chdir to / if we aren't |
1496 | * already there. |
1497 | */ |
1498 | if (!vn_isunder(cwdi->cwdi_cdir, vp, l)) { |
1499 | /* |
1500 | * XXX would be more failsafe to change directory to a |
1501 | * deadfs node here instead |
1502 | */ |
1503 | vrele(cwdi->cwdi_cdir); |
1504 | vref(vp); |
1505 | cwdi->cwdi_cdir = vp; |
1506 | } |
1507 | rw_exit(&cwdi->cwdi_lock); |
1508 | |
1509 | /* Get a write lock on the process credential. */ |
1510 | proc_crmod_enter(); |
1511 | |
1512 | kauth_cred_clone(p->p_cred, ncred); |
1513 | kauth_proc_chroot(ncred, p->p_cwdi); |
1514 | |
1515 | /* Broadcast our credentials to the process and other LWPs. */ |
1516 | proc_crmod_leave(ncred, p->p_cred, true); |
1517 | } |
1518 | |
1519 | /* |
1520 | * Common routine for chroot and chdir. |
1521 | * XXX "where" should be enum uio_seg |
1522 | */ |
1523 | int |
1524 | chdir_lookup(const char *path, int where, struct vnode **vpp, struct lwp *l) |
1525 | { |
1526 | struct pathbuf *pb; |
1527 | struct nameidata nd; |
1528 | int error; |
1529 | |
1530 | error = pathbuf_maybe_copyin(path, where, &pb); |
1531 | if (error) { |
1532 | return error; |
1533 | } |
1534 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
1535 | if ((error = namei(&nd)) != 0) { |
1536 | pathbuf_destroy(pb); |
1537 | return error; |
1538 | } |
1539 | *vpp = nd.ni_vp; |
1540 | pathbuf_destroy(pb); |
1541 | |
1542 | if ((*vpp)->v_type != VDIR) |
1543 | error = ENOTDIR; |
1544 | else |
1545 | error = VOP_ACCESS(*vpp, VEXEC, l->l_cred); |
1546 | |
1547 | if (error) |
1548 | vput(*vpp); |
1549 | else |
1550 | VOP_UNLOCK(*vpp); |
1551 | return (error); |
1552 | } |
1553 | |
1554 | /* |
1555 | * Internals of sys_open - path has already been converted into a pathbuf |
1556 | * (so we can easily reuse this function from other parts of the kernel, |
1557 | * like posix_spawn post-processing). |
1558 | */ |
1559 | int |
1560 | do_open(lwp_t *l, struct vnode *dvp, struct pathbuf *pb, int open_flags, |
1561 | int open_mode, int *fd) |
1562 | { |
1563 | struct proc *p = l->l_proc; |
1564 | struct cwdinfo *cwdi = p->p_cwdi; |
1565 | file_t *fp; |
1566 | struct vnode *vp; |
1567 | int flags, cmode; |
1568 | int indx, error; |
1569 | struct nameidata nd; |
1570 | |
1571 | if (open_flags & O_SEARCH) { |
1572 | open_flags &= ~(int)O_SEARCH; |
1573 | } |
1574 | |
1575 | flags = FFLAGS(open_flags); |
1576 | if ((flags & (FREAD | FWRITE)) == 0) |
1577 | return EINVAL; |
1578 | |
1579 | if ((error = fd_allocfile(&fp, &indx)) != 0) { |
1580 | return error; |
1581 | } |
1582 | |
1583 | /* We're going to read cwdi->cwdi_cmask unlocked here. */ |
1584 | cmode = ((open_mode &~ cwdi->cwdi_cmask) & ALLPERMS) &~ S_ISTXT; |
1585 | NDINIT(&nd, LOOKUP, FOLLOW | TRYEMULROOT, pb); |
1586 | if (dvp != NULL) |
1587 | NDAT(&nd, dvp); |
1588 | |
1589 | l->l_dupfd = -indx - 1; /* XXX check for fdopen */ |
1590 | if ((error = vn_open(&nd, flags, cmode)) != 0) { |
1591 | fd_abort(p, fp, indx); |
1592 | if ((error == EDUPFD || error == EMOVEFD) && |
1593 | l->l_dupfd >= 0 && /* XXX from fdopen */ |
1594 | (error = |
1595 | fd_dupopen(l->l_dupfd, &indx, flags, error)) == 0) { |
1596 | *fd = indx; |
1597 | return 0; |
1598 | } |
1599 | if (error == ERESTART) |
1600 | error = EINTR; |
1601 | return error; |
1602 | } |
1603 | |
1604 | l->l_dupfd = 0; |
1605 | vp = nd.ni_vp; |
1606 | |
1607 | if ((error = open_setfp(l, fp, vp, indx, flags))) |
1608 | return error; |
1609 | |
1610 | VOP_UNLOCK(vp); |
1611 | *fd = indx; |
1612 | fd_affix(p, fp, indx); |
1613 | return 0; |
1614 | } |
1615 | |
1616 | int |
1617 | fd_open(const char *path, int open_flags, int open_mode, int *fd) |
1618 | { |
1619 | struct pathbuf *pb; |
1620 | int error, oflags; |
1621 | |
1622 | oflags = FFLAGS(open_flags); |
1623 | if ((oflags & (FREAD | FWRITE)) == 0) |
1624 | return EINVAL; |
1625 | |
1626 | pb = pathbuf_create(path); |
1627 | if (pb == NULL) |
1628 | return ENOMEM; |
1629 | |
1630 | error = do_open(curlwp, NULL, pb, open_flags, open_mode, fd); |
1631 | pathbuf_destroy(pb); |
1632 | |
1633 | return error; |
1634 | } |
1635 | |
1636 | static int |
1637 | do_sys_openat(lwp_t *l, int fdat, const char *path, int flags, |
1638 | int mode, int *fd) |
1639 | { |
1640 | file_t *dfp = NULL; |
1641 | struct vnode *dvp = NULL; |
1642 | struct pathbuf *pb; |
1643 | const char *pathstring = NULL; |
1644 | int error; |
1645 | |
1646 | if (path == NULL) { |
1647 | MODULE_HOOK_CALL(vfs_openat_10_hook, (&pb), enosys(), error); |
1648 | if (error == ENOSYS) |
1649 | goto no_compat; |
1650 | if (error) |
1651 | return error; |
1652 | } else { |
1653 | no_compat: |
1654 | error = pathbuf_copyin(path, &pb); |
1655 | if (error) |
1656 | return error; |
1657 | } |
1658 | |
1659 | pathstring = pathbuf_stringcopy_get(pb); |
1660 | |
1661 | /* |
1662 | * fdat is ignored if: |
1663 | * 1) if fdat is AT_FDCWD, which means use current directory as base. |
1664 | * 2) if path is absolute, then fdat is useless. |
1665 | */ |
1666 | if (fdat != AT_FDCWD && pathstring[0] != '/') { |
1667 | /* fd_getvnode() will use the descriptor for us */ |
1668 | if ((error = fd_getvnode(fdat, &dfp)) != 0) |
1669 | goto out; |
1670 | |
1671 | dvp = dfp->f_vnode; |
1672 | } |
1673 | |
1674 | error = do_open(l, dvp, pb, flags, mode, fd); |
1675 | |
1676 | if (dfp != NULL) |
1677 | fd_putfile(fdat); |
1678 | out: |
1679 | pathbuf_stringcopy_put(pb, pathstring); |
1680 | pathbuf_destroy(pb); |
1681 | return error; |
1682 | } |
1683 | |
1684 | int |
1685 | sys_open(struct lwp *l, const struct sys_open_args *uap, register_t *retval) |
1686 | { |
1687 | /* { |
1688 | syscallarg(const char *) path; |
1689 | syscallarg(int) flags; |
1690 | syscallarg(int) mode; |
1691 | } */ |
1692 | int error; |
1693 | int fd; |
1694 | |
1695 | error = do_sys_openat(l, AT_FDCWD, SCARG(uap, path), |
1696 | SCARG(uap, flags), SCARG(uap, mode), &fd); |
1697 | |
1698 | if (error == 0) |
1699 | *retval = fd; |
1700 | |
1701 | return error; |
1702 | } |
1703 | |
1704 | int |
1705 | sys_openat(struct lwp *l, const struct sys_openat_args *uap, register_t *retval) |
1706 | { |
1707 | /* { |
1708 | syscallarg(int) fd; |
1709 | syscallarg(const char *) path; |
1710 | syscallarg(int) oflags; |
1711 | syscallarg(int) mode; |
1712 | } */ |
1713 | int error; |
1714 | int fd; |
1715 | |
1716 | error = do_sys_openat(l, SCARG(uap, fd), SCARG(uap, path), |
1717 | SCARG(uap, oflags), SCARG(uap, mode), &fd); |
1718 | |
1719 | if (error == 0) |
1720 | *retval = fd; |
1721 | |
1722 | return error; |
1723 | } |
1724 | |
1725 | static void |
1726 | vfs__fhfree(fhandle_t *fhp) |
1727 | { |
1728 | size_t fhsize; |
1729 | |
1730 | fhsize = FHANDLE_SIZE(fhp); |
1731 | kmem_free(fhp, fhsize); |
1732 | } |
1733 | |
1734 | /* |
1735 | * vfs_composefh: compose a filehandle. |
1736 | */ |
1737 | |
1738 | int |
1739 | vfs_composefh(struct vnode *vp, fhandle_t *fhp, size_t *fh_size) |
1740 | { |
1741 | struct mount *mp; |
1742 | struct fid *fidp; |
1743 | int error; |
1744 | size_t needfhsize; |
1745 | size_t fidsize; |
1746 | |
1747 | mp = vp->v_mount; |
1748 | fidp = NULL; |
1749 | if (*fh_size < FHANDLE_SIZE_MIN) { |
1750 | fidsize = 0; |
1751 | } else { |
1752 | fidsize = *fh_size - offsetof(fhandle_t, fh_fid); |
1753 | if (fhp != NULL) { |
1754 | memset(fhp, 0, *fh_size); |
1755 | fhp->fh_fsid = mp->mnt_stat.f_fsidx; |
1756 | fidp = &fhp->fh_fid; |
1757 | } |
1758 | } |
1759 | error = VFS_VPTOFH(vp, fidp, &fidsize); |
1760 | needfhsize = FHANDLE_SIZE_FROM_FILEID_SIZE(fidsize); |
1761 | if (error == 0 && *fh_size < needfhsize) { |
1762 | error = E2BIG; |
1763 | } |
1764 | *fh_size = needfhsize; |
1765 | return error; |
1766 | } |
1767 | |
1768 | int |
1769 | vfs_composefh_alloc(struct vnode *vp, fhandle_t **fhpp) |
1770 | { |
1771 | struct mount *mp; |
1772 | fhandle_t *fhp; |
1773 | size_t fhsize; |
1774 | size_t fidsize; |
1775 | int error; |
1776 | |
1777 | mp = vp->v_mount; |
1778 | fidsize = 0; |
1779 | error = VFS_VPTOFH(vp, NULL, &fidsize); |
1780 | KASSERT(error != 0); |
1781 | if (error != E2BIG) { |
1782 | goto out; |
1783 | } |
1784 | fhsize = FHANDLE_SIZE_FROM_FILEID_SIZE(fidsize); |
1785 | fhp = kmem_zalloc(fhsize, KM_SLEEP); |
1786 | fhp->fh_fsid = mp->mnt_stat.f_fsidx; |
1787 | error = VFS_VPTOFH(vp, &fhp->fh_fid, &fidsize); |
1788 | if (error == 0) { |
1789 | KASSERT((FHANDLE_SIZE(fhp) == fhsize && |
1790 | FHANDLE_FILEID(fhp)->fid_len == fidsize)); |
1791 | *fhpp = fhp; |
1792 | } else { |
1793 | kmem_free(fhp, fhsize); |
1794 | } |
1795 | out: |
1796 | return error; |
1797 | } |
1798 | |
1799 | void |
1800 | vfs_composefh_free(fhandle_t *fhp) |
1801 | { |
1802 | |
1803 | vfs__fhfree(fhp); |
1804 | } |
1805 | |
1806 | /* |
1807 | * vfs_fhtovp: lookup a vnode by a filehandle. |
1808 | */ |
1809 | |
1810 | int |
1811 | vfs_fhtovp(fhandle_t *fhp, struct vnode **vpp) |
1812 | { |
1813 | struct mount *mp; |
1814 | int error; |
1815 | |
1816 | *vpp = NULL; |
1817 | mp = vfs_getvfs(FHANDLE_FSID(fhp)); |
1818 | if (mp == NULL) { |
1819 | error = ESTALE; |
1820 | goto out; |
1821 | } |
1822 | if (mp->mnt_op->vfs_fhtovp == NULL) { |
1823 | error = EOPNOTSUPP; |
1824 | goto out; |
1825 | } |
1826 | error = VFS_FHTOVP(mp, FHANDLE_FILEID(fhp), vpp); |
1827 | out: |
1828 | return error; |
1829 | } |
1830 | |
1831 | /* |
1832 | * vfs_copyinfh_alloc: allocate and copyin a filehandle, given |
1833 | * the needed size. |
1834 | */ |
1835 | |
1836 | int |
1837 | vfs_copyinfh_alloc(const void *ufhp, size_t fhsize, fhandle_t **fhpp) |
1838 | { |
1839 | fhandle_t *fhp; |
1840 | int error; |
1841 | |
1842 | if (fhsize > FHANDLE_SIZE_MAX) { |
1843 | return EINVAL; |
1844 | } |
1845 | if (fhsize < FHANDLE_SIZE_MIN) { |
1846 | return EINVAL; |
1847 | } |
1848 | again: |
1849 | fhp = kmem_alloc(fhsize, KM_SLEEP); |
1850 | error = copyin(ufhp, fhp, fhsize); |
1851 | if (error == 0) { |
1852 | /* XXX this check shouldn't be here */ |
1853 | if (FHANDLE_SIZE(fhp) == fhsize) { |
1854 | *fhpp = fhp; |
1855 | return 0; |
1856 | } else if (fhsize == NFSX_V2FH && FHANDLE_SIZE(fhp) < fhsize) { |
1857 | /* |
1858 | * a kludge for nfsv2 padded handles. |
1859 | */ |
1860 | size_t sz; |
1861 | |
1862 | sz = FHANDLE_SIZE(fhp); |
1863 | kmem_free(fhp, fhsize); |
1864 | fhsize = sz; |
1865 | goto again; |
1866 | } else { |
1867 | /* |
1868 | * userland told us wrong size. |
1869 | */ |
1870 | error = EINVAL; |
1871 | } |
1872 | } |
1873 | kmem_free(fhp, fhsize); |
1874 | return error; |
1875 | } |
1876 | |
1877 | void |
1878 | vfs_copyinfh_free(fhandle_t *fhp) |
1879 | { |
1880 | |
1881 | vfs__fhfree(fhp); |
1882 | } |
1883 | |
1884 | /* |
1885 | * Get file handle system call |
1886 | */ |
1887 | int |
1888 | sys___getfh30(struct lwp *l, const struct sys___getfh30_args *uap, register_t *retval) |
1889 | { |
1890 | /* { |
1891 | syscallarg(char *) fname; |
1892 | syscallarg(fhandle_t *) fhp; |
1893 | syscallarg(size_t *) fh_size; |
1894 | } */ |
1895 | struct vnode *vp; |
1896 | fhandle_t *fh; |
1897 | int error; |
1898 | struct pathbuf *pb; |
1899 | struct nameidata nd; |
1900 | size_t sz; |
1901 | size_t usz; |
1902 | |
1903 | /* |
1904 | * Must be super user |
1905 | */ |
1906 | error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, |
1907 | 0, NULL, NULL, NULL); |
1908 | if (error) |
1909 | return (error); |
1910 | |
1911 | error = pathbuf_copyin(SCARG(uap, fname), &pb); |
1912 | if (error) { |
1913 | return error; |
1914 | } |
1915 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
1916 | error = namei(&nd); |
1917 | if (error) { |
1918 | pathbuf_destroy(pb); |
1919 | return error; |
1920 | } |
1921 | vp = nd.ni_vp; |
1922 | pathbuf_destroy(pb); |
1923 | |
1924 | error = vfs_composefh_alloc(vp, &fh); |
1925 | vput(vp); |
1926 | if (error != 0) { |
1927 | return error; |
1928 | } |
1929 | error = copyin(SCARG(uap, fh_size), &usz, sizeof(size_t)); |
1930 | if (error != 0) { |
1931 | goto out; |
1932 | } |
1933 | sz = FHANDLE_SIZE(fh); |
1934 | error = copyout(&sz, SCARG(uap, fh_size), sizeof(size_t)); |
1935 | if (error != 0) { |
1936 | goto out; |
1937 | } |
1938 | if (usz >= sz) { |
1939 | error = copyout(fh, SCARG(uap, fhp), sz); |
1940 | } else { |
1941 | error = E2BIG; |
1942 | } |
1943 | out: |
1944 | vfs_composefh_free(fh); |
1945 | return (error); |
1946 | } |
1947 | |
1948 | /* |
1949 | * Open a file given a file handle. |
1950 | * |
1951 | * Check permissions, allocate an open file structure, |
1952 | * and call the device open routine if any. |
1953 | */ |
1954 | |
1955 | int |
1956 | dofhopen(struct lwp *l, const void *ufhp, size_t fhsize, int oflags, |
1957 | register_t *retval) |
1958 | { |
1959 | file_t *fp; |
1960 | struct vnode *vp = NULL; |
1961 | kauth_cred_t cred = l->l_cred; |
1962 | file_t *nfp; |
1963 | int indx, error; |
1964 | struct vattr va; |
1965 | fhandle_t *fh; |
1966 | int flags; |
1967 | proc_t *p; |
1968 | |
1969 | p = curproc; |
1970 | |
1971 | /* |
1972 | * Must be super user |
1973 | */ |
1974 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, |
1975 | 0, NULL, NULL, NULL))) |
1976 | return (error); |
1977 | |
1978 | if (oflags & O_SEARCH) { |
1979 | oflags &= ~(int)O_SEARCH; |
1980 | } |
1981 | |
1982 | flags = FFLAGS(oflags); |
1983 | if ((flags & (FREAD | FWRITE)) == 0) |
1984 | return (EINVAL); |
1985 | if ((flags & O_CREAT)) |
1986 | return (EINVAL); |
1987 | if ((error = fd_allocfile(&nfp, &indx)) != 0) |
1988 | return (error); |
1989 | fp = nfp; |
1990 | error = vfs_copyinfh_alloc(ufhp, fhsize, &fh); |
1991 | if (error != 0) { |
1992 | goto bad; |
1993 | } |
1994 | error = vfs_fhtovp(fh, &vp); |
1995 | vfs_copyinfh_free(fh); |
1996 | if (error != 0) { |
1997 | goto bad; |
1998 | } |
1999 | |
2000 | /* Now do an effective vn_open */ |
2001 | |
2002 | if (vp->v_type == VSOCK) { |
2003 | error = EOPNOTSUPP; |
2004 | goto bad; |
2005 | } |
2006 | error = vn_openchk(vp, cred, flags); |
2007 | if (error != 0) |
2008 | goto bad; |
2009 | if (flags & O_TRUNC) { |
2010 | VOP_UNLOCK(vp); /* XXX */ |
2011 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* XXX */ |
2012 | vattr_null(&va); |
2013 | va.va_size = 0; |
2014 | error = VOP_SETATTR(vp, &va, cred); |
2015 | if (error) |
2016 | goto bad; |
2017 | } |
2018 | if ((error = VOP_OPEN(vp, flags, cred)) != 0) |
2019 | goto bad; |
2020 | if (flags & FWRITE) { |
2021 | mutex_enter(vp->v_interlock); |
2022 | vp->v_writecount++; |
2023 | mutex_exit(vp->v_interlock); |
2024 | } |
2025 | |
2026 | /* done with modified vn_open, now finish what sys_open does. */ |
2027 | if ((error = open_setfp(l, fp, vp, indx, flags))) |
2028 | return error; |
2029 | |
2030 | VOP_UNLOCK(vp); |
2031 | *retval = indx; |
2032 | fd_affix(p, fp, indx); |
2033 | return (0); |
2034 | |
2035 | bad: |
2036 | fd_abort(p, fp, indx); |
2037 | if (vp != NULL) |
2038 | vput(vp); |
2039 | return (error); |
2040 | } |
2041 | |
2042 | int |
2043 | sys___fhopen40(struct lwp *l, const struct sys___fhopen40_args *uap, register_t *retval) |
2044 | { |
2045 | /* { |
2046 | syscallarg(const void *) fhp; |
2047 | syscallarg(size_t) fh_size; |
2048 | syscallarg(int) flags; |
2049 | } */ |
2050 | |
2051 | return dofhopen(l, SCARG(uap, fhp), SCARG(uap, fh_size), |
2052 | SCARG(uap, flags), retval); |
2053 | } |
2054 | |
2055 | int |
2056 | do_fhstat(struct lwp *l, const void *ufhp, size_t fhsize, struct stat *sb) |
2057 | { |
2058 | int error; |
2059 | fhandle_t *fh; |
2060 | struct vnode *vp; |
2061 | |
2062 | /* |
2063 | * Must be super user |
2064 | */ |
2065 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, |
2066 | 0, NULL, NULL, NULL))) |
2067 | return (error); |
2068 | |
2069 | error = vfs_copyinfh_alloc(ufhp, fhsize, &fh); |
2070 | if (error != 0) |
2071 | return error; |
2072 | |
2073 | error = vfs_fhtovp(fh, &vp); |
2074 | vfs_copyinfh_free(fh); |
2075 | if (error != 0) |
2076 | return error; |
2077 | |
2078 | error = vn_stat(vp, sb); |
2079 | vput(vp); |
2080 | return error; |
2081 | } |
2082 | |
2083 | |
2084 | /* ARGSUSED */ |
2085 | int |
2086 | sys___fhstat50(struct lwp *l, const struct sys___fhstat50_args *uap, register_t *retval) |
2087 | { |
2088 | /* { |
2089 | syscallarg(const void *) fhp; |
2090 | syscallarg(size_t) fh_size; |
2091 | syscallarg(struct stat *) sb; |
2092 | } */ |
2093 | struct stat sb; |
2094 | int error; |
2095 | |
2096 | error = do_fhstat(l, SCARG(uap, fhp), SCARG(uap, fh_size), &sb); |
2097 | if (error) |
2098 | return error; |
2099 | return copyout(&sb, SCARG(uap, sb), sizeof(sb)); |
2100 | } |
2101 | |
2102 | int |
2103 | do_fhstatvfs(struct lwp *l, const void *ufhp, size_t fhsize, struct statvfs *sb, |
2104 | int flags) |
2105 | { |
2106 | fhandle_t *fh; |
2107 | struct mount *mp; |
2108 | struct vnode *vp; |
2109 | int error; |
2110 | |
2111 | /* |
2112 | * Must be super user |
2113 | */ |
2114 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, |
2115 | 0, NULL, NULL, NULL))) |
2116 | return error; |
2117 | |
2118 | error = vfs_copyinfh_alloc(ufhp, fhsize, &fh); |
2119 | if (error != 0) |
2120 | return error; |
2121 | |
2122 | error = vfs_fhtovp(fh, &vp); |
2123 | vfs_copyinfh_free(fh); |
2124 | if (error != 0) |
2125 | return error; |
2126 | |
2127 | mp = vp->v_mount; |
2128 | error = dostatvfs(mp, sb, l, flags, 1); |
2129 | vput(vp); |
2130 | return error; |
2131 | } |
2132 | |
2133 | /* ARGSUSED */ |
2134 | int |
2135 | sys___fhstatvfs140(struct lwp *l, const struct sys___fhstatvfs140_args *uap, register_t *retval) |
2136 | { |
2137 | /* { |
2138 | syscallarg(const void *) fhp; |
2139 | syscallarg(size_t) fh_size; |
2140 | syscallarg(struct statvfs *) buf; |
2141 | syscallarg(int) flags; |
2142 | } */ |
2143 | struct statvfs *sb = STATVFSBUF_GET(); |
2144 | int error; |
2145 | |
2146 | error = do_fhstatvfs(l, SCARG(uap, fhp), SCARG(uap, fh_size), sb, |
2147 | SCARG(uap, flags)); |
2148 | if (error == 0) |
2149 | error = copyout(sb, SCARG(uap, buf), sizeof(*sb)); |
2150 | STATVFSBUF_PUT(sb); |
2151 | return error; |
2152 | } |
2153 | |
2154 | int |
2155 | do_posix_mknodat(struct lwp *l, int fdat, const char *pathname, mode_t mode, |
2156 | dev_t dev) |
2157 | { |
2158 | |
2159 | /* |
2160 | * The POSIX mknod(2) call is an alias for mkfifo(2) for S_IFIFO |
2161 | * in mode and dev=0. |
2162 | * |
2163 | * In all the other cases it's implementation defined behavior. |
2164 | */ |
2165 | |
2166 | if ((mode & S_IFIFO) && dev == 0) |
2167 | return do_sys_mkfifoat(l, fdat, pathname, mode); |
2168 | else |
2169 | return do_sys_mknodat(l, fdat, pathname, mode, dev, |
2170 | UIO_USERSPACE); |
2171 | } |
2172 | |
2173 | /* |
2174 | * Create a special file. |
2175 | */ |
2176 | /* ARGSUSED */ |
2177 | int |
2178 | sys___mknod50(struct lwp *l, const struct sys___mknod50_args *uap, |
2179 | register_t *retval) |
2180 | { |
2181 | /* { |
2182 | syscallarg(const char *) path; |
2183 | syscallarg(mode_t) mode; |
2184 | syscallarg(dev_t) dev; |
2185 | } */ |
2186 | return do_posix_mknodat(l, AT_FDCWD, SCARG(uap, path), |
2187 | SCARG(uap, mode), SCARG(uap, dev)); |
2188 | } |
2189 | |
2190 | int |
2191 | sys_mknodat(struct lwp *l, const struct sys_mknodat_args *uap, |
2192 | register_t *retval) |
2193 | { |
2194 | /* { |
2195 | syscallarg(int) fd; |
2196 | syscallarg(const char *) path; |
2197 | syscallarg(mode_t) mode; |
2198 | syscallarg(int) pad; |
2199 | syscallarg(dev_t) dev; |
2200 | } */ |
2201 | |
2202 | return do_posix_mknodat(l, SCARG(uap, fd), SCARG(uap, path), |
2203 | SCARG(uap, mode), SCARG(uap, dev)); |
2204 | } |
2205 | |
2206 | int |
2207 | do_sys_mknod(struct lwp *l, const char *pathname, mode_t mode, dev_t dev, |
2208 | enum uio_seg seg) |
2209 | { |
2210 | return do_sys_mknodat(l, AT_FDCWD, pathname, mode, dev, seg); |
2211 | } |
2212 | |
2213 | int |
2214 | do_sys_mknodat(struct lwp *l, int fdat, const char *pathname, mode_t mode, |
2215 | dev_t dev, enum uio_seg seg) |
2216 | { |
2217 | struct proc *p = l->l_proc; |
2218 | struct vnode *vp; |
2219 | struct vattr vattr; |
2220 | int error, optype; |
2221 | struct pathbuf *pb; |
2222 | struct nameidata nd; |
2223 | const char *pathstring; |
2224 | |
2225 | if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MKNOD, |
2226 | 0, NULL, NULL, NULL)) != 0) |
2227 | return (error); |
2228 | |
2229 | optype = VOP_MKNOD_DESCOFFSET; |
2230 | |
2231 | error = pathbuf_maybe_copyin(pathname, seg, &pb); |
2232 | if (error) { |
2233 | return error; |
2234 | } |
2235 | pathstring = pathbuf_stringcopy_get(pb); |
2236 | if (pathstring == NULL) { |
2237 | pathbuf_destroy(pb); |
2238 | return ENOMEM; |
2239 | } |
2240 | |
2241 | NDINIT(&nd, CREATE, LOCKPARENT | TRYEMULROOT, pb); |
2242 | |
2243 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) |
2244 | goto out; |
2245 | vp = nd.ni_vp; |
2246 | |
2247 | if (vp != NULL) |
2248 | error = EEXIST; |
2249 | else { |
2250 | vattr_null(&vattr); |
2251 | /* We will read cwdi->cwdi_cmask unlocked. */ |
2252 | vattr.va_mode = (mode & ALLPERMS) &~ p->p_cwdi->cwdi_cmask; |
2253 | vattr.va_rdev = dev; |
2254 | |
2255 | switch (mode & S_IFMT) { |
2256 | case S_IFMT: /* used by badsect to flag bad sectors */ |
2257 | vattr.va_type = VBAD; |
2258 | break; |
2259 | case S_IFCHR: |
2260 | vattr.va_type = VCHR; |
2261 | break; |
2262 | case S_IFBLK: |
2263 | vattr.va_type = VBLK; |
2264 | break; |
2265 | case S_IFWHT: |
2266 | optype = VOP_WHITEOUT_DESCOFFSET; |
2267 | break; |
2268 | case S_IFREG: |
2269 | #if NVERIEXEC > 0 |
2270 | error = veriexec_openchk(l, nd.ni_vp, pathstring, |
2271 | O_CREAT); |
2272 | #endif /* NVERIEXEC > 0 */ |
2273 | vattr.va_type = VREG; |
2274 | vattr.va_rdev = VNOVAL; |
2275 | optype = VOP_CREATE_DESCOFFSET; |
2276 | break; |
2277 | default: |
2278 | error = EINVAL; |
2279 | break; |
2280 | } |
2281 | } |
2282 | if (error == 0 && optype == VOP_MKNOD_DESCOFFSET |
2283 | && vattr.va_rdev == VNOVAL) |
2284 | error = EINVAL; |
2285 | if (!error) { |
2286 | switch (optype) { |
2287 | case VOP_WHITEOUT_DESCOFFSET: |
2288 | error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); |
2289 | if (error) |
2290 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2291 | vput(nd.ni_dvp); |
2292 | break; |
2293 | |
2294 | case VOP_MKNOD_DESCOFFSET: |
2295 | error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, |
2296 | &nd.ni_cnd, &vattr); |
2297 | if (error == 0) |
2298 | vrele(nd.ni_vp); |
2299 | vput(nd.ni_dvp); |
2300 | break; |
2301 | |
2302 | case VOP_CREATE_DESCOFFSET: |
2303 | error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, |
2304 | &nd.ni_cnd, &vattr); |
2305 | if (error == 0) |
2306 | vrele(nd.ni_vp); |
2307 | vput(nd.ni_dvp); |
2308 | break; |
2309 | } |
2310 | } else { |
2311 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2312 | if (nd.ni_dvp == vp) |
2313 | vrele(nd.ni_dvp); |
2314 | else |
2315 | vput(nd.ni_dvp); |
2316 | if (vp) |
2317 | vrele(vp); |
2318 | } |
2319 | out: |
2320 | pathbuf_stringcopy_put(pb, pathstring); |
2321 | pathbuf_destroy(pb); |
2322 | return (error); |
2323 | } |
2324 | |
2325 | /* |
2326 | * Create a named pipe. |
2327 | */ |
2328 | /* ARGSUSED */ |
2329 | int |
2330 | sys_mkfifo(struct lwp *l, const struct sys_mkfifo_args *uap, register_t *retval) |
2331 | { |
2332 | /* { |
2333 | syscallarg(const char *) path; |
2334 | syscallarg(int) mode; |
2335 | } */ |
2336 | return do_sys_mkfifoat(l, AT_FDCWD, SCARG(uap, path), SCARG(uap, mode)); |
2337 | } |
2338 | |
2339 | int |
2340 | sys_mkfifoat(struct lwp *l, const struct sys_mkfifoat_args *uap, |
2341 | register_t *retval) |
2342 | { |
2343 | /* { |
2344 | syscallarg(int) fd; |
2345 | syscallarg(const char *) path; |
2346 | syscallarg(int) mode; |
2347 | } */ |
2348 | |
2349 | return do_sys_mkfifoat(l, SCARG(uap, fd), SCARG(uap, path), |
2350 | SCARG(uap, mode)); |
2351 | } |
2352 | |
2353 | static int |
2354 | do_sys_mkfifoat(struct lwp *l, int fdat, const char *path, mode_t mode) |
2355 | { |
2356 | struct proc *p = l->l_proc; |
2357 | struct vattr vattr; |
2358 | int error; |
2359 | struct pathbuf *pb; |
2360 | struct nameidata nd; |
2361 | |
2362 | error = pathbuf_copyin(path, &pb); |
2363 | if (error) { |
2364 | return error; |
2365 | } |
2366 | NDINIT(&nd, CREATE, LOCKPARENT | TRYEMULROOT, pb); |
2367 | |
2368 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) { |
2369 | pathbuf_destroy(pb); |
2370 | return error; |
2371 | } |
2372 | if (nd.ni_vp != NULL) { |
2373 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2374 | if (nd.ni_dvp == nd.ni_vp) |
2375 | vrele(nd.ni_dvp); |
2376 | else |
2377 | vput(nd.ni_dvp); |
2378 | vrele(nd.ni_vp); |
2379 | pathbuf_destroy(pb); |
2380 | return (EEXIST); |
2381 | } |
2382 | vattr_null(&vattr); |
2383 | vattr.va_type = VFIFO; |
2384 | /* We will read cwdi->cwdi_cmask unlocked. */ |
2385 | vattr.va_mode = (mode & ALLPERMS) &~ p->p_cwdi->cwdi_cmask; |
2386 | error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); |
2387 | if (error == 0) |
2388 | vrele(nd.ni_vp); |
2389 | vput(nd.ni_dvp); |
2390 | pathbuf_destroy(pb); |
2391 | return (error); |
2392 | } |
2393 | |
2394 | /* |
2395 | * Make a hard file link. |
2396 | */ |
2397 | /* ARGSUSED */ |
2398 | int |
2399 | do_sys_linkat(struct lwp *l, int fdpath, const char *path, int fdlink, |
2400 | const char *link, int follow, register_t *retval) |
2401 | { |
2402 | struct vnode *vp; |
2403 | struct pathbuf *linkpb; |
2404 | struct nameidata nd; |
2405 | namei_simple_flags_t ns_flags; |
2406 | int error; |
2407 | |
2408 | if (follow & AT_SYMLINK_FOLLOW) |
2409 | ns_flags = NSM_FOLLOW_TRYEMULROOT; |
2410 | else |
2411 | ns_flags = NSM_NOFOLLOW_TRYEMULROOT; |
2412 | |
2413 | error = fd_nameiat_simple_user(l, fdpath, path, ns_flags, &vp); |
2414 | if (error != 0) |
2415 | return (error); |
2416 | error = pathbuf_copyin(link, &linkpb); |
2417 | if (error) { |
2418 | goto out1; |
2419 | } |
2420 | NDINIT(&nd, CREATE, LOCKPARENT | TRYEMULROOT, linkpb); |
2421 | if ((error = fd_nameiat(l, fdlink, &nd)) != 0) |
2422 | goto out2; |
2423 | if (nd.ni_vp) { |
2424 | error = EEXIST; |
2425 | goto abortop; |
2426 | } |
2427 | /* Prevent hard links on directories. */ |
2428 | if (vp->v_type == VDIR) { |
2429 | error = EPERM; |
2430 | goto abortop; |
2431 | } |
2432 | /* Prevent cross-mount operation. */ |
2433 | if (nd.ni_dvp->v_mount != vp->v_mount) { |
2434 | error = EXDEV; |
2435 | goto abortop; |
2436 | } |
2437 | error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); |
2438 | VOP_UNLOCK(nd.ni_dvp); |
2439 | vrele(nd.ni_dvp); |
2440 | out2: |
2441 | pathbuf_destroy(linkpb); |
2442 | out1: |
2443 | vrele(vp); |
2444 | return (error); |
2445 | abortop: |
2446 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2447 | if (nd.ni_dvp == nd.ni_vp) |
2448 | vrele(nd.ni_dvp); |
2449 | else |
2450 | vput(nd.ni_dvp); |
2451 | if (nd.ni_vp != NULL) |
2452 | vrele(nd.ni_vp); |
2453 | goto out2; |
2454 | } |
2455 | |
2456 | int |
2457 | sys_link(struct lwp *l, const struct sys_link_args *uap, register_t *retval) |
2458 | { |
2459 | /* { |
2460 | syscallarg(const char *) path; |
2461 | syscallarg(const char *) link; |
2462 | } */ |
2463 | const char *path = SCARG(uap, path); |
2464 | const char *link = SCARG(uap, link); |
2465 | |
2466 | return do_sys_linkat(l, AT_FDCWD, path, AT_FDCWD, link, |
2467 | AT_SYMLINK_FOLLOW, retval); |
2468 | } |
2469 | |
2470 | int |
2471 | sys_linkat(struct lwp *l, const struct sys_linkat_args *uap, |
2472 | register_t *retval) |
2473 | { |
2474 | /* { |
2475 | syscallarg(int) fd1; |
2476 | syscallarg(const char *) name1; |
2477 | syscallarg(int) fd2; |
2478 | syscallarg(const char *) name2; |
2479 | syscallarg(int) flags; |
2480 | } */ |
2481 | int fd1 = SCARG(uap, fd1); |
2482 | const char *name1 = SCARG(uap, name1); |
2483 | int fd2 = SCARG(uap, fd2); |
2484 | const char *name2 = SCARG(uap, name2); |
2485 | int follow; |
2486 | |
2487 | follow = SCARG(uap, flags) & AT_SYMLINK_FOLLOW; |
2488 | |
2489 | return do_sys_linkat(l, fd1, name1, fd2, name2, follow, retval); |
2490 | } |
2491 | |
2492 | |
2493 | int |
2494 | do_sys_symlink(const char *patharg, const char *link, enum uio_seg seg) |
2495 | { |
2496 | return do_sys_symlinkat(NULL, patharg, AT_FDCWD, link, seg); |
2497 | } |
2498 | |
2499 | static int |
2500 | do_sys_symlinkat(struct lwp *l, const char *patharg, int fdat, |
2501 | const char *link, enum uio_seg seg) |
2502 | { |
2503 | struct proc *p = curproc; |
2504 | struct vattr vattr; |
2505 | char *path; |
2506 | int error; |
2507 | size_t len; |
2508 | struct pathbuf *linkpb; |
2509 | struct nameidata nd; |
2510 | |
2511 | KASSERT(l != NULL || fdat == AT_FDCWD); |
2512 | |
2513 | path = PNBUF_GET(); |
2514 | if (seg == UIO_USERSPACE) { |
2515 | if ((error = copyinstr(patharg, path, MAXPATHLEN, &len)) != 0) |
2516 | goto out1; |
2517 | if ((error = pathbuf_copyin(link, &linkpb)) != 0) |
2518 | goto out1; |
2519 | } else { |
2520 | len = strlen(patharg) + 1; |
2521 | KASSERT(len <= MAXPATHLEN); |
2522 | memcpy(path, patharg, len); |
2523 | linkpb = pathbuf_create(link); |
2524 | if (linkpb == NULL) { |
2525 | error = ENOMEM; |
2526 | goto out1; |
2527 | } |
2528 | } |
2529 | ktrkuser("symlink-target" , path, len - 1); |
2530 | |
2531 | NDINIT(&nd, CREATE, LOCKPARENT | TRYEMULROOT, linkpb); |
2532 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) |
2533 | goto out2; |
2534 | if (nd.ni_vp) { |
2535 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2536 | if (nd.ni_dvp == nd.ni_vp) |
2537 | vrele(nd.ni_dvp); |
2538 | else |
2539 | vput(nd.ni_dvp); |
2540 | vrele(nd.ni_vp); |
2541 | error = EEXIST; |
2542 | goto out2; |
2543 | } |
2544 | vattr_null(&vattr); |
2545 | vattr.va_type = VLNK; |
2546 | /* We will read cwdi->cwdi_cmask unlocked. */ |
2547 | vattr.va_mode = ACCESSPERMS &~ p->p_cwdi->cwdi_cmask; |
2548 | error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); |
2549 | if (error == 0) |
2550 | vrele(nd.ni_vp); |
2551 | vput(nd.ni_dvp); |
2552 | out2: |
2553 | pathbuf_destroy(linkpb); |
2554 | out1: |
2555 | PNBUF_PUT(path); |
2556 | return (error); |
2557 | } |
2558 | |
2559 | /* |
2560 | * Make a symbolic link. |
2561 | */ |
2562 | /* ARGSUSED */ |
2563 | int |
2564 | sys_symlink(struct lwp *l, const struct sys_symlink_args *uap, register_t *retval) |
2565 | { |
2566 | /* { |
2567 | syscallarg(const char *) path; |
2568 | syscallarg(const char *) link; |
2569 | } */ |
2570 | |
2571 | return do_sys_symlinkat(l, SCARG(uap, path), AT_FDCWD, SCARG(uap, link), |
2572 | UIO_USERSPACE); |
2573 | } |
2574 | |
2575 | int |
2576 | sys_symlinkat(struct lwp *l, const struct sys_symlinkat_args *uap, |
2577 | register_t *retval) |
2578 | { |
2579 | /* { |
2580 | syscallarg(const char *) path1; |
2581 | syscallarg(int) fd; |
2582 | syscallarg(const char *) path2; |
2583 | } */ |
2584 | |
2585 | return do_sys_symlinkat(l, SCARG(uap, path1), SCARG(uap, fd), |
2586 | SCARG(uap, path2), UIO_USERSPACE); |
2587 | } |
2588 | |
2589 | /* |
2590 | * Delete a whiteout from the filesystem. |
2591 | */ |
2592 | /* ARGSUSED */ |
2593 | int |
2594 | sys_undelete(struct lwp *l, const struct sys_undelete_args *uap, register_t *retval) |
2595 | { |
2596 | /* { |
2597 | syscallarg(const char *) path; |
2598 | } */ |
2599 | int error; |
2600 | struct pathbuf *pb; |
2601 | struct nameidata nd; |
2602 | |
2603 | error = pathbuf_copyin(SCARG(uap, path), &pb); |
2604 | if (error) { |
2605 | return error; |
2606 | } |
2607 | |
2608 | NDINIT(&nd, DELETE, LOCKPARENT | DOWHITEOUT | TRYEMULROOT, pb); |
2609 | error = namei(&nd); |
2610 | if (error) { |
2611 | pathbuf_destroy(pb); |
2612 | return (error); |
2613 | } |
2614 | |
2615 | if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { |
2616 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2617 | if (nd.ni_dvp == nd.ni_vp) |
2618 | vrele(nd.ni_dvp); |
2619 | else |
2620 | vput(nd.ni_dvp); |
2621 | if (nd.ni_vp) |
2622 | vrele(nd.ni_vp); |
2623 | pathbuf_destroy(pb); |
2624 | return (EEXIST); |
2625 | } |
2626 | if ((error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) != 0) |
2627 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2628 | vput(nd.ni_dvp); |
2629 | pathbuf_destroy(pb); |
2630 | return (error); |
2631 | } |
2632 | |
2633 | /* |
2634 | * Delete a name from the filesystem. |
2635 | */ |
2636 | /* ARGSUSED */ |
2637 | int |
2638 | sys_unlink(struct lwp *l, const struct sys_unlink_args *uap, register_t *retval) |
2639 | { |
2640 | /* { |
2641 | syscallarg(const char *) path; |
2642 | } */ |
2643 | |
2644 | return do_sys_unlinkat(l, AT_FDCWD, SCARG(uap, path), 0, UIO_USERSPACE); |
2645 | } |
2646 | |
2647 | int |
2648 | sys_unlinkat(struct lwp *l, const struct sys_unlinkat_args *uap, |
2649 | register_t *retval) |
2650 | { |
2651 | /* { |
2652 | syscallarg(int) fd; |
2653 | syscallarg(const char *) path; |
2654 | syscallarg(int) flag; |
2655 | } */ |
2656 | |
2657 | return do_sys_unlinkat(l, SCARG(uap, fd), SCARG(uap, path), |
2658 | SCARG(uap, flag), UIO_USERSPACE); |
2659 | } |
2660 | |
2661 | int |
2662 | do_sys_unlink(const char *arg, enum uio_seg seg) |
2663 | { |
2664 | return do_sys_unlinkat(NULL, AT_FDCWD, arg, 0, seg); |
2665 | } |
2666 | |
2667 | static int |
2668 | do_sys_unlinkat(struct lwp *l, int fdat, const char *arg, int flags, |
2669 | enum uio_seg seg) |
2670 | { |
2671 | struct vnode *vp; |
2672 | int error; |
2673 | struct pathbuf *pb; |
2674 | struct nameidata nd; |
2675 | const char *pathstring; |
2676 | |
2677 | KASSERT(l != NULL || fdat == AT_FDCWD); |
2678 | |
2679 | error = pathbuf_maybe_copyin(arg, seg, &pb); |
2680 | if (error) { |
2681 | return error; |
2682 | } |
2683 | pathstring = pathbuf_stringcopy_get(pb); |
2684 | if (pathstring == NULL) { |
2685 | pathbuf_destroy(pb); |
2686 | return ENOMEM; |
2687 | } |
2688 | |
2689 | NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF | TRYEMULROOT, pb); |
2690 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) |
2691 | goto out; |
2692 | vp = nd.ni_vp; |
2693 | |
2694 | /* |
2695 | * The root of a mounted filesystem cannot be deleted. |
2696 | */ |
2697 | if ((vp->v_vflag & VV_ROOT) != 0) { |
2698 | error = EBUSY; |
2699 | goto abort; |
2700 | } |
2701 | |
2702 | if ((vp->v_type == VDIR) && (vp->v_mountedhere != NULL)) { |
2703 | error = EBUSY; |
2704 | goto abort; |
2705 | } |
2706 | |
2707 | /* |
2708 | * No rmdir "." please. |
2709 | */ |
2710 | if (nd.ni_dvp == vp) { |
2711 | error = EINVAL; |
2712 | goto abort; |
2713 | } |
2714 | |
2715 | /* |
2716 | * AT_REMOVEDIR is required to remove a directory |
2717 | */ |
2718 | if (vp->v_type == VDIR) { |
2719 | if (!(flags & AT_REMOVEDIR)) { |
2720 | error = EPERM; |
2721 | goto abort; |
2722 | } else { |
2723 | error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); |
2724 | vput(nd.ni_dvp); |
2725 | goto out; |
2726 | } |
2727 | } |
2728 | |
2729 | /* |
2730 | * Starting here we only deal with non directories. |
2731 | */ |
2732 | if (flags & AT_REMOVEDIR) { |
2733 | error = ENOTDIR; |
2734 | goto abort; |
2735 | } |
2736 | |
2737 | #if NVERIEXEC > 0 |
2738 | /* Handle remove requests for veriexec entries. */ |
2739 | if ((error = veriexec_removechk(curlwp, nd.ni_vp, pathstring)) != 0) { |
2740 | goto abort; |
2741 | } |
2742 | #endif /* NVERIEXEC > 0 */ |
2743 | |
2744 | #ifdef FILEASSOC |
2745 | (void)fileassoc_file_delete(vp); |
2746 | #endif /* FILEASSOC */ |
2747 | error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); |
2748 | vput(nd.ni_dvp); |
2749 | goto out; |
2750 | |
2751 | abort: |
2752 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
2753 | if (nd.ni_dvp == vp) |
2754 | vrele(nd.ni_dvp); |
2755 | else |
2756 | vput(nd.ni_dvp); |
2757 | vput(vp); |
2758 | |
2759 | out: |
2760 | pathbuf_stringcopy_put(pb, pathstring); |
2761 | pathbuf_destroy(pb); |
2762 | return (error); |
2763 | } |
2764 | |
2765 | /* |
2766 | * Reposition read/write file offset. |
2767 | */ |
2768 | int |
2769 | sys_lseek(struct lwp *l, const struct sys_lseek_args *uap, register_t *retval) |
2770 | { |
2771 | /* { |
2772 | syscallarg(int) fd; |
2773 | syscallarg(int) pad; |
2774 | syscallarg(off_t) offset; |
2775 | syscallarg(int) whence; |
2776 | } */ |
2777 | kauth_cred_t cred = l->l_cred; |
2778 | file_t *fp; |
2779 | struct vnode *vp; |
2780 | struct vattr vattr; |
2781 | off_t newoff; |
2782 | int error, fd; |
2783 | |
2784 | fd = SCARG(uap, fd); |
2785 | |
2786 | if ((fp = fd_getfile(fd)) == NULL) |
2787 | return (EBADF); |
2788 | |
2789 | vp = fp->f_vnode; |
2790 | if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { |
2791 | error = ESPIPE; |
2792 | goto out; |
2793 | } |
2794 | |
2795 | vn_lock(vp, LK_SHARED | LK_RETRY); |
2796 | |
2797 | switch (SCARG(uap, whence)) { |
2798 | case SEEK_CUR: |
2799 | newoff = fp->f_offset + SCARG(uap, offset); |
2800 | break; |
2801 | case SEEK_END: |
2802 | error = VOP_GETATTR(vp, &vattr, cred); |
2803 | if (error) { |
2804 | VOP_UNLOCK(vp); |
2805 | goto out; |
2806 | } |
2807 | newoff = SCARG(uap, offset) + vattr.va_size; |
2808 | break; |
2809 | case SEEK_SET: |
2810 | newoff = SCARG(uap, offset); |
2811 | break; |
2812 | default: |
2813 | error = EINVAL; |
2814 | VOP_UNLOCK(vp); |
2815 | goto out; |
2816 | } |
2817 | VOP_UNLOCK(vp); |
2818 | if ((error = VOP_SEEK(vp, fp->f_offset, newoff, cred)) == 0) { |
2819 | *(off_t *)retval = fp->f_offset = newoff; |
2820 | } |
2821 | out: |
2822 | fd_putfile(fd); |
2823 | return (error); |
2824 | } |
2825 | |
2826 | /* |
2827 | * Positional read system call. |
2828 | */ |
2829 | int |
2830 | sys_pread(struct lwp *l, const struct sys_pread_args *uap, register_t *retval) |
2831 | { |
2832 | /* { |
2833 | syscallarg(int) fd; |
2834 | syscallarg(void *) buf; |
2835 | syscallarg(size_t) nbyte; |
2836 | syscallarg(off_t) offset; |
2837 | } */ |
2838 | file_t *fp; |
2839 | struct vnode *vp; |
2840 | off_t offset; |
2841 | int error, fd = SCARG(uap, fd); |
2842 | |
2843 | if ((fp = fd_getfile(fd)) == NULL) |
2844 | return (EBADF); |
2845 | |
2846 | if ((fp->f_flag & FREAD) == 0) { |
2847 | fd_putfile(fd); |
2848 | return (EBADF); |
2849 | } |
2850 | |
2851 | vp = fp->f_vnode; |
2852 | if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { |
2853 | error = ESPIPE; |
2854 | goto out; |
2855 | } |
2856 | |
2857 | offset = SCARG(uap, offset); |
2858 | |
2859 | /* |
2860 | * XXX This works because no file systems actually |
2861 | * XXX take any action on the seek operation. |
2862 | */ |
2863 | if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0) |
2864 | goto out; |
2865 | |
2866 | /* dofileread() will unuse the descriptor for us */ |
2867 | return (dofileread(fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), |
2868 | &offset, 0, retval)); |
2869 | |
2870 | out: |
2871 | fd_putfile(fd); |
2872 | return (error); |
2873 | } |
2874 | |
2875 | /* |
2876 | * Positional scatter read system call. |
2877 | */ |
2878 | int |
2879 | sys_preadv(struct lwp *l, const struct sys_preadv_args *uap, register_t *retval) |
2880 | { |
2881 | /* { |
2882 | syscallarg(int) fd; |
2883 | syscallarg(const struct iovec *) iovp; |
2884 | syscallarg(int) iovcnt; |
2885 | syscallarg(off_t) offset; |
2886 | } */ |
2887 | off_t offset = SCARG(uap, offset); |
2888 | |
2889 | return do_filereadv(SCARG(uap, fd), SCARG(uap, iovp), |
2890 | SCARG(uap, iovcnt), &offset, 0, retval); |
2891 | } |
2892 | |
2893 | /* |
2894 | * Positional write system call. |
2895 | */ |
2896 | int |
2897 | sys_pwrite(struct lwp *l, const struct sys_pwrite_args *uap, register_t *retval) |
2898 | { |
2899 | /* { |
2900 | syscallarg(int) fd; |
2901 | syscallarg(const void *) buf; |
2902 | syscallarg(size_t) nbyte; |
2903 | syscallarg(off_t) offset; |
2904 | } */ |
2905 | file_t *fp; |
2906 | struct vnode *vp; |
2907 | off_t offset; |
2908 | int error, fd = SCARG(uap, fd); |
2909 | |
2910 | if ((fp = fd_getfile(fd)) == NULL) |
2911 | return (EBADF); |
2912 | |
2913 | if ((fp->f_flag & FWRITE) == 0) { |
2914 | fd_putfile(fd); |
2915 | return (EBADF); |
2916 | } |
2917 | |
2918 | vp = fp->f_vnode; |
2919 | if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { |
2920 | error = ESPIPE; |
2921 | goto out; |
2922 | } |
2923 | |
2924 | offset = SCARG(uap, offset); |
2925 | |
2926 | /* |
2927 | * XXX This works because no file systems actually |
2928 | * XXX take any action on the seek operation. |
2929 | */ |
2930 | if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0) |
2931 | goto out; |
2932 | |
2933 | /* dofilewrite() will unuse the descriptor for us */ |
2934 | return (dofilewrite(fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), |
2935 | &offset, 0, retval)); |
2936 | |
2937 | out: |
2938 | fd_putfile(fd); |
2939 | return (error); |
2940 | } |
2941 | |
2942 | /* |
2943 | * Positional gather write system call. |
2944 | */ |
2945 | int |
2946 | sys_pwritev(struct lwp *l, const struct sys_pwritev_args *uap, register_t *retval) |
2947 | { |
2948 | /* { |
2949 | syscallarg(int) fd; |
2950 | syscallarg(const struct iovec *) iovp; |
2951 | syscallarg(int) iovcnt; |
2952 | syscallarg(off_t) offset; |
2953 | } */ |
2954 | off_t offset = SCARG(uap, offset); |
2955 | |
2956 | return do_filewritev(SCARG(uap, fd), SCARG(uap, iovp), |
2957 | SCARG(uap, iovcnt), &offset, 0, retval); |
2958 | } |
2959 | |
2960 | /* |
2961 | * Check access permissions. |
2962 | */ |
2963 | int |
2964 | sys_access(struct lwp *l, const struct sys_access_args *uap, register_t *retval) |
2965 | { |
2966 | /* { |
2967 | syscallarg(const char *) path; |
2968 | syscallarg(int) flags; |
2969 | } */ |
2970 | |
2971 | return do_sys_accessat(l, AT_FDCWD, SCARG(uap, path), |
2972 | SCARG(uap, flags), 0); |
2973 | } |
2974 | |
2975 | int |
2976 | do_sys_accessat(struct lwp *l, int fdat, const char *path, |
2977 | int mode, int flags) |
2978 | { |
2979 | kauth_cred_t cred; |
2980 | struct vnode *vp; |
2981 | int error, nd_flag, vmode; |
2982 | struct pathbuf *pb; |
2983 | struct nameidata nd; |
2984 | |
2985 | CTASSERT(F_OK == 0); |
2986 | if ((mode & ~(R_OK | W_OK | X_OK)) != 0) { |
2987 | /* nonsense mode */ |
2988 | return EINVAL; |
2989 | } |
2990 | |
2991 | nd_flag = FOLLOW | LOCKLEAF | TRYEMULROOT; |
2992 | if (flags & AT_SYMLINK_NOFOLLOW) |
2993 | nd_flag &= ~FOLLOW; |
2994 | |
2995 | error = pathbuf_copyin(path, &pb); |
2996 | if (error) |
2997 | return error; |
2998 | |
2999 | NDINIT(&nd, LOOKUP, nd_flag, pb); |
3000 | |
3001 | /* Override default credentials */ |
3002 | cred = kauth_cred_dup(l->l_cred); |
3003 | if (!(flags & AT_EACCESS)) { |
3004 | kauth_cred_seteuid(cred, kauth_cred_getuid(l->l_cred)); |
3005 | kauth_cred_setegid(cred, kauth_cred_getgid(l->l_cred)); |
3006 | } |
3007 | nd.ni_cnd.cn_cred = cred; |
3008 | |
3009 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) { |
3010 | pathbuf_destroy(pb); |
3011 | goto out; |
3012 | } |
3013 | vp = nd.ni_vp; |
3014 | pathbuf_destroy(pb); |
3015 | |
3016 | /* Flags == 0 means only check for existence. */ |
3017 | if (mode) { |
3018 | vmode = 0; |
3019 | if (mode & R_OK) |
3020 | vmode |= VREAD; |
3021 | if (mode & W_OK) |
3022 | vmode |= VWRITE; |
3023 | if (mode & X_OK) |
3024 | vmode |= VEXEC; |
3025 | |
3026 | error = VOP_ACCESS(vp, vmode, cred); |
3027 | if (!error && (vmode & VWRITE)) |
3028 | error = vn_writechk(vp); |
3029 | } |
3030 | vput(vp); |
3031 | out: |
3032 | kauth_cred_free(cred); |
3033 | return (error); |
3034 | } |
3035 | |
3036 | int |
3037 | sys_faccessat(struct lwp *l, const struct sys_faccessat_args *uap, |
3038 | register_t *retval) |
3039 | { |
3040 | /* { |
3041 | syscallarg(int) fd; |
3042 | syscallarg(const char *) path; |
3043 | syscallarg(int) amode; |
3044 | syscallarg(int) flag; |
3045 | } */ |
3046 | |
3047 | return do_sys_accessat(l, SCARG(uap, fd), SCARG(uap, path), |
3048 | SCARG(uap, amode), SCARG(uap, flag)); |
3049 | } |
3050 | |
3051 | /* |
3052 | * Common code for all sys_stat functions, including compat versions. |
3053 | */ |
3054 | int |
3055 | do_sys_stat(const char *userpath, unsigned int nd_flag, |
3056 | struct stat *sb) |
3057 | { |
3058 | return do_sys_statat(NULL, AT_FDCWD, userpath, nd_flag, sb); |
3059 | } |
3060 | |
3061 | int |
3062 | do_sys_statat(struct lwp *l, int fdat, const char *userpath, |
3063 | unsigned int nd_flag, struct stat *sb) |
3064 | { |
3065 | int error; |
3066 | struct pathbuf *pb; |
3067 | struct nameidata nd; |
3068 | |
3069 | KASSERT(l != NULL || fdat == AT_FDCWD); |
3070 | |
3071 | error = pathbuf_copyin(userpath, &pb); |
3072 | if (error) { |
3073 | return error; |
3074 | } |
3075 | |
3076 | NDINIT(&nd, LOOKUP, nd_flag | LOCKLEAF | TRYEMULROOT, pb); |
3077 | |
3078 | error = fd_nameiat(l, fdat, &nd); |
3079 | if (error != 0) { |
3080 | pathbuf_destroy(pb); |
3081 | return error; |
3082 | } |
3083 | error = vn_stat(nd.ni_vp, sb); |
3084 | vput(nd.ni_vp); |
3085 | pathbuf_destroy(pb); |
3086 | return error; |
3087 | } |
3088 | |
3089 | /* |
3090 | * Get file status; this version follows links. |
3091 | */ |
3092 | /* ARGSUSED */ |
3093 | int |
3094 | sys___stat50(struct lwp *l, const struct sys___stat50_args *uap, register_t *retval) |
3095 | { |
3096 | /* { |
3097 | syscallarg(const char *) path; |
3098 | syscallarg(struct stat *) ub; |
3099 | } */ |
3100 | struct stat sb; |
3101 | int error; |
3102 | |
3103 | error = do_sys_statat(l, AT_FDCWD, SCARG(uap, path), FOLLOW, &sb); |
3104 | if (error) |
3105 | return error; |
3106 | return copyout(&sb, SCARG(uap, ub), sizeof(sb)); |
3107 | } |
3108 | |
3109 | /* |
3110 | * Get file status; this version does not follow links. |
3111 | */ |
3112 | /* ARGSUSED */ |
3113 | int |
3114 | sys___lstat50(struct lwp *l, const struct sys___lstat50_args *uap, register_t *retval) |
3115 | { |
3116 | /* { |
3117 | syscallarg(const char *) path; |
3118 | syscallarg(struct stat *) ub; |
3119 | } */ |
3120 | struct stat sb; |
3121 | int error; |
3122 | |
3123 | error = do_sys_statat(l, AT_FDCWD, SCARG(uap, path), NOFOLLOW, &sb); |
3124 | if (error) |
3125 | return error; |
3126 | return copyout(&sb, SCARG(uap, ub), sizeof(sb)); |
3127 | } |
3128 | |
3129 | int |
3130 | sys_fstatat(struct lwp *l, const struct sys_fstatat_args *uap, |
3131 | register_t *retval) |
3132 | { |
3133 | /* { |
3134 | syscallarg(int) fd; |
3135 | syscallarg(const char *) path; |
3136 | syscallarg(struct stat *) buf; |
3137 | syscallarg(int) flag; |
3138 | } */ |
3139 | unsigned int nd_flag; |
3140 | struct stat sb; |
3141 | int error; |
3142 | |
3143 | if (SCARG(uap, flag) & AT_SYMLINK_NOFOLLOW) |
3144 | nd_flag = NOFOLLOW; |
3145 | else |
3146 | nd_flag = FOLLOW; |
3147 | |
3148 | error = do_sys_statat(l, SCARG(uap, fd), SCARG(uap, path), nd_flag, |
3149 | &sb); |
3150 | if (error) |
3151 | return error; |
3152 | return copyout(&sb, SCARG(uap, buf), sizeof(sb)); |
3153 | } |
3154 | |
3155 | /* |
3156 | * Get configurable pathname variables. |
3157 | */ |
3158 | /* ARGSUSED */ |
3159 | int |
3160 | sys_pathconf(struct lwp *l, const struct sys_pathconf_args *uap, register_t *retval) |
3161 | { |
3162 | /* { |
3163 | syscallarg(const char *) path; |
3164 | syscallarg(int) name; |
3165 | } */ |
3166 | int error; |
3167 | struct pathbuf *pb; |
3168 | struct nameidata nd; |
3169 | |
3170 | error = pathbuf_copyin(SCARG(uap, path), &pb); |
3171 | if (error) { |
3172 | return error; |
3173 | } |
3174 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
3175 | if ((error = namei(&nd)) != 0) { |
3176 | pathbuf_destroy(pb); |
3177 | return (error); |
3178 | } |
3179 | error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval); |
3180 | vput(nd.ni_vp); |
3181 | pathbuf_destroy(pb); |
3182 | return (error); |
3183 | } |
3184 | |
3185 | /* |
3186 | * Return target name of a symbolic link. |
3187 | */ |
3188 | /* ARGSUSED */ |
3189 | int |
3190 | sys_readlink(struct lwp *l, const struct sys_readlink_args *uap, |
3191 | register_t *retval) |
3192 | { |
3193 | /* { |
3194 | syscallarg(const char *) path; |
3195 | syscallarg(char *) buf; |
3196 | syscallarg(size_t) count; |
3197 | } */ |
3198 | return do_sys_readlinkat(l, AT_FDCWD, SCARG(uap, path), |
3199 | SCARG(uap, buf), SCARG(uap, count), retval); |
3200 | } |
3201 | |
3202 | static int |
3203 | do_sys_readlinkat(struct lwp *l, int fdat, const char *path, char *buf, |
3204 | size_t count, register_t *retval) |
3205 | { |
3206 | struct vnode *vp; |
3207 | struct iovec aiov; |
3208 | struct uio auio; |
3209 | int error; |
3210 | struct pathbuf *pb; |
3211 | struct nameidata nd; |
3212 | |
3213 | error = pathbuf_copyin(path, &pb); |
3214 | if (error) { |
3215 | return error; |
3216 | } |
3217 | NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
3218 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) { |
3219 | pathbuf_destroy(pb); |
3220 | return error; |
3221 | } |
3222 | vp = nd.ni_vp; |
3223 | pathbuf_destroy(pb); |
3224 | if (vp->v_type != VLNK) |
3225 | error = EINVAL; |
3226 | else if (!(vp->v_mount->mnt_flag & MNT_SYMPERM) || |
3227 | (error = VOP_ACCESS(vp, VREAD, l->l_cred)) == 0) { |
3228 | aiov.iov_base = buf; |
3229 | aiov.iov_len = count; |
3230 | auio.uio_iov = &aiov; |
3231 | auio.uio_iovcnt = 1; |
3232 | auio.uio_offset = 0; |
3233 | auio.uio_rw = UIO_READ; |
3234 | KASSERT(l == curlwp); |
3235 | auio.uio_vmspace = l->l_proc->p_vmspace; |
3236 | auio.uio_resid = count; |
3237 | if ((error = VOP_READLINK(vp, &auio, l->l_cred)) == 0) |
3238 | *retval = count - auio.uio_resid; |
3239 | } |
3240 | vput(vp); |
3241 | return (error); |
3242 | } |
3243 | |
3244 | int |
3245 | sys_readlinkat(struct lwp *l, const struct sys_readlinkat_args *uap, |
3246 | register_t *retval) |
3247 | { |
3248 | /* { |
3249 | syscallarg(int) fd; |
3250 | syscallarg(const char *) path; |
3251 | syscallarg(char *) buf; |
3252 | syscallarg(size_t) bufsize; |
3253 | } */ |
3254 | |
3255 | return do_sys_readlinkat(l, SCARG(uap, fd), SCARG(uap, path), |
3256 | SCARG(uap, buf), SCARG(uap, bufsize), retval); |
3257 | } |
3258 | |
3259 | /* |
3260 | * Change flags of a file given a path name. |
3261 | */ |
3262 | /* ARGSUSED */ |
3263 | int |
3264 | sys_chflags(struct lwp *l, const struct sys_chflags_args *uap, register_t *retval) |
3265 | { |
3266 | /* { |
3267 | syscallarg(const char *) path; |
3268 | syscallarg(u_long) flags; |
3269 | } */ |
3270 | struct vnode *vp; |
3271 | int error; |
3272 | |
3273 | error = namei_simple_user(SCARG(uap, path), |
3274 | NSM_FOLLOW_TRYEMULROOT, &vp); |
3275 | if (error != 0) |
3276 | return (error); |
3277 | error = change_flags(vp, SCARG(uap, flags), l); |
3278 | vput(vp); |
3279 | return (error); |
3280 | } |
3281 | |
3282 | /* |
3283 | * Change flags of a file given a file descriptor. |
3284 | */ |
3285 | /* ARGSUSED */ |
3286 | int |
3287 | sys_fchflags(struct lwp *l, const struct sys_fchflags_args *uap, register_t *retval) |
3288 | { |
3289 | /* { |
3290 | syscallarg(int) fd; |
3291 | syscallarg(u_long) flags; |
3292 | } */ |
3293 | struct vnode *vp; |
3294 | file_t *fp; |
3295 | int error; |
3296 | |
3297 | /* fd_getvnode() will use the descriptor for us */ |
3298 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3299 | return (error); |
3300 | vp = fp->f_vnode; |
3301 | error = change_flags(vp, SCARG(uap, flags), l); |
3302 | VOP_UNLOCK(vp); |
3303 | fd_putfile(SCARG(uap, fd)); |
3304 | return (error); |
3305 | } |
3306 | |
3307 | /* |
3308 | * Change flags of a file given a path name; this version does |
3309 | * not follow links. |
3310 | */ |
3311 | int |
3312 | sys_lchflags(struct lwp *l, const struct sys_lchflags_args *uap, register_t *retval) |
3313 | { |
3314 | /* { |
3315 | syscallarg(const char *) path; |
3316 | syscallarg(u_long) flags; |
3317 | } */ |
3318 | struct vnode *vp; |
3319 | int error; |
3320 | |
3321 | error = namei_simple_user(SCARG(uap, path), |
3322 | NSM_NOFOLLOW_TRYEMULROOT, &vp); |
3323 | if (error != 0) |
3324 | return (error); |
3325 | error = change_flags(vp, SCARG(uap, flags), l); |
3326 | vput(vp); |
3327 | return (error); |
3328 | } |
3329 | |
3330 | /* |
3331 | * Common routine to change flags of a file. |
3332 | */ |
3333 | int |
3334 | change_flags(struct vnode *vp, u_long flags, struct lwp *l) |
3335 | { |
3336 | struct vattr vattr; |
3337 | int error; |
3338 | |
3339 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
3340 | |
3341 | vattr_null(&vattr); |
3342 | vattr.va_flags = flags; |
3343 | error = VOP_SETATTR(vp, &vattr, l->l_cred); |
3344 | |
3345 | return (error); |
3346 | } |
3347 | |
3348 | /* |
3349 | * Change mode of a file given path name; this version follows links. |
3350 | */ |
3351 | /* ARGSUSED */ |
3352 | int |
3353 | sys_chmod(struct lwp *l, const struct sys_chmod_args *uap, register_t *retval) |
3354 | { |
3355 | /* { |
3356 | syscallarg(const char *) path; |
3357 | syscallarg(int) mode; |
3358 | } */ |
3359 | return do_sys_chmodat(l, AT_FDCWD, SCARG(uap, path), |
3360 | SCARG(uap, mode), 0); |
3361 | } |
3362 | |
3363 | int |
3364 | do_sys_chmodat(struct lwp *l, int fdat, const char *path, int mode, int flags) |
3365 | { |
3366 | int error; |
3367 | struct vnode *vp; |
3368 | namei_simple_flags_t ns_flag; |
3369 | |
3370 | if (flags & AT_SYMLINK_NOFOLLOW) |
3371 | ns_flag = NSM_NOFOLLOW_TRYEMULROOT; |
3372 | else |
3373 | ns_flag = NSM_FOLLOW_TRYEMULROOT; |
3374 | |
3375 | error = fd_nameiat_simple_user(l, fdat, path, ns_flag, &vp); |
3376 | if (error != 0) |
3377 | return error; |
3378 | |
3379 | error = change_mode(vp, mode, l); |
3380 | |
3381 | vrele(vp); |
3382 | |
3383 | return (error); |
3384 | } |
3385 | |
3386 | /* |
3387 | * Change mode of a file given a file descriptor. |
3388 | */ |
3389 | /* ARGSUSED */ |
3390 | int |
3391 | sys_fchmod(struct lwp *l, const struct sys_fchmod_args *uap, register_t *retval) |
3392 | { |
3393 | /* { |
3394 | syscallarg(int) fd; |
3395 | syscallarg(int) mode; |
3396 | } */ |
3397 | file_t *fp; |
3398 | int error; |
3399 | |
3400 | /* fd_getvnode() will use the descriptor for us */ |
3401 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3402 | return (error); |
3403 | error = change_mode(fp->f_vnode, SCARG(uap, mode), l); |
3404 | fd_putfile(SCARG(uap, fd)); |
3405 | return (error); |
3406 | } |
3407 | |
3408 | int |
3409 | sys_fchmodat(struct lwp *l, const struct sys_fchmodat_args *uap, |
3410 | register_t *retval) |
3411 | { |
3412 | /* { |
3413 | syscallarg(int) fd; |
3414 | syscallarg(const char *) path; |
3415 | syscallarg(int) mode; |
3416 | syscallarg(int) flag; |
3417 | } */ |
3418 | |
3419 | return do_sys_chmodat(l, SCARG(uap, fd), SCARG(uap, path), |
3420 | SCARG(uap, mode), SCARG(uap, flag)); |
3421 | } |
3422 | |
3423 | /* |
3424 | * Change mode of a file given path name; this version does not follow links. |
3425 | */ |
3426 | /* ARGSUSED */ |
3427 | int |
3428 | sys_lchmod(struct lwp *l, const struct sys_lchmod_args *uap, register_t *retval) |
3429 | { |
3430 | /* { |
3431 | syscallarg(const char *) path; |
3432 | syscallarg(int) mode; |
3433 | } */ |
3434 | int error; |
3435 | struct vnode *vp; |
3436 | |
3437 | error = namei_simple_user(SCARG(uap, path), |
3438 | NSM_NOFOLLOW_TRYEMULROOT, &vp); |
3439 | if (error != 0) |
3440 | return (error); |
3441 | |
3442 | error = change_mode(vp, SCARG(uap, mode), l); |
3443 | |
3444 | vrele(vp); |
3445 | return (error); |
3446 | } |
3447 | |
3448 | /* |
3449 | * Common routine to set mode given a vnode. |
3450 | */ |
3451 | static int |
3452 | change_mode(struct vnode *vp, int mode, struct lwp *l) |
3453 | { |
3454 | struct vattr vattr; |
3455 | int error; |
3456 | |
3457 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
3458 | vattr_null(&vattr); |
3459 | vattr.va_mode = mode & ALLPERMS; |
3460 | error = VOP_SETATTR(vp, &vattr, l->l_cred); |
3461 | VOP_UNLOCK(vp); |
3462 | return (error); |
3463 | } |
3464 | |
3465 | /* |
3466 | * Set ownership given a path name; this version follows links. |
3467 | */ |
3468 | /* ARGSUSED */ |
3469 | int |
3470 | sys_chown(struct lwp *l, const struct sys_chown_args *uap, register_t *retval) |
3471 | { |
3472 | /* { |
3473 | syscallarg(const char *) path; |
3474 | syscallarg(uid_t) uid; |
3475 | syscallarg(gid_t) gid; |
3476 | } */ |
3477 | return do_sys_chownat(l, AT_FDCWD, SCARG(uap, path), SCARG(uap,uid), |
3478 | SCARG(uap, gid), 0); |
3479 | } |
3480 | |
3481 | int |
3482 | do_sys_chownat(struct lwp *l, int fdat, const char *path, uid_t uid, |
3483 | gid_t gid, int flags) |
3484 | { |
3485 | int error; |
3486 | struct vnode *vp; |
3487 | namei_simple_flags_t ns_flag; |
3488 | |
3489 | if (flags & AT_SYMLINK_NOFOLLOW) |
3490 | ns_flag = NSM_NOFOLLOW_TRYEMULROOT; |
3491 | else |
3492 | ns_flag = NSM_FOLLOW_TRYEMULROOT; |
3493 | |
3494 | error = fd_nameiat_simple_user(l, fdat, path, ns_flag, &vp); |
3495 | if (error != 0) |
3496 | return error; |
3497 | |
3498 | error = change_owner(vp, uid, gid, l, 0); |
3499 | |
3500 | vrele(vp); |
3501 | |
3502 | return (error); |
3503 | } |
3504 | |
3505 | /* |
3506 | * Set ownership given a path name; this version follows links. |
3507 | * Provides POSIX semantics. |
3508 | */ |
3509 | /* ARGSUSED */ |
3510 | int |
3511 | sys___posix_chown(struct lwp *l, const struct sys___posix_chown_args *uap, register_t *retval) |
3512 | { |
3513 | /* { |
3514 | syscallarg(const char *) path; |
3515 | syscallarg(uid_t) uid; |
3516 | syscallarg(gid_t) gid; |
3517 | } */ |
3518 | int error; |
3519 | struct vnode *vp; |
3520 | |
3521 | error = namei_simple_user(SCARG(uap, path), |
3522 | NSM_FOLLOW_TRYEMULROOT, &vp); |
3523 | if (error != 0) |
3524 | return (error); |
3525 | |
3526 | error = change_owner(vp, SCARG(uap, uid), SCARG(uap, gid), l, 1); |
3527 | |
3528 | vrele(vp); |
3529 | return (error); |
3530 | } |
3531 | |
3532 | /* |
3533 | * Set ownership given a file descriptor. |
3534 | */ |
3535 | /* ARGSUSED */ |
3536 | int |
3537 | sys_fchown(struct lwp *l, const struct sys_fchown_args *uap, register_t *retval) |
3538 | { |
3539 | /* { |
3540 | syscallarg(int) fd; |
3541 | syscallarg(uid_t) uid; |
3542 | syscallarg(gid_t) gid; |
3543 | } */ |
3544 | int error; |
3545 | file_t *fp; |
3546 | |
3547 | /* fd_getvnode() will use the descriptor for us */ |
3548 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3549 | return (error); |
3550 | error = change_owner(fp->f_vnode, SCARG(uap, uid), SCARG(uap, gid), |
3551 | l, 0); |
3552 | fd_putfile(SCARG(uap, fd)); |
3553 | return (error); |
3554 | } |
3555 | |
3556 | int |
3557 | sys_fchownat(struct lwp *l, const struct sys_fchownat_args *uap, |
3558 | register_t *retval) |
3559 | { |
3560 | /* { |
3561 | syscallarg(int) fd; |
3562 | syscallarg(const char *) path; |
3563 | syscallarg(uid_t) owner; |
3564 | syscallarg(gid_t) group; |
3565 | syscallarg(int) flag; |
3566 | } */ |
3567 | |
3568 | return do_sys_chownat(l, SCARG(uap, fd), SCARG(uap, path), |
3569 | SCARG(uap, owner), SCARG(uap, group), |
3570 | SCARG(uap, flag)); |
3571 | } |
3572 | |
3573 | /* |
3574 | * Set ownership given a file descriptor, providing POSIX/XPG semantics. |
3575 | */ |
3576 | /* ARGSUSED */ |
3577 | int |
3578 | sys___posix_fchown(struct lwp *l, const struct sys___posix_fchown_args *uap, register_t *retval) |
3579 | { |
3580 | /* { |
3581 | syscallarg(int) fd; |
3582 | syscallarg(uid_t) uid; |
3583 | syscallarg(gid_t) gid; |
3584 | } */ |
3585 | int error; |
3586 | file_t *fp; |
3587 | |
3588 | /* fd_getvnode() will use the descriptor for us */ |
3589 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3590 | return (error); |
3591 | error = change_owner(fp->f_vnode, SCARG(uap, uid), SCARG(uap, gid), |
3592 | l, 1); |
3593 | fd_putfile(SCARG(uap, fd)); |
3594 | return (error); |
3595 | } |
3596 | |
3597 | /* |
3598 | * Set ownership given a path name; this version does not follow links. |
3599 | */ |
3600 | /* ARGSUSED */ |
3601 | int |
3602 | sys_lchown(struct lwp *l, const struct sys_lchown_args *uap, register_t *retval) |
3603 | { |
3604 | /* { |
3605 | syscallarg(const char *) path; |
3606 | syscallarg(uid_t) uid; |
3607 | syscallarg(gid_t) gid; |
3608 | } */ |
3609 | int error; |
3610 | struct vnode *vp; |
3611 | |
3612 | error = namei_simple_user(SCARG(uap, path), |
3613 | NSM_NOFOLLOW_TRYEMULROOT, &vp); |
3614 | if (error != 0) |
3615 | return (error); |
3616 | |
3617 | error = change_owner(vp, SCARG(uap, uid), SCARG(uap, gid), l, 0); |
3618 | |
3619 | vrele(vp); |
3620 | return (error); |
3621 | } |
3622 | |
3623 | /* |
3624 | * Set ownership given a path name; this version does not follow links. |
3625 | * Provides POSIX/XPG semantics. |
3626 | */ |
3627 | /* ARGSUSED */ |
3628 | int |
3629 | sys___posix_lchown(struct lwp *l, const struct sys___posix_lchown_args *uap, register_t *retval) |
3630 | { |
3631 | /* { |
3632 | syscallarg(const char *) path; |
3633 | syscallarg(uid_t) uid; |
3634 | syscallarg(gid_t) gid; |
3635 | } */ |
3636 | int error; |
3637 | struct vnode *vp; |
3638 | |
3639 | error = namei_simple_user(SCARG(uap, path), |
3640 | NSM_NOFOLLOW_TRYEMULROOT, &vp); |
3641 | if (error != 0) |
3642 | return (error); |
3643 | |
3644 | error = change_owner(vp, SCARG(uap, uid), SCARG(uap, gid), l, 1); |
3645 | |
3646 | vrele(vp); |
3647 | return (error); |
3648 | } |
3649 | |
3650 | /* |
3651 | * Common routine to set ownership given a vnode. |
3652 | */ |
3653 | static int |
3654 | change_owner(struct vnode *vp, uid_t uid, gid_t gid, struct lwp *l, |
3655 | int posix_semantics) |
3656 | { |
3657 | struct vattr vattr; |
3658 | mode_t newmode; |
3659 | int error; |
3660 | |
3661 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
3662 | if ((error = VOP_GETATTR(vp, &vattr, l->l_cred)) != 0) |
3663 | goto out; |
3664 | |
3665 | #define CHANGED(x) ((int)(x) != -1) |
3666 | newmode = vattr.va_mode; |
3667 | if (posix_semantics) { |
3668 | /* |
3669 | * POSIX/XPG semantics: if the caller is not the super-user, |
3670 | * clear set-user-id and set-group-id bits. Both POSIX and |
3671 | * the XPG consider the behaviour for calls by the super-user |
3672 | * implementation-defined; we leave the set-user-id and set- |
3673 | * group-id settings intact in that case. |
3674 | */ |
3675 | if (vattr.va_mode & S_ISUID) { |
3676 | if (kauth_authorize_vnode(l->l_cred, |
3677 | KAUTH_VNODE_RETAIN_SUID, vp, NULL, EPERM) != 0) |
3678 | newmode &= ~S_ISUID; |
3679 | } |
3680 | if (vattr.va_mode & S_ISGID) { |
3681 | if (kauth_authorize_vnode(l->l_cred, |
3682 | KAUTH_VNODE_RETAIN_SGID, vp, NULL, EPERM) != 0) |
3683 | newmode &= ~S_ISGID; |
3684 | } |
3685 | } else { |
3686 | /* |
3687 | * NetBSD semantics: when changing owner and/or group, |
3688 | * clear the respective bit(s). |
3689 | */ |
3690 | if (CHANGED(uid)) |
3691 | newmode &= ~S_ISUID; |
3692 | if (CHANGED(gid)) |
3693 | newmode &= ~S_ISGID; |
3694 | } |
3695 | /* Update va_mode iff altered. */ |
3696 | if (vattr.va_mode == newmode) |
3697 | newmode = VNOVAL; |
3698 | |
3699 | vattr_null(&vattr); |
3700 | vattr.va_uid = CHANGED(uid) ? uid : (uid_t)VNOVAL; |
3701 | vattr.va_gid = CHANGED(gid) ? gid : (gid_t)VNOVAL; |
3702 | vattr.va_mode = newmode; |
3703 | error = VOP_SETATTR(vp, &vattr, l->l_cred); |
3704 | #undef CHANGED |
3705 | |
3706 | out: |
3707 | VOP_UNLOCK(vp); |
3708 | return (error); |
3709 | } |
3710 | |
3711 | /* |
3712 | * Set the access and modification times given a path name; this |
3713 | * version follows links. |
3714 | */ |
3715 | /* ARGSUSED */ |
3716 | int |
3717 | sys___utimes50(struct lwp *l, const struct sys___utimes50_args *uap, |
3718 | register_t *retval) |
3719 | { |
3720 | /* { |
3721 | syscallarg(const char *) path; |
3722 | syscallarg(const struct timeval *) tptr; |
3723 | } */ |
3724 | |
3725 | return do_sys_utimes(l, NULL, SCARG(uap, path), FOLLOW, |
3726 | SCARG(uap, tptr), UIO_USERSPACE); |
3727 | } |
3728 | |
3729 | /* |
3730 | * Set the access and modification times given a file descriptor. |
3731 | */ |
3732 | /* ARGSUSED */ |
3733 | int |
3734 | sys___futimes50(struct lwp *l, const struct sys___futimes50_args *uap, |
3735 | register_t *retval) |
3736 | { |
3737 | /* { |
3738 | syscallarg(int) fd; |
3739 | syscallarg(const struct timeval *) tptr; |
3740 | } */ |
3741 | int error; |
3742 | file_t *fp; |
3743 | |
3744 | /* fd_getvnode() will use the descriptor for us */ |
3745 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3746 | return (error); |
3747 | error = do_sys_utimes(l, fp->f_vnode, NULL, 0, SCARG(uap, tptr), |
3748 | UIO_USERSPACE); |
3749 | fd_putfile(SCARG(uap, fd)); |
3750 | return (error); |
3751 | } |
3752 | |
3753 | int |
3754 | sys_futimens(struct lwp *l, const struct sys_futimens_args *uap, |
3755 | register_t *retval) |
3756 | { |
3757 | /* { |
3758 | syscallarg(int) fd; |
3759 | syscallarg(const struct timespec *) tptr; |
3760 | } */ |
3761 | int error; |
3762 | file_t *fp; |
3763 | |
3764 | /* fd_getvnode() will use the descriptor for us */ |
3765 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3766 | return (error); |
3767 | error = do_sys_utimensat(l, AT_FDCWD, fp->f_vnode, NULL, 0, |
3768 | SCARG(uap, tptr), UIO_USERSPACE); |
3769 | fd_putfile(SCARG(uap, fd)); |
3770 | return (error); |
3771 | } |
3772 | |
3773 | /* |
3774 | * Set the access and modification times given a path name; this |
3775 | * version does not follow links. |
3776 | */ |
3777 | int |
3778 | sys___lutimes50(struct lwp *l, const struct sys___lutimes50_args *uap, |
3779 | register_t *retval) |
3780 | { |
3781 | /* { |
3782 | syscallarg(const char *) path; |
3783 | syscallarg(const struct timeval *) tptr; |
3784 | } */ |
3785 | |
3786 | return do_sys_utimes(l, NULL, SCARG(uap, path), NOFOLLOW, |
3787 | SCARG(uap, tptr), UIO_USERSPACE); |
3788 | } |
3789 | |
3790 | int |
3791 | sys_utimensat(struct lwp *l, const struct sys_utimensat_args *uap, |
3792 | register_t *retval) |
3793 | { |
3794 | /* { |
3795 | syscallarg(int) fd; |
3796 | syscallarg(const char *) path; |
3797 | syscallarg(const struct timespec *) tptr; |
3798 | syscallarg(int) flag; |
3799 | } */ |
3800 | int follow; |
3801 | const struct timespec *tptr; |
3802 | int error; |
3803 | |
3804 | tptr = SCARG(uap, tptr); |
3805 | follow = (SCARG(uap, flag) & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; |
3806 | |
3807 | error = do_sys_utimensat(l, SCARG(uap, fd), NULL, |
3808 | SCARG(uap, path), follow, tptr, UIO_USERSPACE); |
3809 | |
3810 | return error; |
3811 | } |
3812 | |
3813 | /* |
3814 | * Common routine to set access and modification times given a vnode. |
3815 | */ |
3816 | int |
3817 | do_sys_utimens(struct lwp *l, struct vnode *vp, const char *path, int flag, |
3818 | const struct timespec *tptr, enum uio_seg seg) |
3819 | { |
3820 | return do_sys_utimensat(l, AT_FDCWD, vp, path, flag, tptr, seg); |
3821 | } |
3822 | |
3823 | int |
3824 | do_sys_utimensat(struct lwp *l, int fdat, struct vnode *vp, |
3825 | const char *path, int flag, const struct timespec *tptr, enum uio_seg seg) |
3826 | { |
3827 | struct vattr vattr; |
3828 | int error, dorele = 0; |
3829 | namei_simple_flags_t sflags; |
3830 | bool vanull, setbirthtime; |
3831 | struct timespec ts[2]; |
3832 | |
3833 | KASSERT(l != NULL || fdat == AT_FDCWD); |
3834 | |
3835 | /* |
3836 | * I have checked all callers and they pass either FOLLOW, |
3837 | * NOFOLLOW, or 0 (when they don't pass a path), and NOFOLLOW |
3838 | * is 0. More to the point, they don't pass anything else. |
3839 | * Let's keep it that way at least until the namei interfaces |
3840 | * are fully sanitized. |
3841 | */ |
3842 | KASSERT(flag == NOFOLLOW || flag == FOLLOW); |
3843 | sflags = (flag == FOLLOW) ? |
3844 | NSM_FOLLOW_TRYEMULROOT : NSM_NOFOLLOW_TRYEMULROOT; |
3845 | |
3846 | if (tptr == NULL) { |
3847 | vanull = true; |
3848 | nanotime(&ts[0]); |
3849 | ts[1] = ts[0]; |
3850 | } else { |
3851 | vanull = false; |
3852 | if (seg != UIO_SYSSPACE) { |
3853 | error = copyin(tptr, ts, sizeof (ts)); |
3854 | if (error != 0) |
3855 | return error; |
3856 | } else { |
3857 | ts[0] = tptr[0]; |
3858 | ts[1] = tptr[1]; |
3859 | } |
3860 | } |
3861 | |
3862 | if (ts[0].tv_nsec == UTIME_NOW) { |
3863 | nanotime(&ts[0]); |
3864 | if (ts[1].tv_nsec == UTIME_NOW) { |
3865 | vanull = true; |
3866 | ts[1] = ts[0]; |
3867 | } |
3868 | } else if (ts[1].tv_nsec == UTIME_NOW) |
3869 | nanotime(&ts[1]); |
3870 | |
3871 | if (vp == NULL) { |
3872 | /* note: SEG describes TPTR, not PATH; PATH is always user */ |
3873 | error = fd_nameiat_simple_user(l, fdat, path, sflags, &vp); |
3874 | if (error != 0) |
3875 | return error; |
3876 | dorele = 1; |
3877 | } |
3878 | |
3879 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
3880 | setbirthtime = (VOP_GETATTR(vp, &vattr, l->l_cred) == 0 && |
3881 | timespeccmp(&ts[1], &vattr.va_birthtime, <)); |
3882 | vattr_null(&vattr); |
3883 | |
3884 | if (ts[0].tv_nsec != UTIME_OMIT) |
3885 | vattr.va_atime = ts[0]; |
3886 | |
3887 | if (ts[1].tv_nsec != UTIME_OMIT) { |
3888 | vattr.va_mtime = ts[1]; |
3889 | if (setbirthtime) |
3890 | vattr.va_birthtime = ts[1]; |
3891 | } |
3892 | |
3893 | if (vanull) |
3894 | vattr.va_vaflags |= VA_UTIMES_NULL; |
3895 | error = VOP_SETATTR(vp, &vattr, l->l_cred); |
3896 | VOP_UNLOCK(vp); |
3897 | |
3898 | if (dorele != 0) |
3899 | vrele(vp); |
3900 | |
3901 | return error; |
3902 | } |
3903 | |
3904 | int |
3905 | do_sys_utimes(struct lwp *l, struct vnode *vp, const char *path, int flag, |
3906 | const struct timeval *tptr, enum uio_seg seg) |
3907 | { |
3908 | struct timespec ts[2]; |
3909 | struct timespec *tsptr = NULL; |
3910 | int error; |
3911 | |
3912 | if (tptr != NULL) { |
3913 | struct timeval tv[2]; |
3914 | |
3915 | if (seg != UIO_SYSSPACE) { |
3916 | error = copyin(tptr, tv, sizeof(tv)); |
3917 | if (error != 0) |
3918 | return error; |
3919 | tptr = tv; |
3920 | } |
3921 | |
3922 | if ((tptr[0].tv_usec == UTIME_NOW) || |
3923 | (tptr[0].tv_usec == UTIME_OMIT)) |
3924 | ts[0].tv_nsec = tptr[0].tv_usec; |
3925 | else |
3926 | TIMEVAL_TO_TIMESPEC(&tptr[0], &ts[0]); |
3927 | |
3928 | if ((tptr[1].tv_usec == UTIME_NOW) || |
3929 | (tptr[1].tv_usec == UTIME_OMIT)) |
3930 | ts[1].tv_nsec = tptr[1].tv_usec; |
3931 | else |
3932 | TIMEVAL_TO_TIMESPEC(&tptr[1], &ts[1]); |
3933 | |
3934 | tsptr = &ts[0]; |
3935 | } |
3936 | |
3937 | return do_sys_utimens(l, vp, path, flag, tsptr, UIO_SYSSPACE); |
3938 | } |
3939 | |
3940 | /* |
3941 | * Truncate a file given its path name. |
3942 | */ |
3943 | /* ARGSUSED */ |
3944 | int |
3945 | sys_truncate(struct lwp *l, const struct sys_truncate_args *uap, register_t *retval) |
3946 | { |
3947 | /* { |
3948 | syscallarg(const char *) path; |
3949 | syscallarg(int) pad; |
3950 | syscallarg(off_t) length; |
3951 | } */ |
3952 | struct vnode *vp; |
3953 | struct vattr vattr; |
3954 | int error; |
3955 | |
3956 | if (SCARG(uap, length) < 0) |
3957 | return EINVAL; |
3958 | |
3959 | error = namei_simple_user(SCARG(uap, path), |
3960 | NSM_FOLLOW_TRYEMULROOT, &vp); |
3961 | if (error != 0) |
3962 | return (error); |
3963 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
3964 | if (vp->v_type == VDIR) |
3965 | error = EISDIR; |
3966 | else if ((error = vn_writechk(vp)) == 0 && |
3967 | (error = VOP_ACCESS(vp, VWRITE, l->l_cred)) == 0) { |
3968 | vattr_null(&vattr); |
3969 | vattr.va_size = SCARG(uap, length); |
3970 | error = VOP_SETATTR(vp, &vattr, l->l_cred); |
3971 | } |
3972 | vput(vp); |
3973 | return (error); |
3974 | } |
3975 | |
3976 | /* |
3977 | * Truncate a file given a file descriptor. |
3978 | */ |
3979 | /* ARGSUSED */ |
3980 | int |
3981 | sys_ftruncate(struct lwp *l, const struct sys_ftruncate_args *uap, register_t *retval) |
3982 | { |
3983 | /* { |
3984 | syscallarg(int) fd; |
3985 | syscallarg(int) pad; |
3986 | syscallarg(off_t) length; |
3987 | } */ |
3988 | struct vattr vattr; |
3989 | struct vnode *vp; |
3990 | file_t *fp; |
3991 | int error; |
3992 | |
3993 | if (SCARG(uap, length) < 0) |
3994 | return EINVAL; |
3995 | |
3996 | /* fd_getvnode() will use the descriptor for us */ |
3997 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
3998 | return (error); |
3999 | if ((fp->f_flag & FWRITE) == 0) { |
4000 | error = EINVAL; |
4001 | goto out; |
4002 | } |
4003 | vp = fp->f_vnode; |
4004 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4005 | if (vp->v_type == VDIR) |
4006 | error = EISDIR; |
4007 | else if ((error = vn_writechk(vp)) == 0) { |
4008 | vattr_null(&vattr); |
4009 | vattr.va_size = SCARG(uap, length); |
4010 | error = VOP_SETATTR(vp, &vattr, fp->f_cred); |
4011 | } |
4012 | VOP_UNLOCK(vp); |
4013 | out: |
4014 | fd_putfile(SCARG(uap, fd)); |
4015 | return (error); |
4016 | } |
4017 | |
4018 | /* |
4019 | * Sync an open file. |
4020 | */ |
4021 | /* ARGSUSED */ |
4022 | int |
4023 | sys_fsync(struct lwp *l, const struct sys_fsync_args *uap, register_t *retval) |
4024 | { |
4025 | /* { |
4026 | syscallarg(int) fd; |
4027 | } */ |
4028 | struct vnode *vp; |
4029 | file_t *fp; |
4030 | int error; |
4031 | |
4032 | /* fd_getvnode() will use the descriptor for us */ |
4033 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
4034 | return (error); |
4035 | vp = fp->f_vnode; |
4036 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4037 | error = VOP_FSYNC(vp, fp->f_cred, FSYNC_WAIT, 0, 0); |
4038 | VOP_UNLOCK(vp); |
4039 | fd_putfile(SCARG(uap, fd)); |
4040 | return (error); |
4041 | } |
4042 | |
4043 | /* |
4044 | * Sync a range of file data. API modeled after that found in AIX. |
4045 | * |
4046 | * FDATASYNC indicates that we need only save enough metadata to be able |
4047 | * to re-read the written data. Note we duplicate AIX's requirement that |
4048 | * the file be open for writing. |
4049 | */ |
4050 | /* ARGSUSED */ |
4051 | int |
4052 | sys_fsync_range(struct lwp *l, const struct sys_fsync_range_args *uap, register_t *retval) |
4053 | { |
4054 | /* { |
4055 | syscallarg(int) fd; |
4056 | syscallarg(int) flags; |
4057 | syscallarg(off_t) start; |
4058 | syscallarg(off_t) length; |
4059 | } */ |
4060 | struct vnode *vp; |
4061 | file_t *fp; |
4062 | int flags, nflags; |
4063 | off_t s, e, len; |
4064 | int error; |
4065 | |
4066 | /* fd_getvnode() will use the descriptor for us */ |
4067 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
4068 | return (error); |
4069 | |
4070 | if ((fp->f_flag & FWRITE) == 0) { |
4071 | error = EBADF; |
4072 | goto out; |
4073 | } |
4074 | |
4075 | flags = SCARG(uap, flags); |
4076 | if (((flags & (FDATASYNC | FFILESYNC)) == 0) || |
4077 | ((~flags & (FDATASYNC | FFILESYNC)) == 0)) { |
4078 | error = EINVAL; |
4079 | goto out; |
4080 | } |
4081 | /* Now set up the flags for value(s) to pass to VOP_FSYNC() */ |
4082 | if (flags & FDATASYNC) |
4083 | nflags = FSYNC_DATAONLY | FSYNC_WAIT; |
4084 | else |
4085 | nflags = FSYNC_WAIT; |
4086 | if (flags & FDISKSYNC) |
4087 | nflags |= FSYNC_CACHE; |
4088 | |
4089 | len = SCARG(uap, length); |
4090 | /* If length == 0, we do the whole file, and s = e = 0 will do that */ |
4091 | if (len) { |
4092 | s = SCARG(uap, start); |
4093 | e = s + len; |
4094 | if (e < s) { |
4095 | error = EINVAL; |
4096 | goto out; |
4097 | } |
4098 | } else { |
4099 | e = 0; |
4100 | s = 0; |
4101 | } |
4102 | |
4103 | vp = fp->f_vnode; |
4104 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4105 | error = VOP_FSYNC(vp, fp->f_cred, nflags, s, e); |
4106 | VOP_UNLOCK(vp); |
4107 | out: |
4108 | fd_putfile(SCARG(uap, fd)); |
4109 | return (error); |
4110 | } |
4111 | |
4112 | /* |
4113 | * Sync the data of an open file. |
4114 | */ |
4115 | /* ARGSUSED */ |
4116 | int |
4117 | sys_fdatasync(struct lwp *l, const struct sys_fdatasync_args *uap, register_t *retval) |
4118 | { |
4119 | /* { |
4120 | syscallarg(int) fd; |
4121 | } */ |
4122 | struct vnode *vp; |
4123 | file_t *fp; |
4124 | int error; |
4125 | |
4126 | /* fd_getvnode() will use the descriptor for us */ |
4127 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
4128 | return (error); |
4129 | if ((fp->f_flag & FWRITE) == 0) { |
4130 | fd_putfile(SCARG(uap, fd)); |
4131 | return (EBADF); |
4132 | } |
4133 | vp = fp->f_vnode; |
4134 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4135 | error = VOP_FSYNC(vp, fp->f_cred, FSYNC_WAIT|FSYNC_DATAONLY, 0, 0); |
4136 | VOP_UNLOCK(vp); |
4137 | fd_putfile(SCARG(uap, fd)); |
4138 | return (error); |
4139 | } |
4140 | |
4141 | /* |
4142 | * Rename files, (standard) BSD semantics frontend. |
4143 | */ |
4144 | /* ARGSUSED */ |
4145 | int |
4146 | sys_rename(struct lwp *l, const struct sys_rename_args *uap, register_t *retval) |
4147 | { |
4148 | /* { |
4149 | syscallarg(const char *) from; |
4150 | syscallarg(const char *) to; |
4151 | } */ |
4152 | |
4153 | return (do_sys_renameat(l, AT_FDCWD, SCARG(uap, from), AT_FDCWD, |
4154 | SCARG(uap, to), UIO_USERSPACE, 0)); |
4155 | } |
4156 | |
4157 | int |
4158 | sys_renameat(struct lwp *l, const struct sys_renameat_args *uap, |
4159 | register_t *retval) |
4160 | { |
4161 | /* { |
4162 | syscallarg(int) fromfd; |
4163 | syscallarg(const char *) from; |
4164 | syscallarg(int) tofd; |
4165 | syscallarg(const char *) to; |
4166 | } */ |
4167 | |
4168 | return (do_sys_renameat(l, SCARG(uap, fromfd), SCARG(uap, from), |
4169 | SCARG(uap, tofd), SCARG(uap, to), UIO_USERSPACE, 0)); |
4170 | } |
4171 | |
4172 | /* |
4173 | * Rename files, POSIX semantics frontend. |
4174 | */ |
4175 | /* ARGSUSED */ |
4176 | int |
4177 | sys___posix_rename(struct lwp *l, const struct sys___posix_rename_args *uap, register_t *retval) |
4178 | { |
4179 | /* { |
4180 | syscallarg(const char *) from; |
4181 | syscallarg(const char *) to; |
4182 | } */ |
4183 | |
4184 | return (do_sys_renameat(l, AT_FDCWD, SCARG(uap, from), AT_FDCWD, |
4185 | SCARG(uap, to), UIO_USERSPACE, 1)); |
4186 | } |
4187 | |
4188 | /* |
4189 | * Rename files. Source and destination must either both be directories, |
4190 | * or both not be directories. If target is a directory, it must be empty. |
4191 | * If `from' and `to' refer to the same object, the value of the `retain' |
4192 | * argument is used to determine whether `from' will be |
4193 | * |
4194 | * (retain == 0) deleted unless `from' and `to' refer to the same |
4195 | * object in the file system's name space (BSD). |
4196 | * (retain == 1) always retained (POSIX). |
4197 | * |
4198 | * XXX Synchronize with nfsrv_rename in nfs_serv.c. |
4199 | */ |
4200 | int |
4201 | do_sys_rename(const char *from, const char *to, enum uio_seg seg, int retain) |
4202 | { |
4203 | return do_sys_renameat(NULL, AT_FDCWD, from, AT_FDCWD, to, seg, retain); |
4204 | } |
4205 | |
4206 | static int |
4207 | do_sys_renameat(struct lwp *l, int fromfd, const char *from, int tofd, |
4208 | const char *to, enum uio_seg seg, int retain) |
4209 | { |
4210 | struct pathbuf *fpb, *tpb; |
4211 | struct nameidata fnd, tnd; |
4212 | struct vnode *fdvp, *fvp; |
4213 | struct vnode *tdvp, *tvp; |
4214 | struct mount *mp, *tmp; |
4215 | int error; |
4216 | |
4217 | KASSERT(l != NULL || (fromfd == AT_FDCWD && tofd == AT_FDCWD)); |
4218 | |
4219 | error = pathbuf_maybe_copyin(from, seg, &fpb); |
4220 | if (error) |
4221 | goto out0; |
4222 | KASSERT(fpb != NULL); |
4223 | |
4224 | error = pathbuf_maybe_copyin(to, seg, &tpb); |
4225 | if (error) |
4226 | goto out1; |
4227 | KASSERT(tpb != NULL); |
4228 | |
4229 | /* |
4230 | * Lookup from. |
4231 | * |
4232 | * XXX LOCKPARENT is wrong because we don't actually want it |
4233 | * locked yet, but (a) namei is insane, and (b) VOP_RENAME is |
4234 | * insane, so for the time being we need to leave it like this. |
4235 | */ |
4236 | NDINIT(&fnd, DELETE, (LOCKPARENT | TRYEMULROOT), fpb); |
4237 | if ((error = fd_nameiat(l, fromfd, &fnd)) != 0) |
4238 | goto out2; |
4239 | |
4240 | /* |
4241 | * Pull out the important results of the lookup, fdvp and fvp. |
4242 | * Of course, fvp is bogus because we're about to unlock fdvp. |
4243 | */ |
4244 | fdvp = fnd.ni_dvp; |
4245 | fvp = fnd.ni_vp; |
4246 | mp = fdvp->v_mount; |
4247 | KASSERT(fdvp != NULL); |
4248 | KASSERT(fvp != NULL); |
4249 | KASSERT((fdvp == fvp) || (VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE)); |
4250 | /* |
4251 | * Bracket the operation with fstrans_start()/fstrans_done(). |
4252 | * |
4253 | * Inside the bracket this file system cannot be unmounted so |
4254 | * a vnode on this file system cannot change its v_mount. |
4255 | * A vnode on another file system may still change to dead mount. |
4256 | */ |
4257 | fstrans_start(mp); |
4258 | |
4259 | /* |
4260 | * Make sure neither fdvp nor fvp is locked. |
4261 | */ |
4262 | if (fdvp != fvp) |
4263 | VOP_UNLOCK(fdvp); |
4264 | /* XXX KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ |
4265 | /* XXX KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ |
4266 | |
4267 | /* |
4268 | * Reject renaming `.' and `..'. Can't do this until after |
4269 | * namei because we need namei's parsing to find the final |
4270 | * component name. (namei should just leave us with the final |
4271 | * component name and not look it up itself, but anyway...) |
4272 | * |
4273 | * This was here before because we used to relookup from |
4274 | * instead of to and relookup requires the caller to check |
4275 | * this, but now file systems may depend on this check, so we |
4276 | * must retain it until the file systems are all rototilled. |
4277 | */ |
4278 | if (((fnd.ni_cnd.cn_namelen == 1) && |
4279 | (fnd.ni_cnd.cn_nameptr[0] == '.')) || |
4280 | ((fnd.ni_cnd.cn_namelen == 2) && |
4281 | (fnd.ni_cnd.cn_nameptr[0] == '.') && |
4282 | (fnd.ni_cnd.cn_nameptr[1] == '.'))) { |
4283 | error = EINVAL; /* XXX EISDIR? */ |
4284 | goto abort0; |
4285 | } |
4286 | |
4287 | /* |
4288 | * Lookup to. |
4289 | * |
4290 | * XXX LOCKPARENT is wrong, but...insanity, &c. Also, using |
4291 | * fvp here to decide whether to add CREATEDIR is a load of |
4292 | * bollocks because fvp might be the wrong node by now, since |
4293 | * fdvp is unlocked. |
4294 | * |
4295 | * XXX Why not pass CREATEDIR always? |
4296 | */ |
4297 | NDINIT(&tnd, RENAME, |
4298 | (LOCKPARENT | NOCACHE | TRYEMULROOT | |
4299 | ((fvp->v_type == VDIR)? CREATEDIR : 0)), |
4300 | tpb); |
4301 | if ((error = fd_nameiat(l, tofd, &tnd)) != 0) |
4302 | goto abort0; |
4303 | |
4304 | /* |
4305 | * Pull out the important results of the lookup, tdvp and tvp. |
4306 | * Of course, tvp is bogus because we're about to unlock tdvp. |
4307 | */ |
4308 | tdvp = tnd.ni_dvp; |
4309 | tvp = tnd.ni_vp; |
4310 | KASSERT(tdvp != NULL); |
4311 | KASSERT((tdvp == tvp) || (VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE)); |
4312 | |
4313 | /* |
4314 | * Make sure neither tdvp nor tvp is locked. |
4315 | */ |
4316 | if (tdvp != tvp) |
4317 | VOP_UNLOCK(tdvp); |
4318 | /* XXX KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */ |
4319 | /* XXX KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) != LK_EXCLUSIVE)); */ |
4320 | |
4321 | /* |
4322 | * Reject renaming onto `.' or `..'. relookup is unhappy with |
4323 | * these, which is why we must do this here. Once upon a time |
4324 | * we relooked up from instead of to, and consequently didn't |
4325 | * need this check, but now that we relookup to instead of |
4326 | * from, we need this; and we shall need it forever forward |
4327 | * until the VOP_RENAME protocol changes, because file systems |
4328 | * will no doubt begin to depend on this check. |
4329 | */ |
4330 | if ((tnd.ni_cnd.cn_namelen == 1) && (tnd.ni_cnd.cn_nameptr[0] == '.')) { |
4331 | error = EISDIR; |
4332 | goto abort1; |
4333 | } |
4334 | if ((tnd.ni_cnd.cn_namelen == 2) && |
4335 | (tnd.ni_cnd.cn_nameptr[0] == '.') && |
4336 | (tnd.ni_cnd.cn_nameptr[1] == '.')) { |
4337 | error = EINVAL; |
4338 | goto abort1; |
4339 | } |
4340 | |
4341 | /* |
4342 | * Make sure the mount points match. Although we don't hold |
4343 | * any vnode locks, the v_mount on fdvp file system are stable. |
4344 | * |
4345 | * Unmounting another file system at an inopportune moment may |
4346 | * cause tdvp to disappear and change its v_mount to dead. |
4347 | * |
4348 | * So in either case different v_mount means cross-device rename. |
4349 | */ |
4350 | KASSERT(mp != NULL); |
4351 | tmp = tdvp->v_mount; |
4352 | |
4353 | if (mp != tmp) { |
4354 | error = EXDEV; |
4355 | goto abort1; |
4356 | } |
4357 | |
4358 | /* |
4359 | * Take the vfs rename lock to avoid cross-directory screw cases. |
4360 | * Nothing is locked currently, so taking this lock is safe. |
4361 | */ |
4362 | error = VFS_RENAMELOCK_ENTER(mp); |
4363 | if (error) |
4364 | goto abort1; |
4365 | |
4366 | /* |
4367 | * Now fdvp, fvp, tdvp, and (if nonnull) tvp are referenced, |
4368 | * and nothing is locked except for the vfs rename lock. |
4369 | * |
4370 | * The next step is a little rain dance to conform to the |
4371 | * insane lock protocol, even though it does nothing to ward |
4372 | * off race conditions. |
4373 | * |
4374 | * We need tdvp and tvp to be locked. However, because we have |
4375 | * unlocked tdvp in order to hold no locks while we take the |
4376 | * vfs rename lock, tvp may be wrong here, and we can't safely |
4377 | * lock it even if the sensible file systems will just unlock |
4378 | * it straight away. Consequently, we must lock tdvp and then |
4379 | * relookup tvp to get it locked. |
4380 | * |
4381 | * Finally, because the VOP_RENAME protocol is brain-damaged |
4382 | * and various file systems insanely depend on the semantics of |
4383 | * this brain damage, the lookup of to must be the last lookup |
4384 | * before VOP_RENAME. |
4385 | */ |
4386 | vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY); |
4387 | error = relookup(tdvp, &tnd.ni_vp, &tnd.ni_cnd, 0); |
4388 | if (error) |
4389 | goto abort2; |
4390 | |
4391 | /* |
4392 | * Drop the old tvp and pick up the new one -- which might be |
4393 | * the same, but that doesn't matter to us. After this, tdvp |
4394 | * and tvp should both be locked. |
4395 | */ |
4396 | if (tvp != NULL) |
4397 | vrele(tvp); |
4398 | tvp = tnd.ni_vp; |
4399 | KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); |
4400 | KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); |
4401 | |
4402 | /* |
4403 | * The old do_sys_rename had various consistency checks here |
4404 | * involving fvp and tvp. fvp is bogus already here, and tvp |
4405 | * will become bogus soon in any sensible file system, so the |
4406 | * only purpose in putting these checks here is to give lip |
4407 | * service to these screw cases and to acknowledge that they |
4408 | * exist, not actually to handle them, but here you go |
4409 | * anyway... |
4410 | */ |
4411 | |
4412 | /* |
4413 | * Acknowledge that directories and non-directories aren't |
4414 | * suposed to mix. |
4415 | */ |
4416 | if (tvp != NULL) { |
4417 | if ((fvp->v_type == VDIR) && (tvp->v_type != VDIR)) { |
4418 | error = ENOTDIR; |
4419 | goto abort3; |
4420 | } else if ((fvp->v_type != VDIR) && (tvp->v_type == VDIR)) { |
4421 | error = EISDIR; |
4422 | goto abort3; |
4423 | } |
4424 | } |
4425 | |
4426 | /* |
4427 | * Acknowledge some random screw case, among the dozens that |
4428 | * might arise. |
4429 | */ |
4430 | if (fvp == tdvp) { |
4431 | error = EINVAL; |
4432 | goto abort3; |
4433 | } |
4434 | |
4435 | /* |
4436 | * Acknowledge that POSIX has a wacky screw case. |
4437 | * |
4438 | * XXX Eventually the retain flag needs to be passed on to |
4439 | * VOP_RENAME. |
4440 | */ |
4441 | if (fvp == tvp) { |
4442 | if (retain) { |
4443 | error = 0; |
4444 | goto abort3; |
4445 | } else if ((fdvp == tdvp) && |
4446 | (fnd.ni_cnd.cn_namelen == tnd.ni_cnd.cn_namelen) && |
4447 | (0 == memcmp(fnd.ni_cnd.cn_nameptr, tnd.ni_cnd.cn_nameptr, |
4448 | fnd.ni_cnd.cn_namelen))) { |
4449 | error = 0; |
4450 | goto abort3; |
4451 | } |
4452 | } |
4453 | |
4454 | /* |
4455 | * Make sure veriexec can screw us up. (But a race can screw |
4456 | * up veriexec, of course -- remember, fvp and (soon) tvp are |
4457 | * bogus.) |
4458 | */ |
4459 | #if NVERIEXEC > 0 |
4460 | { |
4461 | char *f1, *f2; |
4462 | size_t f1_len; |
4463 | size_t f2_len; |
4464 | |
4465 | f1_len = fnd.ni_cnd.cn_namelen + 1; |
4466 | f1 = kmem_alloc(f1_len, KM_SLEEP); |
4467 | strlcpy(f1, fnd.ni_cnd.cn_nameptr, f1_len); |
4468 | |
4469 | f2_len = tnd.ni_cnd.cn_namelen + 1; |
4470 | f2 = kmem_alloc(f2_len, KM_SLEEP); |
4471 | strlcpy(f2, tnd.ni_cnd.cn_nameptr, f2_len); |
4472 | |
4473 | error = veriexec_renamechk(curlwp, fvp, f1, tvp, f2); |
4474 | |
4475 | kmem_free(f1, f1_len); |
4476 | kmem_free(f2, f2_len); |
4477 | |
4478 | if (error) |
4479 | goto abort3; |
4480 | } |
4481 | #endif /* NVERIEXEC > 0 */ |
4482 | |
4483 | /* |
4484 | * All ready. Incant the rename vop. |
4485 | */ |
4486 | /* XXX KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ |
4487 | /* XXX KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ |
4488 | KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); |
4489 | KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); |
4490 | error = VOP_RENAME(fdvp, fvp, &fnd.ni_cnd, tdvp, tvp, &tnd.ni_cnd); |
4491 | |
4492 | /* |
4493 | * VOP_RENAME releases fdvp, fvp, tdvp, and tvp, and unlocks |
4494 | * tdvp and tvp. But we can't assert any of that. |
4495 | */ |
4496 | /* XXX KASSERT(VOP_ISLOCKED(fdvp) != LK_EXCLUSIVE); */ |
4497 | /* XXX KASSERT(VOP_ISLOCKED(fvp) != LK_EXCLUSIVE); */ |
4498 | /* XXX KASSERT(VOP_ISLOCKED(tdvp) != LK_EXCLUSIVE); */ |
4499 | /* XXX KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) != LK_EXCLUSIVE)); */ |
4500 | |
4501 | /* |
4502 | * So all we have left to do is to drop the rename lock and |
4503 | * destroy the pathbufs. |
4504 | */ |
4505 | VFS_RENAMELOCK_EXIT(mp); |
4506 | fstrans_done(mp); |
4507 | goto out2; |
4508 | |
4509 | abort3: if ((tvp != NULL) && (tvp != tdvp)) |
4510 | VOP_UNLOCK(tvp); |
4511 | abort2: VOP_UNLOCK(tdvp); |
4512 | VFS_RENAMELOCK_EXIT(mp); |
4513 | abort1: VOP_ABORTOP(tdvp, &tnd.ni_cnd); |
4514 | vrele(tdvp); |
4515 | if (tvp != NULL) |
4516 | vrele(tvp); |
4517 | abort0: VOP_ABORTOP(fdvp, &fnd.ni_cnd); |
4518 | vrele(fdvp); |
4519 | vrele(fvp); |
4520 | fstrans_done(mp); |
4521 | out2: pathbuf_destroy(tpb); |
4522 | out1: pathbuf_destroy(fpb); |
4523 | out0: return error; |
4524 | } |
4525 | |
4526 | /* |
4527 | * Make a directory file. |
4528 | */ |
4529 | /* ARGSUSED */ |
4530 | int |
4531 | sys_mkdir(struct lwp *l, const struct sys_mkdir_args *uap, register_t *retval) |
4532 | { |
4533 | /* { |
4534 | syscallarg(const char *) path; |
4535 | syscallarg(int) mode; |
4536 | } */ |
4537 | |
4538 | return do_sys_mkdirat(l, AT_FDCWD, SCARG(uap, path), |
4539 | SCARG(uap, mode), UIO_USERSPACE); |
4540 | } |
4541 | |
4542 | int |
4543 | sys_mkdirat(struct lwp *l, const struct sys_mkdirat_args *uap, |
4544 | register_t *retval) |
4545 | { |
4546 | /* { |
4547 | syscallarg(int) fd; |
4548 | syscallarg(const char *) path; |
4549 | syscallarg(int) mode; |
4550 | } */ |
4551 | |
4552 | return do_sys_mkdirat(l, SCARG(uap, fd), SCARG(uap, path), |
4553 | SCARG(uap, mode), UIO_USERSPACE); |
4554 | } |
4555 | |
4556 | |
4557 | int |
4558 | do_sys_mkdir(const char *path, mode_t mode, enum uio_seg seg) |
4559 | { |
4560 | return do_sys_mkdirat(NULL, AT_FDCWD, path, mode, seg); |
4561 | } |
4562 | |
4563 | static int |
4564 | do_sys_mkdirat(struct lwp *l, int fdat, const char *path, mode_t mode, |
4565 | enum uio_seg seg) |
4566 | { |
4567 | struct proc *p = curlwp->l_proc; |
4568 | struct vnode *vp; |
4569 | struct vattr vattr; |
4570 | int error; |
4571 | struct pathbuf *pb; |
4572 | struct nameidata nd; |
4573 | |
4574 | KASSERT(l != NULL || fdat == AT_FDCWD); |
4575 | |
4576 | /* XXX bollocks, should pass in a pathbuf */ |
4577 | error = pathbuf_maybe_copyin(path, seg, &pb); |
4578 | if (error) { |
4579 | return error; |
4580 | } |
4581 | |
4582 | NDINIT(&nd, CREATE, LOCKPARENT | CREATEDIR | TRYEMULROOT, pb); |
4583 | |
4584 | if ((error = fd_nameiat(l, fdat, &nd)) != 0) { |
4585 | pathbuf_destroy(pb); |
4586 | return (error); |
4587 | } |
4588 | vp = nd.ni_vp; |
4589 | if (vp != NULL) { |
4590 | VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); |
4591 | if (nd.ni_dvp == vp) |
4592 | vrele(nd.ni_dvp); |
4593 | else |
4594 | vput(nd.ni_dvp); |
4595 | vrele(vp); |
4596 | pathbuf_destroy(pb); |
4597 | return (EEXIST); |
4598 | } |
4599 | vattr_null(&vattr); |
4600 | vattr.va_type = VDIR; |
4601 | /* We will read cwdi->cwdi_cmask unlocked. */ |
4602 | vattr.va_mode = (mode & ACCESSPERMS) &~ p->p_cwdi->cwdi_cmask; |
4603 | error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); |
4604 | if (!error) |
4605 | vrele(nd.ni_vp); |
4606 | vput(nd.ni_dvp); |
4607 | pathbuf_destroy(pb); |
4608 | return (error); |
4609 | } |
4610 | |
4611 | /* |
4612 | * Remove a directory file. |
4613 | */ |
4614 | /* ARGSUSED */ |
4615 | int |
4616 | sys_rmdir(struct lwp *l, const struct sys_rmdir_args *uap, register_t *retval) |
4617 | { |
4618 | return do_sys_unlinkat(l, AT_FDCWD, SCARG(uap, path), |
4619 | AT_REMOVEDIR, UIO_USERSPACE); |
4620 | } |
4621 | |
4622 | /* |
4623 | * Read a block of directory entries in a file system independent format. |
4624 | */ |
4625 | int |
4626 | sys___getdents30(struct lwp *l, const struct sys___getdents30_args *uap, register_t *retval) |
4627 | { |
4628 | /* { |
4629 | syscallarg(int) fd; |
4630 | syscallarg(char *) buf; |
4631 | syscallarg(size_t) count; |
4632 | } */ |
4633 | file_t *fp; |
4634 | int error, done; |
4635 | |
4636 | /* fd_getvnode() will use the descriptor for us */ |
4637 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
4638 | return (error); |
4639 | if ((fp->f_flag & FREAD) == 0) { |
4640 | error = EBADF; |
4641 | goto out; |
4642 | } |
4643 | error = vn_readdir(fp, SCARG(uap, buf), UIO_USERSPACE, |
4644 | SCARG(uap, count), &done, l, 0, 0); |
4645 | ktrgenio(SCARG(uap, fd), UIO_READ, SCARG(uap, buf), done, error); |
4646 | *retval = done; |
4647 | out: |
4648 | fd_putfile(SCARG(uap, fd)); |
4649 | return (error); |
4650 | } |
4651 | |
4652 | /* |
4653 | * Set the mode mask for creation of filesystem nodes. |
4654 | */ |
4655 | int |
4656 | sys_umask(struct lwp *l, const struct sys_umask_args *uap, register_t *retval) |
4657 | { |
4658 | /* { |
4659 | syscallarg(mode_t) newmask; |
4660 | } */ |
4661 | struct proc *p = l->l_proc; |
4662 | struct cwdinfo *cwdi; |
4663 | |
4664 | /* |
4665 | * cwdi->cwdi_cmask will be read unlocked elsewhere. What's |
4666 | * important is that we serialize changes to the mask. The |
4667 | * rw_exit() will issue a write memory barrier on our behalf, |
4668 | * and force the changes out to other CPUs (as it must use an |
4669 | * atomic operation, draining the local CPU's store buffers). |
4670 | */ |
4671 | cwdi = p->p_cwdi; |
4672 | rw_enter(&cwdi->cwdi_lock, RW_WRITER); |
4673 | *retval = cwdi->cwdi_cmask; |
4674 | cwdi->cwdi_cmask = SCARG(uap, newmask) & ALLPERMS; |
4675 | rw_exit(&cwdi->cwdi_lock); |
4676 | |
4677 | return (0); |
4678 | } |
4679 | |
4680 | int |
4681 | dorevoke(struct vnode *vp, kauth_cred_t cred) |
4682 | { |
4683 | struct vattr vattr; |
4684 | int error, fs_decision; |
4685 | |
4686 | vn_lock(vp, LK_SHARED | LK_RETRY); |
4687 | error = VOP_GETATTR(vp, &vattr, cred); |
4688 | VOP_UNLOCK(vp); |
4689 | if (error != 0) |
4690 | return error; |
4691 | fs_decision = (kauth_cred_geteuid(cred) == vattr.va_uid) ? 0 : EPERM; |
4692 | error = kauth_authorize_vnode(cred, KAUTH_VNODE_REVOKE, vp, NULL, |
4693 | fs_decision); |
4694 | if (!error) |
4695 | VOP_REVOKE(vp, REVOKEALL); |
4696 | return (error); |
4697 | } |
4698 | |
4699 | /* |
4700 | * Void all references to file by ripping underlying filesystem |
4701 | * away from vnode. |
4702 | */ |
4703 | /* ARGSUSED */ |
4704 | int |
4705 | sys_revoke(struct lwp *l, const struct sys_revoke_args *uap, register_t *retval) |
4706 | { |
4707 | /* { |
4708 | syscallarg(const char *) path; |
4709 | } */ |
4710 | struct vnode *vp; |
4711 | int error; |
4712 | |
4713 | error = namei_simple_user(SCARG(uap, path), |
4714 | NSM_FOLLOW_TRYEMULROOT, &vp); |
4715 | if (error != 0) |
4716 | return (error); |
4717 | error = dorevoke(vp, l->l_cred); |
4718 | vrele(vp); |
4719 | return (error); |
4720 | } |
4721 | |
4722 | /* |
4723 | * Allocate backing store for a file, filling a hole without having to |
4724 | * explicitly write anything out. |
4725 | */ |
4726 | /* ARGSUSED */ |
4727 | int |
4728 | sys_posix_fallocate(struct lwp *l, const struct sys_posix_fallocate_args *uap, |
4729 | register_t *retval) |
4730 | { |
4731 | /* { |
4732 | syscallarg(int) fd; |
4733 | syscallarg(off_t) pos; |
4734 | syscallarg(off_t) len; |
4735 | } */ |
4736 | int fd; |
4737 | off_t pos, len; |
4738 | struct file *fp; |
4739 | struct vnode *vp; |
4740 | int error; |
4741 | |
4742 | fd = SCARG(uap, fd); |
4743 | pos = SCARG(uap, pos); |
4744 | len = SCARG(uap, len); |
4745 | |
4746 | if (pos < 0 || len < 0 || len > OFF_T_MAX - pos) { |
4747 | *retval = EINVAL; |
4748 | return 0; |
4749 | } |
4750 | |
4751 | error = fd_getvnode(fd, &fp); |
4752 | if (error) { |
4753 | *retval = error; |
4754 | return 0; |
4755 | } |
4756 | if ((fp->f_flag & FWRITE) == 0) { |
4757 | error = EBADF; |
4758 | goto fail; |
4759 | } |
4760 | vp = fp->f_vnode; |
4761 | |
4762 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4763 | if (vp->v_type == VDIR) { |
4764 | error = EISDIR; |
4765 | } else { |
4766 | error = VOP_FALLOCATE(vp, pos, len); |
4767 | } |
4768 | VOP_UNLOCK(vp); |
4769 | |
4770 | fail: |
4771 | fd_putfile(fd); |
4772 | *retval = error; |
4773 | return 0; |
4774 | } |
4775 | |
4776 | /* |
4777 | * Deallocate backing store for a file, creating a hole. Also used for |
4778 | * invoking TRIM on disks. |
4779 | */ |
4780 | /* ARGSUSED */ |
4781 | int |
4782 | sys_fdiscard(struct lwp *l, const struct sys_fdiscard_args *uap, |
4783 | register_t *retval) |
4784 | { |
4785 | /* { |
4786 | syscallarg(int) fd; |
4787 | syscallarg(off_t) pos; |
4788 | syscallarg(off_t) len; |
4789 | } */ |
4790 | int fd; |
4791 | off_t pos, len; |
4792 | struct file *fp; |
4793 | struct vnode *vp; |
4794 | int error; |
4795 | |
4796 | fd = SCARG(uap, fd); |
4797 | pos = SCARG(uap, pos); |
4798 | len = SCARG(uap, len); |
4799 | |
4800 | if (pos < 0 || len < 0 || len > OFF_T_MAX - pos) { |
4801 | return EINVAL; |
4802 | } |
4803 | |
4804 | error = fd_getvnode(fd, &fp); |
4805 | if (error) { |
4806 | return error; |
4807 | } |
4808 | if ((fp->f_flag & FWRITE) == 0) { |
4809 | error = EBADF; |
4810 | goto fail; |
4811 | } |
4812 | vp = fp->f_vnode; |
4813 | |
4814 | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); |
4815 | if (vp->v_type == VDIR) { |
4816 | error = EISDIR; |
4817 | } else { |
4818 | error = VOP_FDISCARD(vp, pos, len); |
4819 | } |
4820 | VOP_UNLOCK(vp); |
4821 | |
4822 | fail: |
4823 | fd_putfile(fd); |
4824 | return error; |
4825 | } |
4826 | |