Page MenuHomeFreeBSD

D37478.id113874.diff
No OneTemporary

D37478.id113874.diff

diff --git a/sbin/mount/getmntopts.c b/sbin/mount/getmntopts.c
--- a/sbin/mount/getmntopts.c
+++ b/sbin/mount/getmntopts.c
@@ -139,6 +139,20 @@
return (0);
}
+int
+checkpath_allow_file(const char *path, char *resolved)
+{
+ struct stat sb;
+
+ if (realpath(path, resolved) == NULL || stat(resolved, &sb) != 0)
+ return (1);
+ if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
+ errno = ENOTDIR;
+ return (1);
+ }
+ return (0);
+}
+
void
build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
size_t len)
diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h
--- a/sbin/mount/mntopts.h
+++ b/sbin/mount/mntopts.h
@@ -103,6 +103,7 @@
void getmntopts(const char *, const struct mntopt *, int *, int *);
void rmslashes(char *, char *);
int checkpath(const char *, char resolved_path[]);
+int checkpath_allow_file(const char *, char resolved_path[]);
extern int getmnt_silent;
void build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, size_t len);
void build_iovec_argf(struct iovec **iov, int *iovlen, const char *name, const char *fmt, ...);
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
--- a/sbin/mount/mount.c
+++ b/sbin/mount/mount.c
@@ -89,6 +89,7 @@
int hasopt(const char *, const char *);
int ismounted(struct fstab *, struct statfs *, int);
int isremountable(const char *);
+int allow_file_mount(const char *);
void mangle(char *, struct cpa *);
char *update_options(char *, char *, int);
int mountfs(const char *, const char *, const char *,
@@ -502,6 +503,15 @@
return (0);
}
+int
+allow_file_mount(const char *vfsname)
+{
+
+ if (strcmp(vfsname, "nullfs") == 0)
+ return (1);
+ return (0);
+}
+
int
hasopt(const char *mntopts, const char *option)
{
@@ -548,9 +558,16 @@
static struct cpa mnt_argv;
/* resolve the mountpoint with realpath(3) */
- if (checkpath(name, mntpath) != 0) {
- xo_warn("%s", mntpath);
- return (1);
+ if (allow_file_mount(vfstype)) {
+ if (checkpath_allow_file(name, mntpath) != 0) {
+ xo_warn("%s", mntpath);
+ return (1);
+ }
+ } else {
+ if (checkpath(name, mntpath) != 0) {
+ xo_warn("%s", mntpath);
+ return (1);
+ }
}
name = mntpath;
diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8
--- a/sbin/mount_nullfs/mount_nullfs.8
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -64,6 +64,17 @@
.Pp
The
.Nm
+utility supports mounting both directories and single files.
+Both
+.Ar target
+and
+.Ar mount_point
+must be the same type.
+Mounting directoriess to files or files to
+directories is not supported.
+.Pp
+The
+.Nm
file system differs from a traditional
loopback file system in two respects: it is implemented using
a stackable layers techniques, and its
diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c
--- a/sbin/mount_nullfs/mount_nullfs.c
+++ b/sbin/mount_nullfs/mount_nullfs.c
@@ -48,6 +48,7 @@
#include <sys/param.h>
#include <sys/mount.h>
+#include <sys/stat.h>
#include <sys/uio.h>
#include <err.h>
@@ -61,6 +62,14 @@
static void usage(void) __dead2;
+static int
+stat_realpath(const char *path, char *resolved, struct stat *sbp)
+{
+ if (realpath(path, resolved) == NULL || stat(resolved, sbp) != 0)
+ return (1);
+ return (0);
+}
+
int
main(int argc, char *argv[])
{
@@ -71,6 +80,8 @@
char errmsg[255];
int ch, iovlen;
char nullfs[] = "nullfs";
+ struct stat target_stat;
+ struct stat mountpoint_stat;
iov = NULL;
iovlen = 0;
@@ -98,10 +109,18 @@
usage();
/* resolve target and mountpoint with realpath(3) */
- if (checkpath(argv[0], target) != 0)
+ if (stat_realpath(argv[0], target, &target_stat) != 0)
err(EX_USAGE, "%s", target);
- if (checkpath(argv[1], mountpoint) != 0)
+ if (stat_realpath(argv[1], mountpoint, &mountpoint_stat) != 0)
err(EX_USAGE, "%s", mountpoint);
+ if (!S_ISDIR(target_stat.st_mode) && !S_ISREG(target_stat.st_mode))
+ errx(EX_USAGE, "%s: must be either a file or directory",
+ target);
+ if ((target_stat.st_mode & S_IFMT) !=
+ (mountpoint_stat.st_mode & S_IFMT))
+ errx(EX_USAGE,
+ "%s: must be same type as %s (file or directory)",
+ mountpoint, target);
build_iovec(&iov, &iovlen, "fstype", nullfs, (size_t)-1);
build_iovec(&iov, &iovlen, "fspath", mountpoint, (size_t)-1);
diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c
--- a/sys/fs/nullfs/null_vfsops.c
+++ b/sys/fs/nullfs/null_vfsops.c
@@ -156,6 +156,17 @@
}
}
+ /*
+ * Lower vnode must be the same type as the covered vnode - we
+ * don't allow mounting directories to files or vice versa.
+ */
+ if ((lowerrootvp->v_type != VDIR && lowerrootvp->v_type != VREG) ||
+ lowerrootvp->v_type != mp->mnt_vnodecovered->v_type) {
+ NULLFSDEBUG("nullfs_mount: target must be same type as fspath");
+ vput(lowerrootvp);
+ return (EINVAL);
+ }
+
xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
M_NULLFSMNT, M_WAITOK | M_ZERO);
@@ -492,4 +503,4 @@
.vfs_unlink_lowervp = nullfs_unlink_lowervp,
};
-VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);
+VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL | VFCF_FILEMOUNT);
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -3147,12 +3147,37 @@
pathseg, path, fd, &cap_fstat_rights);
if ((error = namei(&nd)) != 0)
return (error);
- error = vn_fullpath_hardlink(nd.ni_vp, nd.ni_dvp, nd.ni_cnd.cn_nameptr,
- nd.ni_cnd.cn_namelen, &retbuf, &freebuf, &size);
+ if (nd.ni_vp->v_type == VREG && nd.ni_dvp->v_type != VDIR &&
+ (nd.ni_vp->v_vflag & VV_ROOT) != 0) {
+ /*
+ * This happens if vp is a file mount. The call to
+ * vn_fullpath_hardlink can panic if path resolution can't be
+ * handled without the directory.
+ *
+ * To resolve this, we find the vnode which was mounted on -
+ * this should have a unique global path since we disallow
+ * mounting on linked files.
+ */
+ struct vnode *covered_vp;
+ KASSERT(nd.ni_vp->v_mount != NULL,
+ ("%s: namei failed to return ni_dvp for non file mount", __func__));
+ error = vfs_busy(nd.ni_vp->v_mount, 0);
+ if (error)
+ goto out;
+ covered_vp = nd.ni_vp->v_mount->mnt_vnodecovered;
+ vref(covered_vp);
+ vfs_unbusy(nd.ni_vp->v_mount);
+ error = vn_fullpath(covered_vp, &retbuf, &freebuf);
+ vrele(covered_vp);
+ } else {
+ error = vn_fullpath_hardlink(nd.ni_vp, nd.ni_dvp, nd.ni_cnd.cn_nameptr,
+ nd.ni_cnd.cn_namelen, &retbuf, &freebuf, &size);
+ }
if (error == 0) {
error = copyout(retbuf, buf, size);
free(freebuf, M_TEMP);
}
+out:
NDFREE(&nd, 0);
return (error);
}
@@ -3810,6 +3835,71 @@
return (error);
}
+/*
+ * This is similar to vn_path_to_global_path but allows for regular
+ * files which may not be present in the cache.
+ *
+ * Requires a locked, referenced vnode.
+ * Vnode is re-locked on success or ENODEV, otherwise unlocked.
+ */
+int
+vn_path_to_global_path_hardlink(struct thread *td, struct vnode *vp,
+ struct vnode *dvp, char *path, u_int pathlen, const char *leaf_name,
+ size_t leaf_length)
+{
+ struct nameidata nd;
+ struct vnode *vp1;
+ char *rpath, *fbuf;
+ size_t len;
+ int error;
+
+ ASSERT_VOP_ELOCKED(vp, __func__);
+
+ /*
+ * Construct global filesystem path from dvp, vp and leaf
+ * name.
+ */
+ VOP_UNLOCK(vp);
+ error = vn_fullpath_hardlink(vp, dvp, leaf_name, leaf_length,
+ &rpath, &fbuf, &len);
+
+ if (error != 0) {
+ vrele(vp);
+ goto out;
+ }
+
+ if (strlen(rpath) >= pathlen) {
+ vrele(vp);
+ error = ENAMETOOLONG;
+ goto out;
+ }
+
+ /*
+ * Re-lookup the vnode by path to detect a possible rename.
+ * As a side effect, the vnode is relocked.
+ * If vnode was renamed, return ENOENT.
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_SYSSPACE, path);
+ error = namei(&nd);
+ if (error != 0) {
+ vrele(vp);
+ goto out;
+ }
+ NDFREE_PNBUF(&nd);
+ vp1 = nd.ni_vp;
+ vrele(vp);
+ if (vp1 == vp)
+ strcpy(path, rpath);
+ else {
+ vput(vp1);
+ error = ENOENT;
+ }
+
+out:
+ free(fbuf, M_TEMP);
+ return (error);
+}
+
#ifdef DDB
static void
db_print_vpath(struct vnode *vp)
@@ -5350,7 +5440,7 @@
vp = fpl->tvp;
vp_seqc = fpl->tvp_seqc;
- VNPASS(vp->v_type == VDIR || vp->v_type == VBAD, vp);
+ VNPASS(vp->v_type == VDIR || vp->v_type == VREG || vp->v_type == VBAD, vp);
mp = atomic_load_ptr(&vp->v_mountedhere);
if (__predict_false(mp == NULL)) {
return (0);
@@ -5407,7 +5497,7 @@
vp = fpl->tvp;
vp_seqc = fpl->tvp_seqc;
- VNPASS(vp->v_type == VDIR || vp->v_type == VBAD, vp);
+ VNPASS(vp->v_type == VDIR || vp->v_type == VREG || vp->v_type == VBAD, vp);
mp = atomic_load_ptr(&vp->v_mountedhere);
if (__predict_false(mp == NULL)) {
return (0);
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -1103,8 +1103,19 @@
error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN);
if (error == 0)
error = vinvalbuf(vp, V_SAVE, 0, 0);
- if (error == 0 && vp->v_type != VDIR)
- error = ENOTDIR;
+ if (vfsp->vfc_flags & VFCF_FILEMOUNT) {
+ if (error == 0 && vp->v_type != VDIR && vp->v_type != VREG)
+ error = EINVAL;
+ /*
+ * For file mounts, ensure that there is only one hardlink to
+ * the file.
+ */
+ if (error == 0 && vp->v_type == VREG && va.va_nlink != 1)
+ error = EINVAL;
+ } else {
+ if (error == 0 && vp->v_type != VDIR)
+ error = ENOTDIR;
+ }
if (error == 0 && (fsflags & MNT_EMPTYDIR) != 0)
error = vfs_emptydir(vp);
if (error == 0) {
@@ -1533,22 +1544,43 @@
/*
* Get vnode to be covered or mount point's vnode in case of MNT_UPDATE.
*/
- NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, UIO_SYSSPACE,
- fspath);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1 | WANTPARENT,
+ UIO_SYSSPACE, fspath);
error = namei(&nd);
if (error != 0)
return (error);
- NDFREE_PNBUF(&nd);
vp = nd.ni_vp;
if ((fsflags & MNT_UPDATE) == 0) {
if ((vp->v_vflag & VV_ROOT) != 0 &&
(fsflags & MNT_NOCOVER) != 0) {
vput(vp);
- return (EBUSY);
+ error = EBUSY;
+ goto out;
}
pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
strcpy(pathbuf, fspath);
- error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
+ /*
+ * Note: we allow any vnode type here. If the path sanity check
+ * succeeds, the type will be validated in vfs_domount_first
+ * above.
+ */
+ if (vp->v_type == VDIR) {
+ error = vn_path_to_global_path(td, vp, pathbuf,
+ MNAMELEN);
+ } else {
+ /*
+ * We don't allow attempts to mount over non-directory
+ * mounts. This check needs to be done before
+ * vn_path_to_global_path_hardlink to avoid a panic
+ * caused by nd.ni_dvp being set to vp_crossmp.
+ */
+ if ((nd.ni_vp->v_vflag & VV_ROOT) != 0)
+ error = EINVAL;
+ else
+ error = vn_path_to_global_path_hardlink(td, vp,
+ nd.ni_dvp, pathbuf, MNAMELEN,
+ nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen);
+ }
if (error == 0) {
error = vfs_domount_first(td, vfsp, pathbuf, vp,
fsflags, optlist);
@@ -1557,6 +1589,10 @@
} else
error = vfs_domount_update(td, vp, fsflags, optlist);
+out:
+ NDFREE_PNBUF(&nd);
+ vrele(nd.ni_dvp);
+
return (error);
}
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -678,6 +678,7 @@
#define VFCF_DELEGADMIN 0x00800000 /* supports delegated administration */
#define VFCF_SBDRY 0x01000000 /* Stop at Boundary: defer stop requests
to kernel->user (AST) transition */
+#define VFCF_FILEMOUNT 0x02000000 /* allow mounting files */
typedef uint32_t fsctlop_t;
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -714,6 +714,9 @@
int vn_commname(struct vnode *vn, char *buf, u_int buflen);
int vn_path_to_global_path(struct thread *td, struct vnode *vp,
char *path, u_int pathlen);
+int vn_path_to_global_path_hardlink(struct thread *td, struct vnode *vp,
+ struct vnode *dvp, char *path, u_int pathlen, const char *leaf_name,
+ size_t leaf_length);
int vaccess(enum vtype type, mode_t file_mode, uid_t file_uid,
gid_t file_gid, accmode_t accmode, struct ucred *cred);
int vaccess_vexec_smr(mode_t file_mode, uid_t file_uid, gid_t file_gid,

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 4, 4:18 PM (14 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24762639
Default Alt Text
D37478.id113874.diff (11 KB)

Event Timeline