Add 'zfs mount' support

By design the zfs utility is supposed to handle mounting and unmounting
a zfs filesystem.  We could allow zfs to do this directly.  There are
system calls available to mount/umount a filesystem.  And there are
library calls available to manipulate /etc/mtab.  But there are a
couple very good reasons not to take this appraoch... for now.

Instead of directly calling the system and library calls to (u)mount
the filesystem we fork and exec a (u)mount process.  The principle
reason for this is to delegate the responsibility for locking and
updating /etc/mtab to (u)mount(8).  This ensures maximum portability
and ensures the right locking scheme for your version of (u)mount
will be used.  If we didn't do this we would have to resort to an
autoconf test to determine what locking mechanism is used.

The downside to using mount(8) instead of mount(2) is that we lose
the exact errno which was returned by the kernel.  The return code
from mount(8) provides some insight in to what went wrong but it
not quite as good.  For the moment this is translated as a best
guess in to a errno for the higher layers of zfs.

In the long term a shared library called libmount is under development
which provides a common API to address the locking and errno issues.
Once the standard mount utility has been updated to use this library
we can then leverage it.  Until then this is the only safe solution.

  http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
This commit is contained in:
Brian Behlendorf 2010-12-16 16:16:25 -08:00
parent feb46b92a7
commit 3fb1fcdea1
5 changed files with 169 additions and 132 deletions

View File

@ -3275,7 +3275,7 @@ share_mount(int op, int argc, char **argv)
int flags = 0; int flags = 0;
/* check options */ /* check options */
while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:" : "a"))
!= -1) { != -1) {
switch (c) { switch (c) {
case 'a': case 'a':
@ -3298,9 +3298,6 @@ share_mount(int op, int argc, char **argv)
append_options(options, optarg); append_options(options, optarg);
break; break;
case 'O':
flags |= MS_OVERLAY;
break;
case ':': case ':':
(void) fprintf(stderr, gettext("missing argument for " (void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt); "'%c' option\n"), optopt);

View File

@ -29,114 +29,74 @@
#ifndef _SYS_MNTENT_H #ifndef _SYS_MNTENT_H
#define _SYS_MNTENT_H #define _SYS_MNTENT_H
#define MNTTYPE_ZFS "zfs" /* ZFS file system */
#define FSTAB "/etc/fstab"
#ifdef __cplusplus
extern "C" {
#endif
#define MNTTAB "/proc/mounts"
#define VFSTAB "/etc/vfstab"
#define MNTMAXSTR 128 #define MNTMAXSTR 128
#define MNTTYPE_ZFS "zfs" /* ZFS file system */ #define MOUNT_SUCCESS 0x00 /* Success */
#define MNTTYPE_UFS "ufs" /* Unix file system */ #define MOUNT_USAGE 0x01 /* Invalid invocation or permissions */
#define MNTTYPE_SMBFS "smbfs" /* SMBFS file system */ #define MOUNT_SYSERR 0x02 /* System error (ENOMEM, etc) */
#define MNTTYPE_NFS "nfs" /* NFS file system */ #define MOUNT_SOFTWARE 0x04 /* Internal mount bug */
#define MNTTYPE_NFS3 "nfs3" /* NFS Version 3 file system */ #define MOUNT_USER 0x08 /* Interrupted by user (EINTR) */
#define MNTTYPE_NFS4 "nfs4" /* NFS Version 4 file system */ #define MOUNT_FILEIO 0x10 /* Error updating/locking /etc/mtab */
#define MNTTYPE_CACHEFS "cachefs" /* Cache File System */ #define MOUNT_FAIL 0x20 /* Mount failed */
#define MNTTYPE_PCFS "pcfs" /* PC (MSDOS) file system */ #define MOUNT_SOMEOK 0x40 /* At least on mount succeeded */
#define MNTTYPE_PC MNTTYPE_PCFS /* Deprecated name; use MNTTYPE_PCFS */
#define MNTTYPE_LOFS "lofs" /* Loop back file system */
#define MNTTYPE_LO MNTTYPE_LOFS /* Deprecated name; use MNTTYPE_LOFS */
#define MNTTYPE_HSFS "hsfs" /* High Sierra (9660) file system */
#define MNTTYPE_SWAP "swap" /* Swap file system */
#define MNTTYPE_TMPFS "tmpfs" /* Tmp volatile file system */
#define MNTTYPE_AUTOFS "autofs" /* Automounter ``file'' system */
#define MNTTYPE_MNTFS "mntfs" /* In-kernel mnttab */
#define MNTTYPE_DEV "dev" /* /dev file system */
#define MNTTYPE_CTFS "ctfs" /* Contract file system */
#define MNTTYPE_OBJFS "objfs" /* Kernel object file system */
#define MNTTYPE_SHAREFS "sharefs" /* Kernel sharetab file system */
#define MNTOPT_ASYNC "async" /* all I/O is asynchronous */
#define MNTOPT_RO "ro" /* Read only */
#define MNTOPT_RW "rw" /* Read/write */
#define MNTOPT_RQ "rq" /* Read/write with quotas */
#define MNTOPT_QUOTA "quota" /* Check quotas */
#define MNTOPT_NOQUOTA "noquota" /* Don't check quotas */
#define MNTOPT_ONERROR "onerror" /* action to taken on error */
#define MNTOPT_SOFT "soft" /* Soft mount */
#define MNTOPT_SEMISOFT "semisoft" /* partial soft, uncommited interface */
#define MNTOPT_HARD "hard" /* Hard mount */
#define MNTOPT_SUID "suid" /* Both setuid and devices allowed */
#define MNTOPT_NOSUID "nosuid" /* Neither setuid nor devices allowed */
#define MNTOPT_DEVICES "devices" /* Device-special allowed */
#define MNTOPT_NODEVICES "nodevices" /* Device-special disallowed */
#define MNTOPT_SETUID "setuid" /* Set uid allowed */
#define MNTOPT_NOSETUID "nosetuid" /* Set uid not allowed */
#define MNTOPT_GRPID "grpid" /* SysV-compatible gid on create */
#define MNTOPT_REMOUNT "remount" /* Change mount options */
#define MNTOPT_NOSUB "nosub" /* Disallow mounts on subdirs */
#define MNTOPT_MULTI "multi" /* Do multi-component lookup */
#define MNTOPT_INTR "intr" /* Allow NFS ops to be interrupted */
#define MNTOPT_NOINTR "nointr" /* Don't allow interrupted ops */
#define MNTOPT_PORT "port" /* NFS server IP port number */
#define MNTOPT_SECURE "secure" /* Secure (AUTH_DES) mounting */
#define MNTOPT_RSIZE "rsize" /* Max NFS read size (bytes) */
#define MNTOPT_WSIZE "wsize" /* Max NFS write size (bytes) */
#define MNTOPT_TIMEO "timeo" /* NFS timeout (1/10 sec) */
#define MNTOPT_RETRANS "retrans" /* Max retransmissions (soft mnts) */
#define MNTOPT_ACTIMEO "actimeo" /* Attr cache timeout (sec) */
#define MNTOPT_ACREGMIN "acregmin" /* Min attr cache timeout (files) */
#define MNTOPT_ACREGMAX "acregmax" /* Max attr cache timeout (files) */
#define MNTOPT_ACDIRMIN "acdirmin" /* Min attr cache timeout (dirs) */
#define MNTOPT_ACDIRMAX "acdirmax" /* Max attr cache timeout (dirs) */
#define MNTOPT_NOAC "noac" /* Don't cache attributes at all */
#define MNTOPT_NOCTO "nocto" /* No close-to-open consistency */
#define MNTOPT_BG "bg" /* Do mount retries in background */
#define MNTOPT_FG "fg" /* Do mount retries in foreground */
#define MNTOPT_RETRY "retry" /* Number of mount retries */
#define MNTOPT_DEV "dev" /* Device id of mounted fs */
#define MNTOPT_POSIX "posix" /* Get static pathconf for mount */
#define MNTOPT_MAP "map" /* Automount map */
#define MNTOPT_DIRECT "direct" /* Automount direct map mount */
#define MNTOPT_INDIRECT "indirect" /* Automount indirect map mount */
#define MNTOPT_LLOCK "llock" /* Local locking (no lock manager) */
#define MNTOPT_IGNORE "ignore" /* Ignore this entry */
#define MNTOPT_VERS "vers" /* protocol version number indicator */
#define MNTOPT_PROTO "proto" /* protocol network_id indicator */
#define MNTOPT_SEC "sec" /* Security flavor indicator */
#define MNTOPT_SYNCDIR "syncdir" /* Synchronous local directory ops */
#define MNTOPT_NOSETSEC "nosec" /* Do no allow setting sec attrs */
#define MNTOPT_NOPRINT "noprint" /* Do not print messages */
#define MNTOPT_LARGEFILES "largefiles" /* allow large files */
#define MNTOPT_NOLARGEFILES "nolargefiles" /* don't allow large files */
#define MNTOPT_FORCEDIRECTIO "forcedirectio" /* Force DirectIO on all files */
#define MNTOPT_NOFORCEDIRECTIO "noforcedirectio" /* No Force DirectIO */
#define MNTOPT_DISABLEDIRECTIO "disabledirectio" /* Disable DirectIO ioctls */
#define MNTOPT_PUBLIC "public" /* Use NFS public file handlee */
#define MNTOPT_LOGGING "logging" /* enable logging */
#define MNTOPT_NOLOGGING "nologging" /* disable logging */
#define MNTOPT_ATIME "atime" /* update atime for files */ #define MNTOPT_ATIME "atime" /* update atime for files */
#define MNTOPT_NOATIME "noatime" /* do not update atime for files */ #define MNTOPT_NOATIME "noatime" /* do not update atime for files */
#define MNTOPT_GLOBAL "global" /* Cluster-wide global mount */ #define MNTOPT_AUTO "auto" /* automount */
#define MNTOPT_NOGLOBAL "noglobal" /* Mount local to single node */ #define MNTOPT_NOAUTO "noauto" /* do not automount */
#define MNTOPT_DFRATIME "dfratime" /* Deferred access time updates */ #define MNTOPT_CONTEXT "context" /* selinux context */
#define MNTOPT_NODFRATIME "nodfratime" /* No Deferred access time updates */ #define MNTOPT_FSCONTEXT "fscontext" /* selinux fscontext */
#define MNTOPT_NBMAND "nbmand" /* allow non-blocking mandatory locks */ #define MNTOPT_DEFCONTEXT "defcontext" /* selinux defcontext */
#define MNTOPT_NONBMAND "nonbmand" /* deny non-blocking mandatory locks */ #define MNTOPT_ROOTCONTEXT "rootcontext" /* selinux rootcontext */
#define MNTOPT_XATTR "xattr" /* enable extended attributes */ #define MNTOPT_DEFAULTS "defaults" /* defaults */
#define MNTOPT_NOXATTR "noxattr" /* disable extended attributes */ #define MNTOPT_DEVICES "dev" /* device-special allowed */
#define MNTOPT_NODEVICES "nodev" /* device-special disallowed */
#define MNTOPT_DIRATIME "diratime" /* update atime for dirs */
#define MNTOPT_NODIRATIME "nodiratime" /* do not update atime for dirs */
#define MNTOPT_DIRSYNC "dirsync" /* do dir updates synchronously */
#define MNTOPT_EXEC "exec" /* enable executables */ #define MNTOPT_EXEC "exec" /* enable executables */
#define MNTOPT_NOEXEC "noexec" /* disable executables */ #define MNTOPT_NOEXEC "noexec" /* disable executables */
#define MNTOPT_RESTRICT "restrict" /* restricted autofs mount */ #define MNTOPT_GROUP "group" /* allow group mount */
#define MNTOPT_BROWSE "browse" /* browsable autofs mount */ #define MNTOPT_NOGROUP "nogroup" /* do not allow group mount */
#define MNTOPT_NOBROWSE "nobrowse" /* non-browsable autofs mount */ #define MNTOPT_IVERSION "iversion" /* update inode version */
#define MNTOPT_NOIVERSION "noiversion" /* do not update inode version */
#ifdef __cplusplus #define MNTOPT_NBMAND "mand" /* allow non-blocking mandatory locks */
} #define MNTOPT_NONBMAND "nomand" /* deny non-blocking mandatory locks */
#endif #define MNTOPT_NETDEV "_netdev" /* network device */
#define MNTOPT_NOFAIL "nofail" /* no failure */
#define MNTOPT_RELATIME "relatime" /* allow relative time updates */
#define MNTOPT_NORELATIME "norelatime" /* do not allow relative time updates */
#define MNTOPT_DFRATIME "strictatime" /* Deferred access time updates */
#define MNTOPT_NODFRATIME "nostrictatime" /* No Deferred access time updates */
#define MNTOPT_SETUID "suid" /* Both setuid and devices allowed */
#define MNTOPT_NOSETUID "nosuid" /* Neither setuid nor devices allowed */
#define MNTOPT_OWNER "owner" /* allow owner mount */
#define MNTOPT_NOOWNER "noowner" /* do not allow owner mount */
#define MNTOPT_REMOUNT "remount" /* change mount options */
#define MNTOPT_RO "ro" /* read only */
#define MNTOPT_RW "rw" /* read/write */
#define MNTOPT_SYNC "sync" /* all I/O is synchronous */
#define MNTOPT_USER "user" /* allow user mount */
#define MNTOPT_NOUSER "nouser" /* do not allow user mount */
#define MNTOPT_USERS "users" /* allow user mount */
#define MNTOPT_NOUSERS "nousers" /* do not allow user mount */
#define MNTOPT_SUB "sub" /* allow mounts on subdirs */
#define MNTOPT_NOSUB "nosub" /* do not allow mounts on subdirs */
#define MNTOPT_QUIET "quiet" /* quiet mount */
#define MNTOPT_LOUD "loud" /* verbose mount */
#define MNTOPT_BIND "bind" /* remount part of a tree */
#define MNTOPT_RBIND "rbind" /* include subtrees */
#define MNTOPT_XATTR "user_xattr" /* enable extended attributes */
#define MNTOPT_NOXATTR "nouser_xattr" /* disable extended attributes */
#define MNTOPT_COMMENT "comment" /* comment */
#define MNTOPT_BOOTWAIT "bootwait"
#define MNTOPT_NOBOOTWAIT "nobootwait"
#define MNTOPT_OPTIONAL "optional"
#define MNTOPT_SHOWTHROUGH "showthrough"
#define MNTOPT_ZFSUTIL "zfsutil" /* called by zfs utility */
#endif /* _SYS_MNTENT_H */ #endif /* _SYS_MNTENT_H */

View File

@ -36,14 +36,14 @@
#ifdef MNTTAB #ifdef MNTTAB
#undef MNTTAB #undef MNTTAB
#endif #endif /* MNTTAB */
#define MNTTAB "/proc/mounts" #define MNTTAB "/etc/mtab"
#define MNT_LINE_MAX 1024 #define MNT_LINE_MAX 1024
#define MNT_TOOLONG 1 /* entry exceeds MNT_LINE_MAX */ #define MNT_TOOLONG 1 /* entry exceeds MNT_LINE_MAX */
#define MNT_TOOMANY 2 /* too many fields in line */ #define MNT_TOOMANY 2 /* too many fields in line */
#define MNT_TOOFEW 3 /* too few fields in line */ #define MNT_TOOFEW 3 /* too few fields in line */
struct mnttab { struct mnttab {
char *mnt_special; char *mnt_special;

View File

@ -42,9 +42,11 @@
#define BLKGETSIZE64 _IOR(0x12, 114, size_t) #define BLKGETSIZE64 _IOR(0x12, 114, size_t)
#endif #endif
#define MS_FORCE MNT_FORCE #define MS_USERS 0x40000000
#define MS_OVERLAY 32768 #define MS_OWNER 0x10000000
#define MS_NOMNTTAB 0 /* Not supported in Linux */ #define MS_GROUP 0x08000000
#define MS_OPTIONSTR 0 /* Not necessary in Linux */ #define MS_COMMENT 0x02000000
#define MS_FORCE MNT_FORCE
#define MS_DETACH MNT_DETACH
#endif /* _LIBSPL_SYS_MOUNT_H */ #endif /* _LIBSPL_SYS_MOUNT_H */

View File

@ -258,6 +258,82 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
return (B_TRUE); return (B_TRUE);
} }
/*
* The filesystem is mounted by invoking the system mount utility rather
* than by the system call mount(2). This ensures that the /etc/mtab
* file is correctly locked for the update. Performing our own locking
* and /etc/mtab update requires making an unsafe assumption about how
* the mount utility performs its locking. Unfortunately, this also means
* in the case of a mount failure we do not have the exact errno. We must
* make due with return value from the mount process.
*
* In the long term a shared library called libmount is under development
* which provides a common API to address the locking and errno issues.
* Once the standard mount utility has been updated to use this library
* we can add an autoconf check to conditionally use it.
*
* http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
*/
static int
do_mount(const char *src, const char *mntpt, char *opts)
{
char *argv[8] = {
"/bin/mount",
"-t", MNTTYPE_ZFS,
"-o", opts,
(char *)src,
(char *)mntpt,
(char *)NULL };
int rc;
/* Return only the most critical mount error */
rc = libzfs_run_process(argv[0], argv);
if (rc) {
if (rc & MOUNT_FILEIO)
return EIO;
if (rc & MOUNT_USER)
return EINTR;
if (rc & MOUNT_SOFTWARE)
return EPIPE;
if (rc & MOUNT_SYSERR)
return EAGAIN;
if (rc & MOUNT_USAGE)
return EINVAL;
return ENXIO; /* Generic error */
}
return 0;
}
static int
do_unmount(const char *mntpt, int flags)
{
char force_opt[] = "-f";
char lazy_opt[] = "-l";
char *argv[7] = {
"/bin/umount",
"-t", MNTTYPE_ZFS,
NULL, NULL, NULL, NULL };
int rc, count = 3;
if (flags & MS_FORCE) {
argv[count] = force_opt;
count++;
}
if (flags & MS_DETACH) {
argv[count] = lazy_opt;
count++;
}
argv[count] = (char *)mntpt;
rc = libzfs_run_process(argv[0], argv);
return (rc ? EINVAL : 0);
}
/* /*
* Mount the given filesystem. * Mount the given filesystem.
*/ */
@ -268,9 +344,10 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
char mountpoint[ZFS_MAXPROPLEN]; char mountpoint[ZFS_MAXPROPLEN];
char mntopts[MNT_LINE_MAX]; char mntopts[MNT_LINE_MAX];
libzfs_handle_t *hdl = zhp->zfs_hdl; libzfs_handle_t *hdl = zhp->zfs_hdl;
int rc;
if (options == NULL) if (options == NULL)
mntopts[0] = '\0'; (void) strlcpy(mntopts, MNTOPT_DEFAULTS, sizeof (mntopts));
else else
(void) strlcpy(mntopts, options, sizeof (mntopts)); (void) strlcpy(mntopts, options, sizeof (mntopts));
@ -278,7 +355,12 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
* If the pool is imported read-only then all mounts must be read-only * If the pool is imported read-only then all mounts must be read-only
*/ */
if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL)) if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL))
flags |= MS_RDONLY; (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts));
/*
* Append zfsutil option so the mount helper allow the mount
*/
strlcat(mntopts, "," MNTOPT_ZFSUTIL, sizeof (mntopts));
#ifdef HAVE_LIBSELINUX #ifdef HAVE_LIBSELINUX
if (is_selinux_enabled()) if (is_selinux_enabled())
@ -302,12 +384,9 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
/* /*
* Determine if the mountpoint is empty. If so, refuse to perform the * Determine if the mountpoint is empty. If so, refuse to perform the
* mount. We don't perform this check if MS_OVERLAY is specified, which * mount. We don't perform this check if 'remount' is specified.
* would defeat the point. We also avoid this check if 'remount' is
* specified.
*/ */
if ((flags & MS_OVERLAY) == 0 && if (strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
!dir_is_empty(mountpoint)) { !dir_is_empty(mountpoint)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"directory is not empty")); "directory is not empty"));
@ -316,20 +395,20 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
} }
/* perform the mount */ /* perform the mount */
if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags, rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts);
MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) { if (rc) {
/* /*
* Generic errors are nasty, but there are just way too many * Generic errors are nasty, but there are just way too many
* from mount(), and they're well-understood. We pick a few * from mount(), and they're well-understood. We pick a few
* common ones to improve upon. * common ones to improve upon.
*/ */
if (errno == EBUSY) { if (rc == EBUSY) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"mountpoint or dataset is busy")); "mountpoint or dataset is busy"));
} else if (errno == EPERM) { } else if (rc == EPERM) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"Insufficient privileges")); "Insufficient privileges"));
} else if (errno == ENOTSUP) { } else if (rc == ENOTSUP) {
char buf[256]; char buf[256];
int spa_version; int spa_version;
@ -342,7 +421,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
ZFS_PROP_VERSION), spa_version); ZFS_PROP_VERSION), spa_version);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf)); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf));
} else { } else {
zfs_error_aux(hdl, strerror(errno)); zfs_error_aux(hdl, strerror(rc));
} }
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED, return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot mount '%s'"), dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
@ -350,8 +429,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
} }
/* add the mounted entry into our cache */ /* add the mounted entry into our cache */
libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint, mntopts);
mntopts);
return (0); return (0);
} }
@ -361,7 +439,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
static int static int
unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags) unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
{ {
if (umount2(mountpoint, flags) != 0) { if (do_unmount(mountpoint, flags) != 0) {
zfs_error_aux(hdl, strerror(errno)); zfs_error_aux(hdl, strerror(errno));
return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED, return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot unmount '%s'"), dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),