Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F134622472
D37478.id113874.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D37478.id113874.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D37478: Add support for mounting single files in nullfs
Attached
Detach File
Event Timeline
Log In to Comment