2 * Copyright (c) 1995-2010 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright (c) 1989, 1993
30 * The Regents of the University of California. All rights reserved.
31 * (c) UNIX System Laboratories, Inc.
32 * All or some portions of this file are derived from material licensed
33 * to the University of California by American Telephone and Telegraph
34 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
35 * the permission of UNIX System Laboratories, Inc.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed by the University of
48 * California, Berkeley and its contributors.
49 * 4. 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.
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
65 * @(#)vfs_syscalls.c 8.41 (Berkeley) 6/15/95
68 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
69 * support for mandatory and extensible security protections. This notice
70 * is included in support of clause 2.2 (b) of the Apple Public License,
74 #include <sys/param.h>
75 #include <sys/systm.h>
76 #include <sys/namei.h>
77 #include <sys/filedesc.h>
78 #include <sys/kernel.h>
79 #include <sys/file_internal.h>
81 #include <sys/vnode_internal.h>
82 #include <sys/mount_internal.h>
83 #include <sys/proc_internal.h>
84 #include <sys/kauth.h>
85 #include <sys/uio_internal.h>
86 #include <sys/malloc.h>
88 #include <sys/dirent.h>
90 #include <sys/sysctl.h>
92 #include <sys/quota.h>
93 #include <sys/kdebug.h>
94 #include <sys/fsevents.h>
95 #include <sys/imgsrc.h>
96 #include <sys/sysproto.h>
97 #include <sys/xattr.h>
98 #include <sys/fcntl.h>
99 #include <sys/fsctl.h>
100 #include <sys/ubc_internal.h>
101 #include <sys/disk.h>
102 #include <machine/cons.h>
103 #include <machine/limits.h>
104 #include <miscfs/specfs/specdev.h>
106 #include <security/audit/audit.h>
107 #include <bsm/audit_kevents.h>
109 #include <mach/mach_types.h>
110 #include <kern/kern_types.h>
111 #include <kern/kalloc.h>
112 #include <kern/task.h>
114 #include <vm/vm_pageout.h>
116 #include <libkern/OSAtomic.h>
117 #include <pexpert/pexpert.h>
120 #include <security/mac.h>
121 #include <security/mac_framework.h>
125 #define GET_PATH(x) \
126 (x) = get_pathbuff();
127 #define RELEASE_PATH(x) \
130 #define GET_PATH(x) \
131 MALLOC_ZONE((x), char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
132 #define RELEASE_PATH(x) \
133 FREE_ZONE((x), MAXPATHLEN, M_NAMEI);
134 #endif /* CONFIG_FSE */
136 /* struct for checkdirs iteration */
141 /* callback for checkdirs iteration */
142 static int checkdirs_callback(proc_t p
, void * arg
);
144 static int change_dir(struct nameidata
*ndp
, vfs_context_t ctx
);
145 static int checkdirs(vnode_t olddp
, vfs_context_t ctx
);
146 void enablequotas(struct mount
*mp
, vfs_context_t ctx
);
147 static int getfsstat_callback(mount_t mp
, void * arg
);
148 static int getutimes(user_addr_t usrtvp
, struct timespec
*tsp
);
149 static int setutimes(vfs_context_t ctx
, vnode_t vp
, const struct timespec
*ts
, int nullflag
);
150 static int sync_callback(mount_t
, void *);
151 static int munge_statfs(struct mount
*mp
, struct vfsstatfs
*sfsp
,
152 user_addr_t bufp
, int *sizep
, boolean_t is_64_bit
,
153 boolean_t partial_copy
);
154 static int statfs64_common(struct mount
*mp
, struct vfsstatfs
*sfsp
,
156 static int fsync_common(proc_t p
, struct fsync_args
*uap
, int flags
);
157 static int mount_common(char *fstypename
, vnode_t pvp
, vnode_t vp
,
158 struct componentname
*cnp
, user_addr_t fsmountargs
,
159 int flags
, uint32_t internal_flags
, char *labelstr
, boolean_t kernelmount
,
161 void vfs_notify_mount(vnode_t pdvp
);
163 int prepare_coveredvp(vnode_t vp
, vfs_context_t ctx
, struct componentname
*cnp
, const char *fsname
, boolean_t skip_auth
);
165 #ifdef CONFIG_IMGSRC_ACCESS
166 static int authorize_devpath_and_update_mntfromname(mount_t mp
, user_addr_t devpath
, vnode_t
*devvpp
, vfs_context_t ctx
);
167 static int place_mount_and_checkdirs(mount_t mp
, vnode_t vp
, vfs_context_t ctx
);
168 static void undo_place_on_covered_vp(mount_t mp
, vnode_t vp
);
169 static int mount_begin_update(mount_t mp
, vfs_context_t ctx
, int flags
);
170 static void mount_end_update(mount_t mp
);
171 static int relocate_imageboot_source(vnode_t pvp
, vnode_t vp
, struct componentname
*cnp
, const char *fsname
, vfs_context_t ctx
, boolean_t is64bit
, user_addr_t fsmountargs
, boolean_t by_index
);
172 #endif /* CONFIG_IMGSRC_ACCESS */
174 int (*union_dircheckp
)(struct vnode
**, struct fileproc
*, vfs_context_t
);
177 int sync_internal(void);
180 int open1(vfs_context_t
, struct nameidata
*, int, struct vnode_attr
*, int32_t *);
183 int unlink1(vfs_context_t
, struct nameidata
*, int);
186 #ifdef __APPLE_API_OBSOLETE
188 int fd
; /* file descriptor of the target file */
189 struct vstat
*vsb
; /* vstat structure for returned info */
192 const char *path
; /* pathname of the target file */
193 struct vstat
*vsb
; /* vstat structure for returned info */
195 struct mkcomplex_args
{
196 const char *path
; /* pathname of the file to be created */
197 mode_t mode
; /* access mode for the newly created file */
198 u_int32_t type
; /* format of the complex file */
201 const char *path
; /* pathname of the target file */
202 struct vstat
*vsb
; /* vstat structure for returned info */
205 int fstatv(proc_t p
, struct fstatv_args
*uap
, int32_t *retval
);
206 int lstatv(proc_t p
, struct lstatv_args
*uap
, int32_t *retval
);
207 int mkcomplex(proc_t p
, struct mkcomplex_args
*uap
, int32_t *retval
);
208 int statv(proc_t p
, struct statv_args
*uap
, int32_t *retval
);
210 #endif /* __APPLE_API_OBSOLETE */
213 * incremented each time a mount or unmount operation occurs
214 * used to invalidate the cached value of the rootvp in the
215 * mount structure utilized by cache_lookup_path
217 uint32_t mount_generation
= 0;
219 /* counts number of mount and unmount operations */
220 unsigned int vfs_nummntops
=0;
222 extern struct fileops vnops
;
223 extern errno_t
rmdir_remove_orphaned_appleDouble(vnode_t
, vfs_context_t
, int *);
227 * Virtual File System System Calls
232 * Private in-kernel mounting spi (NFS only, not exported)
236 vfs_iskernelmount(mount_t mp
)
238 return ((mp
->mnt_kern_flag
& MNTK_KERNEL_MOUNT
) ? TRUE
: FALSE
);
243 kernel_mount(char *fstype
, vnode_t pvp
, vnode_t vp
, const char *path
,
244 void *data
, __unused
size_t datalen
, int syscall_flags
, __unused
uint32_t kern_flags
, vfs_context_t ctx
)
250 NDINIT(&nd
, LOOKUP
, OP_MOUNT
, FOLLOW
| AUDITVNPATH1
| WANTPARENT
,
251 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
254 * Get the vnode to be covered if it's not supplied
264 char *pnbuf
= CAST_DOWN(char *, path
);
266 nd
.ni_cnd
.cn_pnbuf
= pnbuf
;
267 nd
.ni_cnd
.cn_pnlen
= strlen(pnbuf
) + 1;
271 error
= mount_common(fstype
, pvp
, vp
, &nd
.ni_cnd
, CAST_USER_ADDR_T(data
),
272 syscall_flags
, kern_flags
, NULL
, TRUE
, ctx
);
282 #endif /* NFSCLIENT */
285 * Mount a file system.
289 mount(proc_t p
, struct mount_args
*uap
, __unused
int32_t *retval
)
291 struct __mac_mount_args muap
;
293 muap
.type
= uap
->type
;
294 muap
.path
= uap
->path
;
295 muap
.flags
= uap
->flags
;
296 muap
.data
= uap
->data
;
297 muap
.mac_p
= USER_ADDR_NULL
;
298 return (__mac_mount(p
, &muap
, retval
));
302 vfs_notify_mount(vnode_t pdvp
)
304 vfs_event_signal(NULL
, VQ_MOUNT
, (intptr_t)NULL
);
305 lock_vnode_and_post(pdvp
, NOTE_WRITE
);
310 * Mount a file system taking into account MAC label behavior.
311 * See mount(2) man page for more information
313 * Parameters: p Process requesting the mount
314 * uap User argument descriptor (see below)
317 * Indirect: uap->type Filesystem type
318 * uap->path Path to mount
319 * uap->data Mount arguments
320 * uap->mac_p MAC info
321 * uap->flags Mount flags
327 boolean_t root_fs_upgrade_try
= FALSE
;
330 __mac_mount(struct proc
*p
, register struct __mac_mount_args
*uap
, __unused
int32_t *retval
)
333 vfs_context_t ctx
= vfs_context_current();
334 char fstypename
[MFSNAMELEN
];
337 char *labelstr
= NULL
;
338 int flags
= uap
->flags
;
340 boolean_t is_64bit
= IS_64BIT_PROCESS(p
);
343 * Get the fs type name from user space
345 error
= copyinstr(uap
->type
, fstypename
, MFSNAMELEN
, &dummy
);
350 * Get the vnode to be covered
352 NDINIT(&nd
, LOOKUP
, OP_MOUNT
, NOTRIGGER
| FOLLOW
| AUDITVNPATH1
| WANTPARENT
,
353 UIO_USERSPACE
, uap
->path
, ctx
);
360 #ifdef CONFIG_IMGSRC_ACCESS
361 /* Mounting image source cannot be batched with other operations */
362 if (flags
== MNT_IMGSRC_BY_INDEX
) {
363 error
= relocate_imageboot_source(pvp
, vp
, &nd
.ni_cnd
, fstypename
,
364 ctx
, is_64bit
, uap
->data
, (flags
== MNT_IMGSRC_BY_INDEX
));
367 #endif /* CONFIG_IMGSRC_ACCESS */
371 * Get the label string (if any) from user space
373 if (uap
->mac_p
!= USER_ADDR_NULL
) {
378 struct user64_mac mac64
;
379 error
= copyin(uap
->mac_p
, &mac64
, sizeof(mac64
));
380 mac
.m_buflen
= mac64
.m_buflen
;
381 mac
.m_string
= mac64
.m_string
;
383 struct user32_mac mac32
;
384 error
= copyin(uap
->mac_p
, &mac32
, sizeof(mac32
));
385 mac
.m_buflen
= mac32
.m_buflen
;
386 mac
.m_string
= mac32
.m_string
;
390 if ((mac
.m_buflen
> MAC_MAX_LABEL_BUF_LEN
) ||
391 (mac
.m_buflen
< 2)) {
395 MALLOC(labelstr
, char *, mac
.m_buflen
, M_MACTEMP
, M_WAITOK
);
396 error
= copyinstr(mac
.m_string
, labelstr
, mac
.m_buflen
, &ulen
);
400 AUDIT_ARG(mac_string
, labelstr
);
402 #endif /* CONFIG_MACF */
404 AUDIT_ARG(fflags
, flags
);
406 if ((vp
->v_flag
& VROOT
) &&
407 (vp
->v_mount
->mnt_flag
& MNT_ROOTFS
)) {
410 * See 7392553 for more details on why this check exists.
411 * Suffice to say: If this check is ON and something tries
412 * to mount the rootFS RW, we'll turn off the codesign
413 * bitmap optimization.
415 #if CHECK_CS_VALIDATION_BITMAP
416 if ( !(flags
& MNT_RDONLY
) ) {
417 root_fs_upgrade_try
= TRUE
;
422 error
= mount_common(fstypename
, pvp
, vp
, &nd
.ni_cnd
, uap
->data
, flags
, 0,
423 labelstr
, FALSE
, ctx
);
427 FREE(labelstr
, M_MACTEMP
);
428 #endif /* CONFIG_MACF */
438 * common mount implementation (final stage of mounting)
441 * fstypename file system type (ie it's vfs name)
442 * pvp parent of covered vnode
444 * cnp component name (ie path) of covered vnode
445 * flags generic mount flags
446 * fsmountargs file system specific data
447 * labelstr optional MAC label
448 * kernelmount TRUE for mounts initiated from inside the kernel
449 * ctx caller's context
452 mount_common(char *fstypename
, vnode_t pvp
, vnode_t vp
,
453 struct componentname
*cnp
, user_addr_t fsmountargs
, int flags
, uint32_t internal_flags
,
454 char *labelstr
, boolean_t kernelmount
, vfs_context_t ctx
)
456 struct vnode
*devvp
= NULLVP
;
457 struct vnode
*device_vnode
= NULLVP
;
462 struct vfstable
*vfsp
= (struct vfstable
*)0;
463 struct proc
*p
= vfs_context_proc(ctx
);
465 user_addr_t devpath
= USER_ADDR_NULL
;
468 boolean_t vfsp_ref
= FALSE
;
469 boolean_t is_rwlock_locked
= FALSE
;
470 boolean_t did_rele
= FALSE
;
471 boolean_t have_usecount
= FALSE
;
474 * Process an update for an existing mount
476 if (flags
& MNT_UPDATE
) {
477 if ((vp
->v_flag
& VROOT
) == 0) {
483 /* unmount in progress return error */
485 if (mp
->mnt_lflag
& MNT_LUNMOUNT
) {
491 lck_rw_lock_exclusive(&mp
->mnt_rwlock
);
492 is_rwlock_locked
= TRUE
;
494 * We only allow the filesystem to be reloaded if it
495 * is currently mounted read-only.
497 if ((flags
& MNT_RELOAD
) &&
498 ((mp
->mnt_flag
& MNT_RDONLY
) == 0)) {
503 #ifdef CONFIG_IMGSRC_ACCESS
504 /* Can't downgrade the backer of the root FS */
505 if ((mp
->mnt_kern_flag
& MNTK_BACKS_ROOT
) &&
506 (!vfs_isrdonly(mp
)) && (flags
& MNT_RDONLY
)) {
510 #endif /* CONFIG_IMGSRC_ACCESS */
513 * Only root, or the user that did the original mount is
514 * permitted to update it.
516 if (mp
->mnt_vfsstat
.f_owner
!= kauth_cred_getuid(vfs_context_ucred(ctx
)) &&
517 (error
= suser(vfs_context_ucred(ctx
), &p
->p_acflag
))) {
521 error
= mac_mount_check_remount(ctx
, mp
);
527 * For non-root users, silently enforce MNT_NOSUID and MNT_NODEV,
528 * and MNT_NOEXEC if mount point is already MNT_NOEXEC.
530 if ((!kernelmount
) && suser(vfs_context_ucred(ctx
), NULL
)) {
531 flags
|= MNT_NOSUID
| MNT_NODEV
;
532 if (mp
->mnt_flag
& MNT_NOEXEC
)
537 mp
->mnt_flag
|= flags
& (MNT_RELOAD
| MNT_FORCE
| MNT_UPDATE
);
539 vfsp
= mp
->mnt_vtable
;
543 * For non-root users, silently enforce MNT_NOSUID and MNT_NODEV, and
544 * MNT_NOEXEC if mount point is already MNT_NOEXEC.
546 if ((!kernelmount
) && suser(vfs_context_ucred(ctx
), NULL
)) {
547 flags
|= MNT_NOSUID
| MNT_NODEV
;
548 if (vp
->v_mount
->mnt_flag
& MNT_NOEXEC
)
552 /* XXXAUDIT: Should we capture the type on the error path as well? */
553 AUDIT_ARG(text
, fstypename
);
555 for (vfsp
= vfsconf
; vfsp
; vfsp
= vfsp
->vfc_next
)
556 if (!strncmp(vfsp
->vfc_name
, fstypename
, MFSNAMELEN
)) {
557 vfsp
->vfc_refcount
++;
568 * VFC_VFSLOCALARGS is not currently supported for kernel mounts
570 if (kernelmount
&& (vfsp
->vfc_vfsflags
& VFC_VFSLOCALARGS
)) {
571 error
= EINVAL
; /* unsupported request */
575 error
= prepare_coveredvp(vp
, ctx
, cnp
, fstypename
, ((internal_flags
& KERNEL_MOUNT_NOAUTH
) != 0));
581 * Allocate and initialize the filesystem (mount_t)
583 MALLOC_ZONE(mp
, struct mount
*, (u_int32_t
)sizeof(struct mount
),
585 bzero((char *)mp
, (u_int32_t
)sizeof(struct mount
));
588 /* Initialize the default IO constraints */
589 mp
->mnt_maxreadcnt
= mp
->mnt_maxwritecnt
= MAXPHYS
;
590 mp
->mnt_segreadcnt
= mp
->mnt_segwritecnt
= 32;
591 mp
->mnt_maxsegreadsize
= mp
->mnt_maxreadcnt
;
592 mp
->mnt_maxsegwritesize
= mp
->mnt_maxwritecnt
;
593 mp
->mnt_devblocksize
= DEV_BSIZE
;
594 mp
->mnt_alignmentmask
= PAGE_MASK
;
595 mp
->mnt_ioqueue_depth
= MNT_DEFAULT_IOQUEUE_DEPTH
;
598 mp
->mnt_realrootvp
= NULLVP
;
599 mp
->mnt_authcache_ttl
= CACHED_LOOKUP_RIGHT_TTL
;
601 TAILQ_INIT(&mp
->mnt_vnodelist
);
602 TAILQ_INIT(&mp
->mnt_workerqueue
);
603 TAILQ_INIT(&mp
->mnt_newvnodes
);
605 lck_rw_lock_exclusive(&mp
->mnt_rwlock
);
606 is_rwlock_locked
= TRUE
;
607 mp
->mnt_op
= vfsp
->vfc_vfsops
;
608 mp
->mnt_vtable
= vfsp
;
609 //mp->mnt_stat.f_type = vfsp->vfc_typenum;
610 mp
->mnt_flag
|= vfsp
->vfc_flags
& MNT_VISFLAGMASK
;
611 strncpy(mp
->mnt_vfsstat
.f_fstypename
, vfsp
->vfc_name
, MFSTYPENAMELEN
);
612 strncpy(mp
->mnt_vfsstat
.f_mntonname
, cnp
->cn_pnbuf
, MAXPATHLEN
);
613 mp
->mnt_vnodecovered
= vp
;
614 mp
->mnt_vfsstat
.f_owner
= kauth_cred_getuid(vfs_context_ucred(ctx
));
615 mp
->mnt_throttle_mask
= LOWPRI_MAX_NUM_DEV
- 1;
616 mp
->mnt_devbsdunit
= 0;
618 /* XXX 3762912 hack to support HFS filesystem 'owner' - filesystem may update later */
619 vfs_setowner(mp
, KAUTH_UID_NONE
, KAUTH_GID_NONE
);
623 mp
->mnt_kern_flag
|= MNTK_KERNEL_MOUNT
;
624 if ((internal_flags
& KERNEL_MOUNT_PERMIT_UNMOUNT
) != 0)
625 mp
->mnt_kern_flag
|= MNTK_PERMIT_UNMOUNT
;
626 #endif /* NFSCLIENT */
630 * Set the mount level flags.
632 if (flags
& MNT_RDONLY
)
633 mp
->mnt_flag
|= MNT_RDONLY
;
634 else if (mp
->mnt_flag
& MNT_RDONLY
) {
635 // disallow read/write upgrades of file systems that
636 // had the TYPENAME_OVERRIDE feature set.
637 if (mp
->mnt_kern_flag
& MNTK_TYPENAME_OVERRIDE
) {
641 mp
->mnt_kern_flag
|= MNTK_WANTRDWR
;
643 mp
->mnt_flag
&= ~(MNT_NOSUID
| MNT_NOEXEC
| MNT_NODEV
|
644 MNT_SYNCHRONOUS
| MNT_UNION
| MNT_ASYNC
|
645 MNT_UNKNOWNPERMISSIONS
| MNT_DONTBROWSE
|
646 MNT_AUTOMOUNTED
| MNT_DEFWRITE
| MNT_NOATIME
|
647 MNT_QUARANTINE
| MNT_CPROTECT
);
648 mp
->mnt_flag
|= flags
& (MNT_NOSUID
| MNT_NOEXEC
| MNT_NODEV
|
649 MNT_SYNCHRONOUS
| MNT_UNION
| MNT_ASYNC
|
650 MNT_UNKNOWNPERMISSIONS
| MNT_DONTBROWSE
|
651 MNT_AUTOMOUNTED
| MNT_DEFWRITE
| MNT_NOATIME
|
652 MNT_QUARANTINE
| MNT_CPROTECT
);
655 if (flags
& MNT_MULTILABEL
) {
656 if (vfsp
->vfc_vfsflags
& VFC_VFSNOMACLABEL
) {
660 mp
->mnt_flag
|= MNT_MULTILABEL
;
664 * Process device path for local file systems if requested
666 if (vfsp
->vfc_vfsflags
& VFC_VFSLOCALARGS
) {
667 if (vfs_context_is64bit(ctx
)) {
668 if ( (error
= copyin(fsmountargs
, (caddr_t
)&devpath
, sizeof(devpath
))) )
670 fsmountargs
+= sizeof(devpath
);
673 if ( (error
= copyin(fsmountargs
, (caddr_t
)&tmp
, sizeof(tmp
))) )
675 /* munge into LP64 addr */
676 devpath
= CAST_USER_ADDR_T(tmp
);
677 fsmountargs
+= sizeof(tmp
);
680 /* Lookup device and authorize access to it */
684 NDINIT(&nd
, LOOKUP
, OP_MOUNT
, FOLLOW
, UIO_USERSPACE
, devpath
, ctx
);
685 if ( (error
= namei(&nd
)) )
688 strncpy(mp
->mnt_vfsstat
.f_mntfromname
, nd
.ni_cnd
.cn_pnbuf
, MAXPATHLEN
);
693 if (devvp
->v_type
!= VBLK
) {
697 if (major(devvp
->v_rdev
) >= nblkdev
) {
702 * If mount by non-root, then verify that user has necessary
703 * permissions on the device.
705 if (suser(vfs_context_ucred(ctx
), NULL
) != 0) {
706 mode_t accessmode
= KAUTH_VNODE_READ_DATA
;
708 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0)
709 accessmode
|= KAUTH_VNODE_WRITE_DATA
;
710 if ((error
= vnode_authorize(devvp
, NULL
, accessmode
, ctx
)) != 0)
714 /* On first mount, preflight and open device */
715 if (devpath
&& ((flags
& MNT_UPDATE
) == 0)) {
716 if ( (error
= vnode_ref(devvp
)) )
719 * Disallow multiple mounts of the same device.
720 * Disallow mounting of a device that is currently in use
721 * (except for root, which might share swap device for miniroot).
722 * Flush out any old buffers remaining from a previous use.
724 if ( (error
= vfs_mountedon(devvp
)) )
727 if (vcount(devvp
) > 1 && !(vfs_flags(mp
) & MNT_ROOTFS
)) {
731 if ( (error
= VNOP_FSYNC(devvp
, MNT_WAIT
, ctx
)) ) {
735 if ( (error
= buf_invalidateblks(devvp
, BUF_WRITE_DATA
, 0, 0)) )
738 ronly
= (mp
->mnt_flag
& MNT_RDONLY
) != 0;
740 error
= mac_vnode_check_open(ctx
,
742 ronly
? FREAD
: FREAD
|FWRITE
);
746 if ( (error
= VNOP_OPEN(devvp
, ronly
? FREAD
: FREAD
|FWRITE
, ctx
)) )
749 mp
->mnt_devvp
= devvp
;
750 device_vnode
= devvp
;
752 } else if ((mp
->mnt_flag
& MNT_RDONLY
) &&
753 (mp
->mnt_kern_flag
& MNTK_WANTRDWR
) &&
754 (device_vnode
= mp
->mnt_devvp
)) {
758 * If upgrade to read-write by non-root, then verify
759 * that user has necessary permissions on the device.
761 vnode_getalways(device_vnode
);
763 if (suser(vfs_context_ucred(ctx
), NULL
) &&
764 (error
= vnode_authorize(device_vnode
, NULL
,
765 KAUTH_VNODE_READ_DATA
| KAUTH_VNODE_WRITE_DATA
,
767 vnode_put(device_vnode
);
771 /* Tell the device that we're upgrading */
772 dev
= (dev_t
)device_vnode
->v_rdev
;
775 if ((u_int
)maj
>= (u_int
)nblkdev
)
776 panic("Volume mounted on a device with invalid major number.");
778 error
= bdevsw
[maj
].d_open(dev
, FREAD
| FWRITE
, S_IFBLK
, p
);
779 vnode_put(device_vnode
);
780 device_vnode
= NULLVP
;
787 if ((flags
& MNT_UPDATE
) == 0) {
788 mac_mount_label_init(mp
);
789 mac_mount_label_associate(ctx
, mp
);
792 if ((flags
& MNT_UPDATE
) != 0) {
793 error
= mac_mount_check_label_update(ctx
, mp
);
800 * Mount the filesystem.
802 error
= VFS_MOUNT(mp
, device_vnode
, fsmountargs
, ctx
);
804 if (flags
& MNT_UPDATE
) {
805 if (mp
->mnt_kern_flag
& MNTK_WANTRDWR
)
806 mp
->mnt_flag
&= ~MNT_RDONLY
;
808 (MNT_UPDATE
| MNT_RELOAD
| MNT_FORCE
);
809 mp
->mnt_kern_flag
&=~ MNTK_WANTRDWR
;
811 mp
->mnt_flag
= flag
; /* restore flag value */
812 vfs_event_signal(NULL
, VQ_UPDATE
, (intptr_t)NULL
);
813 lck_rw_done(&mp
->mnt_rwlock
);
814 is_rwlock_locked
= FALSE
;
816 enablequotas(mp
, ctx
);
821 * Put the new filesystem on the mount list after root.
824 struct vfs_attr vfsattr
;
826 if (vfs_flags(mp
) & MNT_MULTILABEL
) {
827 error
= VFS_ROOT(mp
, &rvp
, ctx
);
829 printf("%s() VFS_ROOT returned %d\n", __func__
, error
);
832 error
= vnode_label(mp
, NULL
, rvp
, NULL
, 0, ctx
);
834 * drop reference provided by VFS_ROOT
844 CLR(vp
->v_flag
, VMOUNT
);
845 vp
->v_mountedhere
= mp
;
849 * taking the name_cache_lock exclusively will
850 * insure that everyone is out of the fast path who
851 * might be trying to use a now stale copy of
852 * vp->v_mountedhere->mnt_realrootvp
853 * bumping mount_generation causes the cached values
860 error
= vnode_ref(vp
);
865 have_usecount
= TRUE
;
867 error
= checkdirs(vp
, ctx
);
869 /* Unmount the filesystem as cdir/rdirs cannot be updated */
873 * there is no cleanup code here so I have made it void
874 * we need to revisit this
876 (void)VFS_START(mp
, 0, ctx
);
878 if (mount_list_add(mp
) != 0) {
880 * The system is shutting down trying to umount
881 * everything, so fail with a plausible errno.
886 lck_rw_done(&mp
->mnt_rwlock
);
887 is_rwlock_locked
= FALSE
;
889 /* Check if this mounted file system supports EAs or named streams. */
890 /* Skip WebDAV file systems for now since they hang in VFS_GETATTR here. */
891 VFSATTR_INIT(&vfsattr
);
892 VFSATTR_WANTED(&vfsattr
, f_capabilities
);
893 if (strncmp(mp
->mnt_vfsstat
.f_fstypename
, "webdav", sizeof("webdav")) != 0 &&
894 vfs_getattr(mp
, &vfsattr
, ctx
) == 0 &&
895 VFSATTR_IS_SUPPORTED(&vfsattr
, f_capabilities
)) {
896 if ((vfsattr
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_EXTENDED_ATTR
) &&
897 (vfsattr
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_EXTENDED_ATTR
)) {
898 mp
->mnt_kern_flag
|= MNTK_EXTENDED_ATTRS
;
901 if ((vfsattr
.f_capabilities
.capabilities
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_NAMEDSTREAMS
) &&
902 (vfsattr
.f_capabilities
.valid
[VOL_CAPABILITIES_INTERFACES
] & VOL_CAP_INT_NAMEDSTREAMS
)) {
903 mp
->mnt_kern_flag
|= MNTK_NAMED_STREAMS
;
906 /* Check if this file system supports path from id lookups. */
907 if ((vfsattr
.f_capabilities
.capabilities
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_PATH_FROM_ID
) &&
908 (vfsattr
.f_capabilities
.valid
[VOL_CAPABILITIES_FORMAT
] & VOL_CAP_FMT_PATH_FROM_ID
)) {
909 mp
->mnt_kern_flag
|= MNTK_PATH_FROM_ID
;
910 } else if (mp
->mnt_flag
& MNT_DOVOLFS
) {
911 /* Legacy MNT_DOVOLFS flag also implies path from id lookups. */
912 mp
->mnt_kern_flag
|= MNTK_PATH_FROM_ID
;
915 if (mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSNATIVEXATTR
) {
916 mp
->mnt_kern_flag
|= MNTK_EXTENDED_ATTRS
;
918 if (mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSPREFLIGHT
) {
919 mp
->mnt_kern_flag
|= MNTK_UNMOUNT_PREFLIGHT
;
921 /* increment the operations count */
922 OSAddAtomic(1, &vfs_nummntops
);
923 enablequotas(mp
, ctx
);
926 device_vnode
->v_specflags
|= SI_MOUNTEDON
;
929 * cache the IO attributes for the underlying physical media...
930 * an error return indicates the underlying driver doesn't
931 * support all the queries necessary... however, reasonable
932 * defaults will have been set, so no reason to bail or care
934 vfs_init_io_attributes(device_vnode
, mp
);
937 /* Now that mount is setup, notify the listeners */
938 vfs_notify_mount(pvp
);
940 /* If we fail a fresh mount, there should be no vnodes left hooked into the mountpoint. */
941 if (mp
->mnt_vnodelist
.tqh_first
!= NULL
) {
942 panic("mount_common(): mount of %s filesystem failed with %d, but vnode list is not empty.",
943 mp
->mnt_vtable
->vfc_name
, error
);
947 CLR(vp
->v_flag
, VMOUNT
);
950 mp
->mnt_vtable
->vfc_refcount
--;
954 vnode_rele(device_vnode
);
955 VNOP_CLOSE(device_vnode
, ronly
? FREAD
: FREAD
|FWRITE
, ctx
);
957 lck_rw_done(&mp
->mnt_rwlock
);
958 is_rwlock_locked
= FALSE
;
961 * if we get here, we have a mount structure that needs to be freed,
962 * but since the coveredvp hasn't yet been updated to point at it,
963 * no need to worry about other threads holding a crossref on this mp
964 * so it's ok to just free it
966 mount_lock_destroy(mp
);
968 mac_mount_label_destroy(mp
);
970 FREE_ZONE((caddr_t
)mp
, sizeof (struct mount
), M_MOUNT
);
974 * drop I/O count on the device vp if there was one
976 if (devpath
&& devvp
)
981 /* Error condition exits */
983 (void)VFS_UNMOUNT(mp
, MNT_FORCE
, ctx
);
986 * If the mount has been placed on the covered vp,
987 * it may have been discovered by now, so we have
988 * to treat this just like an unmount
991 mp
->mnt_lflag
|= MNT_LDEAD
;
994 if (device_vnode
!= NULLVP
) {
995 vnode_rele(device_vnode
);
996 VNOP_CLOSE(device_vnode
, mp
->mnt_flag
& MNT_RDONLY
? FREAD
: FREAD
|FWRITE
,
1001 vnode_lock_spin(vp
);
1004 vp
->v_mountedhere
= (mount_t
) 0;
1008 if (have_usecount
) {
1012 if (devpath
&& ((flags
& MNT_UPDATE
) == 0) && (!did_rele
))
1015 if (devpath
&& devvp
)
1018 /* Release mnt_rwlock only when it was taken */
1019 if (is_rwlock_locked
== TRUE
) {
1020 lck_rw_done(&mp
->mnt_rwlock
);
1024 if (mp
->mnt_crossref
)
1025 mount_dropcrossref(mp
, vp
, 0);
1027 mount_lock_destroy(mp
);
1029 mac_mount_label_destroy(mp
);
1031 FREE_ZONE((caddr_t
)mp
, sizeof (struct mount
), M_MOUNT
);
1036 vfsp
->vfc_refcount
--;
1037 mount_list_unlock();
1044 * Flush in-core data, check for competing mount attempts,
1048 prepare_coveredvp(vnode_t vp
, vfs_context_t ctx
, struct componentname
*cnp
, const char *fsname
, boolean_t skip_auth
)
1050 struct vnode_attr va
;
1055 * If the user is not root, ensure that they own the directory
1056 * onto which we are attempting to mount.
1059 VATTR_WANTED(&va
, va_uid
);
1060 if ((error
= vnode_getattr(vp
, &va
, ctx
)) ||
1061 (va
.va_uid
!= kauth_cred_getuid(vfs_context_ucred(ctx
)) &&
1062 (!vfs_context_issuser(ctx
)))) {
1068 if ( (error
= VNOP_FSYNC(vp
, MNT_WAIT
, ctx
)) )
1071 if ( (error
= buf_invalidateblks(vp
, BUF_WRITE_DATA
, 0, 0)) )
1074 if (vp
->v_type
!= VDIR
) {
1079 if (ISSET(vp
->v_flag
, VMOUNT
) && (vp
->v_mountedhere
!= NULL
)) {
1085 error
= mac_mount_check_mount(ctx
, vp
,
1091 vnode_lock_spin(vp
);
1092 SET(vp
->v_flag
, VMOUNT
);
1099 #if CONFIG_IMGSRC_ACCESS
1102 #define IMGSRC_DEBUG(args...) printf(args)
1104 #define IMGSRC_DEBUG(args...) do { } while(0)
1108 authorize_devpath_and_update_mntfromname(mount_t mp
, user_addr_t devpath
, vnode_t
*devvpp
, vfs_context_t ctx
)
1110 struct nameidata nd
;
1111 vnode_t vp
, realdevvp
;
1115 NDINIT(&nd
, LOOKUP
, OP_LOOKUP
, FOLLOW
, UIO_USERSPACE
, devpath
, ctx
);
1116 if ( (error
= namei(&nd
)) ) {
1117 IMGSRC_DEBUG("namei() failed with %d\n", error
);
1123 if (!vnode_isblk(vp
)) {
1124 IMGSRC_DEBUG("Not block device.\n");
1129 realdevvp
= mp
->mnt_devvp
;
1130 if (realdevvp
== NULLVP
) {
1131 IMGSRC_DEBUG("No device backs the mount.\n");
1136 error
= vnode_getwithref(realdevvp
);
1138 IMGSRC_DEBUG("Coudn't get iocount on device.\n");
1142 if (vnode_specrdev(vp
) != vnode_specrdev(realdevvp
)) {
1143 IMGSRC_DEBUG("Wrong dev_t.\n");
1148 strlcpy(mp
->mnt_vfsstat
.f_mntfromname
, nd
.ni_cnd
.cn_pnbuf
, MAXPATHLEN
);
1151 * If mount by non-root, then verify that user has necessary
1152 * permissions on the device.
1154 if (!vfs_context_issuser(ctx
)) {
1155 accessmode
= KAUTH_VNODE_READ_DATA
;
1156 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0)
1157 accessmode
|= KAUTH_VNODE_WRITE_DATA
;
1158 if ((error
= vnode_authorize(vp
, NULL
, accessmode
, ctx
)) != 0) {
1159 IMGSRC_DEBUG("Access denied.\n");
1167 vnode_put(realdevvp
);
1178 * Clear VMOUNT, set v_mountedhere, and mnt_vnodecovered, ref the vnode,
1179 * and call checkdirs()
1182 place_mount_and_checkdirs(mount_t mp
, vnode_t vp
, vfs_context_t ctx
)
1186 mp
->mnt_vnodecovered
= vp
; /* XXX This is normally only set at init-time ... */
1188 vnode_lock_spin(vp
);
1189 CLR(vp
->v_flag
, VMOUNT
);
1190 vp
->v_mountedhere
= mp
;
1194 * taking the name_cache_lock exclusively will
1195 * insure that everyone is out of the fast path who
1196 * might be trying to use a now stale copy of
1197 * vp->v_mountedhere->mnt_realrootvp
1198 * bumping mount_generation causes the cached values
1203 name_cache_unlock();
1205 error
= vnode_ref(vp
);
1210 error
= checkdirs(vp
, ctx
);
1212 /* Unmount the filesystem as cdir/rdirs cannot be updated */
1219 mp
->mnt_vnodecovered
= NULLVP
;
1225 undo_place_on_covered_vp(mount_t mp
, vnode_t vp
)
1228 vnode_lock_spin(vp
);
1229 vp
->v_mountedhere
= (mount_t
)NULL
;
1232 mp
->mnt_vnodecovered
= NULLVP
;
1236 mount_begin_update(mount_t mp
, vfs_context_t ctx
, int flags
)
1240 /* unmount in progress return error */
1241 mount_lock_spin(mp
);
1242 if (mp
->mnt_lflag
& MNT_LUNMOUNT
) {
1247 lck_rw_lock_exclusive(&mp
->mnt_rwlock
);
1250 * We only allow the filesystem to be reloaded if it
1251 * is currently mounted read-only.
1253 if ((flags
& MNT_RELOAD
) &&
1254 ((mp
->mnt_flag
& MNT_RDONLY
) == 0)) {
1260 * Only root, or the user that did the original mount is
1261 * permitted to update it.
1263 if (mp
->mnt_vfsstat
.f_owner
!= kauth_cred_getuid(vfs_context_ucred(ctx
)) &&
1264 (!vfs_context_issuser(ctx
))) {
1269 error
= mac_mount_check_remount(ctx
, mp
);
1277 lck_rw_done(&mp
->mnt_rwlock
);
1284 mount_end_update(mount_t mp
)
1286 lck_rw_done(&mp
->mnt_rwlock
);
1290 get_imgsrc_rootvnode(uint32_t height
, vnode_t
*rvpp
)
1294 if (height
>= MAX_IMAGEBOOT_NESTING
) {
1298 vp
= imgsrc_rootvnodes
[height
];
1299 if ((vp
!= NULLVP
) && (vnode_get(vp
) == 0)) {
1308 relocate_imageboot_source(vnode_t pvp
, vnode_t vp
, struct componentname
*cnp
,
1309 const char *fsname
, vfs_context_t ctx
,
1310 boolean_t is64bit
, user_addr_t fsmountargs
, boolean_t by_index
)
1314 boolean_t placed
= FALSE
;
1315 vnode_t devvp
= NULLVP
;
1316 struct vfstable
*vfsp
;
1317 user_addr_t devpath
;
1318 char *old_mntonname
;
1323 /* If we didn't imageboot, nothing to move */
1324 if (imgsrc_rootvnodes
[0] == NULLVP
) {
1328 /* Only root can do this */
1329 if (!vfs_context_issuser(ctx
)) {
1333 IMGSRC_DEBUG("looking for root vnode.\n");
1336 * Get root vnode of filesystem we're moving.
1340 struct user64_mnt_imgsrc_args mia64
;
1341 error
= copyin(fsmountargs
, &mia64
, sizeof(mia64
));
1343 IMGSRC_DEBUG("Failed to copy in arguments.\n");
1347 height
= mia64
.mi_height
;
1348 flags
= mia64
.mi_flags
;
1349 devpath
= mia64
.mi_devpath
;
1351 struct user32_mnt_imgsrc_args mia32
;
1352 error
= copyin(fsmountargs
, &mia32
, sizeof(mia32
));
1354 IMGSRC_DEBUG("Failed to copy in arguments.\n");
1358 height
= mia32
.mi_height
;
1359 flags
= mia32
.mi_flags
;
1360 devpath
= mia32
.mi_devpath
;
1364 * For binary compatibility--assumes one level of nesting.
1367 if ( (error
= copyin(fsmountargs
, (caddr_t
)&devpath
, sizeof(devpath
))) )
1371 if ( (error
= copyin(fsmountargs
, (caddr_t
)&tmp
, sizeof(tmp
))) )
1374 /* munge into LP64 addr */
1375 devpath
= CAST_USER_ADDR_T(tmp
);
1383 IMGSRC_DEBUG("%s: Got nonzero flags.\n", __FUNCTION__
);
1387 error
= get_imgsrc_rootvnode(height
, &rvp
);
1389 IMGSRC_DEBUG("getting root vnode failed with %d\n", error
);
1393 IMGSRC_DEBUG("got root vnode.\n");
1395 MALLOC(old_mntonname
, char*, MAXPATHLEN
, M_TEMP
, M_WAITOK
);
1397 /* Can only move once */
1398 mp
= vnode_mount(rvp
);
1399 if ((mp
->mnt_kern_flag
& MNTK_HAS_MOVED
) == MNTK_HAS_MOVED
) {
1400 IMGSRC_DEBUG("Already moved.\n");
1405 IMGSRC_DEBUG("Starting updated.\n");
1407 /* Get exclusive rwlock on mount, authorize update on mp */
1408 error
= mount_begin_update(mp
, ctx
, 0);
1410 IMGSRC_DEBUG("Starting updated failed with %d\n", error
);
1415 * It can only be moved once. Flag is set under the rwlock,
1416 * so we're now safe to proceed.
1418 if ((mp
->mnt_kern_flag
& MNTK_HAS_MOVED
) == MNTK_HAS_MOVED
) {
1419 IMGSRC_DEBUG("Already moved [2]\n");
1424 IMGSRC_DEBUG("Preparing coveredvp.\n");
1426 /* Mark covered vnode as mount in progress, authorize placing mount on top */
1427 error
= prepare_coveredvp(vp
, ctx
, cnp
, fsname
, FALSE
);
1429 IMGSRC_DEBUG("Preparing coveredvp failed with %d.\n", error
);
1433 IMGSRC_DEBUG("Covered vp OK.\n");
1435 /* Sanity check the name caller has provided */
1436 vfsp
= mp
->mnt_vtable
;
1437 if (strncmp(vfsp
->vfc_name
, fsname
, MFSNAMELEN
) != 0) {
1438 IMGSRC_DEBUG("Wrong fs name.\n");
1443 /* Check the device vnode and update mount-from name, for local filesystems */
1444 if (vfsp
->vfc_vfsflags
& VFC_VFSLOCALARGS
) {
1445 IMGSRC_DEBUG("Local, doing device validation.\n");
1447 if (devpath
!= USER_ADDR_NULL
) {
1448 error
= authorize_devpath_and_update_mntfromname(mp
, devpath
, &devvp
, ctx
);
1450 IMGSRC_DEBUG("authorize_devpath_and_update_mntfromname() failed.\n");
1459 * Place mp on top of vnode, ref the vnode, call checkdirs(),
1460 * and increment the name cache's mount generation
1463 IMGSRC_DEBUG("About to call place_mount_and_checkdirs().\n");
1464 error
= place_mount_and_checkdirs(mp
, vp
, ctx
);
1471 strncpy(old_mntonname
, mp
->mnt_vfsstat
.f_mntonname
, MAXPATHLEN
);
1472 strncpy(mp
->mnt_vfsstat
.f_mntonname
, cnp
->cn_pnbuf
, MAXPATHLEN
);
1474 /* Forbid future moves */
1476 mp
->mnt_kern_flag
|= MNTK_HAS_MOVED
;
1479 /* Finally, add to mount list, completely ready to go */
1480 if (mount_list_add(mp
) != 0) {
1482 * The system is shutting down trying to umount
1483 * everything, so fail with a plausible errno.
1489 mount_end_update(mp
);
1491 FREE(old_mntonname
, M_TEMP
);
1493 vfs_notify_mount(pvp
);
1497 strncpy(mp
->mnt_vfsstat
.f_mntonname
, old_mntonname
, MAXPATHLEN
);
1500 mp
->mnt_kern_flag
&= ~(MNTK_HAS_MOVED
);
1505 * Placing the mp on the vnode clears VMOUNT,
1506 * so cleanup is different after that point
1509 /* Rele the vp, clear VMOUNT and v_mountedhere */
1510 undo_place_on_covered_vp(mp
, vp
);
1512 vnode_lock_spin(vp
);
1513 CLR(vp
->v_flag
, VMOUNT
);
1517 mount_end_update(mp
);
1521 FREE(old_mntonname
, M_TEMP
);
1525 #endif /* CONFIG_IMGSRC_ACCESS */
1528 enablequotas(struct mount
*mp
, vfs_context_t ctx
)
1530 struct nameidata qnd
;
1532 char qfpath
[MAXPATHLEN
];
1533 const char *qfname
= QUOTAFILENAME
;
1534 const char *qfopsname
= QUOTAOPSNAME
;
1535 const char *qfextension
[] = INITQFNAMES
;
1537 /* XXX Shoulkd be an MNTK_ flag, instead of strncmp()'s */
1538 if (strncmp(mp
->mnt_vfsstat
.f_fstypename
, "hfs", sizeof("hfs")) != 0 ) {
1542 * Enable filesystem disk quotas if necessary.
1543 * We ignore errors as this should not interfere with final mount
1545 for (type
=0; type
< MAXQUOTAS
; type
++) {
1546 snprintf(qfpath
, sizeof(qfpath
), "%s/%s.%s", mp
->mnt_vfsstat
.f_mntonname
, qfopsname
, qfextension
[type
]);
1547 NDINIT(&qnd
, LOOKUP
, OP_MOUNT
, FOLLOW
, UIO_SYSSPACE
,
1548 CAST_USER_ADDR_T(qfpath
), ctx
);
1549 if (namei(&qnd
) != 0)
1550 continue; /* option file to trigger quotas is not present */
1551 vnode_put(qnd
.ni_vp
);
1553 snprintf(qfpath
, sizeof(qfpath
), "%s/%s.%s", mp
->mnt_vfsstat
.f_mntonname
, qfname
, qfextension
[type
]);
1555 (void) VFS_QUOTACTL(mp
, QCMD(Q_QUOTAON
, type
), 0, qfpath
, ctx
);
1562 checkdirs_callback(proc_t p
, void * arg
)
1564 struct cdirargs
* cdrp
= (struct cdirargs
* )arg
;
1565 vnode_t olddp
= cdrp
->olddp
;
1566 vnode_t newdp
= cdrp
->newdp
;
1567 struct filedesc
*fdp
;
1571 int cdir_changed
= 0;
1572 int rdir_changed
= 0;
1575 * XXX Also needs to iterate each thread in the process to see if it
1576 * XXX is using a per-thread current working directory, and, if so,
1577 * XXX update that as well.
1582 if (fdp
== (struct filedesc
*)0) {
1584 return(PROC_RETURNED
);
1586 fdp_cvp
= fdp
->fd_cdir
;
1587 fdp_rvp
= fdp
->fd_rdir
;
1590 if (fdp_cvp
== olddp
) {
1597 if (fdp_rvp
== olddp
) {
1604 if (cdir_changed
|| rdir_changed
) {
1606 fdp
->fd_cdir
= fdp_cvp
;
1607 fdp
->fd_rdir
= fdp_rvp
;
1610 return(PROC_RETURNED
);
1616 * Scan all active processes to see if any of them have a current
1617 * or root directory onto which the new filesystem has just been
1618 * mounted. If so, replace them with the new mount point.
1621 checkdirs(vnode_t olddp
, vfs_context_t ctx
)
1626 struct cdirargs cdr
;
1627 struct uthread
* uth
= get_bsdthread_info(current_thread());
1629 if (olddp
->v_usecount
== 1)
1631 if (uth
!= (struct uthread
*)0)
1632 uth
->uu_notrigger
= 1;
1633 err
= VFS_ROOT(olddp
->v_mountedhere
, &newdp
, ctx
);
1634 if (uth
!= (struct uthread
*)0)
1635 uth
->uu_notrigger
= 0;
1639 panic("mount: lost mount: error %d", err
);
1646 /* do not block for exec/fork trans as the vp in cwd & rootdir are not changing */
1647 proc_iterate(PROC_ALLPROCLIST
| PROC_NOWAITTRANS
, checkdirs_callback
, (void *)&cdr
, NULL
, NULL
);
1649 if (rootvnode
== olddp
) {
1661 * Unmount a file system.
1663 * Note: unmount takes a path to the vnode mounted on as argument,
1664 * not special file (as before).
1668 unmount(__unused proc_t p
, struct unmount_args
*uap
, __unused
int32_t *retval
)
1673 struct nameidata nd
;
1674 vfs_context_t ctx
= vfs_context_current();
1676 NDINIT(&nd
, LOOKUP
, OP_UNMOUNT
, NOTRIGGER
| FOLLOW
| AUDITVNPATH1
,
1677 UIO_USERSPACE
, uap
->path
, ctx
);
1686 error
= mac_mount_check_umount(ctx
, mp
);
1693 * Must be the root of the filesystem
1695 if ((vp
->v_flag
& VROOT
) == 0) {
1701 /* safedounmount consumes the mount ref */
1702 return (safedounmount(mp
, uap
->flags
, ctx
));
1706 vfs_unmountbyfsid(fsid_t
* fsid
, int flags
, vfs_context_t ctx
)
1710 mp
= mount_list_lookupby_fsid(fsid
, 0, 1);
1711 if (mp
== (mount_t
)0) {
1716 /* safedounmount consumes the mount ref */
1717 return(safedounmount(mp
, flags
, ctx
));
1722 * The mount struct comes with a mount ref which will be consumed.
1723 * Do the actual file system unmount, prevent some common foot shooting.
1726 safedounmount(struct mount
*mp
, int flags
, vfs_context_t ctx
)
1729 proc_t p
= vfs_context_proc(ctx
);
1732 * Skip authorization if the mount is tagged as permissive and
1733 * this is not a forced-unmount attempt.
1735 if (!(((mp
->mnt_kern_flag
& MNTK_PERMIT_UNMOUNT
) != 0) && ((flags
& MNT_FORCE
) == 0))) {
1737 * Only root, or the user that did the original mount is
1738 * permitted to unmount this filesystem.
1740 if ((mp
->mnt_vfsstat
.f_owner
!= kauth_cred_getuid(kauth_cred_get())) &&
1741 (error
= suser(kauth_cred_get(), &p
->p_acflag
)))
1745 * Don't allow unmounting the root file system.
1747 if (mp
->mnt_flag
& MNT_ROOTFS
) {
1748 error
= EBUSY
; /* the root is always busy */
1752 #ifdef CONFIG_IMGSRC_ACCESS
1753 if (mp
->mnt_kern_flag
& MNTK_BACKS_ROOT
) {
1757 #endif /* CONFIG_IMGSRC_ACCESS */
1759 return (dounmount(mp
, flags
, 1, ctx
));
1767 * Do the actual file system unmount.
1770 dounmount(struct mount
*mp
, int flags
, int withref
, vfs_context_t ctx
)
1772 vnode_t coveredvp
= (vnode_t
)0;
1775 int forcedunmount
= 0;
1777 struct vnode
*devvp
= NULLVP
;
1780 #endif /* CONFIG_TRIGGERS */
1782 if (flags
& MNT_FORCE
)
1786 /* XXX post jaguar fix LK_DRAIN - then clean this up */
1787 if ((flags
& MNT_FORCE
)) {
1788 mp
->mnt_kern_flag
|= MNTK_FRCUNMOUNT
;
1789 mp
->mnt_lflag
|= MNT_LFORCE
;
1791 if (mp
->mnt_lflag
& MNT_LUNMOUNT
) {
1792 mp
->mnt_lflag
|= MNT_LWAIT
;
1795 msleep((caddr_t
)mp
, &mp
->mnt_mlock
, (PVFS
| PDROP
), "dounmount", NULL
);
1797 * The prior unmount attempt has probably succeeded.
1798 * Do not dereference mp here - returning EBUSY is safest.
1802 mp
->mnt_kern_flag
|= MNTK_UNMOUNT
;
1803 mp
->mnt_lflag
|= MNT_LUNMOUNT
;
1804 mp
->mnt_flag
&=~ MNT_ASYNC
;
1806 * anyone currently in the fast path that
1807 * trips over the cached rootvp will be
1808 * dumped out and forced into the slow path
1809 * to regenerate a new cached value
1811 mp
->mnt_realrootvp
= NULLVP
;
1815 * taking the name_cache_lock exclusively will
1816 * insure that everyone is out of the fast path who
1817 * might be trying to use a now stale copy of
1818 * vp->v_mountedhere->mnt_realrootvp
1819 * bumping mount_generation causes the cached values
1824 name_cache_unlock();
1827 lck_rw_lock_exclusive(&mp
->mnt_rwlock
);
1831 fsevent_unmount(mp
); /* has to come first! */
1834 if (forcedunmount
== 0) {
1835 ubc_umount(mp
); /* release cached vnodes */
1836 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0) {
1837 error
= VFS_SYNC(mp
, MNT_WAIT
, ctx
);
1840 mp
->mnt_kern_flag
&= ~MNTK_UNMOUNT
;
1841 mp
->mnt_lflag
&= ~MNT_LUNMOUNT
;
1842 mp
->mnt_lflag
&= ~MNT_LFORCE
;
1849 vfs_nested_trigger_unmounts(mp
, flags
, ctx
);
1853 lflags
|= FORCECLOSE
;
1854 error
= vflush(mp
, NULLVP
, SKIPSWAP
| SKIPSYSTEM
| SKIPROOT
| lflags
);
1855 if ((forcedunmount
== 0) && error
) {
1857 mp
->mnt_kern_flag
&= ~MNTK_UNMOUNT
;
1858 mp
->mnt_lflag
&= ~MNT_LUNMOUNT
;
1859 mp
->mnt_lflag
&= ~MNT_LFORCE
;
1863 /* make sure there are no one in the mount iterations or lookup */
1864 mount_iterdrain(mp
);
1866 error
= VFS_UNMOUNT(mp
, flags
, ctx
);
1868 mount_iterreset(mp
);
1870 mp
->mnt_kern_flag
&= ~MNTK_UNMOUNT
;
1871 mp
->mnt_lflag
&= ~MNT_LUNMOUNT
;
1872 mp
->mnt_lflag
&= ~MNT_LFORCE
;
1876 /* increment the operations count */
1878 OSAddAtomic(1, &vfs_nummntops
);
1880 if ( mp
->mnt_devvp
&& mp
->mnt_vtable
->vfc_vfsflags
& VFC_VFSLOCALARGS
) {
1881 /* hold an io reference and drop the usecount before close */
1882 devvp
= mp
->mnt_devvp
;
1883 vnode_getalways(devvp
);
1885 VNOP_CLOSE(devvp
, mp
->mnt_flag
& MNT_RDONLY
? FREAD
: FREAD
|FWRITE
,
1887 vnode_clearmountedon(devvp
);
1890 lck_rw_done(&mp
->mnt_rwlock
);
1891 mount_list_remove(mp
);
1892 lck_rw_lock_exclusive(&mp
->mnt_rwlock
);
1894 /* mark the mount point hook in the vp but not drop the ref yet */
1895 if ((coveredvp
= mp
->mnt_vnodecovered
) != NULLVP
) {
1896 vnode_getwithref(coveredvp
);
1897 vnode_lock_spin(coveredvp
);
1900 coveredvp
->v_mountedhere
= (struct mount
*)0;
1902 vnode_unlock(coveredvp
);
1903 vnode_put(coveredvp
);
1907 mp
->mnt_vtable
->vfc_refcount
--;
1908 mount_list_unlock();
1910 cache_purgevfs(mp
); /* remove cache entries for this file sys */
1911 vfs_event_signal(NULL
, VQ_UNMOUNT
, (intptr_t)NULL
);
1913 mp
->mnt_lflag
|= MNT_LDEAD
;
1915 if (mp
->mnt_lflag
& MNT_LWAIT
) {
1917 * do the wakeup here
1918 * in case we block in mount_refdrain
1919 * which will drop the mount lock
1920 * and allow anyone blocked in vfs_busy
1921 * to wakeup and see the LDEAD state
1923 mp
->mnt_lflag
&= ~MNT_LWAIT
;
1924 wakeup((caddr_t
)mp
);
1928 if (mp
->mnt_lflag
& MNT_LWAIT
) {
1929 mp
->mnt_lflag
&= ~MNT_LWAIT
;
1936 * Callback and context are set together under the mount lock, and
1937 * never cleared, so we're safe to examine them here, drop the lock,
1940 if (mp
->mnt_triggercallback
!= NULL
) {
1943 mp
->mnt_triggercallback(mp
, VTC_RELEASE
, mp
->mnt_triggerdata
, ctx
);
1944 } else if (did_vflush
) {
1945 mp
->mnt_triggercallback(mp
, VTC_REPLACE
, mp
->mnt_triggerdata
, ctx
);
1952 #endif /* CONFIG_TRIGGERS */
1954 lck_rw_done(&mp
->mnt_rwlock
);
1957 wakeup((caddr_t
)mp
);
1960 if ((coveredvp
!= NULLVP
)) {
1963 vnode_getwithref(coveredvp
);
1964 pvp
= vnode_getparent(coveredvp
);
1965 vnode_rele(coveredvp
);
1967 mount_dropcrossref(mp
, coveredvp
, 0);
1969 if (coveredvp
->v_resolve
)
1970 vnode_trigger_rearm(coveredvp
, ctx
);
1972 vnode_put(coveredvp
);
1975 lock_vnode_and_post(pvp
, NOTE_WRITE
);
1978 } else if (mp
->mnt_flag
& MNT_ROOTFS
) {
1979 mount_lock_destroy(mp
);
1981 mac_mount_label_destroy(mp
);
1983 FREE_ZONE((caddr_t
)mp
, sizeof (struct mount
), M_MOUNT
);
1985 panic("dounmount: no coveredvp");
1991 mount_dropcrossref(mount_t mp
, vnode_t dp
, int need_put
)
1996 if (mp
->mnt_crossref
< 0)
1997 panic("mount cross refs -ve");
1999 if ((mp
!= dp
->v_mountedhere
) && (mp
->mnt_crossref
== 0)) {
2002 vnode_put_locked(dp
);
2005 mount_lock_destroy(mp
);
2007 mac_mount_label_destroy(mp
);
2009 FREE_ZONE((caddr_t
)mp
, sizeof (struct mount
), M_MOUNT
);
2013 vnode_put_locked(dp
);
2019 * Sync each mounted filesystem.
2023 struct ctldebug debug0
= { "syncprt", &syncprt
};
2026 int print_vmpage_stat
=0;
2029 sync_callback(mount_t mp
, void * arg
)
2033 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0) {
2034 asyncflag
= mp
->mnt_flag
& MNT_ASYNC
;
2035 mp
->mnt_flag
&= ~MNT_ASYNC
;
2036 VFS_SYNC(mp
, arg
? MNT_WAIT
: MNT_NOWAIT
, vfs_context_current());
2038 mp
->mnt_flag
|= MNT_ASYNC
;
2040 return(VFS_RETURNED
);
2044 #include <kern/clock.h>
2046 clock_sec_t sync_wait_time
= 0;
2050 sync(__unused proc_t p
, __unused
struct sync_args
*uap
, __unused
int32_t *retval
)
2054 vfs_iterate(LK_NOWAIT
, sync_callback
, (void *)0);
2057 static fsid_t fsid
= { { 0, 0 } };
2059 clock_get_calendar_microtime(&sync_wait_time
, &nsecs
);
2060 vfs_event_signal(&fsid
, VQ_SYNCEVENT
, (intptr_t)NULL
);
2061 wakeup((caddr_t
)&sync_wait_time
);
2065 if(print_vmpage_stat
) {
2066 vm_countdirtypages();
2072 #endif /* DIAGNOSTIC */
2077 * Change filesystem quotas.
2080 static int quotactl_funneled(proc_t p
, struct quotactl_args
*uap
, int32_t *retval
);
2083 quotactl(proc_t p
, struct quotactl_args
*uap
, int32_t *retval
)
2085 boolean_t funnel_state
;
2088 funnel_state
= thread_funnel_set(kernel_flock
, TRUE
);
2089 error
= quotactl_funneled(p
, uap
, retval
);
2090 thread_funnel_set(kernel_flock
, funnel_state
);
2095 quotactl_funneled(proc_t p
, struct quotactl_args
*uap
, __unused
int32_t *retval
)
2098 int error
, quota_cmd
, quota_status
;
2101 struct nameidata nd
;
2102 vfs_context_t ctx
= vfs_context_current();
2103 struct dqblk my_dqblk
;
2105 AUDIT_ARG(uid
, uap
->uid
);
2106 AUDIT_ARG(cmd
, uap
->cmd
);
2107 NDINIT(&nd
, LOOKUP
, OP_LOOKUP
, FOLLOW
| AUDITVNPATH1
, UIO_USERSPACE
,
2112 mp
= nd
.ni_vp
->v_mount
;
2113 vnode_put(nd
.ni_vp
);
2116 /* copyin any data we will need for downstream code */
2117 quota_cmd
= uap
->cmd
>> SUBCMDSHIFT
;
2119 switch (quota_cmd
) {
2121 /* uap->arg specifies a file from which to take the quotas */
2122 fnamelen
= MAXPATHLEN
;
2123 datap
= kalloc(MAXPATHLEN
);
2124 error
= copyinstr(uap
->arg
, datap
, MAXPATHLEN
, &fnamelen
);
2127 /* uap->arg is a pointer to a dqblk structure. */
2128 datap
= (caddr_t
) &my_dqblk
;
2132 /* uap->arg is a pointer to a dqblk structure. */
2133 datap
= (caddr_t
) &my_dqblk
;
2134 if (proc_is64bit(p
)) {
2135 struct user_dqblk my_dqblk64
;
2136 error
= copyin(uap
->arg
, (caddr_t
)&my_dqblk64
, sizeof (my_dqblk64
));
2138 munge_dqblk(&my_dqblk
, &my_dqblk64
, FALSE
);
2142 error
= copyin(uap
->arg
, (caddr_t
)&my_dqblk
, sizeof (my_dqblk
));
2146 /* uap->arg is a pointer to an integer */
2147 datap
= (caddr_t
) "a_status
;
2155 error
= VFS_QUOTACTL(mp
, uap
->cmd
, uap
->uid
, datap
, ctx
);
2158 switch (quota_cmd
) {
2161 kfree(datap
, MAXPATHLEN
);
2164 /* uap->arg is a pointer to a dqblk structure we need to copy out to */
2166 if (proc_is64bit(p
)) {
2167 struct user_dqblk my_dqblk64
;
2168 munge_dqblk(&my_dqblk
, &my_dqblk64
, TRUE
);
2169 error
= copyout((caddr_t
)&my_dqblk64
, uap
->arg
, sizeof (my_dqblk64
));
2172 error
= copyout(datap
, uap
->arg
, sizeof (struct dqblk
));
2177 /* uap->arg is a pointer to an integer */
2179 error
= copyout(datap
, uap
->arg
, sizeof(quota_status
));
2190 quotactl(__unused proc_t p
, __unused
struct quotactl_args
*uap
, __unused
int32_t *retval
)
2192 return (EOPNOTSUPP
);
2197 * Get filesystem statistics.
2199 * Returns: 0 Success
2201 * vfs_update_vfsstat:???
2202 * munge_statfs:EFAULT
2206 statfs(__unused proc_t p
, struct statfs_args
*uap
, __unused
int32_t *retval
)
2209 struct vfsstatfs
*sp
;
2211 struct nameidata nd
;
2212 vfs_context_t ctx
= vfs_context_current();
2215 NDINIT(&nd
, LOOKUP
, OP_STATFS
, NOTRIGGER
| FOLLOW
| AUDITVNPATH1
,
2216 UIO_USERSPACE
, uap
->path
, ctx
);
2222 sp
= &mp
->mnt_vfsstat
;
2225 error
= vfs_update_vfsstat(mp
, ctx
, VFS_USER_EVENT
);
2230 error
= munge_statfs(mp
, sp
, uap
->buf
, NULL
, IS_64BIT_PROCESS(p
), TRUE
);
2235 * Get filesystem statistics.
2239 fstatfs(__unused proc_t p
, struct fstatfs_args
*uap
, __unused
int32_t *retval
)
2243 struct vfsstatfs
*sp
;
2246 AUDIT_ARG(fd
, uap
->fd
);
2248 if ( (error
= file_vnode(uap
->fd
, &vp
)) )
2251 error
= vnode_getwithref(vp
);
2257 AUDIT_ARG(vnpath_withref
, vp
, ARG_VNODE1
);
2264 sp
= &mp
->mnt_vfsstat
;
2265 if ((error
= vfs_update_vfsstat(mp
,vfs_context_current(),VFS_USER_EVENT
)) != 0) {
2269 error
= munge_statfs(mp
, sp
, uap
->buf
, NULL
, IS_64BIT_PROCESS(p
), TRUE
);
2279 * Common routine to handle copying of statfs64 data to user space
2282 statfs64_common(struct mount
*mp
, struct vfsstatfs
*sfsp
, user_addr_t bufp
)
2285 struct statfs64 sfs
;
2287 bzero(&sfs
, sizeof(sfs
));
2289 sfs
.f_bsize
= sfsp
->f_bsize
;
2290 sfs
.f_iosize
= (int32_t)sfsp
->f_iosize
;
2291 sfs
.f_blocks
= sfsp
->f_blocks
;
2292 sfs
.f_bfree
= sfsp
->f_bfree
;
2293 sfs
.f_bavail
= sfsp
->f_bavail
;
2294 sfs
.f_files
= sfsp
->f_files
;
2295 sfs
.f_ffree
= sfsp
->f_ffree
;
2296 sfs
.f_fsid
= sfsp
->f_fsid
;
2297 sfs
.f_owner
= sfsp
->f_owner
;
2298 sfs
.f_type
= mp
->mnt_vtable
->vfc_typenum
;
2299 sfs
.f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
2300 sfs
.f_fssubtype
= sfsp
->f_fssubtype
;
2301 if (mp
->mnt_kern_flag
& MNTK_TYPENAME_OVERRIDE
) {
2302 strlcpy(&sfs
.f_fstypename
[0], &mp
->fstypename_override
[0], MFSTYPENAMELEN
);
2304 strlcpy(&sfs
.f_fstypename
[0], &sfsp
->f_fstypename
[0], MFSTYPENAMELEN
);
2306 strlcpy(&sfs
.f_mntonname
[0], &sfsp
->f_mntonname
[0], MAXPATHLEN
);
2307 strlcpy(&sfs
.f_mntfromname
[0], &sfsp
->f_mntfromname
[0], MAXPATHLEN
);
2309 error
= copyout((caddr_t
)&sfs
, bufp
, sizeof(sfs
));
2315 * Get file system statistics in 64-bit mode
2318 statfs64(__unused
struct proc
*p
, struct statfs64_args
*uap
, __unused
int32_t *retval
)
2321 struct vfsstatfs
*sp
;
2323 struct nameidata nd
;
2324 vfs_context_t ctxp
= vfs_context_current();
2327 NDINIT(&nd
, LOOKUP
, OP_STATFS
, NOTRIGGER
| FOLLOW
| AUDITVNPATH1
,
2328 UIO_USERSPACE
, uap
->path
, ctxp
);
2334 sp
= &mp
->mnt_vfsstat
;
2337 error
= vfs_update_vfsstat(mp
, ctxp
, VFS_USER_EVENT
);
2342 error
= statfs64_common(mp
, sp
, uap
->buf
);
2348 * Get file system statistics in 64-bit mode
2351 fstatfs64(__unused
struct proc
*p
, struct fstatfs64_args
*uap
, __unused
int32_t *retval
)
2355 struct vfsstatfs
*sp
;
2358 AUDIT_ARG(fd
, uap
->fd
);
2360 if ( (error
= file_vnode(uap
->fd
, &vp
)) )
2363 error
= vnode_getwithref(vp
);
2369 AUDIT_ARG(vnpath_withref
, vp
, ARG_VNODE1
);
2376 sp
= &mp
->mnt_vfsstat
;
2377 if ((error
= vfs_update_vfsstat(mp
, vfs_context_current(), VFS_USER_EVENT
)) != 0) {
2381 error
= statfs64_common(mp
, sp
, uap
->buf
);
2390 struct getfsstat_struct
{
2401 getfsstat_callback(mount_t mp
, void * arg
)
2404 struct getfsstat_struct
*fstp
= (struct getfsstat_struct
*)arg
;
2405 struct vfsstatfs
*sp
;
2407 vfs_context_t ctx
= vfs_context_current();
2409 if (fstp
->sfsp
&& fstp
->count
< fstp
->maxcount
) {
2410 sp
= &mp
->mnt_vfsstat
;
2412 * If MNT_NOWAIT is specified, do not refresh the
2413 * fsstat cache. MNT_WAIT/MNT_DWAIT overrides MNT_NOWAIT.
2415 if (((fstp
->flags
& MNT_NOWAIT
) == 0 || (fstp
->flags
& (MNT_WAIT
| MNT_DWAIT
))) &&
2416 (error
= vfs_update_vfsstat(mp
, ctx
,
2418 KAUTH_DEBUG("vfs_update_vfsstat returned %d", error
);
2419 return(VFS_RETURNED
);
2423 * Need to handle LP64 version of struct statfs
2425 error
= munge_statfs(mp
, sp
, fstp
->sfsp
, &my_size
, IS_64BIT_PROCESS(vfs_context_proc(ctx
)), FALSE
);
2427 fstp
->error
= error
;
2428 return(VFS_RETURNED_DONE
);
2430 fstp
->sfsp
+= my_size
;
2433 error
= mac_mount_label_get(mp
, *fstp
->mp
);
2435 fstp
->error
= error
;
2436 return(VFS_RETURNED_DONE
);
2442 return(VFS_RETURNED
);
2446 * Get statistics on all filesystems.
2449 getfsstat(__unused proc_t p
, struct getfsstat_args
*uap
, int *retval
)
2451 struct __mac_getfsstat_args muap
;
2453 muap
.buf
= uap
->buf
;
2454 muap
.bufsize
= uap
->bufsize
;
2455 muap
.mac
= USER_ADDR_NULL
;
2457 muap
.flags
= uap
->flags
;
2459 return (__mac_getfsstat(p
, &muap
, retval
));
2463 * __mac_getfsstat: Get MAC-related file system statistics
2465 * Parameters: p (ignored)
2466 * uap User argument descriptor (see below)
2467 * retval Count of file system statistics (N stats)
2469 * Indirect: uap->bufsize Buffer size
2470 * uap->macsize MAC info size
2471 * uap->buf Buffer where information will be returned
2473 * uap->flags File system flags
2476 * Returns: 0 Success
2481 __mac_getfsstat(__unused proc_t p
, struct __mac_getfsstat_args
*uap
, int *retval
)
2485 size_t count
, maxcount
, bufsize
, macsize
;
2486 struct getfsstat_struct fst
;
2488 bufsize
= (size_t) uap
->bufsize
;
2489 macsize
= (size_t) uap
->macsize
;
2491 if (IS_64BIT_PROCESS(p
)) {
2492 maxcount
= bufsize
/ sizeof(struct user64_statfs
);
2495 maxcount
= bufsize
/ sizeof(struct user32_statfs
);
2503 if (uap
->mac
!= USER_ADDR_NULL
) {
2508 count
= (macsize
/ (IS_64BIT_PROCESS(p
) ? 8 : 4));
2509 if (count
!= maxcount
)
2512 /* Copy in the array */
2513 MALLOC(mp0
, u_int32_t
*, macsize
, M_MACTEMP
, M_WAITOK
);
2518 error
= copyin(uap
->mac
, mp0
, macsize
);
2520 FREE(mp0
, M_MACTEMP
);
2524 /* Normalize to an array of user_addr_t */
2525 MALLOC(mp
, user_addr_t
*, count
* sizeof(user_addr_t
), M_MACTEMP
, M_WAITOK
);
2527 FREE(mp0
, M_MACTEMP
);
2531 for (i
= 0; i
< count
; i
++) {
2532 if (IS_64BIT_PROCESS(p
))
2533 mp
[i
] = ((user_addr_t
*)mp0
)[i
];
2535 mp
[i
] = (user_addr_t
)mp0
[i
];
2537 FREE(mp0
, M_MACTEMP
);
2544 fst
.flags
= uap
->flags
;
2547 fst
.maxcount
= maxcount
;
2550 vfs_iterate(0, getfsstat_callback
, &fst
);
2553 FREE(mp
, M_MACTEMP
);
2556 KAUTH_DEBUG("ERROR - %s gets %d", p
->p_comm
, fst
.error
);
2560 if (fst
.sfsp
&& fst
.count
> fst
.maxcount
)
2561 *retval
= fst
.maxcount
;
2563 *retval
= fst
.count
;
2568 getfsstat64_callback(mount_t mp
, void * arg
)
2570 struct getfsstat_struct
*fstp
= (struct getfsstat_struct
*)arg
;
2571 struct vfsstatfs
*sp
;
2574 if (fstp
->sfsp
&& fstp
->count
< fstp
->maxcount
) {
2575 sp
= &mp
->mnt_vfsstat
;
2577 * If MNT_NOWAIT is specified, do not refresh the fsstat
2578 * cache. MNT_WAIT overrides MNT_NOWAIT.
2580 * We treat MNT_DWAIT as MNT_WAIT for all instances of
2581 * getfsstat, since the constants are out of the same
2584 if (((fstp
->flags
& MNT_NOWAIT
) == 0 ||
2585 (fstp
->flags
& (MNT_WAIT
| MNT_DWAIT
))) &&
2586 (error
= vfs_update_vfsstat(mp
, vfs_context_current(), VFS_USER_EVENT
))) {
2587 KAUTH_DEBUG("vfs_update_vfsstat returned %d", error
);
2588 return(VFS_RETURNED
);
2591 error
= statfs64_common(mp
, sp
, fstp
->sfsp
);
2593 fstp
->error
= error
;
2594 return(VFS_RETURNED_DONE
);
2596 fstp
->sfsp
+= sizeof(struct statfs64
);
2599 return(VFS_RETURNED
);
2603 * Get statistics on all file systems in 64 bit mode.
2606 getfsstat64(__unused proc_t p
, struct getfsstat64_args
*uap
, int *retval
)
2609 int count
, maxcount
;
2610 struct getfsstat_struct fst
;
2612 maxcount
= uap
->bufsize
/ sizeof(struct statfs64
);
2618 fst
.flags
= uap
->flags
;
2621 fst
.maxcount
= maxcount
;
2623 vfs_iterate(0, getfsstat64_callback
, &fst
);
2626 KAUTH_DEBUG("ERROR - %s gets %d", p
->p_comm
, fst
.error
);
2630 if (fst
.sfsp
&& fst
.count
> fst
.maxcount
)
2631 *retval
= fst
.maxcount
;
2633 *retval
= fst
.count
;
2639 * Change current working directory to a given file descriptor.
2643 common_fchdir(proc_t p
, struct fchdir_args
*uap
, int per_thread
)
2645 struct filedesc
*fdp
= p
->p_fd
;
2651 vfs_context_t ctx
= vfs_context_current();
2653 AUDIT_ARG(fd
, uap
->fd
);
2654 if (per_thread
&& uap
->fd
== -1) {
2656 * Switching back from per-thread to per process CWD; verify we
2657 * in fact have one before proceeding. The only success case
2658 * for this code path is to return 0 preemptively after zapping
2659 * the thread structure contents.
2661 thread_t th
= vfs_context_thread(ctx
);
2663 uthread_t uth
= get_bsdthread_info(th
);
2665 uth
->uu_cdir
= NULLVP
;
2666 if (tvp
!= NULLVP
) {
2674 if ( (error
= file_vnode(uap
->fd
, &vp
)) )
2676 if ( (error
= vnode_getwithref(vp
)) ) {
2681 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
2683 if (vp
->v_type
!= VDIR
) {
2689 error
= mac_vnode_check_chdir(ctx
, vp
);
2693 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
);
2697 while (!error
&& (mp
= vp
->v_mountedhere
) != NULL
) {
2698 if (vfs_busy(mp
, LK_NOWAIT
)) {
2702 error
= VFS_ROOT(mp
, &tdp
, ctx
);
2711 if ( (error
= vnode_ref(vp
)) )
2716 thread_t th
= vfs_context_thread(ctx
);
2718 uthread_t uth
= get_bsdthread_info(th
);
2721 OSBitOrAtomic(P_THCWD
, &p
->p_flag
);
2746 fchdir(proc_t p
, struct fchdir_args
*uap
, __unused
int32_t *retval
)
2748 return common_fchdir(p
, uap
, 0);
2752 __pthread_fchdir(proc_t p
, struct __pthread_fchdir_args
*uap
, __unused
int32_t *retval
)
2754 return common_fchdir(p
, (void *)uap
, 1);
2758 * Change current working directory (".").
2760 * Returns: 0 Success
2761 * change_dir:ENOTDIR
2763 * vnode_ref:ENOENT No such file or directory
2767 common_chdir(proc_t p
, struct chdir_args
*uap
, int per_thread
)
2769 struct filedesc
*fdp
= p
->p_fd
;
2771 struct nameidata nd
;
2773 vfs_context_t ctx
= vfs_context_current();
2775 NDINIT(&nd
, LOOKUP
, OP_CHDIR
, FOLLOW
| AUDITVNPATH1
,
2776 UIO_USERSPACE
, uap
->path
, ctx
);
2777 error
= change_dir(&nd
, ctx
);
2780 if ( (error
= vnode_ref(nd
.ni_vp
)) ) {
2781 vnode_put(nd
.ni_vp
);
2785 * drop the iocount we picked up in change_dir
2787 vnode_put(nd
.ni_vp
);
2790 thread_t th
= vfs_context_thread(ctx
);
2792 uthread_t uth
= get_bsdthread_info(th
);
2794 uth
->uu_cdir
= nd
.ni_vp
;
2795 OSBitOrAtomic(P_THCWD
, &p
->p_flag
);
2797 vnode_rele(nd
.ni_vp
);
2803 fdp
->fd_cdir
= nd
.ni_vp
;
2817 * Change current working directory (".") for the entire process
2819 * Parameters: p Process requesting the call
2820 * uap User argument descriptor (see below)
2823 * Indirect parameters: uap->path Directory path
2825 * Returns: 0 Success
2826 * common_chdir: ENOTDIR
2827 * common_chdir: ENOENT No such file or directory
2832 chdir(proc_t p
, struct chdir_args
*uap
, __unused
int32_t *retval
)
2834 return common_chdir(p
, (void *)uap
, 0);
2840 * Change current working directory (".") for a single thread
2842 * Parameters: p Process requesting the call
2843 * uap User argument descriptor (see below)
2846 * Indirect parameters: uap->path Directory path
2848 * Returns: 0 Success
2849 * common_chdir: ENOTDIR
2850 * common_chdir: ENOENT No such file or directory
2855 __pthread_chdir(proc_t p
, struct __pthread_chdir_args
*uap
, __unused
int32_t *retval
)
2857 return common_chdir(p
, (void *)uap
, 1);
2862 * Change notion of root (``/'') directory.
2866 chroot(proc_t p
, struct chroot_args
*uap
, __unused
int32_t *retval
)
2868 struct filedesc
*fdp
= p
->p_fd
;
2870 struct nameidata nd
;
2872 vfs_context_t ctx
= vfs_context_current();
2874 if ((error
= suser(kauth_cred_get(), &p
->p_acflag
)))
2877 NDINIT(&nd
, LOOKUP
, OP_CHROOT
, FOLLOW
| AUDITVNPATH1
,
2878 UIO_USERSPACE
, uap
->path
, ctx
);
2879 error
= change_dir(&nd
, ctx
);
2884 error
= mac_vnode_check_chroot(ctx
, nd
.ni_vp
,
2887 vnode_put(nd
.ni_vp
);
2892 if ( (error
= vnode_ref(nd
.ni_vp
)) ) {
2893 vnode_put(nd
.ni_vp
);
2896 vnode_put(nd
.ni_vp
);
2900 fdp
->fd_rdir
= nd
.ni_vp
;
2901 fdp
->fd_flags
|= FD_CHROOT
;
2911 * Common routine for chroot and chdir.
2913 * Returns: 0 Success
2914 * ENOTDIR Not a directory
2915 * namei:??? [anything namei can return]
2916 * vnode_authorize:??? [anything vnode_authorize can return]
2919 change_dir(struct nameidata
*ndp
, vfs_context_t ctx
)
2924 if ((error
= namei(ndp
)))
2929 if (vp
->v_type
!= VDIR
) {
2935 error
= mac_vnode_check_chdir(ctx
, vp
);
2942 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_SEARCH
, ctx
);
2952 * Check permissions, allocate an open file structure,
2953 * and call the device open routine if any.
2955 * Returns: 0 Success
2966 * XXX Need to implement uid, gid
2969 open1(vfs_context_t ctx
, struct nameidata
*ndp
, int uflags
, struct vnode_attr
*vap
, int32_t *retval
)
2971 proc_t p
= vfs_context_proc(ctx
);
2972 uthread_t uu
= get_bsdthread_info(vfs_context_thread(ctx
));
2973 struct filedesc
*fdp
= p
->p_fd
;
2974 struct fileproc
*fp
;
2977 struct fileproc
*nfp
;
2978 int type
, indx
, error
;
2980 int no_controlling_tty
= 0;
2981 int deny_controlling_tty
= 0;
2982 struct session
*sessp
= SESSION_NULL
;
2983 struct vfs_context context
= *vfs_context_current(); /* local copy */
2987 if ((oflags
& O_ACCMODE
) == O_ACCMODE
)
2989 flags
= FFLAGS(uflags
);
2991 AUDIT_ARG(fflags
, oflags
);
2992 AUDIT_ARG(mode
, vap
->va_mode
);
2994 if ( (error
= falloc(p
, &nfp
, &indx
, ctx
)) ) {
2998 uu
->uu_dupfd
= -indx
- 1;
3000 if (!(p
->p_flag
& P_CONTROLT
)) {
3001 sessp
= proc_session(p
);
3002 no_controlling_tty
= 1;
3004 * If conditions would warrant getting a controlling tty if
3005 * the device being opened is a tty (see ttyopen in tty.c),
3006 * but the open flags deny it, set a flag in the session to
3009 if (SESS_LEADER(p
, sessp
) &&
3010 sessp
->s_ttyvp
== NULL
&&
3011 (flags
& O_NOCTTY
)) {
3012 session_lock(sessp
);
3013 sessp
->s_flags
|= S_NOCTTY
;
3014 session_unlock(sessp
);
3015 deny_controlling_tty
= 1;
3019 if ((error
= vn_open_auth(ndp
, &flags
, vap
))) {
3020 if ((error
== ENODEV
|| error
== ENXIO
) && (uu
->uu_dupfd
>= 0)){ /* XXX from fdopen */
3021 if ((error
= dupfdopen(fdp
, indx
, uu
->uu_dupfd
, flags
, error
)) == 0) {
3022 fp_drop(p
, indx
, NULL
, 0);
3024 if (deny_controlling_tty
) {
3025 session_lock(sessp
);
3026 sessp
->s_flags
&= ~S_NOCTTY
;
3027 session_unlock(sessp
);
3029 if (sessp
!= SESSION_NULL
)
3030 session_rele(sessp
);
3034 if (error
== ERESTART
)
3036 fp_free(p
, indx
, fp
);
3038 if (deny_controlling_tty
) {
3039 session_lock(sessp
);
3040 sessp
->s_flags
&= ~S_NOCTTY
;
3041 session_unlock(sessp
);
3043 if (sessp
!= SESSION_NULL
)
3044 session_rele(sessp
);
3050 fp
->f_fglob
->fg_flag
= flags
& (FMASK
| O_EVTONLY
);
3051 fp
->f_fglob
->fg_type
= DTYPE_VNODE
;
3052 fp
->f_fglob
->fg_ops
= &vnops
;
3053 fp
->f_fglob
->fg_data
= (caddr_t
)vp
;
3055 if (flags
& (O_EXLOCK
| O_SHLOCK
)) {
3056 lf
.l_whence
= SEEK_SET
;
3059 if (flags
& O_EXLOCK
)
3060 lf
.l_type
= F_WRLCK
;
3062 lf
.l_type
= F_RDLCK
;
3064 if ((flags
& FNONBLOCK
) == 0)
3067 error
= mac_file_check_lock(vfs_context_ucred(ctx
), fp
->f_fglob
,
3072 if ((error
= VNOP_ADVLOCK(vp
, (caddr_t
)fp
->f_fglob
, F_SETLK
, &lf
, type
, ctx
)))
3074 fp
->f_fglob
->fg_flag
|= FHASLOCK
;
3077 /* try to truncate by setting the size attribute */
3078 if ((flags
& O_TRUNC
) && ((error
= vnode_setsize(vp
, (off_t
)0, 0, ctx
)) != 0))
3082 * If the open flags denied the acquisition of a controlling tty,
3083 * clear the flag in the session structure that prevented the lower
3084 * level code from assigning one.
3086 if (deny_controlling_tty
) {
3087 session_lock(sessp
);
3088 sessp
->s_flags
&= ~S_NOCTTY
;
3089 session_unlock(sessp
);
3093 * If a controlling tty was set by the tty line discipline, then we
3094 * want to set the vp of the tty into the session structure. We have
3095 * a race here because we can't get to the vp for the tp in ttyopen,
3096 * because it's not passed as a parameter in the open path.
3098 if (no_controlling_tty
&& (p
->p_flag
& P_CONTROLT
)) {
3102 * We already have a ref from vn_open_auth(), so we can demand another reference.
3104 error
= vnode_ref_ext(vp
, 0, VNODE_REF_FORCE
);
3106 panic("vnode_ref_ext() with VNODE_REF_FORCE failed?!");
3109 session_lock(sessp
);
3110 ttyvp
= sessp
->s_ttyvp
;
3111 sessp
->s_ttyvp
= vp
;
3112 sessp
->s_ttyvid
= vnode_vid(vp
);
3113 session_unlock(sessp
);
3114 if (ttyvp
!= NULLVP
)
3121 if (flags
& O_CLOEXEC
)
3122 *fdflags(p
, indx
) |= UF_EXCLOSE
;
3123 procfdtbl_releasefd(p
, indx
, NULL
);
3124 fp_drop(p
, indx
, fp
, 1);
3129 if (sessp
!= SESSION_NULL
)
3130 session_rele(sessp
);
3133 if (deny_controlling_tty
) {
3134 session_lock(sessp
);
3135 sessp
->s_flags
&= ~S_NOCTTY
;
3136 session_unlock(sessp
);
3138 if (sessp
!= SESSION_NULL
)
3139 session_rele(sessp
);
3141 /* Modify local copy (to not damage thread copy) */
3142 context
.vc_ucred
= fp
->f_fglob
->fg_cred
;
3144 vn_close(vp
, fp
->f_fglob
->fg_flag
, &context
);
3146 fp_free(p
, indx
, fp
);
3153 * open_extended: open a file given a path name; with extended argument list (including extended security (ACL)).
3155 * Parameters: p Process requesting the open
3156 * uap User argument descriptor (see below)
3157 * retval Pointer to an area to receive the
3158 * return calue from the system call
3160 * Indirect: uap->path Path to open (same as 'open')
3161 * uap->flags Flags to open (same as 'open'
3162 * uap->uid UID to set, if creating
3163 * uap->gid GID to set, if creating
3164 * uap->mode File mode, if creating (same as 'open')
3165 * uap->xsecurity ACL to set, if creating
3167 * Returns: 0 Success
3170 * Notes: The kauth_filesec_t in 'va', if any, is in host byte order.
3172 * XXX: We should enummerate the possible errno values here, and where
3173 * in the code they originated.
3176 open_extended(proc_t p
, struct open_extended_args
*uap
, int32_t *retval
)
3178 struct filedesc
*fdp
= p
->p_fd
;
3180 kauth_filesec_t xsecdst
;
3181 struct vnode_attr va
;
3182 struct nameidata nd
;
3185 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
3188 if ((uap
->xsecurity
!= USER_ADDR_NULL
) &&
3189 ((ciferror
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0))
3193 cmode
= ((uap
->mode
&~ fdp
->fd_cmask
) & ALLPERMS
) & ~S_ISTXT
;
3194 VATTR_SET(&va
, va_mode
, cmode
);
3195 if (uap
->uid
!= KAUTH_UID_NONE
)
3196 VATTR_SET(&va
, va_uid
, uap
->uid
);
3197 if (uap
->gid
!= KAUTH_GID_NONE
)
3198 VATTR_SET(&va
, va_gid
, uap
->gid
);
3199 if (xsecdst
!= NULL
)
3200 VATTR_SET(&va
, va_acl
, &xsecdst
->fsec_acl
);
3202 NDINIT(&nd
, LOOKUP
, OP_OPEN
, FOLLOW
| AUDITVNPATH1
, UIO_USERSPACE
,
3203 uap
->path
, vfs_context_current());
3205 ciferror
= open1(vfs_context_current(), &nd
, uap
->flags
, &va
, retval
);
3206 if (xsecdst
!= NULL
)
3207 kauth_filesec_free(xsecdst
);
3213 open(proc_t p
, struct open_args
*uap
, int32_t *retval
)
3215 __pthread_testcancel(1);
3216 return(open_nocancel(p
, (struct open_nocancel_args
*)uap
, retval
));
3220 open_nocancel(proc_t p
, struct open_nocancel_args
*uap
, int32_t *retval
)
3222 struct filedesc
*fdp
= p
->p_fd
;
3223 struct vnode_attr va
;
3224 struct nameidata nd
;
3228 /* Mask off all but regular access permissions */
3229 cmode
= ((uap
->mode
&~ fdp
->fd_cmask
) & ALLPERMS
) & ~S_ISTXT
;
3230 VATTR_SET(&va
, va_mode
, cmode
& ACCESSPERMS
);
3232 NDINIT(&nd
, LOOKUP
, OP_OPEN
, FOLLOW
| AUDITVNPATH1
, UIO_USERSPACE
,
3233 uap
->path
, vfs_context_current());
3235 return(open1(vfs_context_current(), &nd
, uap
->flags
, &va
, retval
));
3240 * Create a special file.
3242 static int mkfifo1(vfs_context_t ctx
, user_addr_t upath
, struct vnode_attr
*vap
);
3245 mknod(proc_t p
, struct mknod_args
*uap
, __unused
int32_t *retval
)
3247 struct vnode_attr va
;
3248 vfs_context_t ctx
= vfs_context_current();
3250 struct nameidata nd
;
3254 VATTR_SET(&va
, va_mode
, (uap
->mode
& ALLPERMS
) & ~p
->p_fd
->fd_cmask
);
3255 VATTR_SET(&va
, va_rdev
, uap
->dev
);
3257 /* If it's a mknod() of a FIFO, call mkfifo1() instead */
3258 if ((uap
->mode
& S_IFMT
) == S_IFIFO
)
3259 return(mkfifo1(ctx
, uap
->path
, &va
));
3261 AUDIT_ARG(mode
, uap
->mode
);
3262 AUDIT_ARG(value32
, uap
->dev
);
3264 if ((error
= suser(vfs_context_ucred(ctx
), &p
->p_acflag
)))
3266 NDINIT(&nd
, CREATE
, OP_MKNOD
, LOCKPARENT
| AUDITVNPATH1
,
3267 UIO_USERSPACE
, uap
->path
, ctx
);
3279 switch (uap
->mode
& S_IFMT
) {
3280 case S_IFMT
: /* used by badsect to flag bad sectors */
3281 VATTR_SET(&va
, va_type
, VBAD
);
3284 VATTR_SET(&va
, va_type
, VCHR
);
3287 VATTR_SET(&va
, va_type
, VBLK
);
3295 error
= mac_vnode_check_create(ctx
,
3296 nd
.ni_dvp
, &nd
.ni_cnd
, &va
);
3301 if ((error
= vnode_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
)) != 0)
3304 if ((error
= vn_create(dvp
, &vp
, &nd
, &va
, 0, 0, NULL
, ctx
)) != 0)
3308 int update_flags
= 0;
3310 // Make sure the name & parent pointers are hooked up
3311 if (vp
->v_name
== NULL
)
3312 update_flags
|= VNODE_UPDATE_NAME
;
3313 if (vp
->v_parent
== NULLVP
)
3314 update_flags
|= VNODE_UPDATE_PARENT
;
3317 vnode_update_identity(vp
, dvp
, nd
.ni_cnd
.cn_nameptr
, nd
.ni_cnd
.cn_namelen
, nd
.ni_cnd
.cn_hash
, update_flags
);
3320 add_fsevent(FSE_CREATE_FILE
, ctx
,
3328 * nameidone has to happen before we vnode_put(dvp)
3329 * since it may need to release the fs_nodelock on the dvp
3341 * Create a named pipe.
3343 * Returns: 0 Success
3346 * vnode_authorize:???
3350 mkfifo1(vfs_context_t ctx
, user_addr_t upath
, struct vnode_attr
*vap
)
3354 struct nameidata nd
;
3356 NDINIT(&nd
, CREATE
, OP_MKFIFO
, LOCKPARENT
| AUDITVNPATH1
,
3357 UIO_USERSPACE
, upath
, ctx
);
3364 /* check that this is a new file and authorize addition */
3369 VATTR_SET(vap
, va_type
, VFIFO
);
3371 if ((error
= vn_authorize_create(dvp
, &nd
.ni_cnd
, vap
, ctx
, NULL
)) != 0)
3374 error
= vn_create(dvp
, &vp
, &nd
, vap
, 0, 0, NULL
, ctx
);
3377 * nameidone has to happen before we vnode_put(dvp)
3378 * since it may need to release the fs_nodelock on the dvp
3391 * mkfifo_extended: Create a named pipe; with extended argument list (including extended security (ACL)).
3393 * Parameters: p Process requesting the open
3394 * uap User argument descriptor (see below)
3397 * Indirect: uap->path Path to fifo (same as 'mkfifo')
3398 * uap->uid UID to set
3399 * uap->gid GID to set
3400 * uap->mode File mode to set (same as 'mkfifo')
3401 * uap->xsecurity ACL to set, if creating
3403 * Returns: 0 Success
3406 * Notes: The kauth_filesec_t in 'va', if any, is in host byte order.
3408 * XXX: We should enummerate the possible errno values here, and where
3409 * in the code they originated.
3412 mkfifo_extended(proc_t p
, struct mkfifo_extended_args
*uap
, __unused
int32_t *retval
)
3415 kauth_filesec_t xsecdst
;
3416 struct vnode_attr va
;
3418 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
3420 xsecdst
= KAUTH_FILESEC_NONE
;
3421 if (uap
->xsecurity
!= USER_ADDR_NULL
) {
3422 if ((ciferror
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0)
3427 VATTR_SET(&va
, va_mode
, (uap
->mode
& ALLPERMS
) & ~p
->p_fd
->fd_cmask
);
3428 if (uap
->uid
!= KAUTH_UID_NONE
)
3429 VATTR_SET(&va
, va_uid
, uap
->uid
);
3430 if (uap
->gid
!= KAUTH_GID_NONE
)
3431 VATTR_SET(&va
, va_gid
, uap
->gid
);
3432 if (xsecdst
!= KAUTH_FILESEC_NONE
)
3433 VATTR_SET(&va
, va_acl
, &xsecdst
->fsec_acl
);
3435 ciferror
= mkfifo1(vfs_context_current(), uap
->path
, &va
);
3437 if (xsecdst
!= KAUTH_FILESEC_NONE
)
3438 kauth_filesec_free(xsecdst
);
3444 mkfifo(proc_t p
, struct mkfifo_args
*uap
, __unused
int32_t *retval
)
3446 struct vnode_attr va
;
3449 VATTR_SET(&va
, va_mode
, (uap
->mode
& ALLPERMS
) & ~p
->p_fd
->fd_cmask
);
3451 return(mkfifo1(vfs_context_current(), uap
->path
, &va
));
3456 my_strrchr(char *p
, int ch
)
3460 for (save
= NULL
;; ++p
) {
3469 extern int safe_getpath(struct vnode
*dvp
, char *leafname
, char *path
, int _len
, int *truncated_path
);
3472 safe_getpath(struct vnode
*dvp
, char *leafname
, char *path
, int _len
, int *truncated_path
)
3474 int ret
, len
= _len
;
3476 *truncated_path
= 0;
3477 ret
= vn_getpath(dvp
, path
, &len
);
3478 if (ret
== 0 && len
< (MAXPATHLEN
- 1)) {
3481 len
+= strlcpy(&path
[len
], leafname
, MAXPATHLEN
-len
) + 1;
3482 if (len
> MAXPATHLEN
) {
3485 // the string got truncated!
3486 *truncated_path
= 1;
3487 ptr
= my_strrchr(path
, '/');
3489 *ptr
= '\0'; // chop off the string at the last directory component
3491 len
= strlen(path
) + 1;
3494 } else if (ret
== 0) {
3495 *truncated_path
= 1;
3496 } else if (ret
!= 0) {
3497 struct vnode
*mydvp
=dvp
;
3499 if (ret
!= ENOSPC
) {
3500 printf("safe_getpath: failed to get the path for vp %p (%s) : err %d\n",
3501 dvp
, dvp
->v_name
? dvp
->v_name
: "no-name", ret
);
3503 *truncated_path
= 1;
3506 if (mydvp
->v_parent
!= NULL
) {
3507 mydvp
= mydvp
->v_parent
;
3508 } else if (mydvp
->v_mount
) {
3509 strlcpy(path
, mydvp
->v_mount
->mnt_vfsstat
.f_mntonname
, _len
);
3512 // no parent and no mount point? only thing is to punt and say "/" changed
3513 strlcpy(path
, "/", _len
);
3518 if (mydvp
== NULL
) {
3523 ret
= vn_getpath(mydvp
, path
, &len
);
3524 } while (ret
== ENOSPC
);
3532 * Make a hard file link.
3534 * Returns: 0 Success
3539 * vnode_authorize:???
3544 link(__unused proc_t p
, struct link_args
*uap
, __unused
int32_t *retval
)
3546 vnode_t vp
, dvp
, lvp
;
3547 struct nameidata nd
;
3548 vfs_context_t ctx
= vfs_context_current();
3553 int need_event
, has_listeners
;
3554 char *target_path
= NULL
;
3557 vp
= dvp
= lvp
= NULLVP
;
3559 /* look up the object we are linking to */
3560 NDINIT(&nd
, LOOKUP
, OP_LOOKUP
, FOLLOW
| AUDITVNPATH1
,
3561 UIO_USERSPACE
, uap
->path
, ctx
);
3570 * Normally, linking to directories is not supported.
3571 * However, some file systems may have limited support.
3573 if (vp
->v_type
== VDIR
) {
3574 if (!(vp
->v_mount
->mnt_vtable
->vfc_vfsflags
& VFC_VFSDIRLINKS
)) {
3575 error
= EPERM
; /* POSIX */
3578 /* Linking to a directory requires ownership. */
3579 if (!kauth_cred_issuser(vfs_context_ucred(ctx
))) {
3580 struct vnode_attr dva
;
3583 VATTR_WANTED(&dva
, va_uid
);
3584 if (vnode_getattr(vp
, &dva
, ctx
) != 0 ||
3585 !VATTR_IS_SUPPORTED(&dva
, va_uid
) ||
3586 (dva
.va_uid
!= kauth_cred_getuid(vfs_context_ucred(ctx
)))) {
3593 /* lookup the target node */
3597 nd
.ni_cnd
.cn_nameiop
= CREATE
;
3598 nd
.ni_cnd
.cn_flags
= LOCKPARENT
| AUDITVNPATH2
| CN_NBMOUNTLOOK
;
3599 nd
.ni_dirp
= uap
->link
;
3607 if ((error
= mac_vnode_check_link(ctx
, dvp
, vp
, &nd
.ni_cnd
)) != 0)
3611 /* or to anything that kauth doesn't want us to (eg. immutable items) */
3612 if ((error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_LINKTARGET
, ctx
)) != 0)
3615 /* target node must not exist */
3616 if (lvp
!= NULLVP
) {
3620 /* cannot link across mountpoints */
3621 if (vnode_mount(vp
) != vnode_mount(dvp
)) {
3626 /* authorize creation of the target note */
3627 if ((error
= vnode_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
)) != 0)
3630 /* and finally make the link */
3631 error
= VNOP_LINK(vp
, dvp
, &nd
.ni_cnd
, ctx
);
3636 need_event
= need_fsevent(FSE_CREATE_FILE
, dvp
);
3640 has_listeners
= kauth_authorize_fileop_has_listeners();
3642 if (need_event
|| has_listeners
) {
3643 char *link_to_path
= NULL
;
3644 int len
, link_name_len
;
3646 /* build the path to the new link file */
3647 GET_PATH(target_path
);
3648 if (target_path
== NULL
) {
3653 len
= safe_getpath(dvp
, nd
.ni_cnd
.cn_nameptr
, target_path
, MAXPATHLEN
, &truncated
);
3655 if (has_listeners
) {
3656 /* build the path to file we are linking to */
3657 GET_PATH(link_to_path
);
3658 if (link_to_path
== NULL
) {
3663 link_name_len
= MAXPATHLEN
;
3664 vn_getpath(vp
, link_to_path
, &link_name_len
);
3667 * Call out to allow 3rd party notification of rename.
3668 * Ignore result of kauth_authorize_fileop call.
3670 kauth_authorize_fileop(vfs_context_ucred(ctx
), KAUTH_FILEOP_LINK
,
3671 (uintptr_t)link_to_path
, (uintptr_t)target_path
);
3672 if (link_to_path
!= NULL
) {
3673 RELEASE_PATH(link_to_path
);
3678 /* construct fsevent */
3679 if (get_fse_info(vp
, &finfo
, ctx
) == 0) {
3681 finfo
.mode
|= FSE_TRUNCATED_PATH
;
3684 // build the path to the destination of the link
3685 add_fsevent(FSE_CREATE_FILE
, ctx
,
3686 FSE_ARG_STRING
, len
, target_path
,
3687 FSE_ARG_FINFO
, &finfo
,
3691 add_fsevent(FSE_STAT_CHANGED
, ctx
,
3692 FSE_ARG_VNODE
, vp
->v_parent
,
3700 * nameidone has to happen before we vnode_put(dvp)
3701 * since it may need to release the fs_nodelock on the dvp
3704 if (target_path
!= NULL
) {
3705 RELEASE_PATH(target_path
);
3717 * Make a symbolic link.
3719 * We could add support for ACLs here too...
3723 symlink(proc_t p
, struct symlink_args
*uap
, __unused
int32_t *retval
)
3725 struct vnode_attr va
;
3728 struct nameidata nd
;
3729 vfs_context_t ctx
= vfs_context_current();
3733 MALLOC_ZONE(path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
3734 error
= copyinstr(uap
->path
, path
, MAXPATHLEN
, &dummy
);
3737 AUDIT_ARG(text
, path
); /* This is the link string */
3739 NDINIT(&nd
, CREATE
, OP_SYMLINK
, LOCKPARENT
| AUDITVNPATH1
,
3740 UIO_USERSPACE
, uap
->link
, ctx
);
3748 VATTR_SET(&va
, va_type
, VLNK
);
3749 VATTR_SET(&va
, va_mode
, ACCESSPERMS
& ~p
->p_fd
->fd_cmask
);
3751 error
= mac_vnode_check_create(ctx
,
3752 dvp
, &nd
.ni_cnd
, &va
);
3765 error
= vnode_authorize(dvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
);
3766 /* get default ownership, etc. */
3768 error
= vnode_authattr_new(dvp
, &va
, 0, ctx
);
3770 error
= VNOP_SYMLINK(dvp
, &vp
, &nd
.ni_cnd
, &va
, path
, ctx
);
3772 /* do fallback attribute handling */
3774 error
= vnode_setattr_fallback(vp
, &va
, ctx
);
3777 int update_flags
= 0;
3780 nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
3782 nd
.ni_op
= OP_LOOKUP
;
3784 nd
.ni_cnd
.cn_flags
= 0;
3792 #if 0 /* XXX - kauth_todo - is KAUTH_FILEOP_SYMLINK needed? */
3793 /* call out to allow 3rd party notification of rename.
3794 * Ignore result of kauth_authorize_fileop call.
3796 if (kauth_authorize_fileop_has_listeners() &&
3798 char *new_link_path
= NULL
;
3801 /* build the path to the new link file */
3802 new_link_path
= get_pathbuff();
3804 vn_getpath(dvp
, new_link_path
, &len
);
3805 if ((len
+ 1 + nd
.ni_cnd
.cn_namelen
+ 1) < MAXPATHLEN
) {
3806 new_link_path
[len
- 1] = '/';
3807 strlcpy(&new_link_path
[len
], nd
.ni_cnd
.cn_nameptr
, MAXPATHLEN
-len
);
3810 kauth_authorize_fileop(vfs_context_ucred(ctx
), KAUTH_FILEOP_SYMLINK
,
3811 (uintptr_t)path
, (uintptr_t)new_link_path
);
3812 if (new_link_path
!= NULL
)
3813 release_pathbuff(new_link_path
);
3816 // Make sure the name & parent pointers are hooked up
3817 if (vp
->v_name
== NULL
)
3818 update_flags
|= VNODE_UPDATE_NAME
;
3819 if (vp
->v_parent
== NULLVP
)
3820 update_flags
|= VNODE_UPDATE_PARENT
;
3823 vnode_update_identity(vp
, dvp
, nd
.ni_cnd
.cn_nameptr
, nd
.ni_cnd
.cn_namelen
, nd
.ni_cnd
.cn_hash
, update_flags
);
3826 add_fsevent(FSE_CREATE_FILE
, ctx
,
3834 * nameidone has to happen before we vnode_put(dvp)
3835 * since it may need to release the fs_nodelock on the dvp
3843 FREE_ZONE(path
, MAXPATHLEN
, M_NAMEI
);
3849 * Delete a whiteout from the filesystem.
3850 * XXX authorization not implmented for whiteouts
3853 undelete(__unused proc_t p
, struct undelete_args
*uap
, __unused
int32_t *retval
)
3856 struct nameidata nd
;
3857 vfs_context_t ctx
= vfs_context_current();
3860 NDINIT(&nd
, DELETE
, OP_UNLINK
, LOCKPARENT
| DOWHITEOUT
| AUDITVNPATH1
,
3861 UIO_USERSPACE
, uap
->path
, ctx
);
3868 if (vp
== NULLVP
&& (nd
.ni_cnd
.cn_flags
& ISWHITEOUT
)) {
3869 error
= VNOP_WHITEOUT(dvp
, &nd
.ni_cnd
, DELETE
, ctx
);
3874 * nameidone has to happen before we vnode_put(dvp)
3875 * since it may need to release the fs_nodelock on the dvp
3888 * Delete a name from the filesystem.
3892 unlink1(vfs_context_t ctx
, struct nameidata
*ndp
, int nodelbusy
)
3896 struct componentname
*cnp
;
3901 struct vnode_attr va
;
3905 int has_listeners
= 0;
3906 int truncated_path
=0;
3908 struct vnode_attr
*vap
= NULL
;
3911 /* unlink or delete is allowed on rsrc forks and named streams */
3912 ndp
->ni_cnd
.cn_flags
|= CN_ALLOWRSRCFORK
;
3915 ndp
->ni_cnd
.cn_flags
|= LOCKPARENT
;
3916 ndp
->ni_flag
|= NAMEI_COMPOUNDREMOVE
;
3928 /* With Carbon delete semantics, busy files cannot be deleted */
3930 flags
|= VNODE_REMOVE_NODELETEBUSY
;
3934 batched
= vnode_compound_remove_available(vp
);
3936 * The root of a mounted filesystem cannot be deleted.
3938 if (vp
->v_flag
& VROOT
) {
3943 error
= vn_authorize_unlink(dvp
, vp
, cnp
, ctx
, NULL
);
3951 if (!vnode_compound_remove_available(dvp
)) {
3952 panic("No vp, but no compound remove?");
3957 need_event
= need_fsevent(FSE_DELETE
, dvp
);
3960 if ((vp
->v_flag
& VISHARDLINK
) == 0) {
3961 /* XXX need to get these data in batched VNOP */
3962 get_fse_info(vp
, &finfo
, ctx
);
3965 error
= vfs_get_notify_attributes(&va
);
3974 has_listeners
= kauth_authorize_fileop_has_listeners();
3975 if (need_event
|| has_listeners
) {
3983 len
= safe_getpath(dvp
, ndp
->ni_cnd
.cn_nameptr
, path
, MAXPATHLEN
, &truncated_path
);
3987 if (ndp
->ni_cnd
.cn_flags
& CN_WANTSRSRCFORK
)
3988 error
= vnode_removenamedstream(dvp
, vp
, XATTR_RESOURCEFORK_NAME
, 0, ctx
);
3992 error
= vn_remove(dvp
, &ndp
->ni_vp
, ndp
, flags
, vap
, ctx
);
3994 if (error
== EKEEPLOOKING
) {
3996 panic("EKEEPLOOKING, but not a filesystem that supports compound VNOPs?");
3999 if ((ndp
->ni_flag
& NAMEI_CONTLOOKUP
) == 0) {
4000 panic("EKEEPLOOKING, but continue flag not set?");
4003 if (vnode_isdir(vp
)) {
4007 goto lookup_continue
;
4012 * Call out to allow 3rd party notification of delete.
4013 * Ignore result of kauth_authorize_fileop call.
4016 if (has_listeners
) {
4017 kauth_authorize_fileop(vfs_context_ucred(ctx
),
4018 KAUTH_FILEOP_DELETE
,
4023 if (vp
->v_flag
& VISHARDLINK
) {
4025 // if a hardlink gets deleted we want to blow away the
4026 // v_parent link because the path that got us to this
4027 // instance of the link is no longer valid. this will
4028 // force the next call to get the path to ask the file
4029 // system instead of just following the v_parent link.
4031 vnode_update_identity(vp
, NULL
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
4036 if (vp
->v_flag
& VISHARDLINK
) {
4037 get_fse_info(vp
, &finfo
, ctx
);
4039 vnode_get_fse_info_from_vap(vp
, &finfo
, vap
);
4041 if (truncated_path
) {
4042 finfo
.mode
|= FSE_TRUNCATED_PATH
;
4044 add_fsevent(FSE_DELETE
, ctx
,
4045 FSE_ARG_STRING
, len
, path
,
4046 FSE_ARG_FINFO
, &finfo
,
4057 /* recycle the deleted rsrc fork vnode to force a reclaim, which
4058 * will cause its shadow file to go away if necessary.
4060 if (vp
&& (vnode_isnamedstream(vp
)) &&
4061 (vp
->v_parent
!= NULLVP
) &&
4062 vnode_isshadow(vp
)) {
4067 * nameidone has to happen before we vnode_put(dvp)
4068 * since it may need to release the fs_nodelock on the dvp
4079 * Delete a name from the filesystem using POSIX semantics.
4082 unlink(__unused proc_t p
, struct unlink_args
*uap
, __unused
int32_t *retval
)
4084 struct nameidata nd
;
4085 vfs_context_t ctx
= vfs_context_current();
4087 NDINIT(&nd
, DELETE
, OP_UNLINK
, AUDITVNPATH1
, UIO_USERSPACE
,
4089 return unlink1(ctx
, &nd
, 0);
4093 * Delete a name from the filesystem using Carbon semantics.
4096 delete(__unused proc_t p
, struct delete_args
*uap
, __unused
int32_t *retval
)
4098 struct nameidata nd
;
4099 vfs_context_t ctx
= vfs_context_current();
4101 NDINIT(&nd
, DELETE
, OP_UNLINK
, AUDITVNPATH1
, UIO_USERSPACE
,
4103 return unlink1(ctx
, &nd
, 1);
4107 * Reposition read/write file offset.
4110 lseek(proc_t p
, struct lseek_args
*uap
, off_t
*retval
)
4112 struct fileproc
*fp
;
4114 struct vfs_context
*ctx
;
4115 off_t offset
= uap
->offset
, file_size
;
4118 if ( (error
= fp_getfvp(p
,uap
->fd
, &fp
, &vp
)) ) {
4119 if (error
== ENOTSUP
)
4123 if (vnode_isfifo(vp
)) {
4129 ctx
= vfs_context_current();
4131 if (uap
->whence
== L_INCR
&& uap
->offset
== 0)
4132 error
= mac_file_check_get_offset(vfs_context_ucred(ctx
),
4135 error
= mac_file_check_change_offset(vfs_context_ucred(ctx
),
4142 if ( (error
= vnode_getwithref(vp
)) ) {
4147 switch (uap
->whence
) {
4149 offset
+= fp
->f_fglob
->fg_offset
;
4152 if ((error
= vnode_size(vp
, &file_size
, ctx
)) != 0)
4154 offset
+= file_size
;
4162 if (uap
->offset
> 0 && offset
< 0) {
4163 /* Incremented/relative move past max size */
4167 * Allow negative offsets on character devices, per
4168 * POSIX 1003.1-2001. Most likely for writing disk
4171 if (offset
< 0 && vp
->v_type
!= VCHR
) {
4172 /* Decremented/relative move before start */
4176 fp
->f_fglob
->fg_offset
= offset
;
4177 *retval
= fp
->f_fglob
->fg_offset
;
4183 * An lseek can affect whether data is "available to read." Use
4184 * hint of NOTE_NONE so no EVFILT_VNODE events fire
4186 post_event_if_success(vp
, error
, NOTE_NONE
);
4187 (void)vnode_put(vp
);
4194 * Check access permissions.
4196 * Returns: 0 Success
4197 * vnode_authorize:???
4200 access1(vnode_t vp
, vnode_t dvp
, int uflags
, vfs_context_t ctx
)
4202 kauth_action_t action
;
4206 * If just the regular access bits, convert them to something
4207 * that vnode_authorize will understand.
4209 if (!(uflags
& _ACCESS_EXTENDED_MASK
)) {
4212 action
|= KAUTH_VNODE_READ_DATA
; /* aka KAUTH_VNODE_LIST_DIRECTORY */
4213 if (uflags
& W_OK
) {
4214 if (vnode_isdir(vp
)) {
4215 action
|= KAUTH_VNODE_ADD_FILE
|
4216 KAUTH_VNODE_ADD_SUBDIRECTORY
;
4217 /* might want delete rights here too */
4219 action
|= KAUTH_VNODE_WRITE_DATA
;
4222 if (uflags
& X_OK
) {
4223 if (vnode_isdir(vp
)) {
4224 action
|= KAUTH_VNODE_SEARCH
;
4226 action
|= KAUTH_VNODE_EXECUTE
;
4230 /* take advantage of definition of uflags */
4231 action
= uflags
>> 8;
4235 error
= mac_vnode_check_access(ctx
, vp
, uflags
);
4240 /* action == 0 means only check for existence */
4242 error
= vnode_authorize(vp
, dvp
, action
| KAUTH_VNODE_ACCESS
, ctx
);
4253 * access_extended: Check access permissions in bulk.
4255 * Description: uap->entries Pointer to an array of accessx
4256 * descriptor structs, plus one or
4257 * more NULL terminated strings (see
4258 * "Notes" section below).
4259 * uap->size Size of the area pointed to by
4261 * uap->results Pointer to the results array.
4263 * Returns: 0 Success
4264 * ENOMEM Insufficient memory
4265 * EINVAL Invalid arguments
4266 * namei:EFAULT Bad address
4267 * namei:ENAMETOOLONG Filename too long
4268 * namei:ENOENT No such file or directory
4269 * namei:ELOOP Too many levels of symbolic links
4270 * namei:EBADF Bad file descriptor
4271 * namei:ENOTDIR Not a directory
4276 * uap->results Array contents modified
4278 * Notes: The uap->entries are structured as an arbitrary length array
4279 * of accessx descriptors, followed by one or more NULL terminated
4282 * struct accessx_descriptor[0]
4284 * struct accessx_descriptor[n]
4285 * char name_data[0];
4287 * We determine the entry count by walking the buffer containing
4288 * the uap->entries argument descriptor. For each descriptor we
4289 * see, the valid values for the offset ad_name_offset will be
4290 * in the byte range:
4292 * [ uap->entries + sizeof(struct accessx_descriptor) ]
4294 * [ uap->entries + uap->size - 2 ]
4296 * since we must have at least one string, and the string must
4297 * be at least one character plus the NULL terminator in length.
4299 * XXX: Need to support the check-as uid argument
4302 access_extended(__unused proc_t p
, struct access_extended_args
*uap
, __unused
int32_t *retval
)
4304 struct accessx_descriptor
*input
= NULL
;
4305 errno_t
*result
= NULL
;
4308 unsigned int desc_max
, desc_actual
, i
, j
;
4309 struct vfs_context context
;
4310 struct nameidata nd
;
4314 #define ACCESSX_MAX_DESCR_ON_STACK 10
4315 struct accessx_descriptor stack_input
[ACCESSX_MAX_DESCR_ON_STACK
];
4317 context
.vc_ucred
= NULL
;
4320 * Validate parameters; if valid, copy the descriptor array and string
4321 * arguments into local memory. Before proceeding, the following
4322 * conditions must have been met:
4324 * o The total size is not permitted to exceed ACCESSX_MAX_TABLESIZE
4325 * o There must be sufficient room in the request for at least one
4326 * descriptor and a one yte NUL terminated string.
4327 * o The allocation of local storage must not fail.
4329 if (uap
->size
> ACCESSX_MAX_TABLESIZE
)
4331 if (uap
->size
< (sizeof(struct accessx_descriptor
) + 2))
4333 if (uap
->size
<= sizeof (stack_input
)) {
4334 input
= stack_input
;
4336 MALLOC(input
, struct accessx_descriptor
*, uap
->size
, M_TEMP
, M_WAITOK
);
4337 if (input
== NULL
) {
4342 error
= copyin(uap
->entries
, input
, uap
->size
);
4346 AUDIT_ARG(opaque
, input
, uap
->size
);
4349 * Force NUL termination of the copyin buffer to avoid nami() running
4350 * off the end. If the caller passes us bogus data, they may get a
4353 ((char *)input
)[uap
->size
- 1] = 0;
4356 * Access is defined as checking against the process' real identity,
4357 * even if operations are checking the effective identity. This
4358 * requires that we use a local vfs context.
4360 context
.vc_ucred
= kauth_cred_copy_real(kauth_cred_get());
4361 context
.vc_thread
= current_thread();
4364 * Find out how many entries we have, so we can allocate the result
4365 * array by walking the list and adjusting the count downward by the
4366 * earliest string offset we see.
4368 desc_max
= (uap
->size
- 2) / sizeof(struct accessx_descriptor
);
4369 desc_actual
= desc_max
;
4370 for (i
= 0; i
< desc_actual
; i
++) {
4372 * Take the offset to the name string for this entry and
4373 * convert to an input array index, which would be one off
4374 * the end of the array if this entry was the lowest-addressed
4377 j
= input
[i
].ad_name_offset
/ sizeof(struct accessx_descriptor
);
4380 * An offset greater than the max allowable offset is an error.
4381 * It is also an error for any valid entry to point
4382 * to a location prior to the end of the current entry, if
4383 * it's not a reference to the string of the previous entry.
4385 if (j
> desc_max
|| (j
!= 0 && j
<= i
)) {
4391 * An offset of 0 means use the previous descriptor's offset;
4392 * this is used to chain multiple requests for the same file
4393 * to avoid multiple lookups.
4396 /* This is not valid for the first entry */
4405 * If the offset of the string for this descriptor is before
4406 * what we believe is the current actual last descriptor,
4407 * then we need to adjust our estimate downward; this permits
4408 * the string table following the last descriptor to be out
4409 * of order relative to the descriptor list.
4411 if (j
< desc_actual
)
4416 * We limit the actual number of descriptors we are willing to process
4417 * to a hard maximum of ACCESSX_MAX_DESCRIPTORS. If the number being
4418 * requested does not exceed this limit,
4420 if (desc_actual
> ACCESSX_MAX_DESCRIPTORS
) {
4424 MALLOC(result
, errno_t
*, desc_actual
* sizeof(errno_t
), M_TEMP
, M_WAITOK
);
4425 if (result
== NULL
) {
4431 * Do the work by iterating over the descriptor entries we know to
4432 * at least appear to contain valid data.
4435 for (i
= 0; i
< desc_actual
; i
++) {
4437 * If the ad_name_offset is 0, then we use the previous
4438 * results to make the check; otherwise, we are looking up
4441 if (input
[i
].ad_name_offset
!= 0) {
4442 /* discard old vnodes */
4453 * Scan forward in the descriptor list to see if we
4454 * need the parent vnode. We will need it if we are
4455 * deleting, since we must have rights to remove
4456 * entries in the parent directory, as well as the
4457 * rights to delete the object itself.
4459 wantdelete
= input
[i
].ad_flags
& _DELETE_OK
;
4460 for (j
= i
+ 1; (j
< desc_actual
) && (input
[j
].ad_name_offset
== 0); j
++)
4461 if (input
[j
].ad_flags
& _DELETE_OK
)
4464 niopts
= FOLLOW
| AUDITVNPATH1
;
4466 /* need parent for vnode_authorize for deletion test */
4468 niopts
|= WANTPARENT
;
4471 NDINIT(&nd
, LOOKUP
, OP_ACCESS
, niopts
, UIO_SYSSPACE
,
4472 CAST_USER_ADDR_T(((const char *)input
) + input
[i
].ad_name_offset
),
4484 * Handle lookup errors.
4494 /* run this access check */
4495 result
[i
] = access1(vp
, dvp
, input
[i
].ad_flags
, &context
);
4498 /* fatal lookup error */
4504 AUDIT_ARG(data
, result
, sizeof(errno_t
), desc_actual
);
4506 /* copy out results */
4507 error
= copyout(result
, uap
->results
, desc_actual
* sizeof(errno_t
));
4510 if (input
&& input
!= stack_input
)
4511 FREE(input
, M_TEMP
);
4513 FREE(result
, M_TEMP
);
4518 if (IS_VALID_CRED(context
.vc_ucred
))
4519 kauth_cred_unref(&context
.vc_ucred
);
4525 * Returns: 0 Success
4526 * namei:EFAULT Bad address
4527 * namei:ENAMETOOLONG Filename too long
4528 * namei:ENOENT No such file or directory
4529 * namei:ELOOP Too many levels of symbolic links
4530 * namei:EBADF Bad file descriptor
4531 * namei:ENOTDIR Not a directory
4536 access(__unused proc_t p
, struct access_args
*uap
, __unused
int32_t *retval
)
4539 struct nameidata nd
;
4541 struct vfs_context context
;
4543 int is_namedstream
= 0;
4547 * Access is defined as checking against the process'
4548 * real identity, even if operations are checking the
4549 * effective identity. So we need to tweak the credential
4552 context
.vc_ucred
= kauth_cred_copy_real(kauth_cred_get());
4553 context
.vc_thread
= current_thread();
4555 niopts
= FOLLOW
| AUDITVNPATH1
;
4556 /* need parent for vnode_authorize for deletion test */
4557 if (uap
->flags
& _DELETE_OK
)
4558 niopts
|= WANTPARENT
;
4559 NDINIT(&nd
, LOOKUP
, OP_ACCESS
, niopts
, UIO_USERSPACE
,
4560 uap
->path
, &context
);
4563 /* access(F_OK) calls are allowed for resource forks. */
4564 if (uap
->flags
== F_OK
)
4565 nd
.ni_cnd
.cn_flags
|= CN_ALLOWRSRCFORK
;
4572 /* Grab reference on the shadow stream file vnode to
4573 * force an inactive on release which will mark it
4576 if (vnode_isnamedstream(nd
.ni_vp
) &&
4577 (nd
.ni_vp
->v_parent
!= NULLVP
) &&
4578 vnode_isshadow(nd
.ni_vp
)) {
4580 vnode_ref(nd
.ni_vp
);
4584 error
= access1(nd
.ni_vp
, nd
.ni_dvp
, uap
->flags
, &context
);
4587 if (is_namedstream
) {
4588 vnode_rele(nd
.ni_vp
);
4592 vnode_put(nd
.ni_vp
);
4593 if (uap
->flags
& _DELETE_OK
)
4594 vnode_put(nd
.ni_dvp
);
4598 kauth_cred_unref(&context
.vc_ucred
);
4604 * Returns: 0 Success
4611 stat2(vfs_context_t ctx
, struct nameidata
*ndp
, user_addr_t ub
, user_addr_t xsecurity
, user_addr_t xsecurity_size
, int isstat64
)
4618 struct user64_stat user64_sb
;
4619 struct user32_stat user32_sb
;
4620 struct user64_stat64 user64_sb64
;
4621 struct user32_stat64 user32_sb64
;
4625 kauth_filesec_t fsec
;
4626 size_t xsecurity_bufsize
;
4630 int is_namedstream
= 0;
4631 /* stat calls are allowed for resource forks. */
4632 ndp
->ni_cnd
.cn_flags
|= CN_ALLOWRSRCFORK
;
4637 fsec
= KAUTH_FILESEC_NONE
;
4639 statptr
= (void *)&source
;
4642 /* Grab reference on the shadow stream file vnode to
4643 * force an inactive on release which will mark it
4646 if (vnode_isnamedstream(ndp
->ni_vp
) &&
4647 (ndp
->ni_vp
->v_parent
!= NULLVP
) &&
4648 vnode_isshadow(ndp
->ni_vp
)) {
4650 vnode_ref(ndp
->ni_vp
);
4654 error
= vn_stat(ndp
->ni_vp
, statptr
, (xsecurity
!= USER_ADDR_NULL
? &fsec
: NULL
), isstat64
, ctx
);
4657 if (is_namedstream
) {
4658 vnode_rele(ndp
->ni_vp
);
4661 vnode_put(ndp
->ni_vp
);
4666 /* Zap spare fields */
4667 if (isstat64
!= 0) {
4668 source
.sb64
.st_lspare
= 0;
4669 source
.sb64
.st_qspare
[0] = 0LL;
4670 source
.sb64
.st_qspare
[1] = 0LL;
4671 if (IS_64BIT_PROCESS(vfs_context_proc(ctx
))) {
4672 munge_user64_stat64(&source
.sb64
, &dest
.user64_sb64
);
4673 my_size
= sizeof(dest
.user64_sb64
);
4674 sbp
= (caddr_t
)&dest
.user64_sb64
;
4676 munge_user32_stat64(&source
.sb64
, &dest
.user32_sb64
);
4677 my_size
= sizeof(dest
.user32_sb64
);
4678 sbp
= (caddr_t
)&dest
.user32_sb64
;
4681 * Check if we raced (post lookup) against the last unlink of a file.
4683 if ((source
.sb64
.st_nlink
== 0) && S_ISREG(source
.sb64
.st_mode
)) {
4684 source
.sb64
.st_nlink
= 1;
4687 source
.sb
.st_lspare
= 0;
4688 source
.sb
.st_qspare
[0] = 0LL;
4689 source
.sb
.st_qspare
[1] = 0LL;
4690 if (IS_64BIT_PROCESS(vfs_context_proc(ctx
))) {
4691 munge_user64_stat(&source
.sb
, &dest
.user64_sb
);
4692 my_size
= sizeof(dest
.user64_sb
);
4693 sbp
= (caddr_t
)&dest
.user64_sb
;
4695 munge_user32_stat(&source
.sb
, &dest
.user32_sb
);
4696 my_size
= sizeof(dest
.user32_sb
);
4697 sbp
= (caddr_t
)&dest
.user32_sb
;
4701 * Check if we raced (post lookup) against the last unlink of a file.
4703 if ((source
.sb
.st_nlink
== 0) && S_ISREG(source
.sb
.st_mode
)) {
4704 source
.sb
.st_nlink
= 1;
4707 if ((error
= copyout(sbp
, ub
, my_size
)) != 0)
4710 /* caller wants extended security information? */
4711 if (xsecurity
!= USER_ADDR_NULL
) {
4713 /* did we get any? */
4714 if (fsec
== KAUTH_FILESEC_NONE
) {
4715 if (susize(xsecurity_size
, 0) != 0) {
4720 /* find the user buffer size */
4721 xsecurity_bufsize
= fusize(xsecurity_size
);
4723 /* copy out the actual data size */
4724 if (susize(xsecurity_size
, KAUTH_FILESEC_COPYSIZE(fsec
)) != 0) {
4729 /* if the caller supplied enough room, copy out to it */
4730 if (xsecurity_bufsize
>= KAUTH_FILESEC_COPYSIZE(fsec
))
4731 error
= copyout(fsec
, xsecurity
, KAUTH_FILESEC_COPYSIZE(fsec
));
4735 if (fsec
!= KAUTH_FILESEC_NONE
)
4736 kauth_filesec_free(fsec
);
4741 * Get file status; this version follows links.
4743 * Returns: 0 Success
4744 * stat2:??? [see stat2() in this file]
4747 stat1(user_addr_t path
, user_addr_t ub
, user_addr_t xsecurity
, user_addr_t xsecurity_size
, int isstat64
)
4749 struct nameidata nd
;
4750 vfs_context_t ctx
= vfs_context_current();
4752 NDINIT(&nd
, LOOKUP
, OP_GETATTR
, NOTRIGGER
| FOLLOW
| AUDITVNPATH1
,
4753 UIO_USERSPACE
, path
, ctx
);
4754 return(stat2(ctx
, &nd
, ub
, xsecurity
, xsecurity_size
, isstat64
));
4758 * stat_extended: Get file status; with extended security (ACL).
4760 * Parameters: p (ignored)
4761 * uap User argument descriptor (see below)
4764 * Indirect: uap->path Path of file to get status from
4765 * uap->ub User buffer (holds file status info)
4766 * uap->xsecurity ACL to get (extended security)
4767 * uap->xsecurity_size Size of ACL
4769 * Returns: 0 Success
4774 stat_extended(__unused proc_t p
, struct stat_extended_args
*uap
, __unused
int32_t *retval
)
4776 return (stat1(uap
->path
, uap
->ub
, uap
->xsecurity
, uap
->xsecurity_size
, 0));
4780 * Returns: 0 Success
4781 * stat1:??? [see stat1() in this file]
4784 stat(__unused proc_t p
, struct stat_args
*uap
, __unused
int32_t *retval
)
4786 return(stat1(uap
->path
, uap
->ub
, 0, 0, 0));
4790 stat64(__unused proc_t p
, struct stat64_args
*uap
, __unused
int32_t *retval
)
4792 return(stat1(uap
->path
, uap
->ub
, 0, 0, 1));
4796 * stat64_extended: Get file status; can handle large inode numbers; with extended security (ACL).
4798 * Parameters: p (ignored)
4799 * uap User argument descriptor (see below)
4802 * Indirect: uap->path Path of file to get status from
4803 * uap->ub User buffer (holds file status info)
4804 * uap->xsecurity ACL to get (extended security)
4805 * uap->xsecurity_size Size of ACL
4807 * Returns: 0 Success
4812 stat64_extended(__unused proc_t p
, struct stat64_extended_args
*uap
, __unused
int32_t *retval
)
4814 return (stat1(uap
->path
, uap
->ub
, uap
->xsecurity
, uap
->xsecurity_size
, 1));
4817 * Get file status; this version does not follow links.
4820 lstat1(user_addr_t path
, user_addr_t ub
, user_addr_t xsecurity
, user_addr_t xsecurity_size
, int isstat64
)
4822 struct nameidata nd
;
4823 vfs_context_t ctx
= vfs_context_current();
4825 NDINIT(&nd
, LOOKUP
, OP_GETATTR
, NOTRIGGER
| NOFOLLOW
| AUDITVNPATH1
,
4826 UIO_USERSPACE
, path
, ctx
);
4828 return(stat2(ctx
, &nd
, ub
, xsecurity
, xsecurity_size
, isstat64
));
4832 * lstat_extended: Get file status; does not follow links; with extended security (ACL).
4834 * Parameters: p (ignored)
4835 * uap User argument descriptor (see below)
4838 * Indirect: uap->path Path of file to get status from
4839 * uap->ub User buffer (holds file status info)
4840 * uap->xsecurity ACL to get (extended security)
4841 * uap->xsecurity_size Size of ACL
4843 * Returns: 0 Success
4848 lstat_extended(__unused proc_t p
, struct lstat_extended_args
*uap
, __unused
int32_t *retval
)
4850 return (lstat1(uap
->path
, uap
->ub
, uap
->xsecurity
, uap
->xsecurity_size
, 0));
4854 lstat(__unused proc_t p
, struct lstat_args
*uap
, __unused
int32_t *retval
)
4856 return(lstat1(uap
->path
, uap
->ub
, 0, 0, 0));
4860 lstat64(__unused proc_t p
, struct lstat64_args
*uap
, __unused
int32_t *retval
)
4862 return(lstat1(uap
->path
, uap
->ub
, 0, 0, 1));
4866 * lstat64_extended: Get file status; can handle large inode numbers; does not
4867 * follow links; with extended security (ACL).
4869 * Parameters: p (ignored)
4870 * uap User argument descriptor (see below)
4873 * Indirect: uap->path Path of file to get status from
4874 * uap->ub User buffer (holds file status info)
4875 * uap->xsecurity ACL to get (extended security)
4876 * uap->xsecurity_size Size of ACL
4878 * Returns: 0 Success
4883 lstat64_extended(__unused proc_t p
, struct lstat64_extended_args
*uap
, __unused
int32_t *retval
)
4885 return (lstat1(uap
->path
, uap
->ub
, uap
->xsecurity
, uap
->xsecurity_size
, 1));
4889 * Get configurable pathname variables.
4891 * Returns: 0 Success
4895 * Notes: Global implementation constants are intended to be
4896 * implemented in this function directly; all other constants
4897 * are per-FS implementation, and therefore must be handled in
4898 * each respective FS, instead.
4900 * XXX We implement some things globally right now that should actually be
4901 * XXX per-FS; we will need to deal with this at some point.
4905 pathconf(__unused proc_t p
, struct pathconf_args
*uap
, int32_t *retval
)
4908 struct nameidata nd
;
4909 vfs_context_t ctx
= vfs_context_current();
4911 NDINIT(&nd
, LOOKUP
, OP_PATHCONF
, FOLLOW
| AUDITVNPATH1
,
4912 UIO_USERSPACE
, uap
->path
, ctx
);
4917 error
= vn_pathconf(nd
.ni_vp
, uap
->name
, retval
, ctx
);
4919 vnode_put(nd
.ni_vp
);
4925 * Return target name of a symbolic link.
4929 readlink(proc_t p
, struct readlink_args
*uap
, int32_t *retval
)
4933 int spacetype
= proc_is64bit(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
4935 struct nameidata nd
;
4936 vfs_context_t ctx
= vfs_context_current();
4937 char uio_buf
[ UIO_SIZEOF(1) ];
4939 NDINIT(&nd
, LOOKUP
, OP_READLINK
, NOFOLLOW
| AUDITVNPATH1
,
4940 UIO_USERSPACE
, uap
->path
, ctx
);
4948 auio
= uio_createwithbuffer(1, 0, spacetype
, UIO_READ
,
4949 &uio_buf
[0], sizeof(uio_buf
));
4950 uio_addiov(auio
, uap
->buf
, uap
->count
);
4951 if (vp
->v_type
!= VLNK
)
4955 error
= mac_vnode_check_readlink(ctx
,
4959 error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_DATA
, ctx
);
4961 error
= VNOP_READLINK(vp
, auio
, ctx
);
4965 /* Safe: uio_resid() is bounded above by "count", and "count" is an int */
4966 *retval
= uap
->count
- (int)uio_resid(auio
);
4971 * Change file flags.
4974 chflags1(vnode_t vp
, int flags
, vfs_context_t ctx
)
4976 struct vnode_attr va
;
4977 kauth_action_t action
;
4981 VATTR_SET(&va
, va_flags
, flags
);
4984 error
= mac_vnode_check_setflags(ctx
, vp
, flags
);
4989 /* request authorisation, disregard immutability */
4990 if ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)
4993 * Request that the auth layer disregard those file flags it's allowed to when
4994 * authorizing this operation; we need to do this in order to be able to
4995 * clear immutable flags.
4997 if (action
&& ((error
= vnode_authorize(vp
, NULL
, action
| KAUTH_VNODE_NOIMMUTABLE
, ctx
)) != 0))
4999 error
= vnode_setattr(vp
, &va
, ctx
);
5001 if ((error
== 0) && !VATTR_IS_SUPPORTED(&va
, va_flags
)) {
5010 * Change flags of a file given a path name.
5014 chflags(__unused proc_t p
, struct chflags_args
*uap
, __unused
int32_t *retval
)
5017 vfs_context_t ctx
= vfs_context_current();
5019 struct nameidata nd
;
5021 AUDIT_ARG(fflags
, uap
->flags
);
5022 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, FOLLOW
| AUDITVNPATH1
,
5023 UIO_USERSPACE
, uap
->path
, ctx
);
5030 error
= chflags1(vp
, uap
->flags
, ctx
);
5036 * Change flags of a file given a file descriptor.
5040 fchflags(__unused proc_t p
, struct fchflags_args
*uap
, __unused
int32_t *retval
)
5045 AUDIT_ARG(fd
, uap
->fd
);
5046 AUDIT_ARG(fflags
, uap
->flags
);
5047 if ( (error
= file_vnode(uap
->fd
, &vp
)) )
5050 if ((error
= vnode_getwithref(vp
))) {
5055 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5057 error
= chflags1(vp
, uap
->flags
, vfs_context_current());
5064 * Change security information on a filesystem object.
5066 * Returns: 0 Success
5067 * EPERM Operation not permitted
5068 * vnode_authattr:??? [anything vnode_authattr can return]
5069 * vnode_authorize:??? [anything vnode_authorize can return]
5070 * vnode_setattr:??? [anything vnode_setattr can return]
5072 * Notes: If vnode_authattr or vnode_authorize return EACCES, it will be
5073 * translated to EPERM before being returned.
5076 chmod2(vfs_context_t ctx
, vnode_t vp
, struct vnode_attr
*vap
)
5078 kauth_action_t action
;
5081 AUDIT_ARG(mode
, vap
->va_mode
);
5082 /* XXX audit new args */
5085 /* chmod calls are not allowed for resource forks. */
5086 if (vp
->v_flag
& VISNAMEDSTREAM
) {
5092 error
= mac_vnode_check_setmode(ctx
, vp
, (mode_t
)vap
->va_mode
);
5097 /* make sure that the caller is allowed to set this security information */
5098 if (((error
= vnode_authattr(vp
, vap
, &action
, ctx
)) != 0) ||
5099 ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)) {
5100 if (error
== EACCES
)
5105 error
= vnode_setattr(vp
, vap
, ctx
);
5112 * Change mode of a file given a path name.
5114 * Returns: 0 Success
5115 * namei:??? [anything namei can return]
5116 * chmod2:??? [anything chmod2 can return]
5119 chmod1(vfs_context_t ctx
, user_addr_t path
, struct vnode_attr
*vap
)
5121 struct nameidata nd
;
5124 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, FOLLOW
| AUDITVNPATH1
,
5125 UIO_USERSPACE
, path
, ctx
);
5126 if ((error
= namei(&nd
)))
5128 error
= chmod2(ctx
, nd
.ni_vp
, vap
);
5129 vnode_put(nd
.ni_vp
);
5135 * chmod_extended: Change the mode of a file given a path name; with extended
5136 * argument list (including extended security (ACL)).
5138 * Parameters: p Process requesting the open
5139 * uap User argument descriptor (see below)
5142 * Indirect: uap->path Path to object (same as 'chmod')
5143 * uap->uid UID to set
5144 * uap->gid GID to set
5145 * uap->mode File mode to set (same as 'chmod')
5146 * uap->xsecurity ACL to set (or delete)
5148 * Returns: 0 Success
5151 * Notes: The kauth_filesec_t in 'va', if any, is in host byte order.
5153 * XXX: We should enummerate the possible errno values here, and where
5154 * in the code they originated.
5157 chmod_extended(__unused proc_t p
, struct chmod_extended_args
*uap
, __unused
int32_t *retval
)
5160 struct vnode_attr va
;
5161 kauth_filesec_t xsecdst
;
5163 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
5166 if (uap
->mode
!= -1)
5167 VATTR_SET(&va
, va_mode
, uap
->mode
& ALLPERMS
);
5168 if (uap
->uid
!= KAUTH_UID_NONE
)
5169 VATTR_SET(&va
, va_uid
, uap
->uid
);
5170 if (uap
->gid
!= KAUTH_GID_NONE
)
5171 VATTR_SET(&va
, va_gid
, uap
->gid
);
5174 switch(uap
->xsecurity
) {
5175 /* explicit remove request */
5176 case CAST_USER_ADDR_T((void *)1): /* _FILESEC_REMOVE_ACL */
5177 VATTR_SET(&va
, va_acl
, NULL
);
5180 case USER_ADDR_NULL
:
5183 if ((error
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0)
5185 VATTR_SET(&va
, va_acl
, &xsecdst
->fsec_acl
);
5186 KAUTH_DEBUG("CHMOD - setting ACL with %d entries", va
.va_acl
->acl_entrycount
);
5189 error
= chmod1(vfs_context_current(), uap
->path
, &va
);
5191 if (xsecdst
!= NULL
)
5192 kauth_filesec_free(xsecdst
);
5197 * Returns: 0 Success
5198 * chmod1:??? [anything chmod1 can return]
5201 chmod(__unused proc_t p
, struct chmod_args
*uap
, __unused
int32_t *retval
)
5203 struct vnode_attr va
;
5206 VATTR_SET(&va
, va_mode
, uap
->mode
& ALLPERMS
);
5208 return(chmod1(vfs_context_current(), uap
->path
, &va
));
5212 * Change mode of a file given a file descriptor.
5215 fchmod1(__unused proc_t p
, int fd
, struct vnode_attr
*vap
)
5222 if ((error
= file_vnode(fd
, &vp
)) != 0)
5224 if ((error
= vnode_getwithref(vp
)) != 0) {
5228 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5230 error
= chmod2(vfs_context_current(), vp
, vap
);
5231 (void)vnode_put(vp
);
5238 * fchmod_extended: Change mode of a file given a file descriptor; with
5239 * extended argument list (including extended security (ACL)).
5241 * Parameters: p Process requesting to change file mode
5242 * uap User argument descriptor (see below)
5245 * Indirect: uap->mode File mode to set (same as 'chmod')
5246 * uap->uid UID to set
5247 * uap->gid GID to set
5248 * uap->xsecurity ACL to set (or delete)
5249 * uap->fd File descriptor of file to change mode
5251 * Returns: 0 Success
5256 fchmod_extended(proc_t p
, struct fchmod_extended_args
*uap
, __unused
int32_t *retval
)
5259 struct vnode_attr va
;
5260 kauth_filesec_t xsecdst
;
5262 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
5265 if (uap
->mode
!= -1)
5266 VATTR_SET(&va
, va_mode
, uap
->mode
& ALLPERMS
);
5267 if (uap
->uid
!= KAUTH_UID_NONE
)
5268 VATTR_SET(&va
, va_uid
, uap
->uid
);
5269 if (uap
->gid
!= KAUTH_GID_NONE
)
5270 VATTR_SET(&va
, va_gid
, uap
->gid
);
5273 switch(uap
->xsecurity
) {
5274 case USER_ADDR_NULL
:
5275 VATTR_SET(&va
, va_acl
, NULL
);
5277 case CAST_USER_ADDR_T(-1):
5280 if ((error
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0)
5282 VATTR_SET(&va
, va_acl
, &xsecdst
->fsec_acl
);
5285 error
= fchmod1(p
, uap
->fd
, &va
);
5288 switch(uap
->xsecurity
) {
5289 case USER_ADDR_NULL
:
5290 case CAST_USER_ADDR_T(-1):
5293 if (xsecdst
!= NULL
)
5294 kauth_filesec_free(xsecdst
);
5300 fchmod(proc_t p
, struct fchmod_args
*uap
, __unused
int32_t *retval
)
5302 struct vnode_attr va
;
5305 VATTR_SET(&va
, va_mode
, uap
->mode
& ALLPERMS
);
5307 return(fchmod1(p
, uap
->fd
, &va
));
5312 * Set ownership given a path name.
5316 chown1(vfs_context_t ctx
, struct chown_args
*uap
, __unused
int32_t *retval
, int follow
)
5319 struct vnode_attr va
;
5321 struct nameidata nd
;
5322 kauth_action_t action
;
5324 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
5326 NDINIT(&nd
, LOOKUP
, OP_SETATTR
,
5327 (follow
? FOLLOW
: 0) | NOTRIGGER
| AUDITVNPATH1
,
5328 UIO_USERSPACE
, uap
->path
, ctx
);
5337 if (uap
->uid
!= VNOVAL
)
5338 VATTR_SET(&va
, va_uid
, uap
->uid
);
5339 if (uap
->gid
!= VNOVAL
)
5340 VATTR_SET(&va
, va_gid
, uap
->gid
);
5343 error
= mac_vnode_check_setowner(ctx
, vp
, uap
->uid
, uap
->gid
);
5348 /* preflight and authorize attribute changes */
5349 if ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)
5351 if (action
&& ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0))
5353 error
= vnode_setattr(vp
, &va
, ctx
);
5357 * EACCES is only allowed from namei(); permissions failure should
5358 * return EPERM, so we need to translate the error code.
5360 if (error
== EACCES
)
5368 chown(__unused proc_t p
, struct chown_args
*uap
, int32_t *retval
)
5370 return chown1(vfs_context_current(), uap
, retval
, 1);
5374 lchown(__unused proc_t p
, struct lchown_args
*uap
, int32_t *retval
)
5376 /* Argument list identical, but machine generated; cast for chown1() */
5377 return chown1(vfs_context_current(), (struct chown_args
*)uap
, retval
, 0);
5381 * Set ownership given a file descriptor.
5385 fchown(__unused proc_t p
, struct fchown_args
*uap
, __unused
int32_t *retval
)
5387 struct vnode_attr va
;
5388 vfs_context_t ctx
= vfs_context_current();
5391 kauth_action_t action
;
5393 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
5394 AUDIT_ARG(fd
, uap
->fd
);
5396 if ( (error
= file_vnode(uap
->fd
, &vp
)) )
5399 if ( (error
= vnode_getwithref(vp
)) ) {
5403 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5406 if (uap
->uid
!= VNOVAL
)
5407 VATTR_SET(&va
, va_uid
, uap
->uid
);
5408 if (uap
->gid
!= VNOVAL
)
5409 VATTR_SET(&va
, va_gid
, uap
->gid
);
5412 /* chown calls are not allowed for resource forks. */
5413 if (vp
->v_flag
& VISNAMEDSTREAM
) {
5420 error
= mac_vnode_check_setowner(ctx
, vp
, uap
->uid
, uap
->gid
);
5425 /* preflight and authorize attribute changes */
5426 if ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)
5428 if (action
&& ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)) {
5429 if (error
== EACCES
)
5433 error
= vnode_setattr(vp
, &va
, ctx
);
5436 (void)vnode_put(vp
);
5442 getutimes(user_addr_t usrtvp
, struct timespec
*tsp
)
5446 if (usrtvp
== USER_ADDR_NULL
) {
5447 struct timeval old_tv
;
5448 /* XXX Y2038 bug because of microtime argument */
5450 TIMEVAL_TO_TIMESPEC(&old_tv
, &tsp
[0]);
5453 if (IS_64BIT_PROCESS(current_proc())) {
5454 struct user64_timeval tv
[2];
5455 error
= copyin(usrtvp
, (void *)tv
, sizeof(tv
));
5458 TIMEVAL_TO_TIMESPEC(&tv
[0], &tsp
[0]);
5459 TIMEVAL_TO_TIMESPEC(&tv
[1], &tsp
[1]);
5461 struct user32_timeval tv
[2];
5462 error
= copyin(usrtvp
, (void *)tv
, sizeof(tv
));
5465 TIMEVAL_TO_TIMESPEC(&tv
[0], &tsp
[0]);
5466 TIMEVAL_TO_TIMESPEC(&tv
[1], &tsp
[1]);
5473 setutimes(vfs_context_t ctx
, vnode_t vp
, const struct timespec
*ts
,
5477 struct vnode_attr va
;
5478 kauth_action_t action
;
5480 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5483 VATTR_SET(&va
, va_access_time
, ts
[0]);
5484 VATTR_SET(&va
, va_modify_time
, ts
[1]);
5486 va
.va_vaflags
|= VA_UTIMES_NULL
;
5489 /* utimes calls are not allowed for resource forks. */
5490 if (vp
->v_flag
& VISNAMEDSTREAM
) {
5497 error
= mac_vnode_check_setutimes(ctx
, vp
, ts
[0], ts
[1]);
5501 if ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0) {
5502 if (!nullflag
&& error
== EACCES
)
5507 /* since we may not need to auth anything, check here */
5508 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)) {
5509 if (!nullflag
&& error
== EACCES
)
5513 error
= vnode_setattr(vp
, &va
, ctx
);
5520 * Set the access and modification times of a file.
5524 utimes(__unused proc_t p
, struct utimes_args
*uap
, __unused
int32_t *retval
)
5526 struct timespec ts
[2];
5529 struct nameidata nd
;
5530 vfs_context_t ctx
= vfs_context_current();
5533 * AUDIT: Needed to change the order of operations to do the
5534 * name lookup first because auditing wants the path.
5536 NDINIT(&nd
, LOOKUP
, OP_SETATTR
, FOLLOW
| AUDITVNPATH1
,
5537 UIO_USERSPACE
, uap
->path
, ctx
);
5544 * Fetch the user-supplied time. If usrtvp is USER_ADDR_NULL, we fetch
5545 * the current time instead.
5548 if ((error
= getutimes(usrtvp
, ts
)) != 0)
5551 error
= setutimes(ctx
, nd
.ni_vp
, ts
, usrtvp
== USER_ADDR_NULL
);
5554 vnode_put(nd
.ni_vp
);
5559 * Set the access and modification times of a file.
5563 futimes(__unused proc_t p
, struct futimes_args
*uap
, __unused
int32_t *retval
)
5565 struct timespec ts
[2];
5570 AUDIT_ARG(fd
, uap
->fd
);
5572 if ((error
= getutimes(usrtvp
, ts
)) != 0)
5574 if ((error
= file_vnode(uap
->fd
, &vp
)) != 0)
5576 if((error
= vnode_getwithref(vp
))) {
5581 error
= setutimes(vfs_context_current(), vp
, ts
, usrtvp
== 0);
5588 * Truncate a file given its path name.
5592 truncate(__unused proc_t p
, struct truncate_args
*uap
, __unused
int32_t *retval
)
5595 struct vnode_attr va
;
5596 vfs_context_t ctx
= vfs_context_current();
5598 struct nameidata nd
;
5599 kauth_action_t action
;
5601 if (uap
->length
< 0)
5603 NDINIT(&nd
, LOOKUP
, OP_TRUNCATE
, FOLLOW
| AUDITVNPATH1
,
5604 UIO_USERSPACE
, uap
->path
, ctx
);
5605 if ((error
= namei(&nd
)))
5612 VATTR_SET(&va
, va_data_size
, uap
->length
);
5615 error
= mac_vnode_check_truncate(ctx
, NOCRED
, vp
);
5620 if ((error
= vnode_authattr(vp
, &va
, &action
, ctx
)) != 0)
5622 if ((action
!= 0) && ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0))
5624 error
= vnode_setattr(vp
, &va
, ctx
);
5631 * Truncate a file given a file descriptor.
5635 ftruncate(proc_t p
, struct ftruncate_args
*uap
, int32_t *retval
)
5637 vfs_context_t ctx
= vfs_context_current();
5638 struct vnode_attr va
;
5640 struct fileproc
*fp
;
5644 AUDIT_ARG(fd
, uap
->fd
);
5645 if (uap
->length
< 0)
5648 if ( (error
= fp_lookup(p
,fd
,&fp
,0)) ) {
5652 if (fp
->f_fglob
->fg_type
== DTYPE_PSXSHM
) {
5653 error
= pshm_truncate(p
, fp
, uap
->fd
, uap
->length
, retval
);
5656 if (fp
->f_fglob
->fg_type
!= DTYPE_VNODE
) {
5661 vp
= (vnode_t
)fp
->f_fglob
->fg_data
;
5663 if ((fp
->f_fglob
->fg_flag
& FWRITE
) == 0) {
5664 AUDIT_ARG(vnpath_withref
, vp
, ARG_VNODE1
);
5669 if ((error
= vnode_getwithref(vp
)) != 0) {
5673 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5676 error
= mac_vnode_check_truncate(ctx
,
5677 fp
->f_fglob
->fg_cred
, vp
);
5679 (void)vnode_put(vp
);
5684 VATTR_SET(&va
, va_data_size
, uap
->length
);
5685 error
= vnode_setattr(vp
, &va
, ctx
);
5686 (void)vnode_put(vp
);
5694 * Sync an open file with synchronized I/O _file_ integrity completion
5698 fsync(proc_t p
, struct fsync_args
*uap
, __unused
int32_t *retval
)
5700 __pthread_testcancel(1);
5701 return(fsync_common(p
, uap
, MNT_WAIT
));
5706 * Sync an open file with synchronized I/O _file_ integrity completion
5708 * Notes: This is a legacy support function that does not test for
5709 * thread cancellation points.
5713 fsync_nocancel(proc_t p
, struct fsync_nocancel_args
*uap
, __unused
int32_t *retval
)
5715 return(fsync_common(p
, (struct fsync_args
*)uap
, MNT_WAIT
));
5720 * Sync an open file with synchronized I/O _data_ integrity completion
5724 fdatasync(proc_t p
, struct fdatasync_args
*uap
, __unused
int32_t *retval
)
5726 __pthread_testcancel(1);
5727 return(fsync_common(p
, (struct fsync_args
*)uap
, MNT_DWAIT
));
5734 * Common fsync code to support both synchronized I/O file integrity completion
5735 * (normal fsync) and synchronized I/O data integrity completion (fdatasync).
5737 * If 'flags' is MNT_DWAIT, the caller is requesting data integrity, which
5738 * will only guarantee that the file data contents are retrievable. If
5739 * 'flags' is MNT_WAIT, the caller is rewuesting file integrity, which also
5740 * includes additional metadata unnecessary for retrieving the file data
5741 * contents, such as atime, mtime, ctime, etc., also be committed to stable
5744 * Parameters: p The process
5745 * uap->fd The descriptor to synchronize
5746 * flags The data integrity flags
5748 * Returns: int Success
5749 * fp_getfvp:EBADF Bad file descriptor
5750 * fp_getfvp:ENOTSUP fd does not refer to a vnode
5751 * VNOP_FSYNC:??? unspecified
5753 * Notes: We use struct fsync_args because it is a short name, and all
5754 * caller argument structures are otherwise identical.
5757 fsync_common(proc_t p
, struct fsync_args
*uap
, int flags
)
5760 struct fileproc
*fp
;
5761 vfs_context_t ctx
= vfs_context_current();
5764 AUDIT_ARG(fd
, uap
->fd
);
5766 if ( (error
= fp_getfvp(p
, uap
->fd
, &fp
, &vp
)) )
5768 if ( (error
= vnode_getwithref(vp
)) ) {
5773 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
5775 error
= VNOP_FSYNC(vp
, flags
, ctx
);
5778 /* Sync resource fork shadow file if necessary. */
5780 (vp
->v_flag
& VISNAMEDSTREAM
) &&
5781 (vp
->v_parent
!= NULLVP
) &&
5782 vnode_isshadow(vp
) &&
5783 (fp
->f_flags
& FP_WRITTEN
)) {
5784 (void) vnode_flushnamedstream(vp
->v_parent
, vp
, ctx
);
5788 (void)vnode_put(vp
);
5794 * Duplicate files. Source must be a file, target must be a file or
5797 * XXX Copyfile authorisation checking is woefully inadequate, and will not
5798 * perform inheritance correctly.
5802 copyfile(__unused proc_t p
, struct copyfile_args
*uap
, __unused
int32_t *retval
)
5804 vnode_t tvp
, fvp
, tdvp
, sdvp
;
5805 struct nameidata fromnd
, tond
;
5807 vfs_context_t ctx
= vfs_context_current();
5809 /* Check that the flags are valid. */
5811 if (uap
->flags
& ~CPF_MASK
) {
5815 NDINIT(&fromnd
, LOOKUP
, OP_COPYFILE
, SAVESTART
| AUDITVNPATH1
,
5816 UIO_USERSPACE
, uap
->from
, ctx
);
5817 if ((error
= namei(&fromnd
)))
5821 NDINIT(&tond
, CREATE
, OP_LINK
,
5822 LOCKPARENT
| LOCKLEAF
| NOCACHE
| SAVESTART
| AUDITVNPATH2
| CN_NBMOUNTLOOK
,
5823 UIO_USERSPACE
, uap
->to
, ctx
);
5824 if ((error
= namei(&tond
))) {
5831 if (!(uap
->flags
& CPF_OVERWRITE
)) {
5836 if (fvp
->v_type
== VDIR
|| (tvp
&& tvp
->v_type
== VDIR
)) {
5841 if ((error
= vnode_authorize(tdvp
, NULL
, KAUTH_VNODE_ADD_FILE
, ctx
)) != 0)
5847 * If source is the same as the destination (that is the
5848 * same inode number) then there is nothing to do.
5849 * (fixed to have POSIX semantics - CSM 3/2/98)
5854 error
= VNOP_COPYFILE(fvp
, tdvp
, tvp
, &tond
.ni_cnd
, uap
->mode
, uap
->flags
, ctx
);
5856 sdvp
= tond
.ni_startdir
;
5858 * nameidone has to happen before we vnode_put(tdvp)
5859 * since it may need to release the fs_nodelock on the tdvp
5870 if (fromnd
.ni_startdir
)
5871 vnode_put(fromnd
.ni_startdir
);
5881 * Rename files. Source and destination must either both be directories,
5882 * or both not be directories. If target is a directory, it must be empty.
5886 rename(__unused proc_t p
, struct rename_args
*uap
, __unused
int32_t *retval
)
5890 struct nameidata fromnd
, tond
;
5891 vfs_context_t ctx
= vfs_context_current();
5896 const char *oname
= NULL
;
5897 char *from_name
= NULL
, *to_name
= NULL
;
5898 int from_len
=0, to_len
=0;
5899 int holding_mntlock
;
5900 mount_t locked_mp
= NULL
;
5901 vnode_t oparent
= NULLVP
;
5903 fse_info from_finfo
, to_finfo
;
5904 struct vnode_attr fva
, tva
;
5906 int from_truncated
=0, to_truncated
;
5908 struct vnode_attr
*fvap
, *tvap
;
5911 holding_mntlock
= 0;
5919 NDINIT(&fromnd
, DELETE
, OP_UNLINK
, WANTPARENT
| AUDITVNPATH1
,
5920 UIO_USERSPACE
, uap
->from
, ctx
);
5921 fromnd
.ni_flag
= NAMEI_COMPOUNDRENAME
;
5923 NDINIT(&tond
, RENAME
, OP_RENAME
, WANTPARENT
| AUDITVNPATH2
| CN_NBMOUNTLOOK
,
5924 UIO_USERSPACE
, uap
->to
, ctx
);
5925 tond
.ni_flag
= NAMEI_COMPOUNDRENAME
;
5928 if ((fromnd
.ni_flag
& NAMEI_CONTLOOKUP
) != 0 || !continuing
) {
5929 if ( (error
= namei(&fromnd
)) )
5931 fdvp
= fromnd
.ni_dvp
;
5934 if (fvp
&& fvp
->v_type
== VDIR
)
5935 tond
.ni_cnd
.cn_flags
|= WILLBEDIR
;
5938 if ((tond
.ni_flag
& NAMEI_CONTLOOKUP
) != 0 || !continuing
) {
5939 if ( (error
= namei(&tond
)) ) {
5941 * Translate error code for rename("dir1", "dir2/.").
5943 if (error
== EISDIR
&& fvp
->v_type
== VDIR
)
5951 batched
= vnode_compound_rename_available(fdvp
);
5954 * Claim: this check will never reject a valid rename.
5955 * For success, either fvp must be on the same mount as tdvp, or fvp must sit atop a vnode on the same mount as tdvp.
5956 * Suppose fdvp and tdvp are not on the same mount.
5957 * If fvp is on the same mount as tdvp, then fvp is not on the same mount as fdvp, so fvp is the root of its filesystem. If fvp is the root,
5958 * then you can't move it to within another dir on the same mountpoint.
5959 * If fvp sits atop a vnode on the same mount as fdvp, then that vnode must be part of the same mount as fdvp, which is a contradiction.
5961 * If this check passes, then we are safe to pass these vnodes to the same FS.
5963 if (fdvp
->v_mount
!= tdvp
->v_mount
) {
5967 goto skipped_lookup
;
5971 error
= vn_authorize_rename(fdvp
, fvp
, &fromnd
.ni_cnd
, tdvp
, tvp
, &tond
.ni_cnd
, ctx
, NULL
);
5973 if (error
== ENOENT
) {
5975 * We encountered a race where after doing the namei, tvp stops
5976 * being valid. If so, simply re-drive the rename call from the
5986 * If the source and destination are the same (i.e. they're
5987 * links to the same vnode) and the target file system is
5988 * case sensitive, then there is nothing to do.
5990 * XXX Come back to this.
5996 * Note: if _PC_CASE_SENSITIVE selector isn't supported,
5997 * then assume that this file system is case sensitive.
5999 if (VNOP_PATHCONF(fvp
, _PC_CASE_SENSITIVE
, &pathconf_val
, ctx
) != 0 ||
6000 pathconf_val
!= 0) {
6006 * Allow the renaming of mount points.
6007 * - target must not exist
6008 * - target must reside in the same directory as source
6009 * - union mounts cannot be renamed
6010 * - "/" cannot be renamed
6012 * XXX Handle this in VFS after a continued lookup (if we missed
6013 * in the cache to start off)
6015 if ((fvp
->v_flag
& VROOT
) &&
6016 (fvp
->v_type
== VDIR
) &&
6018 (fvp
->v_mountedhere
== NULL
) &&
6020 ((fvp
->v_mount
->mnt_flag
& (MNT_UNION
| MNT_ROOTFS
)) == 0) &&
6021 (fvp
->v_mount
->mnt_vnodecovered
!= NULLVP
)) {
6024 /* switch fvp to the covered vnode */
6025 coveredvp
= fvp
->v_mount
->mnt_vnodecovered
;
6026 if ( (vnode_getwithref(coveredvp
)) ) {
6036 * Check for cross-device rename.
6038 if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
6039 (tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
6045 * If source is the same as the destination (that is the
6046 * same inode number) then there is nothing to do...
6047 * EXCEPT if the underlying file system supports case
6048 * insensitivity and is case preserving. In this case
6049 * the file system needs to handle the special case of
6050 * getting the same vnode as target (fvp) and source (tvp).
6052 * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE
6053 * and _PC_CASE_PRESERVING can have this exception, and they need to
6054 * handle the special case of getting the same vnode as target and
6055 * source. NOTE: Then the target is unlocked going into vnop_rename,
6056 * so not to cause locking problems. There is a single reference on tvp.
6058 * NOTE - that fvp == tvp also occurs if they are hard linked and
6059 * that correct behaviour then is just to return success without doing
6062 * XXX filesystem should take care of this itself, perhaps...
6064 if (fvp
== tvp
&& fdvp
== tdvp
) {
6065 if (fromnd
.ni_cnd
.cn_namelen
== tond
.ni_cnd
.cn_namelen
&&
6066 !bcmp(fromnd
.ni_cnd
.cn_nameptr
, tond
.ni_cnd
.cn_nameptr
,
6067 fromnd
.ni_cnd
.cn_namelen
)) {
6072 if (holding_mntlock
&& fvp
->v_mount
!= locked_mp
) {
6074 * we're holding a reference and lock
6075 * on locked_mp, but it no longer matches
6076 * what we want to do... so drop our hold
6078 mount_unlock_renames(locked_mp
);
6079 mount_drop(locked_mp
, 0);
6080 holding_mntlock
= 0;
6082 if (tdvp
!= fdvp
&& fvp
->v_type
== VDIR
) {
6084 * serialize renames that re-shape
6085 * the tree... if holding_mntlock is
6086 * set, then we're ready to go...
6088 * first need to drop the iocounts
6089 * we picked up, second take the
6090 * lock to serialize the access,
6091 * then finally start the lookup
6092 * process over with the lock held
6094 if (!holding_mntlock
) {
6096 * need to grab a reference on
6097 * the mount point before we
6098 * drop all the iocounts... once
6099 * the iocounts are gone, the mount
6102 locked_mp
= fvp
->v_mount
;
6103 mount_ref(locked_mp
, 0);
6106 * nameidone has to happen before we vnode_put(tvp)
6107 * since it may need to release the fs_nodelock on the tvp
6116 * nameidone has to happen before we vnode_put(fdvp)
6117 * since it may need to release the fs_nodelock on the fvp
6124 mount_lock_renames(locked_mp
);
6125 holding_mntlock
= 1;
6131 * when we dropped the iocounts to take
6132 * the lock, we allowed the identity of
6133 * the various vnodes to change... if they did,
6134 * we may no longer be dealing with a rename
6135 * that reshapes the tree... once we're holding
6136 * the iocounts, the vnodes can't change type
6137 * so we're free to drop the lock at this point
6140 if (holding_mntlock
) {
6141 mount_unlock_renames(locked_mp
);
6142 mount_drop(locked_mp
, 0);
6143 holding_mntlock
= 0;
6147 // save these off so we can later verify that fvp is the same
6148 oname
= fvp
->v_name
;
6149 oparent
= fvp
->v_parent
;
6153 need_event
= need_fsevent(FSE_RENAME
, fdvp
);
6156 get_fse_info(fvp
, &from_finfo
, ctx
);
6158 error
= vfs_get_notify_attributes(&fva
);
6167 get_fse_info(tvp
, &to_finfo
, ctx
);
6168 } else if (batched
) {
6169 error
= vfs_get_notify_attributes(&tva
);
6179 #endif /* CONFIG_FSE */
6181 if (need_event
|| kauth_authorize_fileop_has_listeners()) {
6182 if (from_name
== NULL
) {
6183 GET_PATH(from_name
);
6184 if (from_name
== NULL
) {
6190 from_len
= safe_getpath(fdvp
, fromnd
.ni_cnd
.cn_nameptr
, from_name
, MAXPATHLEN
, &from_truncated
);
6192 if (to_name
== NULL
) {
6194 if (to_name
== NULL
) {
6200 to_len
= safe_getpath(tdvp
, tond
.ni_cnd
.cn_nameptr
, to_name
, MAXPATHLEN
, &to_truncated
);
6203 error
= vn_rename(fdvp
, &fvp
, &fromnd
.ni_cnd
, fvap
,
6204 tdvp
, &tvp
, &tond
.ni_cnd
, tvap
,
6207 if (holding_mntlock
) {
6209 * we can drop our serialization
6212 mount_unlock_renames(locked_mp
);
6213 mount_drop(locked_mp
, 0);
6214 holding_mntlock
= 0;
6217 if (error
== EKEEPLOOKING
) {
6218 if ((fromnd
.ni_flag
& NAMEI_CONTLOOKUP
) == 0) {
6219 if ((tond
.ni_flag
& NAMEI_CONTLOOKUP
) == 0) {
6220 panic("EKEEPLOOKING without NAMEI_CONTLOOKUP on either ndp?");
6227 goto continue_lookup
;
6231 * We may encounter a race in the VNOP where the destination didn't
6232 * exist when we did the namei, but it does by the time we go and
6233 * try to create the entry. In this case, we should re-drive this rename
6234 * call from the top again. Currently, only HFS bubbles out ERECYCLE,
6235 * but other filesystems susceptible to this race could return it, too.
6237 if (error
== ERECYCLE
) {
6244 /* call out to allow 3rd party notification of rename.
6245 * Ignore result of kauth_authorize_fileop call.
6247 kauth_authorize_fileop(vfs_context_ucred(ctx
),
6248 KAUTH_FILEOP_RENAME
,
6249 (uintptr_t)from_name
, (uintptr_t)to_name
);
6252 if (from_name
!= NULL
&& to_name
!= NULL
) {
6253 if (from_truncated
|| to_truncated
) {
6254 // set it here since only the from_finfo gets reported up to user space
6255 from_finfo
.mode
|= FSE_TRUNCATED_PATH
;
6259 vnode_get_fse_info_from_vap(tvp
, &to_finfo
, tvap
);
6262 vnode_get_fse_info_from_vap(fvp
, &from_finfo
, fvap
);
6266 add_fsevent(FSE_RENAME
, ctx
,
6267 FSE_ARG_STRING
, from_len
, from_name
,
6268 FSE_ARG_FINFO
, &from_finfo
,
6269 FSE_ARG_STRING
, to_len
, to_name
,
6270 FSE_ARG_FINFO
, &to_finfo
,
6273 add_fsevent(FSE_RENAME
, ctx
,
6274 FSE_ARG_STRING
, from_len
, from_name
,
6275 FSE_ARG_FINFO
, &from_finfo
,
6276 FSE_ARG_STRING
, to_len
, to_name
,
6280 #endif /* CONFIG_FSE */
6283 * update filesystem's mount point data
6286 char *cp
, *pathend
, *mpname
;
6292 mp
= fvp
->v_mountedhere
;
6294 if (vfs_busy(mp
, LK_NOWAIT
)) {
6298 MALLOC_ZONE(tobuf
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
6300 error
= copyinstr(uap
->to
, tobuf
, MAXPATHLEN
, &len
);
6302 /* find current mount point prefix */
6303 pathend
= &mp
->mnt_vfsstat
.f_mntonname
[0];
6304 for (cp
= pathend
; *cp
!= '\0'; ++cp
) {
6308 /* find last component of target name */
6309 for (mpname
= cp
= tobuf
; *cp
!= '\0'; ++cp
) {
6313 /* append name to prefix */
6314 maxlen
= MAXPATHLEN
- (pathend
- mp
->mnt_vfsstat
.f_mntonname
);
6315 bzero(pathend
, maxlen
);
6316 strlcpy(pathend
, mpname
, maxlen
);
6318 FREE_ZONE(tobuf
, MAXPATHLEN
, M_NAMEI
);
6323 * fix up name & parent pointers. note that we first
6324 * check that fvp has the same name/parent pointers it
6325 * had before the rename call... this is a 'weak' check
6328 * XXX oparent and oname may not be set in the compound vnop case
6330 if (batched
|| (oname
== fvp
->v_name
&& oparent
== fvp
->v_parent
)) {
6333 update_flags
= VNODE_UPDATE_NAME
;
6336 update_flags
|= VNODE_UPDATE_PARENT
;
6338 vnode_update_identity(fvp
, tdvp
, tond
.ni_cnd
.cn_nameptr
, tond
.ni_cnd
.cn_namelen
, tond
.ni_cnd
.cn_hash
, update_flags
);
6341 if (to_name
!= NULL
) {
6342 RELEASE_PATH(to_name
);
6345 if (from_name
!= NULL
) {
6346 RELEASE_PATH(from_name
);
6349 if (holding_mntlock
) {
6350 mount_unlock_renames(locked_mp
);
6351 mount_drop(locked_mp
, 0);
6352 holding_mntlock
= 0;
6356 * nameidone has to happen before we vnode_put(tdvp)
6357 * since it may need to release the fs_nodelock on the tdvp
6367 * nameidone has to happen before we vnode_put(fdvp)
6368 * since it may need to release the fs_nodelock on the fdvp
6378 * If things changed after we did the namei, then we will re-drive
6379 * this rename call from the top.
6390 * Make a directory file.
6392 * Returns: 0 Success
6395 * vnode_authorize:???
6400 mkdir1(vfs_context_t ctx
, user_addr_t path
, struct vnode_attr
*vap
)
6404 int update_flags
= 0;
6406 struct nameidata nd
;
6408 AUDIT_ARG(mode
, vap
->va_mode
);
6409 NDINIT(&nd
, CREATE
, OP_MKDIR
, LOCKPARENT
| AUDITVNPATH1
, UIO_USERSPACE
,
6411 nd
.ni_cnd
.cn_flags
|= WILLBEDIR
;
6412 nd
.ni_flag
= NAMEI_COMPOUNDMKDIR
;
6426 batched
= vnode_compound_mkdir_available(dvp
);
6428 VATTR_SET(vap
, va_type
, VDIR
);
6432 * Don't authorize in VFS for compound VNOP.... mkdir -p today assumes that it will
6433 * only get EXISTS or EISDIR for existing path components, and not that it could see
6434 * EACCESS/EPERM--so if we authorize for mkdir on "/" for "mkdir -p /tmp/foo/bar/baz"
6435 * it will fail in a spurious manner. Need to figure out if this is valid behavior.
6437 if ((error
= vn_authorize_mkdir(dvp
, &nd
.ni_cnd
, vap
, ctx
, NULL
)) != 0) {
6438 if (error
== EACCES
|| error
== EPERM
) {
6446 * Try a lookup without "NAMEI_COMPOUNDVNOP" to make sure we return EEXIST
6447 * rather than EACCESS if the target exists.
6449 NDINIT(&nd
, LOOKUP
, OP_MKDIR
, AUDITVNPATH1
, UIO_USERSPACE
,
6451 error2
= namei(&nd
);
6465 * make the directory
6467 if ((error
= vn_create(dvp
, &vp
, &nd
, vap
, 0, 0, NULL
, ctx
)) != 0) {
6468 if (error
== EKEEPLOOKING
) {
6470 goto continue_lookup
;
6476 // Make sure the name & parent pointers are hooked up
6477 if (vp
->v_name
== NULL
)
6478 update_flags
|= VNODE_UPDATE_NAME
;
6479 if (vp
->v_parent
== NULLVP
)
6480 update_flags
|= VNODE_UPDATE_PARENT
;
6483 vnode_update_identity(vp
, dvp
, nd
.ni_cnd
.cn_nameptr
, nd
.ni_cnd
.cn_namelen
, nd
.ni_cnd
.cn_hash
, update_flags
);
6486 add_fsevent(FSE_CREATE_DIR
, ctx
, FSE_ARG_VNODE
, vp
, FSE_ARG_DONE
);
6491 * nameidone has to happen before we vnode_put(dvp)
6492 * since it may need to release the fs_nodelock on the dvp
6505 * mkdir_extended: Create a directory; with extended security (ACL).
6507 * Parameters: p Process requesting to create the directory
6508 * uap User argument descriptor (see below)
6511 * Indirect: uap->path Path of directory to create
6512 * uap->mode Access permissions to set
6513 * uap->xsecurity ACL to set
6515 * Returns: 0 Success
6520 mkdir_extended(proc_t p
, struct mkdir_extended_args
*uap
, __unused
int32_t *retval
)
6523 kauth_filesec_t xsecdst
;
6524 struct vnode_attr va
;
6526 AUDIT_ARG(owner
, uap
->uid
, uap
->gid
);
6529 if ((uap
->xsecurity
!= USER_ADDR_NULL
) &&
6530 ((ciferror
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0))
6534 VATTR_SET(&va
, va_mode
, (uap
->mode
& ACCESSPERMS
) & ~p
->p_fd
->fd_cmask
);
6535 if (xsecdst
!= NULL
)
6536 VATTR_SET(&va
, va_acl
, &xsecdst
->fsec_acl
);
6538 ciferror
= mkdir1(vfs_context_current(), uap
->path
, &va
);
6539 if (xsecdst
!= NULL
)
6540 kauth_filesec_free(xsecdst
);
6545 mkdir(proc_t p
, struct mkdir_args
*uap
, __unused
int32_t *retval
)
6547 struct vnode_attr va
;
6550 VATTR_SET(&va
, va_mode
, (uap
->mode
& ACCESSPERMS
) & ~p
->p_fd
->fd_cmask
);
6552 return(mkdir1(vfs_context_current(), uap
->path
, &va
));
6556 * Remove a directory file.
6560 rmdir(__unused proc_t p
, struct rmdir_args
*uap
, __unused
int32_t *retval
)
6564 struct nameidata nd
;
6567 int has_listeners
= 0;
6570 vfs_context_t ctx
= vfs_context_current();
6572 struct vnode_attr va
;
6573 #endif /* CONFIG_FSE */
6574 struct vnode_attr
*vap
= NULL
;
6580 * This loop exists to restart rmdir in the unlikely case that two
6581 * processes are simultaneously trying to remove the same directory
6582 * containing orphaned appleDouble files.
6585 NDINIT(&nd
, DELETE
, OP_RMDIR
, LOCKPARENT
| AUDITVNPATH1
,
6586 UIO_USERSPACE
, uap
->path
, ctx
);
6587 nd
.ni_flag
= NAMEI_COMPOUNDRMDIR
;
6600 batched
= vnode_compound_rmdir_available(vp
);
6602 if (vp
->v_flag
& VROOT
) {
6604 * The root of a mounted filesystem cannot be deleted.
6611 * Removed a check here; we used to abort if vp's vid
6612 * was not the same as what we'd seen the last time around.
6613 * I do not think that check was valid, because if we retry
6614 * and all dirents are gone, the directory could legitimately
6615 * be recycled but still be present in a situation where we would
6616 * have had permission to delete. Therefore, we won't make
6617 * an effort to preserve that check now that we may not have a
6622 error
= vn_authorize_rmdir(dvp
, vp
, &nd
.ni_cnd
, ctx
, NULL
);
6630 if (!vnode_compound_rmdir_available(dvp
)) {
6631 panic("No error, but no compound rmdir?");
6638 need_event
= need_fsevent(FSE_DELETE
, dvp
);
6641 get_fse_info(vp
, &finfo
, ctx
);
6643 error
= vfs_get_notify_attributes(&va
);
6652 has_listeners
= kauth_authorize_fileop_has_listeners();
6653 if (need_event
|| has_listeners
) {
6662 len
= safe_getpath(dvp
, nd
.ni_cnd
.cn_nameptr
, path
, MAXPATHLEN
, &truncated
);
6665 finfo
.mode
|= FSE_TRUNCATED_PATH
;
6670 error
= vn_rmdir(dvp
, &vp
, &nd
, vap
, ctx
);
6673 /* Couldn't find a vnode */
6677 if (error
== EKEEPLOOKING
) {
6678 goto continue_lookup
;
6682 * Special case to remove orphaned AppleDouble
6683 * files. I don't like putting this in the kernel,
6684 * but carbon does not like putting this in carbon either,
6687 if (error
== ENOTEMPTY
) {
6688 error
= rmdir_remove_orphaned_appleDouble(vp
, ctx
, &restart_flag
);
6689 if (error
== EBUSY
) {
6695 * Assuming everything went well, we will try the RMDIR again
6698 error
= vn_rmdir(dvp
, &vp
, &nd
, vap
, ctx
);
6702 * Call out to allow 3rd party notification of delete.
6703 * Ignore result of kauth_authorize_fileop call.
6706 if (has_listeners
) {
6707 kauth_authorize_fileop(vfs_context_ucred(ctx
),
6708 KAUTH_FILEOP_DELETE
,
6713 if (vp
->v_flag
& VISHARDLINK
) {
6714 // see the comment in unlink1() about why we update
6715 // the parent of a hard link when it is removed
6716 vnode_update_identity(vp
, NULL
, NULL
, 0, 0, VNODE_UPDATE_PARENT
);
6722 vnode_get_fse_info_from_vap(vp
, &finfo
, vap
);
6724 add_fsevent(FSE_DELETE
, ctx
,
6725 FSE_ARG_STRING
, len
, path
,
6726 FSE_ARG_FINFO
, &finfo
,
6738 * nameidone has to happen before we vnode_put(dvp)
6739 * since it may need to release the fs_nodelock on the dvp
6747 if (restart_flag
== 0) {
6748 wakeup_one((caddr_t
)vp
);
6751 tsleep(vp
, PVFS
, "rm AD", 1);
6753 } while (restart_flag
!= 0);
6759 /* Get direntry length padded to 8 byte alignment */
6760 #define DIRENT64_LEN(namlen) \
6761 ((sizeof(struct direntry) + (namlen) - (MAXPATHLEN-1) + 7) & ~7)
6764 vnode_readdir64(struct vnode
*vp
, struct uio
*uio
, int flags
, int *eofflag
,
6765 int *numdirent
, vfs_context_t ctxp
)
6767 /* Check if fs natively supports VNODE_READDIR_EXTENDED */
6768 if ((vp
->v_mount
->mnt_vtable
->vfc_vfsflags
& VFC_VFSREADDIR_EXTENDED
) &&
6769 ((vp
->v_mount
->mnt_kern_flag
& MNTK_DENY_READDIREXT
) == 0)) {
6770 return VNOP_READDIR(vp
, uio
, flags
, eofflag
, numdirent
, ctxp
);
6775 struct direntry entry64
;
6781 * Our kernel buffer needs to be smaller since re-packing
6782 * will expand each dirent. The worse case (when the name
6783 * length is 3) corresponds to a struct direntry size of 32
6784 * bytes (8-byte aligned) and a struct dirent size of 12 bytes
6785 * (4-byte aligned). So having a buffer that is 3/8 the size
6786 * will prevent us from reading more than we can pack.
6788 * Since this buffer is wired memory, we will limit the
6789 * buffer size to a maximum of 32K. We would really like to
6790 * use 32K in the MIN(), but we use magic number 87371 to
6791 * prevent uio_resid() * 3 / 8 from overflowing.
6793 bufsize
= 3 * MIN(uio_resid(uio
), 87371) / 8;
6794 MALLOC(bufptr
, void *, bufsize
, M_TEMP
, M_WAITOK
);
6795 if (bufptr
== NULL
) {
6799 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_READ
);
6800 uio_addiov(auio
, (uintptr_t)bufptr
, bufsize
);
6801 auio
->uio_offset
= uio
->uio_offset
;
6803 error
= VNOP_READDIR(vp
, auio
, 0, eofflag
, numdirent
, ctxp
);
6805 dep
= (struct dirent
*)bufptr
;
6806 bytesread
= bufsize
- uio_resid(auio
);
6809 * Convert all the entries and copy them out to user's buffer.
6811 while (error
== 0 && (char *)dep
< ((char *)bufptr
+ bytesread
)) {
6812 /* Convert a dirent to a dirent64. */
6813 entry64
.d_ino
= dep
->d_ino
;
6814 entry64
.d_seekoff
= 0;
6815 entry64
.d_reclen
= DIRENT64_LEN(dep
->d_namlen
);
6816 entry64
.d_namlen
= dep
->d_namlen
;
6817 entry64
.d_type
= dep
->d_type
;
6818 bcopy(dep
->d_name
, entry64
.d_name
, dep
->d_namlen
+ 1);
6820 /* Move to next entry. */
6821 dep
= (struct dirent
*)((char *)dep
+ dep
->d_reclen
);
6823 /* Copy entry64 to user's buffer. */
6824 error
= uiomove((caddr_t
)&entry64
, entry64
.d_reclen
, uio
);
6827 /* Update the real offset using the offset we got from VNOP_READDIR. */
6829 uio
->uio_offset
= auio
->uio_offset
;
6832 FREE(bufptr
, M_TEMP
);
6838 * Read a block of directory entries in a file system independent format.
6841 getdirentries_common(int fd
, user_addr_t bufp
, user_size_t bufsize
, ssize_t
*bytesread
,
6842 off_t
*offset
, int flags
)
6845 struct vfs_context context
= *vfs_context_current(); /* local copy */
6846 struct fileproc
*fp
;
6848 int spacetype
= proc_is64bit(vfs_context_proc(&context
)) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
6850 int error
, eofflag
, numdirent
;
6851 char uio_buf
[ UIO_SIZEOF(1) ];
6853 error
= fp_getfvp(vfs_context_proc(&context
), fd
, &fp
, &vp
);
6857 if ((fp
->f_fglob
->fg_flag
& FREAD
) == 0) {
6858 AUDIT_ARG(vnpath_withref
, vp
, ARG_VNODE1
);
6864 error
= mac_file_check_change_offset(vfs_context_ucred(&context
), fp
->f_fglob
);
6868 if ( (error
= vnode_getwithref(vp
)) ) {
6871 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
6874 if (vp
->v_type
!= VDIR
) {
6875 (void)vnode_put(vp
);
6881 error
= mac_vnode_check_readdir(&context
, vp
);
6883 (void)vnode_put(vp
);
6888 loff
= fp
->f_fglob
->fg_offset
;
6889 auio
= uio_createwithbuffer(1, loff
, spacetype
, UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
6890 uio_addiov(auio
, bufp
, bufsize
);
6892 if (flags
& VNODE_READDIR_EXTENDED
) {
6893 error
= vnode_readdir64(vp
, auio
, flags
, &eofflag
, &numdirent
, &context
);
6894 fp
->f_fglob
->fg_offset
= uio_offset(auio
);
6896 error
= VNOP_READDIR(vp
, auio
, 0, &eofflag
, &numdirent
, &context
);
6897 fp
->f_fglob
->fg_offset
= uio_offset(auio
);
6900 (void)vnode_put(vp
);
6904 if ((user_ssize_t
)bufsize
== uio_resid(auio
)){
6905 if (union_dircheckp
) {
6906 error
= union_dircheckp(&vp
, fp
, &context
);
6913 if ((vp
->v_flag
& VROOT
) && (vp
->v_mount
->mnt_flag
& MNT_UNION
)) {
6914 struct vnode
*tvp
= vp
;
6915 vp
= vp
->v_mount
->mnt_vnodecovered
;
6916 vnode_getwithref(vp
);
6918 fp
->f_fglob
->fg_data
= (caddr_t
) vp
;
6919 fp
->f_fglob
->fg_offset
= 0;
6931 *bytesread
= bufsize
- uio_resid(auio
);
6939 getdirentries(__unused
struct proc
*p
, struct getdirentries_args
*uap
, int32_t *retval
)
6945 AUDIT_ARG(fd
, uap
->fd
);
6946 error
= getdirentries_common(uap
->fd
, uap
->buf
, uap
->count
, &bytesread
, &offset
, 0);
6949 if (proc_is64bit(p
)) {
6950 user64_long_t base
= (user64_long_t
)offset
;
6951 error
= copyout((caddr_t
)&base
, uap
->basep
, sizeof(user64_long_t
));
6953 user32_long_t base
= (user32_long_t
)offset
;
6954 error
= copyout((caddr_t
)&base
, uap
->basep
, sizeof(user32_long_t
));
6956 *retval
= bytesread
;
6962 getdirentries64(__unused
struct proc
*p
, struct getdirentries64_args
*uap
, user_ssize_t
*retval
)
6968 AUDIT_ARG(fd
, uap
->fd
);
6969 error
= getdirentries_common(uap
->fd
, uap
->buf
, uap
->bufsize
, &bytesread
, &offset
, VNODE_READDIR_EXTENDED
);
6972 *retval
= bytesread
;
6973 error
= copyout((caddr_t
)&offset
, uap
->position
, sizeof(off_t
));
6980 * Set the mode mask for creation of filesystem nodes.
6981 * XXX implement xsecurity
6983 #define UMASK_NOXSECURITY (void *)1 /* leave existing xsecurity alone */
6985 umask1(proc_t p
, int newmask
, __unused kauth_filesec_t fsec
, int32_t *retval
)
6987 struct filedesc
*fdp
;
6989 AUDIT_ARG(mask
, newmask
);
6992 *retval
= fdp
->fd_cmask
;
6993 fdp
->fd_cmask
= newmask
& ALLPERMS
;
6999 * umask_extended: Set the mode mask for creation of filesystem nodes; with extended security (ACL).
7001 * Parameters: p Process requesting to set the umask
7002 * uap User argument descriptor (see below)
7003 * retval umask of the process (parameter p)
7005 * Indirect: uap->newmask umask to set
7006 * uap->xsecurity ACL to set
7008 * Returns: 0 Success
7013 umask_extended(proc_t p
, struct umask_extended_args
*uap
, int32_t *retval
)
7016 kauth_filesec_t xsecdst
;
7018 xsecdst
= KAUTH_FILESEC_NONE
;
7019 if (uap
->xsecurity
!= USER_ADDR_NULL
) {
7020 if ((ciferror
= kauth_copyinfilesec(uap
->xsecurity
, &xsecdst
)) != 0)
7023 xsecdst
= KAUTH_FILESEC_NONE
;
7026 ciferror
= umask1(p
, uap
->newmask
, xsecdst
, retval
);
7028 if (xsecdst
!= KAUTH_FILESEC_NONE
)
7029 kauth_filesec_free(xsecdst
);
7034 umask(proc_t p
, struct umask_args
*uap
, int32_t *retval
)
7036 return(umask1(p
, uap
->newmask
, UMASK_NOXSECURITY
, retval
));
7040 * Void all references to file by ripping underlying filesystem
7045 revoke(proc_t p
, struct revoke_args
*uap
, __unused
int32_t *retval
)
7048 struct vnode_attr va
;
7049 vfs_context_t ctx
= vfs_context_current();
7051 struct nameidata nd
;
7053 NDINIT(&nd
, LOOKUP
, OP_REVOKE
, FOLLOW
| AUDITVNPATH1
, UIO_USERSPACE
,
7062 if (!(vnode_ischr(vp
) || vnode_isblk(vp
))) {
7067 if (vnode_isblk(vp
) && vnode_ismountedon(vp
)) {
7073 error
= mac_vnode_check_revoke(ctx
, vp
);
7079 VATTR_WANTED(&va
, va_uid
);
7080 if ((error
= vnode_getattr(vp
, &va
, ctx
)))
7082 if (kauth_cred_getuid(vfs_context_ucred(ctx
)) != va
.va_uid
&&
7083 (error
= suser(vfs_context_ucred(ctx
), &p
->p_acflag
)))
7085 if (vp
->v_usecount
> 0 || (vnode_isaliased(vp
)))
7086 VNOP_REVOKE(vp
, REVOKEALL
, ctx
);
7094 * HFS/HFS PlUS SPECIFIC SYSTEM CALLS
7095 * The following system calls are designed to support features
7096 * which are specific to the HFS & HFS Plus volume formats
7099 #ifdef __APPLE_API_OBSOLETE
7101 /************************************************/
7102 /* *** Following calls will be deleted soon *** */
7103 /************************************************/
7106 * Make a complex file. A complex file is one with multiple forks (data streams)
7110 mkcomplex(__unused proc_t p
, __unused
struct mkcomplex_args
*uap
, __unused
int32_t *retval
)
7116 * Extended stat call which returns volumeid and vnodeid as well as other info
7120 statv(__unused proc_t p
,
7121 __unused
struct statv_args
*uap
,
7122 __unused
int32_t *retval
)
7124 return (ENOTSUP
); /* We'll just return an error for now */
7126 } /* end of statv system call */
7129 * Extended lstat call which returns volumeid and vnodeid as well as other info
7133 lstatv(__unused proc_t p
,
7134 __unused
struct lstatv_args
*uap
,
7135 __unused
int32_t *retval
)
7137 return (ENOTSUP
); /* We'll just return an error for now */
7138 } /* end of lstatv system call */
7141 * Extended fstat call which returns volumeid and vnodeid as well as other info
7145 fstatv(__unused proc_t p
,
7146 __unused
struct fstatv_args
*uap
,
7147 __unused
int32_t *retval
)
7149 return (ENOTSUP
); /* We'll just return an error for now */
7150 } /* end of fstatv system call */
7153 /************************************************/
7154 /* *** Preceding calls will be deleted soon *** */
7155 /************************************************/
7157 #endif /* __APPLE_API_OBSOLETE */
7160 * Obtain attribute information on objects in a directory while enumerating
7161 * the directory. This call does not yet support union mounted directories.
7163 * 1.union mounted directories.
7168 getdirentriesattr (proc_t p
, struct getdirentriesattr_args
*uap
, int32_t *retval
)
7171 struct fileproc
*fp
;
7173 int spacetype
= proc_is64bit(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
7178 struct attrlist attributelist
;
7179 vfs_context_t ctx
= vfs_context_current();
7181 char uio_buf
[ UIO_SIZEOF(1) ];
7182 kauth_action_t action
;
7186 /* Get the attributes into kernel space */
7187 if ((error
= copyin(uap
->alist
, (caddr_t
)&attributelist
, sizeof(attributelist
)))) {
7190 if ((error
= copyin(uap
->count
, (caddr_t
)&count
, sizeof(count
)))) {
7193 if ( (error
= fp_getfvp(p
, fd
, &fp
, &vp
)) ) {
7196 if ((fp
->f_fglob
->fg_flag
& FREAD
) == 0) {
7197 AUDIT_ARG(vnpath_withref
, vp
, ARG_VNODE1
);
7204 error
= mac_file_check_change_offset(vfs_context_ucred(ctx
),
7211 if ( (error
= vnode_getwithref(vp
)) )
7214 AUDIT_ARG(vnpath
, vp
, ARG_VNODE1
);
7216 if (vp
->v_type
!= VDIR
) {
7217 (void)vnode_put(vp
);
7223 error
= mac_vnode_check_readdir(ctx
, vp
);
7225 (void)vnode_put(vp
);
7230 /* set up the uio structure which will contain the users return buffer */
7231 loff
= fp
->f_fglob
->fg_offset
;
7232 auio
= uio_createwithbuffer(1, loff
, spacetype
, UIO_READ
,
7233 &uio_buf
[0], sizeof(uio_buf
));
7234 uio_addiov(auio
, uap
->buffer
, uap
->buffersize
);
7237 * If the only item requested is file names, we can let that past with
7238 * just LIST_DIRECTORY. If they want any other attributes, that means
7239 * they need SEARCH as well.
7241 action
= KAUTH_VNODE_LIST_DIRECTORY
;
7242 if ((attributelist
.commonattr
& ~ATTR_CMN_NAME
) ||
7243 attributelist
.fileattr
|| attributelist
.dirattr
)
7244 action
|= KAUTH_VNODE_SEARCH
;
7246 if ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) == 0) {
7248 /* Believe it or not, uap->options only has 32-bits of valid
7249 * info, so truncate before extending again */
7250 error
= VNOP_READDIRATTR(vp
, &attributelist
, auio
,
7252 (u_long
)(uint32_t)uap
->options
, &newstate
, &eofflag
,
7255 (void)vnode_put(vp
);
7259 fp
->f_fglob
->fg_offset
= uio_offset(auio
); /* should be multiple of dirent, not variable */
7261 if ((error
= copyout((caddr_t
) &count
, uap
->count
, sizeof(count
))))
7263 if ((error
= copyout((caddr_t
) &newstate
, uap
->newstate
, sizeof(newstate
))))
7265 if ((error
= copyout((caddr_t
) &loff
, uap
->basep
, sizeof(loff
))))
7268 *retval
= eofflag
; /* similar to getdirentries */
7272 return (error
); /* return error earlier, an retval of 0 or 1 now */
7274 } /* end of getdirentryattr system call */
7277 * Exchange data between two files
7282 exchangedata (__unused proc_t p
, struct exchangedata_args
*uap
, __unused
int32_t *retval
)
7285 struct nameidata fnd
, snd
;
7286 vfs_context_t ctx
= vfs_context_current();
7290 u_int32_t nameiflags
;
7294 int from_truncated
=0, to_truncated
=0;
7296 fse_info f_finfo
, s_finfo
;
7300 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0) nameiflags
|= FOLLOW
;
7302 NDINIT(&fnd
, LOOKUP
, OP_EXCHANGEDATA
, nameiflags
| AUDITVNPATH1
,
7303 UIO_USERSPACE
, uap
->path1
, ctx
);
7305 error
= namei(&fnd
);
7312 NDINIT(&snd
, LOOKUP
, OP_EXCHANGEDATA
, CN_NBMOUNTLOOK
| nameiflags
| AUDITVNPATH2
,
7313 UIO_USERSPACE
, uap
->path2
, ctx
);
7315 error
= namei(&snd
);
7324 * if the files are the same, return an inval error
7332 * if the files are on different volumes, return an error
7334 if (svp
->v_mount
!= fvp
->v_mount
) {
7340 error
= mac_vnode_check_exchangedata(ctx
,
7345 if (((error
= vnode_authorize(fvp
, NULL
, KAUTH_VNODE_READ_DATA
| KAUTH_VNODE_WRITE_DATA
, ctx
)) != 0) ||
7346 ((error
= vnode_authorize(svp
, NULL
, KAUTH_VNODE_READ_DATA
| KAUTH_VNODE_WRITE_DATA
, ctx
)) != 0))
7351 need_fsevent(FSE_EXCHANGE
, fvp
) ||
7353 kauth_authorize_fileop_has_listeners()) {
7356 if (fpath
== NULL
|| spath
== NULL
) {
7361 flen
= safe_getpath(fvp
, NULL
, fpath
, MAXPATHLEN
, &from_truncated
);
7362 slen
= safe_getpath(svp
, NULL
, spath
, MAXPATHLEN
, &to_truncated
);
7365 get_fse_info(fvp
, &f_finfo
, ctx
);
7366 get_fse_info(svp
, &s_finfo
, ctx
);
7367 if (from_truncated
|| to_truncated
) {
7368 // set it here since only the f_finfo gets reported up to user space
7369 f_finfo
.mode
|= FSE_TRUNCATED_PATH
;
7373 /* Ok, make the call */
7374 error
= VNOP_EXCHANGE(fvp
, svp
, 0, ctx
);
7377 const char *tmpname
;
7379 if (fpath
!= NULL
&& spath
!= NULL
) {
7380 /* call out to allow 3rd party notification of exchangedata.
7381 * Ignore result of kauth_authorize_fileop call.
7383 kauth_authorize_fileop(vfs_context_ucred(ctx
), KAUTH_FILEOP_EXCHANGE
,
7384 (uintptr_t)fpath
, (uintptr_t)spath
);
7388 tmpname
= fvp
->v_name
;
7389 fvp
->v_name
= svp
->v_name
;
7390 svp
->v_name
= tmpname
;
7392 if (fvp
->v_parent
!= svp
->v_parent
) {
7395 tmp
= fvp
->v_parent
;
7396 fvp
->v_parent
= svp
->v_parent
;
7397 svp
->v_parent
= tmp
;
7399 name_cache_unlock();
7402 if (fpath
!= NULL
&& spath
!= NULL
) {
7403 add_fsevent(FSE_EXCHANGE
, ctx
,
7404 FSE_ARG_STRING
, flen
, fpath
,
7405 FSE_ARG_FINFO
, &f_finfo
,
7406 FSE_ARG_STRING
, slen
, spath
,
7407 FSE_ARG_FINFO
, &s_finfo
,
7415 RELEASE_PATH(fpath
);
7417 RELEASE_PATH(spath
);
7428 searchfs(proc_t p
, struct searchfs_args
*uap
, __unused
int32_t *retval
)
7433 struct nameidata nd
;
7434 struct user64_fssearchblock searchblock
;
7435 struct searchstate
*state
;
7436 struct attrlist
*returnattrs
;
7437 struct timeval timelimit
;
7438 void *searchparams1
,*searchparams2
;
7440 int spacetype
= proc_is64bit(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
7441 uint32_t nummatches
;
7443 uint32_t nameiflags
;
7444 vfs_context_t ctx
= vfs_context_current();
7445 char uio_buf
[ UIO_SIZEOF(1) ];
7447 /* Start by copying in fsearchblock paramater list */
7448 if (IS_64BIT_PROCESS(p
)) {
7449 error
= copyin(uap
->searchblock
, (caddr_t
) &searchblock
, sizeof(searchblock
));
7450 timelimit
.tv_sec
= searchblock
.timelimit
.tv_sec
;
7451 timelimit
.tv_usec
= searchblock
.timelimit
.tv_usec
;
7454 struct user32_fssearchblock tmp_searchblock
;
7456 error
= copyin(uap
->searchblock
, (caddr_t
) &tmp_searchblock
, sizeof(tmp_searchblock
));
7457 // munge into 64-bit version
7458 searchblock
.returnattrs
= CAST_USER_ADDR_T(tmp_searchblock
.returnattrs
);
7459 searchblock
.returnbuffer
= CAST_USER_ADDR_T(tmp_searchblock
.returnbuffer
);
7460 searchblock
.returnbuffersize
= tmp_searchblock
.returnbuffersize
;
7461 searchblock
.maxmatches
= tmp_searchblock
.maxmatches
;
7463 * These casts are safe. We will promote the tv_sec into a 64 bit long if necessary
7464 * from a 32 bit long, and tv_usec is already a signed 32 bit int.
7466 timelimit
.tv_sec
= (__darwin_time_t
) tmp_searchblock
.timelimit
.tv_sec
;
7467 timelimit
.tv_usec
= (__darwin_useconds_t
) tmp_searchblock
.timelimit
.tv_usec
;
7468 searchblock
.searchparams1
= CAST_USER_ADDR_T(tmp_searchblock
.searchparams1
);
7469 searchblock
.sizeofsearchparams1
= tmp_searchblock
.sizeofsearchparams1
;
7470 searchblock
.searchparams2
= CAST_USER_ADDR_T(tmp_searchblock
.searchparams2
);
7471 searchblock
.sizeofsearchparams2
= tmp_searchblock
.sizeofsearchparams2
;
7472 searchblock
.searchattrs
= tmp_searchblock
.searchattrs
;
7477 /* Do a sanity check on sizeofsearchparams1 and sizeofsearchparams2.
7479 if (searchblock
.sizeofsearchparams1
> SEARCHFS_MAX_SEARCHPARMS
||
7480 searchblock
.sizeofsearchparams2
> SEARCHFS_MAX_SEARCHPARMS
)
7483 /* Now malloc a big bunch of space to hold the search parameters, the attrlists and the search state. */
7484 /* It all has to do into local memory and it's not that big so we might as well put it all together. */
7485 /* Searchparams1 shall be first so we might as well use that to hold the base address of the allocated*/
7488 mallocsize
= searchblock
.sizeofsearchparams1
+ searchblock
.sizeofsearchparams2
+
7489 sizeof(struct attrlist
) + sizeof(struct searchstate
);
7491 MALLOC(searchparams1
, void *, mallocsize
, M_TEMP
, M_WAITOK
);
7493 /* Now set up the various pointers to the correct place in our newly allocated memory */
7495 searchparams2
= (void *) (((caddr_t
) searchparams1
) + searchblock
.sizeofsearchparams1
);
7496 returnattrs
= (struct attrlist
*) (((caddr_t
) searchparams2
) + searchblock
.sizeofsearchparams2
);
7497 state
= (struct searchstate
*) (((caddr_t
) returnattrs
) + sizeof (struct attrlist
));
7499 /* Now copy in the stuff given our local variables. */
7501 if ((error
= copyin(searchblock
.searchparams1
, searchparams1
, searchblock
.sizeofsearchparams1
)))
7504 if ((error
= copyin(searchblock
.searchparams2
, searchparams2
, searchblock
.sizeofsearchparams2
)))
7507 if ((error
= copyin(searchblock
.returnattrs
, (caddr_t
) returnattrs
, sizeof(struct attrlist
))))
7510 if ((error
= copyin(uap
->state
, (caddr_t
) state
, sizeof(struct searchstate
))))
7515 * Because searchparams1 and searchparams2 may contain an ATTR_CMN_NAME search parameter,
7516 * which is passed in with an attrreference_t, we need to inspect the buffer manually here.
7517 * The KPI does not provide us the ability to pass in the length of the buffers searchparams1
7518 * and searchparams2. To obviate the need for all searchfs-supporting filesystems to
7519 * validate the user-supplied data offset of the attrreference_t, we'll do it here.
7522 if (searchblock
.searchattrs
.commonattr
& ATTR_CMN_NAME
) {
7523 attrreference_t
* string_ref
;
7524 u_int32_t
* start_length
;
7525 user64_size_t param_length
;
7527 /* validate searchparams1 */
7528 param_length
= searchblock
.sizeofsearchparams1
;
7529 /* skip the word that specifies length of the buffer */
7530 start_length
= (u_int32_t
*) searchparams1
;
7531 start_length
= start_length
+1;
7532 string_ref
= (attrreference_t
*) start_length
;
7534 /* ensure no negative offsets or too big offsets */
7535 if (string_ref
->attr_dataoffset
< 0 ) {
7539 if (string_ref
->attr_length
> MAXPATHLEN
) {
7544 /* Check for pointer overflow in the string ref */
7545 if (((char*) string_ref
+ string_ref
->attr_dataoffset
) < (char*) string_ref
) {
7550 if (((char*) string_ref
+ string_ref
->attr_dataoffset
) > ((char*)searchparams1
+ param_length
)) {
7554 if (((char*)string_ref
+ string_ref
->attr_dataoffset
+ string_ref
->attr_length
) > ((char*)searchparams1
+ param_length
)) {
7560 /* set up the uio structure which will contain the users return buffer */
7561 auio
= uio_createwithbuffer(1, 0, spacetype
, UIO_READ
,
7562 &uio_buf
[0], sizeof(uio_buf
));
7563 uio_addiov(auio
, searchblock
.returnbuffer
, searchblock
.returnbuffersize
);
7566 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0) nameiflags
|= FOLLOW
;
7567 NDINIT(&nd
, LOOKUP
, OP_SEARCHFS
, nameiflags
| AUDITVNPATH1
,
7568 UIO_USERSPACE
, uap
->path
, ctx
);
7578 error
= mac_vnode_check_searchfs(ctx
, vp
, &searchblock
.searchattrs
);
7587 * If searchblock.maxmatches == 0, then skip the search. This has happened
7588 * before and sometimes the underlyning code doesnt deal with it well.
7590 if (searchblock
.maxmatches
== 0) {
7596 Allright, we have everything we need, so lets make that call.
7598 We keep special track of the return value from the file system:
7599 EAGAIN is an acceptable error condition that shouldn't keep us
7600 from copying out any results...
7603 fserror
= VNOP_SEARCHFS(vp
,
7606 &searchblock
.searchattrs
,
7607 (u_long
)searchblock
.maxmatches
,
7611 (u_long
)uap
->scriptcode
,
7612 (u_long
)uap
->options
,
7621 /* Now copy out the stuff that needs copying out. That means the number of matches, the
7622 search state. Everything was already put into he return buffer by the vop call. */
7624 if ((error
= copyout((caddr_t
) state
, uap
->state
, sizeof(struct searchstate
))) != 0)
7627 if ((error
= suulong(uap
->nummatches
, (uint64_t)nummatches
)) != 0)
7634 FREE(searchparams1
,M_TEMP
);
7639 } /* end of searchfs system call */
7643 lck_grp_attr_t
* nspace_group_attr
;
7644 lck_attr_t
* nspace_lock_attr
;
7645 lck_grp_t
* nspace_mutex_group
;
7647 lck_mtx_t nspace_handler_lock
;
7648 lck_mtx_t nspace_handler_exclusion_lock
;
7650 time_t snapshot_timestamp
=0;
7651 int nspace_allow_virtual_devs
=0;
7653 void nspace_handler_init(void);
7655 typedef struct nspace_item_info
{
7665 #define MAX_NSPACE_ITEMS 128
7666 nspace_item_info nspace_items
[MAX_NSPACE_ITEMS
];
7667 uint32_t nspace_item_idx
=0; // also used as the sleep/wakeup rendezvous address
7668 uint32_t nspace_token_id
=0;
7669 uint32_t nspace_handler_timeout
= 15; // seconds
7671 #define NSPACE_ITEM_NEW 0x0001
7672 #define NSPACE_ITEM_PROCESSING 0x0002
7673 #define NSPACE_ITEM_DEAD 0x0004
7674 #define NSPACE_ITEM_CANCELLED 0x0008
7675 #define NSPACE_ITEM_DONE 0x0010
7676 #define NSPACE_ITEM_RESET_TIMER 0x0020
7678 #define NSPACE_ITEM_NSPACE_EVENT 0x0040
7679 #define NSPACE_ITEM_SNAPSHOT_EVENT 0x0080
7680 #define NSPACE_ITEM_TRACK_EVENT 0x0100
7682 #define NSPACE_ITEM_ALL_EVENT_TYPES (NSPACE_ITEM_NSPACE_EVENT | NSPACE_ITEM_SNAPSHOT_EVENT | NSPACE_ITEM_TRACK_EVENT)
7684 //#pragma optimization_level 0
7687 NSPACE_HANDLER_NSPACE
= 0,
7688 NSPACE_HANDLER_SNAPSHOT
= 1,
7689 NSPACE_HANDLER_TRACK
= 2,
7691 NSPACE_HANDLER_COUNT
,
7695 uint64_t handler_tid
;
7696 struct proc
*handler_proc
;
7700 nspace_handler_t nspace_handlers
[NSPACE_HANDLER_COUNT
];
7702 static inline int nspace_flags_matches_handler(uint32_t event_flags
, nspace_type_t nspace_type
)
7704 switch(nspace_type
) {
7705 case NSPACE_HANDLER_NSPACE
:
7706 return (event_flags
& NSPACE_ITEM_ALL_EVENT_TYPES
) == NSPACE_ITEM_NSPACE_EVENT
;
7707 case NSPACE_HANDLER_SNAPSHOT
:
7708 return (event_flags
& NSPACE_ITEM_ALL_EVENT_TYPES
) == NSPACE_ITEM_SNAPSHOT_EVENT
;
7709 case NSPACE_HANDLER_TRACK
:
7710 return (event_flags
& NSPACE_ITEM_ALL_EVENT_TYPES
) == NSPACE_ITEM_TRACK_EVENT
;
7712 printf("nspace_flags_matches_handler: invalid type %u\n", (int)nspace_type
);
7717 static inline int nspace_item_flags_for_type(nspace_type_t nspace_type
)
7719 switch(nspace_type
) {
7720 case NSPACE_HANDLER_NSPACE
:
7721 return NSPACE_ITEM_NSPACE_EVENT
;
7722 case NSPACE_HANDLER_SNAPSHOT
:
7723 return NSPACE_ITEM_SNAPSHOT_EVENT
;
7724 case NSPACE_HANDLER_TRACK
:
7725 return NSPACE_ITEM_TRACK_EVENT
;
7727 printf("nspace_item_flags_for_type: invalid type %u\n", (int)nspace_type
);
7732 static inline int nspace_open_flags_for_type(nspace_type_t nspace_type
)
7734 switch(nspace_type
) {
7735 case NSPACE_HANDLER_NSPACE
:
7736 return FREAD
| FWRITE
| O_EVTONLY
;
7737 case NSPACE_HANDLER_SNAPSHOT
:
7738 case NSPACE_HANDLER_TRACK
:
7739 return FREAD
| O_EVTONLY
;
7741 printf("nspace_open_flags_for_type: invalid type %u\n", (int)nspace_type
);
7746 static inline nspace_type_t
nspace_type_for_op(uint64_t op
)
7748 switch(op
& NAMESPACE_HANDLER_EVENT_TYPE_MASK
) {
7749 case NAMESPACE_HANDLER_NSPACE_EVENT
:
7750 return NSPACE_HANDLER_NSPACE
;
7751 case NAMESPACE_HANDLER_SNAPSHOT_EVENT
:
7752 return NSPACE_HANDLER_SNAPSHOT
;
7753 case NAMESPACE_HANDLER_TRACK_EVENT
:
7754 return NSPACE_HANDLER_TRACK
;
7756 printf("nspace_type_for_op: invalid op mask %llx\n", op
& NAMESPACE_HANDLER_EVENT_TYPE_MASK
);
7757 return NSPACE_HANDLER_NSPACE
;
7761 static inline int nspace_is_special_process(struct proc
*proc
)
7764 for (i
= 0; i
< NSPACE_HANDLER_COUNT
; i
++) {
7765 if (proc
== nspace_handlers
[i
].handler_proc
)
7772 nspace_handler_init(void)
7774 nspace_lock_attr
= lck_attr_alloc_init();
7775 nspace_group_attr
= lck_grp_attr_alloc_init();
7776 nspace_mutex_group
= lck_grp_alloc_init("nspace-mutex", nspace_group_attr
);
7777 lck_mtx_init(&nspace_handler_lock
, nspace_mutex_group
, nspace_lock_attr
);
7778 lck_mtx_init(&nspace_handler_exclusion_lock
, nspace_mutex_group
, nspace_lock_attr
);
7779 memset(&nspace_items
[0], 0, sizeof(nspace_items
));
7783 nspace_proc_exit(struct proc
*p
)
7785 int i
, event_mask
= 0;
7787 for (i
= 0; i
< NSPACE_HANDLER_COUNT
; i
++) {
7788 if (p
== nspace_handlers
[i
].handler_proc
) {
7789 event_mask
|= nspace_item_flags_for_type(i
);
7790 nspace_handlers
[i
].handler_tid
= 0;
7791 nspace_handlers
[i
].handler_proc
= NULL
;
7795 if (event_mask
== 0) {
7799 if (event_mask
& NSPACE_ITEM_SNAPSHOT_EVENT
) {
7800 // if this process was the snapshot handler, zero snapshot_timeout
7801 snapshot_timestamp
= 0;
7805 // unblock anyone that's waiting for the handler that died
7807 lck_mtx_lock(&nspace_handler_lock
);
7808 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
7809 if (nspace_items
[i
].flags
& (NSPACE_ITEM_NEW
| NSPACE_ITEM_PROCESSING
)) {
7811 if ( nspace_items
[i
].flags
& event_mask
) {
7813 if (nspace_items
[i
].vp
&& (nspace_items
[i
].vp
->v_flag
& VNEEDSSNAPSHOT
)) {
7814 vnode_lock_spin(nspace_items
[i
].vp
);
7815 nspace_items
[i
].vp
->v_flag
&= ~VNEEDSSNAPSHOT
;
7816 vnode_unlock(nspace_items
[i
].vp
);
7818 nspace_items
[i
].vp
= NULL
;
7819 nspace_items
[i
].vid
= 0;
7820 nspace_items
[i
].flags
= NSPACE_ITEM_DONE
;
7821 nspace_items
[i
].token
= 0;
7823 wakeup((caddr_t
)&(nspace_items
[i
].vp
));
7828 wakeup((caddr_t
)&nspace_item_idx
);
7829 lck_mtx_unlock(&nspace_handler_lock
);
7834 resolve_nspace_item(struct vnode
*vp
, uint64_t op
)
7836 return resolve_nspace_item_ext(vp
, op
, NULL
);
7840 resolve_nspace_item_ext(struct vnode
*vp
, uint64_t op
, void *arg
)
7842 int i
, error
, keep_waiting
;
7844 nspace_type_t nspace_type
= nspace_type_for_op(op
);
7846 // only allow namespace events on regular files, directories and symlinks.
7847 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
&& vp
->v_type
!= VLNK
) {
7852 // if this is a snapshot event and the vnode is on a
7853 // disk image just pretend nothing happened since any
7854 // change to the disk image will cause the disk image
7855 // itself to get backed up and this avoids multi-way
7856 // deadlocks between the snapshot handler and the ever
7857 // popular diskimages-helper process. the variable
7858 // nspace_allow_virtual_devs allows this behavior to
7859 // be overridden (for use by the Mobile TimeMachine
7860 // testing infrastructure which uses disk images)
7862 if ( (op
& NAMESPACE_HANDLER_SNAPSHOT_EVENT
)
7863 && (vp
->v_mount
!= NULL
)
7864 && (vp
->v_mount
->mnt_kern_flag
& MNTK_VIRTUALDEV
)
7865 && !nspace_allow_virtual_devs
) {
7870 // if (thread_tid(current_thread()) == namespace_handler_tid) {
7871 if (nspace_handlers
[nspace_type
].handler_proc
== NULL
) {
7875 if (nspace_is_special_process(current_proc())) {
7879 lck_mtx_lock(&nspace_handler_lock
);
7882 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
7883 if (vp
== nspace_items
[i
].vp
&& op
== nspace_items
[i
].op
) {
7888 if (i
>= MAX_NSPACE_ITEMS
) {
7889 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
7890 if (nspace_items
[i
].flags
== 0) {
7895 nspace_items
[i
].refcount
++;
7898 if (i
>= MAX_NSPACE_ITEMS
) {
7899 ts
.tv_sec
= nspace_handler_timeout
;
7902 error
= msleep((caddr_t
)&nspace_token_id
, &nspace_handler_lock
, PVFS
|PCATCH
, "nspace-no-space", &ts
);
7904 // an entry got free'd up, go see if we can get a slot
7907 lck_mtx_unlock(&nspace_handler_lock
);
7913 // if it didn't already exist, add it. if it did exist
7914 // we'll get woken up when someone does a wakeup() on
7915 // the slot in the nspace_items table.
7917 if (vp
!= nspace_items
[i
].vp
) {
7918 nspace_items
[i
].vp
= vp
;
7919 nspace_items
[i
].arg
= arg
;
7920 nspace_items
[i
].op
= op
;
7921 nspace_items
[i
].vid
= vnode_vid(vp
);
7922 nspace_items
[i
].flags
= NSPACE_ITEM_NEW
;
7923 nspace_items
[i
].flags
|= nspace_item_flags_for_type(nspace_type
);
7924 if (nspace_items
[i
].flags
& NSPACE_ITEM_SNAPSHOT_EVENT
) {
7926 vnode_lock_spin(vp
);
7927 vp
->v_flag
|= VNEEDSSNAPSHOT
;
7932 nspace_items
[i
].token
= 0;
7933 nspace_items
[i
].refcount
= 1;
7935 wakeup((caddr_t
)&nspace_item_idx
);
7939 // Now go to sleep until the handler does a wakeup on this
7940 // slot in the nspace_items table (or we timeout).
7943 while(keep_waiting
) {
7944 ts
.tv_sec
= nspace_handler_timeout
;
7946 error
= msleep((caddr_t
)&(nspace_items
[i
].vp
), &nspace_handler_lock
, PVFS
|PCATCH
, "namespace-done", &ts
);
7948 if (nspace_items
[i
].flags
& NSPACE_ITEM_DONE
) {
7950 } else if (nspace_items
[i
].flags
& NSPACE_ITEM_CANCELLED
) {
7951 error
= nspace_items
[i
].token
;
7952 } else if (error
== EWOULDBLOCK
|| error
== ETIMEDOUT
) {
7953 if (nspace_items
[i
].flags
& NSPACE_ITEM_RESET_TIMER
) {
7954 nspace_items
[i
].flags
&= ~NSPACE_ITEM_RESET_TIMER
;
7959 } else if (error
== 0) {
7960 // hmmm, why did we get woken up?
7961 printf("woken up for token %d but it's not done, cancelled or timedout and error == 0.\n",
7962 nspace_items
[i
].token
);
7965 if (--nspace_items
[i
].refcount
== 0) {
7966 nspace_items
[i
].vp
= NULL
; // clear this so that no one will match on it again
7967 nspace_items
[i
].arg
= NULL
;
7968 nspace_items
[i
].token
= 0; // clear this so that the handler will not find it anymore
7969 nspace_items
[i
].flags
= 0; // this clears it for re-use
7971 wakeup(&nspace_token_id
);
7975 lck_mtx_unlock(&nspace_handler_lock
);
7982 get_nspace_item_status(struct vnode
*vp
, int32_t *status
)
7986 lck_mtx_lock(&nspace_handler_lock
);
7987 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
7988 if (nspace_items
[i
].vp
== vp
) {
7993 if (i
>= MAX_NSPACE_ITEMS
) {
7994 lck_mtx_unlock(&nspace_handler_lock
);
7998 *status
= nspace_items
[i
].flags
;
7999 lck_mtx_unlock(&nspace_handler_lock
);
8006 build_volfs_path(struct vnode
*vp
, char *path
, int *len
)
8008 struct vnode_attr va
;
8012 VATTR_WANTED(&va
, va_fsid
);
8013 VATTR_WANTED(&va
, va_fileid
);
8015 if (vnode_getattr(vp
, &va
, vfs_context_kernel()) != 0) {
8016 *len
= snprintf(path
, *len
, "/non/existent/path/because/vnode_getattr/failed") + 1;
8019 *len
= snprintf(path
, *len
, "/.vol/%d/%lld", (dev_t
)va
.va_fsid
, va
.va_fileid
) + 1;
8028 // Note: this function does NOT check permissions on all of the
8029 // parent directories leading to this vnode. It should only be
8030 // called on behalf of a root process. Otherwise a process may
8031 // get access to a file because the file itself is readable even
8032 // though its parent directories would prevent access.
8035 vn_open_with_vp(vnode_t vp
, int fmode
, vfs_context_t ctx
)
8039 if ((error
= suser(kauth_cred_get(), &(current_proc()->p_acflag
)))) {
8044 error
= mac_vnode_check_open(ctx
, vp
, fmode
);
8049 /* compute action to be authorized */
8051 if (fmode
& FREAD
) {
8052 action
|= KAUTH_VNODE_READ_DATA
;
8054 if (fmode
& (FWRITE
| O_TRUNC
)) {
8056 * If we are writing, appending, and not truncating,
8057 * indicate that we are appending so that if the
8058 * UF_APPEND or SF_APPEND bits are set, we do not deny
8061 if ((fmode
& O_APPEND
) && !(fmode
& O_TRUNC
)) {
8062 action
|= KAUTH_VNODE_APPEND_DATA
;
8064 action
|= KAUTH_VNODE_WRITE_DATA
;
8068 if ((error
= vnode_authorize(vp
, NULL
, action
, ctx
)) != 0)
8073 // if the vnode is tagged VOPENEVT and the current process
8074 // has the P_CHECKOPENEVT flag set, then we or in the O_EVTONLY
8075 // flag to the open mode so that this open won't count against
8076 // the vnode when carbon delete() does a vnode_isinuse() to see
8077 // if a file is currently in use. this allows spotlight
8078 // importers to not interfere with carbon apps that depend on
8079 // the no-delete-if-busy semantics of carbon delete().
8081 if ((vp
->v_flag
& VOPENEVT
) && (current_proc()->p_flag
& P_CHECKOPENEVT
)) {
8085 if ( (error
= VNOP_OPEN(vp
, fmode
, ctx
)) ) {
8088 if ( (error
= vnode_ref_ext(vp
, fmode
, 0)) ) {
8089 VNOP_CLOSE(vp
, fmode
, ctx
);
8093 /* call out to allow 3rd party notification of open.
8094 * Ignore result of kauth_authorize_fileop call.
8096 kauth_authorize_fileop(vfs_context_ucred(ctx
), KAUTH_FILEOP_OPEN
,
8104 wait_for_namespace_event(namespace_handler_info_ext
*nhi
, nspace_type_t nspace_type
)
8106 int i
, error
=0, unblock
=0;
8109 lck_mtx_lock(&nspace_handler_exclusion_lock
);
8110 if (nspace_handlers
[nspace_type
].handler_busy
) {
8111 lck_mtx_unlock(&nspace_handler_exclusion_lock
);
8114 nspace_handlers
[nspace_type
].handler_busy
= 1;
8115 lck_mtx_unlock(&nspace_handler_exclusion_lock
);
8118 * Any process that gets here will be one of the namespace handlers.
8119 * As such, they should be prevented from acquiring DMG vnodes during vnode reclamation
8120 * as we can cause deadlocks to occur, because the namespace handler may prevent
8121 * VNOP_INACTIVE from proceeding. Mark the current task as a P_DEPENDENCY_CAPABLE
8124 curtask
= current_task();
8125 bsd_set_dependency_capable (curtask
);
8127 lck_mtx_lock(&nspace_handler_lock
);
8128 if (nspace_handlers
[nspace_type
].handler_proc
== NULL
) {
8129 nspace_handlers
[nspace_type
].handler_tid
= thread_tid(current_thread());
8130 nspace_handlers
[nspace_type
].handler_proc
= current_proc();
8133 while (error
== 0) {
8135 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
8136 if (nspace_items
[i
].flags
& NSPACE_ITEM_NEW
) {
8137 if (!nspace_flags_matches_handler(nspace_items
[i
].flags
, nspace_type
)) {
8144 if (i
< MAX_NSPACE_ITEMS
) {
8145 nspace_items
[i
].flags
&= ~NSPACE_ITEM_NEW
;
8146 nspace_items
[i
].flags
|= NSPACE_ITEM_PROCESSING
;
8147 nspace_items
[i
].token
= ++nspace_token_id
;
8149 if (nspace_items
[i
].vp
) {
8150 struct fileproc
*fp
;
8151 int32_t indx
, fmode
;
8152 struct proc
*p
= current_proc();
8153 vfs_context_t ctx
= vfs_context_current();
8155 fmode
= nspace_open_flags_for_type(nspace_type
);
8157 error
= vnode_getwithvid(nspace_items
[i
].vp
, nspace_items
[i
].vid
);
8162 error
= vn_open_with_vp(nspace_items
[i
].vp
, fmode
, ctx
);
8165 vnode_put(nspace_items
[i
].vp
);
8169 if ((error
= falloc(p
, &fp
, &indx
, ctx
))) {
8170 vn_close(nspace_items
[i
].vp
, fmode
, ctx
);
8171 vnode_put(nspace_items
[i
].vp
);
8176 fp
->f_fglob
->fg_flag
= fmode
;
8177 fp
->f_fglob
->fg_type
= DTYPE_VNODE
;
8178 fp
->f_fglob
->fg_ops
= &vnops
;
8179 fp
->f_fglob
->fg_data
= (caddr_t
)nspace_items
[i
].vp
;
8182 procfdtbl_releasefd(p
, indx
, NULL
);
8183 fp_drop(p
, indx
, fp
, 1);
8186 error
= copyout(&nspace_items
[i
].token
, nhi
->token
, sizeof(uint32_t));
8187 error
= copyout(&nspace_items
[i
].op
, nhi
->flags
, sizeof(uint64_t));
8188 error
= copyout(&indx
, nhi
->fdptr
, sizeof(uint32_t));
8190 uio_t uio
= (uio_t
)nspace_items
[i
].arg
;
8191 uint64_t u_offset
, u_length
;
8194 u_offset
= uio_offset(uio
);
8195 u_length
= uio_resid(uio
);
8200 error
= copyout(&u_offset
, nhi
->infoptr
, sizeof(uint64_t));
8201 error
= copyout(&u_length
, nhi
->infoptr
+sizeof(uint64_t), sizeof(uint64_t));
8204 vn_close(nspace_items
[i
].vp
, fmode
, ctx
);
8205 fp_free(p
, indx
, fp
);
8209 vnode_put(nspace_items
[i
].vp
);
8213 printf("wait_for_nspace_event: failed (nspace_items[%d] == %p error %d, name %s)\n",
8214 i
, nspace_items
[i
].vp
, error
, nspace_items
[i
].vp
->v_name
);
8218 error
= msleep((caddr_t
)&nspace_item_idx
, &nspace_handler_lock
, PVFS
|PCATCH
, "namespace-items", 0);
8219 if ((nspace_type
== NSPACE_HANDLER_SNAPSHOT
) && (snapshot_timestamp
== 0 || snapshot_timestamp
== ~0)) {
8228 if (nspace_items
[i
].vp
&& (nspace_items
[i
].vp
->v_flag
& VNEEDSSNAPSHOT
)) {
8229 vnode_lock_spin(nspace_items
[i
].vp
);
8230 nspace_items
[i
].vp
->v_flag
&= ~VNEEDSSNAPSHOT
;
8231 vnode_unlock(nspace_items
[i
].vp
);
8233 nspace_items
[i
].vp
= NULL
;
8234 nspace_items
[i
].vid
= 0;
8235 nspace_items
[i
].flags
= NSPACE_ITEM_DONE
;
8236 nspace_items
[i
].token
= 0;
8238 wakeup((caddr_t
)&(nspace_items
[i
].vp
));
8241 if (nspace_type
== NSPACE_HANDLER_SNAPSHOT
) {
8242 // just go through every snapshot event and unblock it immediately.
8243 if (error
&& (snapshot_timestamp
== 0 || snapshot_timestamp
== ~0)) {
8244 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
8245 if (nspace_items
[i
].flags
& NSPACE_ITEM_NEW
) {
8246 if (nspace_flags_matches_handler(nspace_items
[i
].flags
, nspace_type
)) {
8247 nspace_items
[i
].vp
= NULL
;
8248 nspace_items
[i
].vid
= 0;
8249 nspace_items
[i
].flags
= NSPACE_ITEM_DONE
;
8250 nspace_items
[i
].token
= 0;
8252 wakeup((caddr_t
)&(nspace_items
[i
].vp
));
8259 lck_mtx_unlock(&nspace_handler_lock
);
8261 lck_mtx_lock(&nspace_handler_exclusion_lock
);
8262 nspace_handlers
[nspace_type
].handler_busy
= 0;
8263 lck_mtx_unlock(&nspace_handler_exclusion_lock
);
8269 static int process_namespace_fsctl(nspace_type_t nspace_type
, int is64bit
, u_int size
, caddr_t data
)
8272 namespace_handler_info_ext nhi
;
8274 if (nspace_type
== NSPACE_HANDLER_SNAPSHOT
&& (snapshot_timestamp
== 0 || snapshot_timestamp
== ~0)) {
8278 if ((error
= suser(kauth_cred_get(), &(current_proc()->p_acflag
)))) {
8282 if ( (is64bit
&& size
!= sizeof(user64_namespace_handler_info
) && size
!= sizeof(user64_namespace_handler_info_ext
))
8283 || (is64bit
== 0 && size
!= sizeof(user32_namespace_handler_info
) && size
!= sizeof(user32_namespace_handler_info_ext
))) {
8285 // either you're 64-bit and passed a 64-bit struct or
8286 // you're 32-bit and passed a 32-bit struct. otherwise
8292 nhi
.token
= (user_addr_t
)((user64_namespace_handler_info
*)data
)->token
;
8293 nhi
.flags
= (user_addr_t
)((user64_namespace_handler_info
*)data
)->flags
;
8294 nhi
.fdptr
= (user_addr_t
)((user64_namespace_handler_info
*)data
)->fdptr
;
8295 if (size
== sizeof(user64_namespace_handler_info_ext
)) {
8296 nhi
.infoptr
= (user_addr_t
)((user64_namespace_handler_info_ext
*)data
)->infoptr
;
8301 nhi
.token
= CAST_USER_ADDR_T(((user32_namespace_handler_info
*)data
)->token
);
8302 nhi
.flags
= CAST_USER_ADDR_T(((user32_namespace_handler_info
*)data
)->flags
);
8303 nhi
.fdptr
= CAST_USER_ADDR_T(((user32_namespace_handler_info
*)data
)->fdptr
);
8304 if (size
== sizeof(user32_namespace_handler_info_ext
)) {
8305 nhi
.infoptr
= CAST_USER_ADDR_T(((user32_namespace_handler_info_ext
*)data
)->infoptr
);
8311 return wait_for_namespace_event(&nhi
, nspace_type
);
8315 * Make a filesystem-specific control call:
8319 fsctl_internal(proc_t p
, vnode_t
*arg_vp
, u_long cmd
, user_addr_t udata
, u_long options
, vfs_context_t ctx
)
8324 #define STK_PARAMS 128
8325 char stkbuf
[STK_PARAMS
];
8327 vnode_t vp
= *arg_vp
;
8329 size
= IOCPARM_LEN(cmd
);
8330 if (size
> IOCPARM_MAX
) return (EINVAL
);
8332 is64bit
= proc_is64bit(p
);
8335 if (size
> sizeof (stkbuf
)) {
8336 if ((memp
= (caddr_t
)kalloc(size
)) == 0) return ENOMEM
;
8344 error
= copyin(udata
, data
, size
);
8345 if (error
) goto FSCtl_Exit
;
8348 *(user_addr_t
*)data
= udata
;
8351 *(uint32_t *)data
= (uint32_t)udata
;
8354 } else if ((cmd
& IOC_OUT
) && size
) {
8356 * Zero the buffer so the user always
8357 * gets back something deterministic.
8360 } else if (cmd
& IOC_VOID
) {
8362 *(user_addr_t
*)data
= udata
;
8365 *(uint32_t *)data
= (uint32_t)udata
;
8369 /* Check to see if it's a generic command */
8370 if (IOCBASECMD(cmd
) == FSCTL_SYNC_VOLUME
) {
8371 mount_t mp
= vp
->v_mount
;
8372 int arg
= *(uint32_t*)data
;
8374 /* record vid of vp so we can drop it below. */
8375 uint32_t vvid
= vp
->v_id
;
8378 * Then grab mount_iterref so that we can release the vnode.
8379 * Without this, a thread may call vnode_iterate_prepare then
8380 * get into a deadlock because we've never released the root vp
8382 error
= mount_iterref (mp
, 0);
8388 /* issue the sync for this volume */
8389 (void)sync_callback(mp
, (arg
& FSCTL_SYNC_WAIT
) ? &arg
: NULL
);
8392 * Then release the mount_iterref once we're done syncing; it's not
8393 * needed for the VNOP_IOCTL below
8397 if (arg
& FSCTL_SYNC_FULLSYNC
) {
8398 /* re-obtain vnode iocount on the root vp, if possible */
8399 error
= vnode_getwithvid (vp
, vvid
);
8401 error
= VNOP_IOCTL(vp
, F_FULLFSYNC
, (caddr_t
)NULL
, 0, ctx
);
8405 /* mark the argument VP as having been released */
8408 } else if (IOCBASECMD(cmd
) == FSCTL_SET_PACKAGE_EXTS
) {
8409 user_addr_t ext_strings
;
8410 uint32_t num_entries
;
8413 if ( (is64bit
&& size
!= sizeof(user64_package_ext_info
))
8414 || (is64bit
== 0 && size
!= sizeof(user32_package_ext_info
))) {
8416 // either you're 64-bit and passed a 64-bit struct or
8417 // you're 32-bit and passed a 32-bit struct. otherwise
8424 ext_strings
= ((user64_package_ext_info
*)data
)->strings
;
8425 num_entries
= ((user64_package_ext_info
*)data
)->num_entries
;
8426 max_width
= ((user64_package_ext_info
*)data
)->max_width
;
8428 ext_strings
= CAST_USER_ADDR_T(((user32_package_ext_info
*)data
)->strings
);
8429 num_entries
= ((user32_package_ext_info
*)data
)->num_entries
;
8430 max_width
= ((user32_package_ext_info
*)data
)->max_width
;
8433 error
= set_package_extensions_table(ext_strings
, num_entries
, max_width
);
8435 } else if (IOCBASECMD(cmd
) == FSCTL_WAIT_FOR_SYNC
) {
8436 error
= tsleep((caddr_t
)&sync_wait_time
, PVFS
|PCATCH
, "sync-wait", 0);
8438 *(uint32_t *)data
= (uint32_t)sync_wait_time
;
8444 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_HANDLER_GET
) {
8445 error
= process_namespace_fsctl(NSPACE_HANDLER_NSPACE
, is64bit
, size
, data
);
8446 } else if (IOCBASECMD(cmd
) == FSCTL_OLD_SNAPSHOT_HANDLER_GET
) {
8447 error
= process_namespace_fsctl(NSPACE_HANDLER_SNAPSHOT
, is64bit
, size
, data
);
8448 } else if (IOCBASECMD(cmd
) == FSCTL_SNAPSHOT_HANDLER_GET_EXT
) {
8449 error
= process_namespace_fsctl(NSPACE_HANDLER_SNAPSHOT
, is64bit
, size
, data
);
8450 } else if (IOCBASECMD(cmd
) == FSCTL_TRACKED_HANDLER_GET
) {
8451 error
= process_namespace_fsctl(NSPACE_HANDLER_TRACK
, is64bit
, size
, data
);
8452 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_HANDLER_UPDATE
) {
8453 uint32_t token
, val
;
8456 if ((error
= suser(kauth_cred_get(), &(p
->p_acflag
)))) {
8460 if (!nspace_is_special_process(p
)) {
8465 token
= ((uint32_t *)data
)[0];
8466 val
= ((uint32_t *)data
)[1];
8468 lck_mtx_lock(&nspace_handler_lock
);
8470 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
8471 if (nspace_items
[i
].token
== token
) {
8476 if (i
>= MAX_NSPACE_ITEMS
) {
8480 // if this bit is set, when resolve_nspace_item() times out
8481 // it will loop and go back to sleep.
8483 nspace_items
[i
].flags
|= NSPACE_ITEM_RESET_TIMER
;
8486 lck_mtx_unlock(&nspace_handler_lock
);
8489 printf("nspace-handler-update: did not find token %u\n", token
);
8492 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_HANDLER_UNBLOCK
) {
8493 uint32_t token
, val
;
8496 if ((error
= suser(kauth_cred_get(), &(p
->p_acflag
)))) {
8500 if (!nspace_is_special_process(p
)) {
8505 token
= ((uint32_t *)data
)[0];
8506 val
= ((uint32_t *)data
)[1];
8508 lck_mtx_lock(&nspace_handler_lock
);
8510 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
8511 if (nspace_items
[i
].token
== token
) {
8516 if (i
>= MAX_NSPACE_ITEMS
) {
8517 printf("nspace-handler-unblock: did not find token %u\n", token
);
8520 if (val
== 0 && nspace_items
[i
].vp
) {
8521 vnode_lock_spin(nspace_items
[i
].vp
);
8522 nspace_items
[i
].vp
->v_flag
&= ~VNEEDSSNAPSHOT
;
8523 vnode_unlock(nspace_items
[i
].vp
);
8526 nspace_items
[i
].vp
= NULL
;
8527 nspace_items
[i
].arg
= NULL
;
8528 nspace_items
[i
].op
= 0;
8529 nspace_items
[i
].vid
= 0;
8530 nspace_items
[i
].flags
= NSPACE_ITEM_DONE
;
8531 nspace_items
[i
].token
= 0;
8533 wakeup((caddr_t
)&(nspace_items
[i
].vp
));
8536 lck_mtx_unlock(&nspace_handler_lock
);
8538 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_HANDLER_CANCEL
) {
8539 uint32_t token
, val
;
8542 if ((error
= suser(kauth_cred_get(), &(p
->p_acflag
)))) {
8546 if (!nspace_is_special_process(p
)) {
8551 token
= ((uint32_t *)data
)[0];
8552 val
= ((uint32_t *)data
)[1];
8554 lck_mtx_lock(&nspace_handler_lock
);
8556 for(i
=0; i
< MAX_NSPACE_ITEMS
; i
++) {
8557 if (nspace_items
[i
].token
== token
) {
8562 if (i
>= MAX_NSPACE_ITEMS
) {
8563 printf("nspace-handler-cancel: did not find token %u\n", token
);
8566 if (nspace_items
[i
].vp
) {
8567 vnode_lock_spin(nspace_items
[i
].vp
);
8568 nspace_items
[i
].vp
->v_flag
&= ~VNEEDSSNAPSHOT
;
8569 vnode_unlock(nspace_items
[i
].vp
);
8572 nspace_items
[i
].vp
= NULL
;
8573 nspace_items
[i
].arg
= NULL
;
8574 nspace_items
[i
].vid
= 0;
8575 nspace_items
[i
].token
= val
;
8576 nspace_items
[i
].flags
&= ~NSPACE_ITEM_PROCESSING
;
8577 nspace_items
[i
].flags
|= NSPACE_ITEM_CANCELLED
;
8579 wakeup((caddr_t
)&(nspace_items
[i
].vp
));
8582 lck_mtx_unlock(&nspace_handler_lock
);
8583 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_HANDLER_SET_SNAPSHOT_TIME
) {
8584 if ((error
= suser(kauth_cred_get(), &(current_proc()->p_acflag
)))) {
8588 // we explicitly do not do the namespace_handler_proc check here
8590 lck_mtx_lock(&nspace_handler_lock
);
8591 snapshot_timestamp
= ((uint32_t *)data
)[0];
8592 wakeup(&nspace_item_idx
);
8593 lck_mtx_unlock(&nspace_handler_lock
);
8594 printf("nspace-handler-set-snapshot-time: %d\n", (int)snapshot_timestamp
);
8596 } else if (IOCBASECMD(cmd
) == FSCTL_NAMESPACE_ALLOW_DMG_SNAPSHOT_EVENTS
) {
8597 if ((error
= suser(kauth_cred_get(), &(current_proc()->p_acflag
)))) {
8601 lck_mtx_lock(&nspace_handler_lock
);
8602 nspace_allow_virtual_devs
= ((uint32_t *)data
)[0];
8603 lck_mtx_unlock(&nspace_handler_lock
);
8604 printf("nspace-snapshot-handler will%s allow events on disk-images\n",
8605 nspace_allow_virtual_devs
? "" : " NOT");
8608 } else if (IOCBASECMD(cmd
) == FSCTL_SET_FSTYPENAME_OVERRIDE
) {
8609 if ((error
= suser(kauth_cred_get(), &(current_proc()->p_acflag
)))) {
8613 mount_lock(vp
->v_mount
);
8615 strlcpy(&vp
->v_mount
->fstypename_override
[0], data
, MFSTYPENAMELEN
);
8616 vp
->v_mount
->mnt_kern_flag
|= MNTK_TYPENAME_OVERRIDE
;
8617 if (vfs_isrdonly(vp
->v_mount
) && strcmp(vp
->v_mount
->fstypename_override
, "mtmfs") == 0) {
8618 vp
->v_mount
->mnt_kern_flag
|= MNTK_EXTENDED_SECURITY
;
8619 vp
->v_mount
->mnt_kern_flag
&= ~MNTK_AUTH_OPAQUE
;
8622 if (strcmp(vp
->v_mount
->fstypename_override
, "mtmfs") == 0) {
8623 vp
->v_mount
->mnt_kern_flag
&= ~MNTK_EXTENDED_SECURITY
;
8625 vp
->v_mount
->mnt_kern_flag
&= ~MNTK_TYPENAME_OVERRIDE
;
8626 vp
->v_mount
->fstypename_override
[0] = '\0';
8628 mount_unlock(vp
->v_mount
);
8631 /* Invoke the filesystem-specific code */
8632 error
= VNOP_IOCTL(vp
, IOCBASECMD(cmd
), data
, options
, ctx
);
8637 * Copy any data to user, size was
8638 * already set and checked above.
8640 if (error
== 0 && (cmd
& IOC_OUT
) && size
)
8641 error
= copyout(data
, udata
, size
);
8644 if (memp
) kfree(memp
, size
);
8651 fsctl (proc_t p
, struct fsctl_args
*uap
, __unused
int32_t *retval
)
8654 struct nameidata nd
;
8657 vfs_context_t ctx
= vfs_context_current();
8659 AUDIT_ARG(cmd
, uap
->cmd
);
8660 AUDIT_ARG(value32
, uap
->options
);
8661 /* Get the vnode for the file we are getting info on: */
8663 if ((uap
->options
& FSOPT_NOFOLLOW
) == 0) nameiflags
|= FOLLOW
;
8664 NDINIT(&nd
, LOOKUP
, OP_FSCTL
, nameiflags
| AUDITVNPATH1
,
8665 UIO_USERSPACE
, uap
->path
, ctx
);
8666 if ((error
= namei(&nd
))) goto done
;
8671 error
= mac_mount_check_fsctl(ctx
, vnode_mount(vp
), uap
->cmd
);
8677 error
= fsctl_internal(p
, &vp
, uap
->cmd
, (user_addr_t
)uap
->data
, uap
->options
, ctx
);
8686 ffsctl (proc_t p
, struct ffsctl_args
*uap
, __unused
int32_t *retval
)
8690 vfs_context_t ctx
= vfs_context_current();
8693 AUDIT_ARG(fd
, uap
->fd
);
8694 AUDIT_ARG(cmd
, uap
->cmd
);
8695 AUDIT_ARG(value32
, uap
->options
);
8697 /* Get the vnode for the file we are getting info on: */
8698 if ((error
= file_vnode(uap
->fd
, &vp
)))
8701 if ((error
= vnode_getwithref(vp
))) {
8706 error
= mac_mount_check_fsctl(ctx
, vnode_mount(vp
), uap
->cmd
);
8712 error
= fsctl_internal(p
, &vp
, uap
->cmd
, (user_addr_t
)uap
->data
, uap
->options
, ctx
);
8722 /* end of fsctl system call */
8725 * An in-kernel sync for power management to call.
8727 __private_extern__
int
8732 struct sync_args data
;
8737 error
= sync(current_proc(), &data
, &retval
[0]);
8741 } /* end of sync_internal call */
8745 * Retrieve the data of an extended attribute.
8748 getxattr(proc_t p
, struct getxattr_args
*uap
, user_ssize_t
*retval
)
8751 struct nameidata nd
;
8752 char attrname
[XATTR_MAXNAMELEN
+1];
8753 vfs_context_t ctx
= vfs_context_current();
8755 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
8756 size_t attrsize
= 0;
8758 u_int32_t nameiflags
;
8760 char uio_buf
[ UIO_SIZEOF(1) ];
8762 if (uap
->options
& (XATTR_NOSECURITY
| XATTR_NODEFAULT
))
8765 nameiflags
= (uap
->options
& XATTR_NOFOLLOW
) ? 0 : FOLLOW
;
8766 NDINIT(&nd
, LOOKUP
, OP_GETXATTR
, nameiflags
, spacetype
, uap
->path
, ctx
);
8767 if ((error
= namei(&nd
))) {
8773 if ((error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
) != 0)) {
8776 if (xattr_protected(attrname
)) {
8777 if (!vfs_context_issuser(ctx
) || strcmp(attrname
, "com.apple.system.Security") != 0) {
8783 * the specific check for 0xffffffff is a hack to preserve
8784 * binaray compatibilty in K64 with applications that discovered
8785 * that passing in a buf pointer and a size of -1 resulted in
8786 * just the size of the indicated extended attribute being returned.
8787 * this isn't part of the documented behavior, but because of the
8788 * original implemtation's check for "uap->size > 0", this behavior
8789 * was allowed. In K32 that check turned into a signed comparison
8790 * even though uap->size is unsigned... in K64, we blow by that
8791 * check because uap->size is unsigned and doesn't get sign smeared
8792 * in the munger for a 32 bit user app. we also need to add a
8793 * check to limit the maximum size of the buffer being passed in...
8794 * unfortunately, the underlying fileystems seem to just malloc
8795 * the requested size even if the actual extended attribute is tiny.
8796 * because that malloc is for kernel wired memory, we have to put a
8799 * U32 running on K64 will yield 0x00000000ffffffff for uap->size
8800 * U64 running on K64 will yield -1 (64 bits wide)
8801 * U32/U64 running on K32 will yield -1 (32 bits wide)
8803 if (uap
->size
== 0xffffffff || uap
->size
== (size_t)-1)
8807 if (uap
->size
> (size_t)XATTR_MAXSIZE
)
8808 uap
->size
= XATTR_MAXSIZE
;
8810 auio
= uio_createwithbuffer(1, uap
->position
, spacetype
, UIO_READ
,
8811 &uio_buf
[0], sizeof(uio_buf
));
8812 uio_addiov(auio
, uap
->value
, uap
->size
);
8815 error
= vn_getxattr(vp
, attrname
, auio
, &attrsize
, uap
->options
, ctx
);
8820 *retval
= uap
->size
- uio_resid(auio
);
8822 *retval
= (user_ssize_t
)attrsize
;
8829 * Retrieve the data of an extended attribute.
8832 fgetxattr(proc_t p
, struct fgetxattr_args
*uap
, user_ssize_t
*retval
)
8835 char attrname
[XATTR_MAXNAMELEN
+1];
8837 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
8838 size_t attrsize
= 0;
8841 char uio_buf
[ UIO_SIZEOF(1) ];
8843 if (uap
->options
& (XATTR_NOFOLLOW
| XATTR_NOSECURITY
| XATTR_NODEFAULT
))
8846 if ( (error
= file_vnode(uap
->fd
, &vp
)) ) {
8849 if ( (error
= vnode_getwithref(vp
)) ) {
8853 if ((error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
) != 0)) {
8856 if (xattr_protected(attrname
)) {
8860 if (uap
->value
&& uap
->size
> 0) {
8861 auio
= uio_createwithbuffer(1, uap
->position
, spacetype
, UIO_READ
,
8862 &uio_buf
[0], sizeof(uio_buf
));
8863 uio_addiov(auio
, uap
->value
, uap
->size
);
8866 error
= vn_getxattr(vp
, attrname
, auio
, &attrsize
, uap
->options
, vfs_context_current());
8868 (void)vnode_put(vp
);
8872 *retval
= uap
->size
- uio_resid(auio
);
8874 *retval
= (user_ssize_t
)attrsize
;
8880 * Set the data of an extended attribute.
8883 setxattr(proc_t p
, struct setxattr_args
*uap
, int *retval
)
8886 struct nameidata nd
;
8887 char attrname
[XATTR_MAXNAMELEN
+1];
8888 vfs_context_t ctx
= vfs_context_current();
8890 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
8892 u_int32_t nameiflags
;
8894 char uio_buf
[ UIO_SIZEOF(1) ];
8896 if (uap
->options
& (XATTR_NOSECURITY
| XATTR_NODEFAULT
))
8899 if ((error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
) != 0)) {
8900 if (error
== EPERM
) {
8901 /* if the string won't fit in attrname, copyinstr emits EPERM */
8902 return (ENAMETOOLONG
);
8904 /* Otherwise return the default error from copyinstr to detect ERANGE, etc */
8907 if (xattr_protected(attrname
))
8909 if (uap
->size
!= 0 && uap
->value
== 0) {
8913 nameiflags
= (uap
->options
& XATTR_NOFOLLOW
) ? 0 : FOLLOW
;
8914 NDINIT(&nd
, LOOKUP
, OP_SETXATTR
, nameiflags
, spacetype
, uap
->path
, ctx
);
8915 if ((error
= namei(&nd
))) {
8921 auio
= uio_createwithbuffer(1, uap
->position
, spacetype
, UIO_WRITE
,
8922 &uio_buf
[0], sizeof(uio_buf
));
8923 uio_addiov(auio
, uap
->value
, uap
->size
);
8925 error
= vn_setxattr(vp
, attrname
, auio
, uap
->options
, ctx
);
8928 add_fsevent(FSE_XATTR_MODIFIED
, ctx
,
8939 * Set the data of an extended attribute.
8942 fsetxattr(proc_t p
, struct fsetxattr_args
*uap
, int *retval
)
8945 char attrname
[XATTR_MAXNAMELEN
+1];
8947 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
8950 char uio_buf
[ UIO_SIZEOF(1) ];
8952 vfs_context_t ctx
= vfs_context_current();
8955 if (uap
->options
& (XATTR_NOFOLLOW
| XATTR_NOSECURITY
| XATTR_NODEFAULT
))
8958 if ((error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
) != 0)) {
8961 if (xattr_protected(attrname
))
8963 if (uap
->size
!= 0 && uap
->value
== 0) {
8966 if ( (error
= file_vnode(uap
->fd
, &vp
)) ) {
8969 if ( (error
= vnode_getwithref(vp
)) ) {
8973 auio
= uio_createwithbuffer(1, uap
->position
, spacetype
, UIO_WRITE
,
8974 &uio_buf
[0], sizeof(uio_buf
));
8975 uio_addiov(auio
, uap
->value
, uap
->size
);
8977 error
= vn_setxattr(vp
, attrname
, auio
, uap
->options
, vfs_context_current());
8980 add_fsevent(FSE_XATTR_MODIFIED
, ctx
,
8992 * Remove an extended attribute.
8993 * XXX Code duplication here.
8996 removexattr(proc_t p
, struct removexattr_args
*uap
, int *retval
)
8999 struct nameidata nd
;
9000 char attrname
[XATTR_MAXNAMELEN
+1];
9001 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
9002 vfs_context_t ctx
= vfs_context_current();
9004 u_int32_t nameiflags
;
9007 if (uap
->options
& (XATTR_NOSECURITY
| XATTR_NODEFAULT
))
9010 error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
);
9014 if (xattr_protected(attrname
))
9016 nameiflags
= (uap
->options
& XATTR_NOFOLLOW
) ? 0 : FOLLOW
;
9017 NDINIT(&nd
, LOOKUP
, OP_REMOVEXATTR
, nameiflags
, spacetype
, uap
->path
, ctx
);
9018 if ((error
= namei(&nd
))) {
9024 error
= vn_removexattr(vp
, attrname
, uap
->options
, ctx
);
9027 add_fsevent(FSE_XATTR_REMOVED
, ctx
,
9038 * Remove an extended attribute.
9039 * XXX Code duplication here.
9042 fremovexattr(__unused proc_t p
, struct fremovexattr_args
*uap
, int *retval
)
9045 char attrname
[XATTR_MAXNAMELEN
+1];
9049 vfs_context_t ctx
= vfs_context_current();
9052 if (uap
->options
& (XATTR_NOFOLLOW
| XATTR_NOSECURITY
| XATTR_NODEFAULT
))
9055 error
= copyinstr(uap
->attrname
, attrname
, sizeof(attrname
), &namelen
);
9059 if (xattr_protected(attrname
))
9061 if ( (error
= file_vnode(uap
->fd
, &vp
)) ) {
9064 if ( (error
= vnode_getwithref(vp
)) ) {
9069 error
= vn_removexattr(vp
, attrname
, uap
->options
, vfs_context_current());
9072 add_fsevent(FSE_XATTR_REMOVED
, ctx
,
9084 * Retrieve the list of extended attribute names.
9085 * XXX Code duplication here.
9088 listxattr(proc_t p
, struct listxattr_args
*uap
, user_ssize_t
*retval
)
9091 struct nameidata nd
;
9092 vfs_context_t ctx
= vfs_context_current();
9094 int spacetype
= IS_64BIT_PROCESS(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
9095 size_t attrsize
= 0;
9096 u_int32_t nameiflags
;
9098 char uio_buf
[ UIO_SIZEOF(1) ];
9100 if (uap
->options
& (XATTR_NOSECURITY
| XATTR_NODEFAULT
))
9103 nameiflags
= ((uap
->options
& XATTR_NOFOLLOW
) ? 0 : FOLLOW
) | NOTRIGGER
;
9104 NDINIT(&nd
, LOOKUP
, OP_LISTXATTR
, nameiflags
, spacetype
, uap
->path
, ctx
);
9105 if ((error
= namei(&nd
))) {
9110 if (uap
->namebuf
!= 0 && uap
->bufsize
> 0) {
9111 auio
= uio_createwithbuffer(1, 0, spacetype
, UIO_READ
,
9112 &uio_buf
[0], sizeof(uio_buf
));
9113 uio_addiov(auio
, uap
->namebuf
, uap
->bufsize
);
9116 error
= vn_listxattr(vp
, auio
, &attrsize
, uap
->options
, ctx
);
9120 *retval
= (user_ssize_t
)uap
->bufsize
- uio_resid(auio
);
9122 *retval
= (user_ssize_t
)attrsize
;
9128 * Retrieve the list of extended attribute names.
9129 * XXX Code duplication here.
9132 flistxattr(proc_t p
, struct flistxattr_args
*uap
, user_ssize_t
*retval
)
9136 int spacetype
= proc_is64bit(p
) ? UIO_USERSPACE64
: UIO_USERSPACE32
;
9137 size_t attrsize
= 0;
9139 char uio_buf
[ UIO_SIZEOF(1) ];
9141 if (uap
->options
& (XATTR_NOFOLLOW
| XATTR_NOSECURITY
| XATTR_NODEFAULT
))
9144 if ( (error
= file_vnode(uap
->fd
, &vp
)) ) {
9147 if ( (error
= vnode_getwithref(vp
)) ) {
9151 if (uap
->namebuf
!= 0 && uap
->bufsize
> 0) {
9152 auio
= uio_createwithbuffer(1, 0, spacetype
,
9153 UIO_READ
, &uio_buf
[0], sizeof(uio_buf
));
9154 uio_addiov(auio
, uap
->namebuf
, uap
->bufsize
);
9157 error
= vn_listxattr(vp
, auio
, &attrsize
, uap
->options
, vfs_context_current());
9162 *retval
= (user_ssize_t
)uap
->bufsize
- uio_resid(auio
);
9164 *retval
= (user_ssize_t
)attrsize
;
9170 * Obtain the full pathname of a file system object by id.
9172 * This is a private SPI used by the File Manager.
9176 fsgetpath(__unused proc_t p
, struct fsgetpath_args
*uap
, user_ssize_t
*retval
)
9179 struct mount
*mp
= NULL
;
9180 vfs_context_t ctx
= vfs_context_current();
9187 if ((error
= copyin(uap
->fsid
, (caddr_t
)&fsid
, sizeof(fsid
)))) {
9190 AUDIT_ARG(value32
, fsid
.val
[0]);
9191 AUDIT_ARG(value64
, uap
->objid
);
9192 /* Restrict output buffer size for now. */
9193 if (uap
->bufsize
> PAGE_SIZE
) {
9196 MALLOC(realpath
, char *, uap
->bufsize
, M_TEMP
, M_WAITOK
);
9197 if (realpath
== NULL
) {
9200 /* Find the target mountpoint. */
9201 if ((mp
= mount_lookupby_volfsid(fsid
.val
[0], 1)) == NULL
) {
9202 error
= ENOTSUP
; /* unexpected failure */
9205 /* Find the target vnode. */
9206 if (uap
->objid
== 2) {
9207 error
= VFS_ROOT(mp
, &vp
, ctx
);
9209 error
= VFS_VGET(mp
, (ino64_t
)uap
->objid
, &vp
, ctx
);
9216 error
= mac_vnode_check_fsgetpath(ctx
, vp
);
9222 /* Obtain the absolute path to this vnode. */
9223 bpflags
= vfs_context_suser(ctx
) ? BUILDPATH_CHECKACCESS
: 0;
9224 error
= build_path(vp
, realpath
, uap
->bufsize
, &length
, bpflags
, ctx
);
9229 AUDIT_ARG(text
, realpath
);
9230 error
= copyout((caddr_t
)realpath
, uap
->buf
, length
);
9232 *retval
= (user_ssize_t
)length
; /* may be superseded by error */
9235 FREE(realpath
, M_TEMP
);
9241 * Common routine to handle various flavors of statfs data heading out
9244 * Returns: 0 Success
9248 munge_statfs(struct mount
*mp
, struct vfsstatfs
*sfsp
,
9249 user_addr_t bufp
, int *sizep
, boolean_t is_64_bit
,
9250 boolean_t partial_copy
)
9253 int my_size
, copy_size
;
9256 struct user64_statfs sfs
;
9257 my_size
= copy_size
= sizeof(sfs
);
9258 bzero(&sfs
, my_size
);
9259 sfs
.f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
9260 sfs
.f_type
= mp
->mnt_vtable
->vfc_typenum
;
9261 sfs
.f_reserved1
= (short)sfsp
->f_fssubtype
;
9262 sfs
.f_bsize
= (user64_long_t
)sfsp
->f_bsize
;
9263 sfs
.f_iosize
= (user64_long_t
)sfsp
->f_iosize
;
9264 sfs
.f_blocks
= (user64_long_t
)sfsp
->f_blocks
;
9265 sfs
.f_bfree
= (user64_long_t
)sfsp
->f_bfree
;
9266 sfs
.f_bavail
= (user64_long_t
)sfsp
->f_bavail
;
9267 sfs
.f_files
= (user64_long_t
)sfsp
->f_files
;
9268 sfs
.f_ffree
= (user64_long_t
)sfsp
->f_ffree
;
9269 sfs
.f_fsid
= sfsp
->f_fsid
;
9270 sfs
.f_owner
= sfsp
->f_owner
;
9271 if (mp
->mnt_kern_flag
& MNTK_TYPENAME_OVERRIDE
) {
9272 strlcpy(&sfs
.f_fstypename
[0], &mp
->fstypename_override
[0], MFSTYPENAMELEN
);
9274 strlcpy(&sfs
.f_fstypename
[0], &sfsp
->f_fstypename
[0], MFSNAMELEN
);
9276 strlcpy(&sfs
.f_mntonname
[0], &sfsp
->f_mntonname
[0], MNAMELEN
);
9277 strlcpy(&sfs
.f_mntfromname
[0], &sfsp
->f_mntfromname
[0], MNAMELEN
);
9280 copy_size
-= (sizeof(sfs
.f_reserved3
) + sizeof(sfs
.f_reserved4
));
9282 error
= copyout((caddr_t
)&sfs
, bufp
, copy_size
);
9285 struct user32_statfs sfs
;
9287 my_size
= copy_size
= sizeof(sfs
);
9288 bzero(&sfs
, my_size
);
9290 sfs
.f_flags
= mp
->mnt_flag
& MNT_VISFLAGMASK
;
9291 sfs
.f_type
= mp
->mnt_vtable
->vfc_typenum
;
9292 sfs
.f_reserved1
= (short)sfsp
->f_fssubtype
;
9295 * It's possible for there to be more than 2^^31 blocks in the filesystem, so we
9296 * have to fudge the numbers here in that case. We inflate the blocksize in order
9297 * to reflect the filesystem size as best we can.
9299 if ((sfsp
->f_blocks
> INT_MAX
)
9300 /* Hack for 4061702 . I think the real fix is for Carbon to
9301 * look for some volume capability and not depend on hidden
9302 * semantics agreed between a FS and carbon.
9303 * f_blocks, f_bfree, and f_bavail set to -1 is the trigger
9304 * for Carbon to set bNoVolumeSizes volume attribute.
9305 * Without this the webdavfs files cannot be copied onto
9306 * disk as they look huge. This change should not affect
9307 * XSAN as they should not setting these to -1..
9309 && (sfsp
->f_blocks
!= 0xffffffffffffffffULL
)
9310 && (sfsp
->f_bfree
!= 0xffffffffffffffffULL
)
9311 && (sfsp
->f_bavail
!= 0xffffffffffffffffULL
)) {
9315 * Work out how far we have to shift the block count down to make it fit.
9316 * Note that it's possible to have to shift so far that the resulting
9317 * blocksize would be unreportably large. At that point, we will clip
9318 * any values that don't fit.
9320 * For safety's sake, we also ensure that f_iosize is never reported as
9321 * being smaller than f_bsize.
9323 for (shift
= 0; shift
< 32; shift
++) {
9324 if ((sfsp
->f_blocks
>> shift
) <= INT_MAX
)
9326 if ((sfsp
->f_bsize
<< (shift
+ 1)) > INT_MAX
)
9329 #define __SHIFT_OR_CLIP(x, s) ((((x) >> (s)) > INT_MAX) ? INT_MAX : ((x) >> (s)))
9330 sfs
.f_blocks
= (user32_long_t
)__SHIFT_OR_CLIP(sfsp
->f_blocks
, shift
);
9331 sfs
.f_bfree
= (user32_long_t
)__SHIFT_OR_CLIP(sfsp
->f_bfree
, shift
);
9332 sfs
.f_bavail
= (user32_long_t
)__SHIFT_OR_CLIP(sfsp
->f_bavail
, shift
);
9333 #undef __SHIFT_OR_CLIP
9334 sfs
.f_bsize
= (user32_long_t
)(sfsp
->f_bsize
<< shift
);
9335 sfs
.f_iosize
= lmax(sfsp
->f_iosize
, sfsp
->f_bsize
);
9337 /* filesystem is small enough to be reported honestly */
9338 sfs
.f_bsize
= (user32_long_t
)sfsp
->f_bsize
;
9339 sfs
.f_iosize
= (user32_long_t
)sfsp
->f_iosize
;
9340 sfs
.f_blocks
= (user32_long_t
)sfsp
->f_blocks
;
9341 sfs
.f_bfree
= (user32_long_t
)sfsp
->f_bfree
;
9342 sfs
.f_bavail
= (user32_long_t
)sfsp
->f_bavail
;
9344 sfs
.f_files
= (user32_long_t
)sfsp
->f_files
;
9345 sfs
.f_ffree
= (user32_long_t
)sfsp
->f_ffree
;
9346 sfs
.f_fsid
= sfsp
->f_fsid
;
9347 sfs
.f_owner
= sfsp
->f_owner
;
9348 if (mp
->mnt_kern_flag
& MNTK_TYPENAME_OVERRIDE
) {
9349 strlcpy(&sfs
.f_fstypename
[0], &mp
->fstypename_override
[0], MFSTYPENAMELEN
);
9351 strlcpy(&sfs
.f_fstypename
[0], &sfsp
->f_fstypename
[0], MFSNAMELEN
);
9353 strlcpy(&sfs
.f_mntonname
[0], &sfsp
->f_mntonname
[0], MNAMELEN
);
9354 strlcpy(&sfs
.f_mntfromname
[0], &sfsp
->f_mntfromname
[0], MNAMELEN
);
9357 copy_size
-= (sizeof(sfs
.f_reserved3
) + sizeof(sfs
.f_reserved4
));
9359 error
= copyout((caddr_t
)&sfs
, bufp
, copy_size
);
9362 if (sizep
!= NULL
) {
9369 * copy stat structure into user_stat structure.
9371 void munge_user64_stat(struct stat
*sbp
, struct user64_stat
*usbp
)
9373 bzero(usbp
, sizeof(*usbp
));
9375 usbp
->st_dev
= sbp
->st_dev
;
9376 usbp
->st_ino
= sbp
->st_ino
;
9377 usbp
->st_mode
= sbp
->st_mode
;
9378 usbp
->st_nlink
= sbp
->st_nlink
;
9379 usbp
->st_uid
= sbp
->st_uid
;
9380 usbp
->st_gid
= sbp
->st_gid
;
9381 usbp
->st_rdev
= sbp
->st_rdev
;
9382 #ifndef _POSIX_C_SOURCE
9383 usbp
->st_atimespec
.tv_sec
= sbp
->st_atimespec
.tv_sec
;
9384 usbp
->st_atimespec
.tv_nsec
= sbp
->st_atimespec
.tv_nsec
;
9385 usbp
->st_mtimespec
.tv_sec
= sbp
->st_mtimespec
.tv_sec
;
9386 usbp
->st_mtimespec
.tv_nsec
= sbp
->st_mtimespec
.tv_nsec
;
9387 usbp
->st_ctimespec
.tv_sec
= sbp
->st_ctimespec
.tv_sec
;
9388 usbp
->st_ctimespec
.tv_nsec
= sbp
->st_ctimespec
.tv_nsec
;
9390 usbp
->st_atime
= sbp
->st_atime
;
9391 usbp
->st_atimensec
= sbp
->st_atimensec
;
9392 usbp
->st_mtime
= sbp
->st_mtime
;
9393 usbp
->st_mtimensec
= sbp
->st_mtimensec
;
9394 usbp
->st_ctime
= sbp
->st_ctime
;
9395 usbp
->st_ctimensec
= sbp
->st_ctimensec
;
9397 usbp
->st_size
= sbp
->st_size
;
9398 usbp
->st_blocks
= sbp
->st_blocks
;
9399 usbp
->st_blksize
= sbp
->st_blksize
;
9400 usbp
->st_flags
= sbp
->st_flags
;
9401 usbp
->st_gen
= sbp
->st_gen
;
9402 usbp
->st_lspare
= sbp
->st_lspare
;
9403 usbp
->st_qspare
[0] = sbp
->st_qspare
[0];
9404 usbp
->st_qspare
[1] = sbp
->st_qspare
[1];
9407 void munge_user32_stat(struct stat
*sbp
, struct user32_stat
*usbp
)
9409 bzero(usbp
, sizeof(*usbp
));
9411 usbp
->st_dev
= sbp
->st_dev
;
9412 usbp
->st_ino
= sbp
->st_ino
;
9413 usbp
->st_mode
= sbp
->st_mode
;
9414 usbp
->st_nlink
= sbp
->st_nlink
;
9415 usbp
->st_uid
= sbp
->st_uid
;
9416 usbp
->st_gid
= sbp
->st_gid
;
9417 usbp
->st_rdev
= sbp
->st_rdev
;
9418 #ifndef _POSIX_C_SOURCE
9419 usbp
->st_atimespec
.tv_sec
= sbp
->st_atimespec
.tv_sec
;
9420 usbp
->st_atimespec
.tv_nsec
= sbp
->st_atimespec
.tv_nsec
;
9421 usbp
->st_mtimespec
.tv_sec
= sbp
->st_mtimespec
.tv_sec
;
9422 usbp
->st_mtimespec
.tv_nsec
= sbp
->st_mtimespec
.tv_nsec
;
9423 usbp
->st_ctimespec
.tv_sec
= sbp
->st_ctimespec
.tv_sec
;
9424 usbp
->st_ctimespec
.tv_nsec
= sbp
->st_ctimespec
.tv_nsec
;
9426 usbp
->st_atime
= sbp
->st_atime
;
9427 usbp
->st_atimensec
= sbp
->st_atimensec
;
9428 usbp
->st_mtime
= sbp
->st_mtime
;
9429 usbp
->st_mtimensec
= sbp
->st_mtimensec
;
9430 usbp
->st_ctime
= sbp
->st_ctime
;
9431 usbp
->st_ctimensec
= sbp
->st_ctimensec
;
9433 usbp
->st_size
= sbp
->st_size
;
9434 usbp
->st_blocks
= sbp
->st_blocks
;
9435 usbp
->st_blksize
= sbp
->st_blksize
;
9436 usbp
->st_flags
= sbp
->st_flags
;
9437 usbp
->st_gen
= sbp
->st_gen
;
9438 usbp
->st_lspare
= sbp
->st_lspare
;
9439 usbp
->st_qspare
[0] = sbp
->st_qspare
[0];
9440 usbp
->st_qspare
[1] = sbp
->st_qspare
[1];
9444 * copy stat64 structure into user_stat64 structure.
9446 void munge_user64_stat64(struct stat64
*sbp
, struct user64_stat64
*usbp
)
9448 bzero(usbp
, sizeof(*usbp
));
9450 usbp
->st_dev
= sbp
->st_dev
;
9451 usbp
->st_ino
= sbp
->st_ino
;
9452 usbp
->st_mode
= sbp
->st_mode
;
9453 usbp
->st_nlink
= sbp
->st_nlink
;
9454 usbp
->st_uid
= sbp
->st_uid
;
9455 usbp
->st_gid
= sbp
->st_gid
;
9456 usbp
->st_rdev
= sbp
->st_rdev
;
9457 #ifndef _POSIX_C_SOURCE
9458 usbp
->st_atimespec
.tv_sec
= sbp
->st_atimespec
.tv_sec
;
9459 usbp
->st_atimespec
.tv_nsec
= sbp
->st_atimespec
.tv_nsec
;
9460 usbp
->st_mtimespec
.tv_sec
= sbp
->st_mtimespec
.tv_sec
;
9461 usbp
->st_mtimespec
.tv_nsec
= sbp
->st_mtimespec
.tv_nsec
;
9462 usbp
->st_ctimespec
.tv_sec
= sbp
->st_ctimespec
.tv_sec
;
9463 usbp
->st_ctimespec
.tv_nsec
= sbp
->st_ctimespec
.tv_nsec
;
9464 usbp
->st_birthtimespec
.tv_sec
= sbp
->st_birthtimespec
.tv_sec
;
9465 usbp
->st_birthtimespec
.tv_nsec
= sbp
->st_birthtimespec
.tv_nsec
;
9467 usbp
->st_atime
= sbp
->st_atime
;
9468 usbp
->st_atimensec
= sbp
->st_atimensec
;
9469 usbp
->st_mtime
= sbp
->st_mtime
;
9470 usbp
->st_mtimensec
= sbp
->st_mtimensec
;
9471 usbp
->st_ctime
= sbp
->st_ctime
;
9472 usbp
->st_ctimensec
= sbp
->st_ctimensec
;
9473 usbp
->st_birthtime
= sbp
->st_birthtime
;
9474 usbp
->st_birthtimensec
= sbp
->st_birthtimensec
;
9476 usbp
->st_size
= sbp
->st_size
;
9477 usbp
->st_blocks
= sbp
->st_blocks
;
9478 usbp
->st_blksize
= sbp
->st_blksize
;
9479 usbp
->st_flags
= sbp
->st_flags
;
9480 usbp
->st_gen
= sbp
->st_gen
;
9481 usbp
->st_lspare
= sbp
->st_lspare
;
9482 usbp
->st_qspare
[0] = sbp
->st_qspare
[0];
9483 usbp
->st_qspare
[1] = sbp
->st_qspare
[1];
9486 void munge_user32_stat64(struct stat64
*sbp
, struct user32_stat64
*usbp
)
9488 bzero(usbp
, sizeof(*usbp
));
9490 usbp
->st_dev
= sbp
->st_dev
;
9491 usbp
->st_ino
= sbp
->st_ino
;
9492 usbp
->st_mode
= sbp
->st_mode
;
9493 usbp
->st_nlink
= sbp
->st_nlink
;
9494 usbp
->st_uid
= sbp
->st_uid
;
9495 usbp
->st_gid
= sbp
->st_gid
;
9496 usbp
->st_rdev
= sbp
->st_rdev
;
9497 #ifndef _POSIX_C_SOURCE
9498 usbp
->st_atimespec
.tv_sec
= sbp
->st_atimespec
.tv_sec
;
9499 usbp
->st_atimespec
.tv_nsec
= sbp
->st_atimespec
.tv_nsec
;
9500 usbp
->st_mtimespec
.tv_sec
= sbp
->st_mtimespec
.tv_sec
;
9501 usbp
->st_mtimespec
.tv_nsec
= sbp
->st_mtimespec
.tv_nsec
;
9502 usbp
->st_ctimespec
.tv_sec
= sbp
->st_ctimespec
.tv_sec
;
9503 usbp
->st_ctimespec
.tv_nsec
= sbp
->st_ctimespec
.tv_nsec
;
9504 usbp
->st_birthtimespec
.tv_sec
= sbp
->st_birthtimespec
.tv_sec
;
9505 usbp
->st_birthtimespec
.tv_nsec
= sbp
->st_birthtimespec
.tv_nsec
;
9507 usbp
->st_atime
= sbp
->st_atime
;
9508 usbp
->st_atimensec
= sbp
->st_atimensec
;
9509 usbp
->st_mtime
= sbp
->st_mtime
;
9510 usbp
->st_mtimensec
= sbp
->st_mtimensec
;
9511 usbp
->st_ctime
= sbp
->st_ctime
;
9512 usbp
->st_ctimensec
= sbp
->st_ctimensec
;
9513 usbp
->st_birthtime
= sbp
->st_birthtime
;
9514 usbp
->st_birthtimensec
= sbp
->st_birthtimensec
;
9516 usbp
->st_size
= sbp
->st_size
;
9517 usbp
->st_blocks
= sbp
->st_blocks
;
9518 usbp
->st_blksize
= sbp
->st_blksize
;
9519 usbp
->st_flags
= sbp
->st_flags
;
9520 usbp
->st_gen
= sbp
->st_gen
;
9521 usbp
->st_lspare
= sbp
->st_lspare
;
9522 usbp
->st_qspare
[0] = sbp
->st_qspare
[0];
9523 usbp
->st_qspare
[1] = sbp
->st_qspare
[1];