4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011 Lawrence Livermore National Security, LLC.
30 #include <sys/mount.h>
34 #ifdef HAVE_LIBSELINUX
35 #include <selinux/selinux.h>
36 #endif /* HAVE_LIBSELINUX */
38 libzfs_handle_t *g_zfs;
40 typedef struct option_map {
42 unsigned long mntmask;
43 unsigned long zfsmask;
46 static const option_map_t option_map[] = {
47 /* Canonicalized filesystem independent options from mount(8) */
48 { MNTOPT_NOAUTO, MS_COMMENT, ZS_COMMENT },
49 { MNTOPT_DEFAULTS, MS_COMMENT, ZS_COMMENT },
50 { MNTOPT_NODEVICES, MS_NODEV, ZS_COMMENT },
51 { MNTOPT_DIRSYNC, MS_DIRSYNC, ZS_COMMENT },
52 { MNTOPT_NOEXEC, MS_NOEXEC, ZS_COMMENT },
53 { MNTOPT_GROUP, MS_GROUP, ZS_COMMENT },
54 { MNTOPT_NETDEV, MS_COMMENT, ZS_COMMENT },
55 { MNTOPT_NOFAIL, MS_COMMENT, ZS_COMMENT },
56 { MNTOPT_NOSUID, MS_NOSUID, ZS_COMMENT },
57 { MNTOPT_OWNER, MS_OWNER, ZS_COMMENT },
58 { MNTOPT_REMOUNT, MS_REMOUNT, ZS_COMMENT },
59 { MNTOPT_RO, MS_RDONLY, ZS_COMMENT },
60 { MNTOPT_RW, MS_COMMENT, ZS_COMMENT },
61 { MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT },
62 { MNTOPT_USER, MS_USERS, ZS_COMMENT },
63 { MNTOPT_USERS, MS_USERS, ZS_COMMENT },
65 { MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT },
68 { MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT },
71 { MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT },
74 { MNTOPT_DFRATIME, MS_STRICTATIME, ZS_COMMENT },
76 { MNTOPT_CONTEXT, MS_COMMENT, ZS_NOCONTEXT },
77 { MNTOPT_NOCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
78 { MNTOPT_FSCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
79 { MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
80 { MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
82 { MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT },
85 { MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT },
87 /* Valid options not found in mount(8) */
88 { MNTOPT_BIND, MS_BIND, ZS_COMMENT },
90 { MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT },
92 { MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT },
94 { MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT },
97 { MNTOPT_QUIET, MS_SILENT, ZS_COMMENT },
99 /* Custom zfs options */
100 { MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT },
101 { MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT },
102 { MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL },
106 * Break the mount option in to a name/value pair. The name is
107 * validated against the option map and mount flags set accordingly.
110 parse_option(char *mntopt, unsigned long *mntflags,
111 unsigned long *zfsflags, int sloppy)
113 const option_map_t *opt;
114 char *ptr, *name, *value = NULL;
117 name = strdup(mntopt);
121 for (ptr = name; ptr && *ptr; ptr++) {
125 VERIFY3P(value, !=, NULL);
130 for (opt = option_map; opt->name != NULL; opt++) {
131 if (strncmp(name, opt->name, strlen(name)) == 0) {
132 *mntflags |= opt->mntmask;
133 *zfsflags |= opt->zfsmask;
142 /* If required further process on the value may be done here */
148 * Translate the mount option string in to MS_* mount flags for the
149 * kernel vfs. When sloppy is non-zero unknown options will be ignored
150 * otherwise they are considered fatal are copied in to badopt.
153 parse_options(char *mntopts, unsigned long *mntflags, unsigned long *zfsflags,
154 int sloppy, char *badopt, char *mtabopt)
156 int error = 0, quote = 0, flag = 0, count = 0;
157 char *ptr, *opt, *opts;
159 opts = strdup(mntopts);
167 * Scan through all mount options which must be comma delimited.
168 * We must be careful to notice regions which are double quoted
169 * and skip commas in these regions. Each option is then checked
170 * to determine if it is a known option.
172 for (ptr = opts; ptr && !flag; ptr++) {
185 if ((*ptr == ',') || (*ptr == '\0')) {
188 error = parse_option(opt, mntflags, zfsflags, sloppy);
195 if (!(*mntflags & MS_REMOUNT) &&
196 !(*zfsflags & ZS_ZFSUTIL)) {
198 strlcat(mtabopt, ",", MNT_LINE_MAX);
200 strlcat(mtabopt, opt, MNT_LINE_MAX);
214 * Return the pool/dataset to mount given the name passed to mount. This
215 * is expected to be of the form pool/dataset, however may also refer to
216 * a block device if that device contains a valid zfs label.
219 parse_dataset(char *dataset)
222 struct stat64 statbuf;
227 * We expect a pool/dataset to be provided, however if we're
228 * given a device which is a member of a zpool we attempt to
229 * extract the pool name stored in the label. Given the pool
230 * name we can mount the root dataset.
232 error = stat64(dataset, &statbuf);
238 fd = open(dataset, O_RDONLY);
242 error = zpool_read_label(fd, &config);
247 error = nvlist_lookup_string(config,
248 ZPOOL_CONFIG_POOL_NAME, &name);
252 dataset = strdup(name);
259 * If a file or directory in your current working directory is
260 * named 'dataset' then mount(8) will prepend your current working
261 * directory to the dataset. There is no way to prevent this
262 * behavior so we simply check for it and strip the prepended
263 * patch when it is added.
265 if (getcwd(cwd, PATH_MAX) == NULL)
270 /* Do not add one when cwd already ends in a trailing '/' */
271 if (!strncmp(cwd, dataset, len))
272 return (dataset + len + (cwd[len-1] != '/'));
278 * Update the mtab_* code to use the libmount library when it is commonly
279 * available otherwise fallback to legacy mode. The mount(8) utility will
280 * manage the lock file for us to prevent racing updates to /etc/mtab.
283 mtab_is_writeable(void)
288 error = lstat(MNTTAB, &st);
289 if (error || S_ISLNK(st.st_mode))
292 fd = open(MNTTAB, O_RDWR | O_CREAT, 0644);
301 mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
307 mnt.mnt_fsname = dataset;
308 mnt.mnt_dir = mntpoint;
310 mnt.mnt_opts = mntopts ? mntopts : "";
314 fp = setmntent(MNTTAB, "a+");
316 (void) fprintf(stderr, gettext(
317 "filesystem '%s' was mounted, but %s "
318 "could not be opened due to error %d\n"),
319 dataset, MNTTAB, errno);
320 return (MOUNT_FILEIO);
323 error = addmntent(fp, &mnt);
325 (void) fprintf(stderr, gettext(
326 "filesystem '%s' was mounted, but %s "
327 "could not be updated due to error %d\n"),
328 dataset, MNTTAB, errno);
329 return (MOUNT_FILEIO);
332 (void) endmntent(fp);
334 return (MOUNT_SUCCESS);
338 main(int argc, char **argv)
341 char legacy[ZFS_MAXPROPLEN];
342 char mntopts[MNT_LINE_MAX] = { '\0' };
343 char badopt[MNT_LINE_MAX] = { '\0' };
344 char mtabopt[MNT_LINE_MAX] = { '\0' };
345 char mntpoint[PATH_MAX];
347 unsigned long mntflags = 0, zfsflags = 0, remount = 0;
348 int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0;
351 (void) setlocale(LC_ALL, "");
352 (void) textdomain(TEXT_DOMAIN);
357 while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) {
372 (void) strlcpy(mntopts, optarg, sizeof (mntopts));
376 (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
378 (void) fprintf(stderr, gettext("Usage: mount.zfs "
379 "[-sfnv] [-o options] <dataset> <mountpoint>\n"));
380 return (MOUNT_USAGE);
387 /* check that we only have two arguments */
390 (void) fprintf(stderr, gettext("missing dataset "
393 (void) fprintf(stderr,
394 gettext("missing mountpoint argument\n"));
396 (void) fprintf(stderr, gettext("too many arguments\n"));
397 (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
398 return (MOUNT_USAGE);
401 dataset = parse_dataset(argv[0]);
403 /* canonicalize the mount point */
404 if (realpath(argv[1], mntpoint) == NULL) {
405 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
406 "mounted at '%s' due to canonicalization error %d.\n"),
407 dataset, argv[1], errno);
408 return (MOUNT_SYSERR);
411 /* validate mount options and set mntflags */
412 error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
417 (void) fprintf(stderr, gettext("filesystem '%s' "
418 "cannot be mounted due to a memory allocation "
419 "failure.\n"), dataset);
420 return (MOUNT_SYSERR);
422 (void) fprintf(stderr, gettext("filesystem '%s' "
423 "cannot be mounted of due invalid option "
424 "'%s'.\n"), dataset, badopt);
425 (void) fprintf(stderr, gettext("Use the '-s' option "
426 "to ignore the bad mount option.\n"));
427 return (MOUNT_USAGE);
429 (void) fprintf(stderr, gettext("filesystem '%s' "
430 "cannot be mounted due to internal error %d.\n"),
432 return (MOUNT_SOFTWARE);
436 #ifdef HAVE_LIBSELINUX
438 * Automatically add the default zfs context when selinux is enabled
439 * and the caller has not specified their own context. This must be
440 * done until zfs is added to the default selinux policy configuration
441 * as a known filesystem type which supports xattrs.
443 if (is_selinux_enabled() && !(zfsflags & ZS_NOCONTEXT)) {
444 (void) strlcat(mntopts, ",context=\"system_u:"
445 "object_r:file_t:s0\"", sizeof (mntopts));
446 (void) strlcat(mtabopt, ",context=\"system_u:"
447 "object_r:file_t:s0\"", sizeof (mtabopt));
449 #endif /* HAVE_LIBSELINUX */
453 (void) fprintf(stdout, gettext("mount.zfs:\n"
454 " dataset: \"%s\"\n mountpoint: \"%s\"\n"
455 " mountflags: 0x%lx\n zfsflags: 0x%lx\n"
456 " mountopts: \"%s\"\n mtabopts: \"%s\"\n"),
457 dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
459 if (mntflags & MS_REMOUNT) {
464 if (zfsflags & ZS_ZFSUTIL)
467 if ((g_zfs = libzfs_init()) == NULL)
468 return (MOUNT_SYSERR);
470 /* try to open the dataset to access the mount point */
471 if ((zhp = zfs_open(g_zfs, dataset,
472 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) {
473 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
474 "mounted, unable to open the dataset\n"), dataset);
476 return (MOUNT_USAGE);
479 /* treat all snapshots as legacy mount points */
480 if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
481 (void) strlcpy(legacy, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
483 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
484 sizeof (legacy), NULL, NULL, 0, B_FALSE);
490 * Legacy mount points may only be mounted using 'mount', never using
491 * 'zfs mount'. However, since 'zfs mount' actually invokes 'mount'
492 * we differentiate the two cases using the 'zfsutil' mount option.
493 * This mount option should only be supplied by the 'zfs mount' util.
495 * The only exception to the above rule is '-o remount' which is
496 * always allowed for non-legacy datasets. This is done because when
497 * using zfs as your root file system both rc.sysinit/umountroot and
498 * systemd depend on 'mount -o remount <mountpoint>' to work.
500 if (zfsutil && !strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
501 (void) fprintf(stderr, gettext(
502 "filesystem '%s' cannot be mounted using 'zfs mount'.\n"
503 "Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n"
504 "See zfs(8) for more information.\n"),
505 dataset, mntpoint, dataset, mntpoint);
506 return (MOUNT_USAGE);
509 if (!zfsutil && !(remount || fake) &&
510 strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
511 (void) fprintf(stderr, gettext(
512 "filesystem '%s' cannot be mounted using 'mount'.\n"
513 "Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n"
514 "See zfs(8) for more information.\n"),
515 dataset, "legacy", dataset);
516 return (MOUNT_USAGE);
520 error = mount(dataset, mntpoint, MNTTYPE_ZFS,
525 (void) fprintf(stderr, gettext("mount point "
526 "'%s' does not exist\n"), mntpoint);
527 return (MOUNT_SYSERR);
529 (void) fprintf(stderr, gettext("filesystem "
530 "'%s' is already mounted\n"), dataset);
531 return (MOUNT_SYSERR);
533 (void) fprintf(stderr, gettext("filesystem "
534 "'%s' can not be mounted due to error "
535 "%d\n"), dataset, errno);
536 return (MOUNT_USAGE);
541 if (!nomtab && mtab_is_writeable()) {
542 error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt);
547 return (MOUNT_SUCCESS);