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