From owner-svn-src-head@freebsd.org Mon Sep 23 04:28:10 2019 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 2368CD53F7; Mon, 23 Sep 2019 04:28:10 +0000 (UTC) (envelope-from sef@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46cBBx5S0Zz4WsD; Mon, 23 Sep 2019 04:28:09 +0000 (UTC) (envelope-from sef@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 9E65A967F; Mon, 23 Sep 2019 04:28:09 +0000 (UTC) (envelope-from sef@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x8N4S9Js066148; Mon, 23 Sep 2019 04:28:09 GMT (envelope-from sef@FreeBSD.org) Received: (from sef@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x8N4S8ud066140; Mon, 23 Sep 2019 04:28:08 GMT (envelope-from sef@FreeBSD.org) Message-Id: <201909230428.x8N4S8ud066140@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: sef set sender to sef@FreeBSD.org using -f From: Sean Eric Fagan Date: Mon, 23 Sep 2019 04:28:08 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r352614 - in head: lib/libc/sys sbin/mount sys/kern sys/sys X-SVN-Group: head X-SVN-Commit-Author: sef X-SVN-Commit-Paths: in head: lib/libc/sys sbin/mount sys/kern sys/sys X-SVN-Commit-Revision: 352614 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 23 Sep 2019 04:28:10 -0000 Author: sef Date: Mon Sep 23 04:28:07 2019 New Revision: 352614 URL: https://svnweb.freebsd.org/changeset/base/352614 Log: Add two options to allow mount to avoid covering up existing mount points. The two options are * nocover/cover: Prevent/allow mounting over an existing root mountpoint. E.g., "mount -t ufs -o nocover /dev/sd1a /usr/local" will fail if /usr/local is already a mountpoint. * emptydir/noemptydir: Prevent/allow mounting on a non-empty directory. E.g., "mount -t ufs -o emptydir /dev/sd1a /usr" will fail. Neither of these options is intended to be a default, for historical and compatibility reasons. Reviewed by: allanjude, kib Differential Revision: https://reviews.freebsd.org/D21458 Modified: head/lib/libc/sys/mount.2 head/sbin/mount/mntopts.h head/sbin/mount/mount.8 head/sbin/mount/mount.c head/sys/kern/vfs_mount.c head/sys/kern/vfs_subr.c head/sys/sys/mount.h head/sys/sys/vnode.h Modified: head/lib/libc/sys/mount.2 ============================================================================== --- head/lib/libc/sys/mount.2 Sun Sep 22 20:50:24 2019 (r352613) +++ head/lib/libc/sys/mount.2 Mon Sep 23 04:28:07 2019 (r352614) @@ -28,7 +28,7 @@ .\" @(#)mount.2 8.3 (Berkeley) 5/24/95 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd August 28, 2019 .Dt MOUNT 2 .Os .Sh NAME @@ -157,6 +157,10 @@ mount even if some files are open for writing. Disable read clustering. .It Dv MNT_NOCLUSTERW Disable write clustering. +.It Dv MNT_NOCOVER +Do not mount over the root of another mount point. +.It Dv MNT_EMPTYDIR +Require an empty directory for the mount point directory. .El .Pp The flag @@ -260,6 +264,11 @@ is not a directory. .It Bq Er EBUSY Another process currently holds a reference to .Fa dir . +.It Bq Er EBUSY +The +.Dv MNT_NOCOVER +option was given, and the requested mount point +is already the root of another mount point. .It Bq Er EFAULT The .Fa dir @@ -280,6 +289,11 @@ The .Fa fspec argument is not a block device. +.It Bq Er ENOTEMPTY +The +.Dv MNT_EMPTYDIR +option was specified, and the requested mount point +is not an empty directory. .It Bq Er ENXIO The major device number of .Fa fspec Modified: head/sbin/mount/mntopts.h ============================================================================== --- head/sbin/mount/mntopts.h Sun Sep 22 20:50:24 2019 (r352613) +++ head/sbin/mount/mntopts.h Mon Sep 23 04:28:07 2019 (r352614) @@ -65,7 +65,8 @@ struct mntopt { #define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 } #define MOPT_RO { "ro", 0, MNT_RDONLY, 0 } #define MOPT_RW { "rw", 1, MNT_RDONLY, 0 } - +#define MOPT_NOCOVER { "cover", 1, MNT_NOCOVER, 0 } +#define MOPT_EMPTYDIR { "emptydir", 0, MNT_EMPTYDIR, 0 } /* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */ #define MOPT_AUTO { "auto", 0, 0, 0 } @@ -95,7 +96,9 @@ struct mntopt { MOPT_ACLS, \ MOPT_NFS4ACLS, \ MOPT_AUTOMOUNTED, \ - MOPT_UNTRUSTED + MOPT_UNTRUSTED, \ + MOPT_NOCOVER, \ + MOPT_EMPTYDIR void getmntopts(const char *, const struct mntopt *, int *, int *); void rmslashes(char *, char *); Modified: head/sbin/mount/mount.8 ============================================================================== --- head/sbin/mount/mount.8 Sun Sep 22 20:50:24 2019 (r352613) +++ head/sbin/mount/mount.8 Mon Sep 23 04:28:07 2019 (r352614) @@ -28,7 +28,7 @@ .\" @(#)mount.8 8.8 (Berkeley) 6/16/94 .\" $FreeBSD$ .\" -.Dd March 22, 2017 +.Dd August 28, 2019 .Dt MOUNT 8 .Os .Sh NAME @@ -162,6 +162,8 @@ When used with the .Fl u flag, this is the same as specifying the options currently in effect for the mounted file system. +.It Cm emptydir +Require that the mount point directory be empty. .It Cm force The same as .Fl f ; @@ -237,6 +239,9 @@ flag. Disable read clustering. .It Cm noclusterw Disable write clustering. +.It Cm nocover +Do not mount if the requested mount point is already +the root of a mount point. .It Cm noexec Do not allow execution of any binaries on the mounted file system. This option is useful for a server that has file systems containing Modified: head/sbin/mount/mount.c ============================================================================== --- head/sbin/mount/mount.c Sun Sep 22 20:50:24 2019 (r352613) +++ head/sbin/mount/mount.c Mon Sep 23 04:28:07 2019 (r352614) @@ -119,6 +119,8 @@ static struct opt { { MNT_AUTOMOUNTED, "automounted" }, { MNT_VERIFIED, "verified" }, { MNT_UNTRUSTED, "untrusted" }, + { MNT_NOCOVER, "nocover" }, + { MNT_EMPTYDIR, "emptydir" }, { 0, NULL } }; @@ -975,6 +977,8 @@ flags2opts(int flags) if (flags & MNT_ACLS) res = catopt(res, "acls"); if (flags & MNT_NFS4ACLS) res = catopt(res, "nfsv4acls"); if (flags & MNT_UNTRUSTED) res = catopt(res, "untrusted"); + if (flags & MNT_NOCOVER) res = catopt(res, "nocover"); + if (flags & MNT_EMPTYDIR) res = catopt(res, "emptydir"); return (res); } Modified: head/sys/kern/vfs_mount.c ============================================================================== --- head/sys/kern/vfs_mount.c Sun Sep 22 20:50:24 2019 (r352613) +++ head/sys/kern/vfs_mount.c Mon Sep 23 04:28:07 2019 (r352614) @@ -668,19 +668,21 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru * when we want to update the root filesystem. */ TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) { + int do_freeopt = 0; + if (strcmp(opt->name, "update") == 0) { fsflags |= MNT_UPDATE; - vfs_freeopt(optlist, opt); + do_freeopt = 1; } else if (strcmp(opt->name, "async") == 0) fsflags |= MNT_ASYNC; else if (strcmp(opt->name, "force") == 0) { fsflags |= MNT_FORCE; - vfs_freeopt(optlist, opt); + do_freeopt = 1; } else if (strcmp(opt->name, "reload") == 0) { fsflags |= MNT_RELOAD; - vfs_freeopt(optlist, opt); + do_freeopt = 1; } else if (strcmp(opt->name, "multilabel") == 0) fsflags |= MNT_MULTILABEL; @@ -741,7 +743,7 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru autoro = false; } else if (strcmp(opt->name, "autoro") == 0) { - vfs_freeopt(optlist, opt); + do_freeopt = 1; autoro = true; } else if (strcmp(opt->name, "suiddir") == 0) @@ -752,8 +754,22 @@ vfs_donmount(struct thread *td, uint64_t fsflags, stru fsflags |= MNT_UNION; else if (strcmp(opt->name, "automounted") == 0) { fsflags |= MNT_AUTOMOUNTED; - vfs_freeopt(optlist, opt); + do_freeopt = 1; + } else if (strcmp(opt->name, "nocover") == 0) { + fsflags |= MNT_NOCOVER; + do_freeopt = 1; + } else if (strcmp(opt->name, "cover") == 0) { + fsflags &= ~MNT_NOCOVER; + do_freeopt = 1; + } else if (strcmp(opt->name, "emptydir") == 0) { + fsflags |= MNT_EMPTYDIR; + do_freeopt = 1; + } else if (strcmp(opt->name, "noemptydir") == 0) { + fsflags &= ~MNT_EMPTYDIR; + do_freeopt = 1; } + if (do_freeopt) + vfs_freeopt(optlist, opt); } /* @@ -889,6 +905,14 @@ vfs_domount_first( ASSERT_VOP_ELOCKED(vp, __func__); KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here")); + if ((fsflags & MNT_EMPTYDIR) != 0) { + error = vfs_emptydir(vp); + if (error != 0) { + vput(vp); + return (error); + } + } + /* * If the jail of the calling thread lacks permission for this type of * file system, deny immediately. @@ -1229,6 +1253,11 @@ vfs_domount( NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if ((fsflags & MNT_UPDATE) == 0) { + if ((vp->v_vflag & VV_ROOT) != 0 && + (fsflags & MNT_NOCOVER) != 0) { + vput(vp); + return (EBUSY); + } pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK); strcpy(pathbuf, fspath); error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN); Modified: head/sys/kern/vfs_subr.c ============================================================================== --- head/sys/kern/vfs_subr.c Sun Sep 22 20:50:24 2019 (r352613) +++ head/sys/kern/vfs_subr.c Mon Sep 23 04:28:07 2019 (r352614) @@ -5535,6 +5535,76 @@ filt_vfsvnode(struct knote *kn, long hint) return (res); } +/* + * Returns whether the directory is empty or not. + * If it is empty, the return value is 0; otherwise + * the return value is an error value (which may + * be ENOTEMPTY). + */ +int +vfs_emptydir(struct vnode *vp) +{ + struct uio uio; + struct iovec iov; + struct dirent *dirent, *dp, *endp; + int error, eof; + + error = 0; + eof = 0; + + ASSERT_VOP_LOCKED(vp, "vfs_emptydir"); + + dirent = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK); + iov.iov_base = dirent; + iov.iov_len = sizeof(struct dirent); + + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = 0; + uio.uio_resid = sizeof(struct dirent); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_td = curthread; + + while (eof == 0 && error == 0) { + error = VOP_READDIR(vp, &uio, curthread->td_ucred, &eof, + NULL, NULL); + if (error != 0) + break; + endp = (void *)((uint8_t *)dirent + + sizeof(struct dirent) - uio.uio_resid); + for (dp = dirent; dp < endp; + dp = (void *)((uint8_t *)dp + GENERIC_DIRSIZ(dp))) { + if (dp->d_type == DT_WHT) + continue; + if (dp->d_namlen == 0) + continue; + if (dp->d_type != DT_DIR && + dp->d_type != DT_UNKNOWN) { + error = ENOTEMPTY; + break; + } + if (dp->d_namlen > 2) { + error = ENOTEMPTY; + break; + } + if (dp->d_namlen == 1 && + dp->d_name[0] != '.') { + error = ENOTEMPTY; + break; + } + if (dp->d_namlen == 2 && + dp->d_name[1] != '.') { + error = ENOTEMPTY; + break; + } + uio.uio_resid = sizeof(struct dirent); + } + } + free(dirent, M_TEMP); + return (error); +} + int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off) { Modified: head/sys/sys/mount.h ============================================================================== --- head/sys/sys/mount.h Sun Sep 22 20:50:24 2019 (r352613) +++ head/sys/sys/mount.h Mon Sep 23 04:28:07 2019 (r352614) @@ -373,9 +373,11 @@ void __mnt_vnode_markerfree_active(struct vno #define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */ #define MNT_NONBUSY 0x0000000004000000ULL /* check vnode use counts. */ #define MNT_BYFSID 0x0000000008000000ULL /* specify filesystem by ID. */ +#define MNT_NOCOVER 0x0000001000000000ULL /* Do not cover a mount point */ +#define MNT_EMPTYDIR 0x0000002000000000ULL /* Only mount on empty dir */ #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | \ - MNT_BYFSID) + MNT_BYFSID | MNT_NOCOVER | MNT_EMPTYDIR) /* * Internal filesystem control flags stored in mnt_kern_flag. * Modified: head/sys/sys/vnode.h ============================================================================== --- head/sys/sys/vnode.h Sun Sep 22 20:50:24 2019 (r352613) +++ head/sys/sys/vnode.h Mon Sep 23 04:28:07 2019 (r352614) @@ -930,6 +930,7 @@ int vfs_kqfilter(struct vop_kqfilter_args *); void vfs_mark_atime(struct vnode *vp, struct ucred *cred); struct dirent; int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off); +int vfs_emptydir(struct vnode *vp); int vfs_unixify_accmode(accmode_t *accmode);