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