]> git.proxmox.com Git - mirror_zfs.git/blame - cmd/mount_zfs/mount_zfs.c
Linux compat 2.6.39: mount_nodev()
[mirror_zfs.git] / cmd / mount_zfs / mount_zfs.c
CommitLineData
d53368f6
BB
1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011 Lawrence Livermore National Security, LLC.
25 */
26
27#include <libintl.h>
28#include <unistd.h>
29#include <sys/file.h>
30#include <sys/mount.h>
31#include <sys/stat.h>
32#include <libzfs.h>
33#ifdef HAVE_LIBSELINUX
34#include <selinux/selinux.h>
35#endif /* HAVE_LIBSELINUX */
36
37libzfs_handle_t *g_zfs;
38
39typedef struct option_map {
40 const char *name;
41 unsigned long mntmask;
42 unsigned long zfsmask;
43} option_map_t;
44
45static const option_map_t option_map[] = {
46 /* Canonicalized filesystem independent options from mount(8) */
47 { MNTOPT_NOAUTO, MS_COMMENT, ZS_COMMENT },
48 { MNTOPT_DEFAULTS, MS_COMMENT, ZS_COMMENT },
49 { MNTOPT_NODEVICES, MS_NODEV, ZS_COMMENT },
50 { MNTOPT_DIRSYNC, MS_DIRSYNC, ZS_COMMENT },
51 { MNTOPT_NOEXEC, MS_NOEXEC, ZS_COMMENT },
52 { MNTOPT_GROUP, MS_GROUP, ZS_COMMENT },
53 { MNTOPT_NETDEV, MS_COMMENT, ZS_COMMENT },
54 { MNTOPT_NOFAIL, MS_COMMENT, ZS_COMMENT },
55 { MNTOPT_NOSUID, MS_NOSUID, ZS_COMMENT },
56 { MNTOPT_OWNER, MS_OWNER, ZS_COMMENT },
57 { MNTOPT_REMOUNT, MS_REMOUNT, ZS_COMMENT },
58 { MNTOPT_RO, MS_RDONLY, ZS_COMMENT },
59 { MNTOPT_RW, MS_COMMENT, ZS_COMMENT },
60 { MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT },
61 { MNTOPT_USER, MS_USERS, ZS_COMMENT },
62 { MNTOPT_USERS, MS_USERS, ZS_COMMENT },
63#ifdef MS_NOATIME
64 { MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT },
65#endif
66#ifdef MS_NODIRATIME
67 { MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT },
68#endif
69#ifdef MS_RELATIME
70 { MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT },
71#endif
72#ifdef MS_STRICTATIME
73 { MNTOPT_DFRATIME, MS_STRICTATIME, ZS_COMMENT },
74#endif
d53368f6
BB
75 { MNTOPT_CONTEXT, MS_COMMENT, ZS_NOCONTEXT },
76 { MNTOPT_NOCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
77 { MNTOPT_FSCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
78 { MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
79 { MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_NOCONTEXT },
d53368f6
BB
80#ifdef MS_I_VERSION
81 { MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT },
82#endif
83#ifdef MS_MANDLOCK
84 { MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT },
85#endif
86 /* Valid options not found in mount(8) */
87 { MNTOPT_BIND, MS_BIND, ZS_COMMENT },
88#ifdef MS_REC
89 { MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT },
90#endif
91 { MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT },
92#ifdef MS_NOSUB
93 { MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT },
94#endif
95#ifdef MS_SILENT
96 { MNTOPT_QUIET, MS_SILENT, ZS_COMMENT },
97#endif
98 /* Custom zfs options */
2cf7f52b 99 { MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT },
d53368f6
BB
100 { MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT },
101 { MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL },
102 { NULL, 0, 0 } };
103
104/*
105 * Break the mount option in to a name/value pair. The name is
106 * validated against the option map and mount flags set accordingly.
107 */
108static int
109parse_option(char *mntopt, unsigned long *mntflags,
110 unsigned long *zfsflags, int sloppy)
111{
112 const option_map_t *opt;
113 char *ptr, *name, *value = NULL;
114 int error = 0;
115
116 name = strdup(mntopt);
117 if (name == NULL)
118 return (ENOMEM);
119
120 for (ptr = name; ptr && *ptr; ptr++) {
121 if (*ptr == '=') {
122 *ptr = '\0';
123 value = ptr+1;
03514b01 124 VERIFY3P(value, !=, NULL);
d53368f6
BB
125 break;
126 }
127 }
128
129 for (opt = option_map; opt->name != NULL; opt++) {
130 if (strncmp(name, opt->name, strlen(name)) == 0) {
131 *mntflags |= opt->mntmask;
132 *zfsflags |= opt->zfsmask;
133
134 /* MS_USERS implies default user options */
135 if (opt->mntmask & (MS_USERS))
136 *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV);
137
138 /* MS_OWNER|MS_GROUP imply default owner options */
139 if (opt->mntmask & (MS_OWNER | MS_GROUP))
140 *mntflags |= (MS_NOSUID|MS_NODEV);
141
142 error = 0;
143 goto out;
144 }
145 }
146
147 if (!sloppy)
148 error = ENOENT;
149out:
150 /* If required further process on the value may be done here */
151 free(name);
152 return (error);
153}
154
155/*
156 * Translate the mount option string in to MS_* mount flags for the
157 * kernel vfs. When sloppy is non-zero unknown options will be ignored
158 * otherwise they are considered fatal are copied in to badopt.
159 */
160static int
3aff7755
BB
161parse_options(char *mntopts, unsigned long *mntflags, unsigned long *zfsflags,
162 int sloppy, char *badopt, char *mtabopt)
d53368f6 163{
3aff7755 164 int error = 0, quote = 0, flag = 0, count = 0;
d53368f6
BB
165 char *ptr, *opt, *opts;
166
167 opts = strdup(mntopts);
168 if (opts == NULL)
169 return (ENOMEM);
170
171 *mntflags = 0;
172 opt = NULL;
173
174 /*
175 * Scan through all mount options which must be comma delimited.
176 * We must be careful to notice regions which are double quoted
177 * and skip commas in these regions. Each option is then checked
178 * to determine if it is a known option.
179 */
180 for (ptr = opts; ptr && !flag; ptr++) {
181 if (opt == NULL)
182 opt = ptr;
183
184 if (*ptr == '"')
185 quote = !quote;
186
187 if (quote)
188 continue;
189
190 if (*ptr == '\0')
191 flag = 1;
192
193 if ((*ptr == ',') || (*ptr == '\0')) {
194 *ptr = '\0';
195
196 error = parse_option(opt, mntflags, zfsflags, sloppy);
197 if (error) {
198 strcpy(badopt, opt);
199 goto out;
3aff7755
BB
200
201 }
202
203 if (!(*mntflags & MS_REMOUNT) &&
204 !(*zfsflags & ZS_ZFSUTIL)) {
205 if (count > 0)
206 strlcat(mtabopt, ",", MNT_LINE_MAX);
207
208 strlcat(mtabopt, opt, MNT_LINE_MAX);
209 count++;
d53368f6
BB
210 }
211
212 opt = NULL;
213 }
214 }
215
216out:
217 free(opts);
218 return (error);
219}
220
221/*
222 * If a file or directory in your current working directory is named
223 * 'dataset' then mount(8) will prepend your current working directory
224 * to dataset. The is no way to prevent this behavior so we simply
225 * check for it and strip the prepended patch when it is added.
226 */
227static char *
228parse_dataset(char *dataset)
229{
230 char cwd[PATH_MAX];
a6cba65c 231 int len;
d53368f6 232
ec49a5f0
BB
233 if (getcwd(cwd, PATH_MAX) == NULL)
234 return (dataset);
235
a6cba65c
BB
236 len = strlen(cwd);
237
238 /* Do not add one when cwd already ends in a trailing '/' */
239 if (!strncmp(cwd, dataset, len))
240 return (dataset + len + (cwd[len-1] != '/'));
d53368f6
BB
241
242 return (dataset);
243}
244
245/*
246 * Update the mtab_* code to use the libmount library when it is commonly
247 * available otherwise fallback to legacy mode. The mount(8) utility will
248 * manage the lock file for us to prevent racing updates to /etc/mtab.
249 */
250static int
251mtab_is_writeable(void)
252{
253 struct stat st;
254 int error, fd;
255
e130330a 256 error = lstat(MNTTAB, &st);
d53368f6
BB
257 if (error || S_ISLNK(st.st_mode))
258 return (0);
259
260 fd = open(MNTTAB, O_RDWR | O_CREAT, 0644);
261 if (fd < 0)
262 return (0);
263
264 close(fd);
265 return (1);
266}
267
268static int
269mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
270{
271 struct mntent mnt;
272 FILE *fp;
273 int error;
274
275 mnt.mnt_fsname = dataset;
276 mnt.mnt_dir = mntpoint;
277 mnt.mnt_type = type;
278 mnt.mnt_opts = mntopts ? mntopts : "";
279 mnt.mnt_freq = 0;
280 mnt.mnt_passno = 0;
281
282 fp = setmntent(MNTTAB, "a+");
283 if (!fp) {
284 (void) fprintf(stderr, gettext(
285 "filesystem '%s' was mounted, but %s "
286 "could not be opened due to error %d\n"),
287 dataset, MNTTAB, errno);
288 return (MOUNT_FILEIO);
289 }
290
291 error = addmntent(fp, &mnt);
292 if (error) {
293 (void) fprintf(stderr, gettext(
294 "filesystem '%s' was mounted, but %s "
295 "could not be updated due to error %d\n"),
296 dataset, MNTTAB, errno);
297 return (MOUNT_FILEIO);
298 }
299
300 (void) endmntent(fp);
301
302 return (MOUNT_SUCCESS);
303}
304
305int
306main(int argc, char **argv)
307{
308 zfs_handle_t *zhp;
309 char legacy[ZFS_MAXPROPLEN];
310 char mntopts[MNT_LINE_MAX] = { '\0' };
311 char badopt[MNT_LINE_MAX] = { '\0' };
3aff7755 312 char mtabopt[MNT_LINE_MAX] = { '\0' };
d53368f6 313 char *dataset, *mntpoint;
093aa692 314 unsigned long mntflags = 0, zfsflags = 0, remount_ro = 0;
d53368f6
BB
315 int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0;
316 int error, c;
317
318 (void) setlocale(LC_ALL, "");
319 (void) textdomain(TEXT_DOMAIN);
320
321 opterr = 0;
322
323 /* check options */
324 while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) {
325 switch (c) {
326 case 's':
327 sloppy = 1;
328 break;
329 case 'f':
330 fake = 1;
331 break;
332 case 'n':
333 nomtab = 1;
334 break;
335 case 'v':
336 verbose++;
337 break;
338 case 'o':
339 (void) strlcpy(mntopts, optarg, sizeof (mntopts));
340 break;
341 case 'h':
342 case '?':
343 (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
344 optopt);
345 (void) fprintf(stderr, gettext("Usage: mount.zfs "
346 "[-sfnv] [-o options] <dataset> <mountpoint>\n"));
347 return (MOUNT_USAGE);
348 }
349 }
350
351 argc -= optind;
352 argv += optind;
353
354 /* check that we only have two arguments */
355 if (argc != 2) {
356 if (argc == 0)
357 (void) fprintf(stderr, gettext("missing dataset "
358 "argument\n"));
359 else if (argc == 1)
360 (void) fprintf(stderr,
361 gettext("missing mountpoint argument\n"));
362 else
363 (void) fprintf(stderr, gettext("too many arguments\n"));
364 (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
365 return (MOUNT_USAGE);
366 }
367
368 dataset = parse_dataset(argv[0]);
369 mntpoint = argv[1];
370
371 /* validate mount options and set mntflags */
3aff7755
BB
372 error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
373 badopt, mtabopt);
d53368f6
BB
374 if (error) {
375 switch (error) {
376 case ENOMEM:
377 (void) fprintf(stderr, gettext("filesystem '%s' "
378 "cannot be mounted due to a memory allocation "
3aff7755 379 "failure.\n"), dataset);
d53368f6 380 return (MOUNT_SYSERR);
3aff7755 381 case ENOENT:
d53368f6 382 (void) fprintf(stderr, gettext("filesystem '%s' "
3aff7755
BB
383 "cannot be mounted of due invalid option "
384 "'%s'.\n"), dataset, badopt);
d53368f6
BB
385 (void) fprintf(stderr, gettext("Use the '-s' option "
386 "to ignore the bad mount option.\n"));
387 return (MOUNT_USAGE);
388 default:
389 (void) fprintf(stderr, gettext("filesystem '%s' "
3aff7755 390 "cannot be mounted due to internal error %d.\n"),
d53368f6
BB
391 dataset, error);
392 return (MOUNT_SOFTWARE);
393 }
394 }
395
396#ifdef HAVE_LIBSELINUX
397 /*
398 * Automatically add the default zfs context when selinux is enabled
399 * and the caller has not specified their own context. This must be
400 * done until zfs is added to the default selinux policy configuration
401 * as a known filesystem type which supports xattrs.
402 */
3aff7755 403 if (is_selinux_enabled() && !(zfsflags & ZS_NOCONTEXT)) {
d53368f6
BB
404 (void) strlcat(mntopts, ",context=\"system_u:"
405 "object_r:file_t:s0\"", sizeof (mntopts));
3aff7755
BB
406 (void) strlcat(mtabopt, ",context=\"system_u:"
407 "object_r:file_t:s0\"", sizeof (mtabopt));
408 }
d53368f6
BB
409#endif /* HAVE_LIBSELINUX */
410
411
412 if (verbose)
413 (void) fprintf(stdout, gettext("mount.zfs:\n"
414 " dataset: \"%s\"\n mountpoint: \"%s\"\n"
415 " mountflags: 0x%lx\n zfsflags: 0x%lx\n"
3aff7755
BB
416 " mountopts: \"%s\"\n mtabopts: \"%s\"\n"),
417 dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
d53368f6
BB
418
419 if (mntflags & MS_REMOUNT)
420 nomtab = 1;
421
093aa692
BB
422 if ((mntflags & MS_REMOUNT) && (mntflags & MS_RDONLY))
423 remount_ro = 1;
424
425 if (zfsflags & ZS_ZFSUTIL)
d53368f6
BB
426 zfsutil = 1;
427
428 if ((g_zfs = libzfs_init()) == NULL)
429 return (MOUNT_SYSERR);
430
431 /* try to open the dataset to access the mount point */
3613204c
BB
432 if ((zhp = zfs_open(g_zfs, dataset,
433 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) {
d53368f6
BB
434 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
435 "mounted, unable to open the dataset\n"), dataset);
436 libzfs_fini(g_zfs);
437 return (MOUNT_USAGE);
438 }
439
3613204c
BB
440 /* treat all snapshots as legacy mount points */
441 if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
442 (void) strlcpy(legacy, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
443 else
444 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
445 sizeof (legacy), NULL, NULL, 0, B_FALSE);
d53368f6
BB
446
447 zfs_close(zhp);
448 libzfs_fini(g_zfs);
449
450 /*
451 * Legacy mount points may only be mounted using 'mount', never using
452 * 'zfs mount'. However, since 'zfs mount' actually invokes 'mount'
453 * we differentiate the two cases using the 'zfsutil' mount option.
454 * This mount option should only be supplied by the 'zfs mount' util.
093aa692
BB
455 *
456 * The only exception to the above rule is '-o remount,ro'. This is
457 * always allowed for non-legacy datasets for rc.sysinit/umountroot
458 * to safely remount the root filesystem and flush its cache.
d53368f6
BB
459 */
460 if (zfsutil && !strcmp(legacy, ZFS_MOUNTPOINT_LEGACY)) {
461 (void) fprintf(stderr, gettext(
462 "filesystem '%s' cannot be mounted using 'zfs mount'.\n"
463 "Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n"
464 "See zfs(8) for more information.\n"),
465 dataset, mntpoint, dataset, mntpoint);
466 return (MOUNT_USAGE);
467 }
468
093aa692 469 if (!zfsutil && strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) && !remount_ro) {
d53368f6
BB
470 (void) fprintf(stderr, gettext(
471 "filesystem '%s' cannot be mounted using 'mount'.\n"
472 "Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n"
473 "See zfs(8) for more information.\n"),
474 dataset, "legacy", dataset);
475 return (MOUNT_USAGE);
476 }
477
478 if (!fake) {
479 error = mount(dataset, mntpoint, MNTTYPE_ZFS,
480 mntflags, mntopts);
481 if (error) {
482 switch (errno) {
483 case EBUSY:
484 (void) fprintf(stderr, gettext("filesystem "
485 "'%s' is already mounted\n"), dataset);
486 return (MOUNT_SYSERR);
487 default:
488 (void) fprintf(stderr, gettext("filesystem "
489 "'%s' can not be mounted due to error "
490 "%d\n"), dataset, errno);
491 return (MOUNT_USAGE);
492 }
493 }
494 }
495
496 if (!nomtab && mtab_is_writeable()) {
3aff7755 497 error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt);
d53368f6
BB
498 if (error)
499 return (error);
500 }
501
502 return (MOUNT_SUCCESS);
503}