MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE |
MNT_AUTOMOUNTED | MNT_DEFWRITE | MNT_NOATIME |
MNT_QUARANTINE | MNT_CPROTECT);
+
+#if SECURE_KERNEL
+#if !CONFIG_MNT_SUID
+ /*
+ * On release builds of iOS based platforms, always enforce NOSUID and NODEV on
+ * all mounts. We do this here because we can catch update mounts as well as
+ * non-update mounts in this case.
+ */
+ mp->mnt_flag |= (MNT_NOSUID);
+#endif
+#endif
+
mp->mnt_flag |= flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC |
MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE |
int pflags_save = 0;
#endif /* CONFIG_TRIGGERS */
+#if CONFIG_FSE
+ if (!(flags & MNT_FORCE)) {
+ fsevent_unmount(mp, ctx); /* has to come first! */
+ }
+#endif
+
mount_lock(mp);
/*
lck_rw_lock_exclusive(&mp->mnt_rwlock);
if (withref != 0)
mount_drop(mp, 0);
-#if CONFIG_FSE
- fsevent_unmount(mp); /* has to come first! */
-#endif
error = 0;
if (forcedunmount == 0) {
ubc_umount(mp); /* release cached vnodes */
nd.ni_flag |= NAMEI_COMPOUNDREMOVE;
cnp = &nd.ni_cnd;
-lookup_continue:
+continue_lookup:
error = nameiat(&nd, fd);
if (error)
return (error);
error = EISDIR;
goto out;
}
- goto lookup_continue;
+ goto continue_lookup;
} else if (error == ENOENT && batched) {
assert(retry_count < MAX_AUTHORIZE_ENOENT_RETRIES);
if (retry_count < MAX_AUTHORIZE_ENOENT_RETRIES) {
break;
case L_SET:
break;
+ case SEEK_HOLE:
+ error = VNOP_IOCTL(vp, FSCTL_FIOSEEKHOLE, (caddr_t)&offset, 0, ctx);
+ break;
+ case SEEK_DATA:
+ error = VNOP_IOCTL(vp, FSCTL_FIOSEEKDATA, (caddr_t)&offset, 0, ctx);
+ break;
default:
error = EINVAL;
}
/*
* Change file flags.
+ *
+ * NOTE: this will vnode_put() `vp'
*/
static int
chflags1(vnode_t vp, int flags, vfs_context_t ctx)
vp = nd.ni_vp;
nameidone(&nd);
+ /* we don't vnode_put() here because chflags1 does internally */
error = chflags1(vp, uap->flags, ctx);
return(error);
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
+ /* we don't vnode_put() here because chflags1 does internally */
error = chflags1(vp, uap->flags, vfs_context_current());
file_drop(uap->fd);
struct nameidata tond;
int error;
int follow;
- boolean_t free_acl;
+ boolean_t free_src_acl;
boolean_t attr_cleanup;
enum vtype v_type;
kauth_action_t action;
struct componentname *cnp;
uint32_t defaulted;
struct vnode_attr va;
+ struct vnode_attr nva;
v_type = vnode_vtype(fvp);
switch (v_type) {
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
- free_acl = FALSE;
+ free_src_acl = FALSE;
attr_cleanup = FALSE;
if (tvp != NULL) {
* those here.
*/
VATTR_INIT(&va);
- VATTR_WANTED(&va, va_type);
+ VATTR_WANTED(&va, va_uid);
+ VATTR_WANTED(&va, va_gid);
VATTR_WANTED(&va, va_mode);
VATTR_WANTED(&va, va_flags);
VATTR_WANTED(&va, va_acl);
if ((error = vnode_getattr(fvp, &va, ctx)) != 0)
goto out;
- if (!VATTR_IS_SUPPORTED(&va, va_acl))
- VATTR_CLEAR_ACTIVE(&va, va_acl);
- else if (va.va_acl != NULL)
- free_acl = TRUE;
-
- if (!VATTR_IS_SUPPORTED(&va, va_mode)) {
- VATTR_CLEAR_ACTIVE(&va, va_mode);
- } else {
- proc_t p = vfs_context_proc(ctx);
-
- VATTR_SET(&va, va_mode,
- (va.va_mode & ACCESSPERMS) & ~p->p_fd->fd_cmask);
- }
-
- if (!VATTR_IS_SUPPORTED(&va, va_flags)) {
- VATTR_CLEAR_ACTIVE(&va, va_flags);
- } else if (va.va_flags & SF_RESTRICTED) {
- /*
- * Turn off SF_RESTRICTED from source, if the destination needs
- * it, it will be handled in vnode_authattr_new.
- */
- VATTR_SET(&va, va_flags, (va.va_flags & ~SF_RESTRICTED));
+ VATTR_INIT(&nva);
+ VATTR_SET(&nva, va_type, v_type);
+ if (VATTR_IS_SUPPORTED(&va, va_acl) && va.va_acl != NULL) {
+ VATTR_SET(&nva, va_acl, va.va_acl);
+ free_src_acl = TRUE;
}
/* Handle ACL inheritance, initialize vap. */
if (v_type == VLNK) {
- error = vnode_authattr_new(tdvp, &va, 0, ctx);
+ error = vnode_authattr_new(tdvp, &nva, 0, ctx);
} else {
- error = vn_attribute_prepare(tdvp, &va, &defaulted, ctx);
+ error = vn_attribute_prepare(tdvp, &nva, &defaulted, ctx);
+ if (error)
+ goto out;
attr_cleanup = TRUE;
}
- if (error) {
- attr_cleanup = FALSE;
- goto out;
+ /*
+ * We've got initial values for all security parameters,
+ * If we are superuser, then we can change owners to be the
+ * same as the source. Both superuser and the owner have default
+ * WRITE_SECURITY privileges so all other fields can be taken
+ * from source as well.
+ */
+ if (vfs_context_issuser(ctx)) {
+ if (VATTR_IS_SUPPORTED(&va, va_uid))
+ VATTR_SET(&nva, va_uid, va.va_uid);
+ if (VATTR_IS_SUPPORTED(&va, va_gid))
+ VATTR_SET(&nva, va_gid, va.va_gid);
+ }
+ if (VATTR_IS_SUPPORTED(&va, va_mode))
+ VATTR_SET(&nva, va_mode, va.va_mode);
+ if (VATTR_IS_SUPPORTED(&va, va_flags)) {
+ VATTR_SET(&nva, va_flags,
+ ((va.va_flags & ~SF_RESTRICTED) | /* Turn off from source */
+ (nva.va_flags & SF_RESTRICTED)));
}
- error = VNOP_CLONEFILE(fvp, tdvp, &tvp, cnp, &va, flags, ctx);
+ error = VNOP_CLONEFILE(fvp, tdvp, &tvp, cnp, &nva,
+ VNODE_CLONEFILE_DEFAULT, ctx);
if (!error && tvp) {
int update_flags = 0;
* VNOP, use our fallback code.
*/
if (!VATTR_ALL_SUPPORTED(&va))
- (void)vnode_setattr_fallback(tvp, &va, ctx);
+ (void)vnode_setattr_fallback(tvp, &nva, ctx);
// Make sure the name & parent pointers are hooked up
if (tvp->v_name == NULL)
}
#endif /* CONFIG_FSE */
}
-#if CLONE_SNAPSHOT_FALLBACKS_ENABLED
- else if (error == ENOTSUP) {
- struct vfs_attr vfa;
-
- /*
- * Fallback to VNOP_COPYFILE but check first that the
- * filesystem supports cloning.
- */
- VFSATTR_INIT(&vfa);
- VFSATTR_WANTED(&vfa, f_capabilities);
- if ((vfs_getattr(vnode_mount(tdvp), &vfa, ctx) == 0) &&
- VFSATTR_IS_SUPPORTED(&vfa, f_capabilities) &&
- (vfa.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_CLONE) &&
- (vfa.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_CLONE)) {
-
- error = VNOP_COPYFILE(fvp, tdvp, tvp, cnp, 0,
- 0, ctx);
- }
- }
-#endif /* CLONE_SNAPSHOT_FALLBACKS_ENABLED */
out:
if (attr_cleanup)
- vn_attribute_cleanup(&va, defaulted);
- if (free_acl && va.va_acl)
+ vn_attribute_cleanup(&nva, defaulted);
+ if (free_src_acl && va.va_acl)
kauth_acl_free(va.va_acl);
nameidone(&tond);
if (tvp)
error = union_dircheckp(&vp, fp, &context);
if (error == -1)
goto unionread;
- if (error)
+ if (error) {
+ (void)vnode_put(vp);
goto out;
+ }
}
if ((vp->v_mount->mnt_flag & MNT_UNION)) {
error = ENOENT;
goto freeandexit;
}
- vnode_getwithref(vp);
+ error = vnode_getwithref(vp);
vnode_put(tvp);
+ if (error)
+ goto freeandexit;
}
#if CONFIG_MACF
vnode_t vp;
int length;
int bpflags;
+ /* maximum number of times to retry build_path */
+ unsigned int retries = 0x10;
if (bufsize > PAGE_SIZE) {
return (EINVAL);
return (ENOMEM);
}
+retry:
if ((mp = mount_lookupby_volfsid(volfs_id, 1)) == NULL) {
error = ENOTSUP; /* unexpected failure */
return ENOTSUP;
vnode_put(vp);
if (error) {
+ /* there was a race building the path, try a few more times */
+ if (error == EAGAIN) {
+ --retries;
+ if (retries > 0)
+ goto retry;
+
+ error = ENOENT;
+ }
goto out;
}
int
vnode_get_snapdir(vnode_t rvp, vnode_t *sdvpp, vfs_context_t ctx)
{
- int error;
-
- error = VFS_VGET_SNAPDIR(vnode_mount(rvp), sdvpp, ctx);
-
-#if CLONE_SNAPSHOT_FALLBACKS_ENABLED
- if (error == ENOTSUP) {
- struct nameidata snapnd;
-
- /*
- * Temporary fallback to <mountpoint>/.snaps lookup
- * XXX: To be removed.
- */
- NDINIT(&snapnd, LOOKUP, OP_LOOKUP, USEDVP,
- UIO_SYSSPACE, CAST_USER_ADDR_T(".snaps"), ctx);
- snapnd.ni_dvp = rvp;
-
- if ((error = namei(&snapnd))) {
- error = ENOTSUP;
- *sdvpp = NULLVP;
- } else {
- *sdvpp = snapnd.ni_vp;
- nameidone(&snapnd);
- }
- }
-#endif /* CLONE_SNAPSHOT_FALLBACKS_ENABLED */
- return (error);
+ return (VFS_VGET_SNAPDIR(vnode_mount(rvp), sdvpp, ctx));
}
/*
VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT, 0, NULL, ctx);
if (!error && vp)
vnode_put(vp);
-#if CLONE_SNAPSHOT_FALLBACKS_ENABLED
- else if (error) {
- error = VNOP_COPYFILE(rvp, rvp, NULLVP, &namend.ni_cnd,
- 0, 0, ctx);
- }
-#endif /* CLONE_SNAPSHOT_FALLBACKS_ENABLED */
}
nameidone(&namend);
}
mp = vnode_mount(rvp);
+ MALLOC(name_buf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK);
+ error = copyinstr(name, name_buf, MAXPATHLEN, &name_len);
+ if (error) {
+ FREE(name_buf, M_TEMP);
+ vnode_put(rvp);
+ return (error);
+ }
+
+#if CONFIG_MACF
+ error = mac_mount_check_snapshot_revert(ctx, mp, name_buf);
+ if (error) {
+ FREE(name_buf, M_TEMP);
+ vnode_put(rvp);
+ return (error);
+ }
+#endif
+
/*
* Grab mount_iterref so that we can release the vnode,
* since VFSIOC_REVERT_SNAPSHOT could conceivably cause a sync.
error = mount_iterref (mp, 0);
vnode_put(rvp);
if (error) {
- return (error);
- }
-
- MALLOC(name_buf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK);
- error = copyinstr(name, name_buf, MAXPATHLEN, &name_len);
- if (error) {
- mount_iterdrop(mp);
FREE(name_buf, M_TEMP);
return (error);
}
*/
static int
snapshot_mount(int dirfd, user_addr_t name, user_addr_t directory,
- user_addr_t mnt_data, __unused uint32_t flags, vfs_context_t ctx)
+ __unused user_addr_t mnt_data, __unused uint32_t flags, vfs_context_t ctx)
{
vnode_t rvp, snapdvp, snapvp, vp, pvp;
int error;
error = mount_common(mp->mnt_vfsstat.f_fstypename, pvp, vp,
&dirndp->ni_cnd, CAST_USER_ADDR_T(&smnt_data), 0,
KERNEL_MOUNT_SNAPSHOT, NULL, FALSE, ctx);
- if (error) {
- /* Retry with user passed args */
- error = mount_common(mp->mnt_vfsstat.f_fstypename, pvp,
- vp, &dirndp->ni_cnd, CAST_USER_ADDR_T(mnt_data), 0,
- 0, NULL, FALSE, ctx);
- }
}
vnode_put(vp);
return (error);
}
+/*
+ * Root from a snapshot of the filesystem
+ *
+ * Marks the filesystem to root from the given snapshot on next boot.
+ */
+static int
+snapshot_root(int dirfd, user_addr_t name, __unused uint32_t flags,
+ vfs_context_t ctx)
+{
+ int error;
+ vnode_t rvp;
+ mount_t mp;
+ struct fs_snapshot_root_args root_data;
+ struct componentname cnp;
+ caddr_t name_buf;
+ size_t name_len;
+
+ error = vnode_getfromfd(ctx, dirfd, &rvp);
+ if (error) {
+ return (error);
+ }
+ mp = vnode_mount(rvp);
+
+ MALLOC(name_buf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK);
+ error = copyinstr(name, name_buf, MAXPATHLEN, &name_len);
+ if (error) {
+ FREE(name_buf, M_TEMP);
+ vnode_put(rvp);
+ return (error);
+ }
+
+ // XXX MAC checks ?
+
+ /*
+ * Grab mount_iterref so that we can release the vnode,
+ * since VFSIOC_ROOT_SNAPSHOT could conceivably cause a sync.
+ */
+ error = mount_iterref (mp, 0);
+ vnode_put(rvp);
+ if (error) {
+ FREE(name_buf, M_TEMP);
+ return (error);
+ }
+
+ memset(&cnp, 0, sizeof(cnp));
+ cnp.cn_pnbuf = (char *)name_buf;
+ cnp.cn_nameiop = LOOKUP;
+ cnp.cn_flags = ISLASTCN | HASBUF;
+ cnp.cn_pnlen = MAXPATHLEN;
+ cnp.cn_nameptr = cnp.cn_pnbuf;
+ cnp.cn_namelen = (int)name_len;
+ root_data.sr_cnp = &cnp;
+
+ error = VFS_IOCTL(mp, VFSIOC_ROOT_SNAPSHOT, (caddr_t)&root_data, 0, ctx);
+
+ mount_iterdrop(mp);
+ FREE(name_buf, M_TEMP);
+
+ return (error);
+}
+
/*
* FS snapshot operations dispatcher
*/
int error;
vfs_context_t ctx = vfs_context_current();
+ AUDIT_ARG(fd, uap->dirfd);
+ AUDIT_ARG(value32, uap->op);
+
error = priv_check_cred(vfs_context_ucred(ctx), PRIV_VFS_SNAPSHOT, 0);
if (error)
return (error);
case SNAPSHOT_OP_REVERT:
error = snapshot_revert(uap->dirfd, uap->name1, uap->flags, ctx);
break;
+ case SNAPSHOT_OP_ROOT:
+ error = snapshot_root(uap->dirfd, uap->name1, uap->flags, ctx);
+ break;
default:
error = ENOSYS;
}