]>
Commit | Line | Data |
---|---|---|
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> | |
0282c413 | 31 | #include <sys/mntent.h> |
d53368f6 BB |
32 | #include <sys/stat.h> |
33 | #include <libzfs.h> | |
92e91da2 | 34 | #include <locale.h> |
d93b45ae | 35 | #include <getopt.h> |
6d723925 | 36 | #include <fcntl.h> |
d53368f6 | 37 | |
0282c413 BB |
38 | #define ZS_COMMENT 0x00000000 /* comment */ |
39 | #define ZS_ZFSUTIL 0x00000001 /* caller is zfs(8) */ | |
40 | ||
d53368f6 BB |
41 | libzfs_handle_t *g_zfs; |
42 | ||
43 | typedef struct option_map { | |
44 | const char *name; | |
45 | unsigned long mntmask; | |
46 | unsigned long zfsmask; | |
47 | } option_map_t; | |
48 | ||
49 | static const option_map_t option_map[] = { | |
50 | /* Canonicalized filesystem independent options from mount(8) */ | |
51 | { MNTOPT_NOAUTO, MS_COMMENT, ZS_COMMENT }, | |
52 | { MNTOPT_DEFAULTS, MS_COMMENT, ZS_COMMENT }, | |
53 | { MNTOPT_NODEVICES, MS_NODEV, ZS_COMMENT }, | |
54 | { MNTOPT_DIRSYNC, MS_DIRSYNC, ZS_COMMENT }, | |
55 | { MNTOPT_NOEXEC, MS_NOEXEC, ZS_COMMENT }, | |
56 | { MNTOPT_GROUP, MS_GROUP, ZS_COMMENT }, | |
57 | { MNTOPT_NETDEV, MS_COMMENT, ZS_COMMENT }, | |
58 | { MNTOPT_NOFAIL, MS_COMMENT, ZS_COMMENT }, | |
59 | { MNTOPT_NOSUID, MS_NOSUID, ZS_COMMENT }, | |
60 | { MNTOPT_OWNER, MS_OWNER, ZS_COMMENT }, | |
61 | { MNTOPT_REMOUNT, MS_REMOUNT, ZS_COMMENT }, | |
62 | { MNTOPT_RO, MS_RDONLY, ZS_COMMENT }, | |
63 | { MNTOPT_RW, MS_COMMENT, ZS_COMMENT }, | |
64 | { MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT }, | |
65 | { MNTOPT_USER, MS_USERS, ZS_COMMENT }, | |
66 | { MNTOPT_USERS, MS_USERS, ZS_COMMENT }, | |
a5f36651 | 67 | /* acl flags passed with util-linux-2.24 mount command */ |
68 | { MNTOPT_ACL, MS_POSIXACL, ZS_COMMENT }, | |
69 | { MNTOPT_NOACL, MS_COMMENT, ZS_COMMENT }, | |
70 | { MNTOPT_POSIXACL, MS_POSIXACL, ZS_COMMENT }, | |
d53368f6 BB |
71 | #ifdef MS_NOATIME |
72 | { MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT }, | |
73 | #endif | |
74 | #ifdef MS_NODIRATIME | |
75 | { MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT }, | |
76 | #endif | |
77 | #ifdef MS_RELATIME | |
78 | { MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT }, | |
79 | #endif | |
80 | #ifdef MS_STRICTATIME | |
67600771 CC |
81 | { MNTOPT_STRICTATIME, MS_STRICTATIME, ZS_COMMENT }, |
82 | #endif | |
83 | #ifdef MS_LAZYTIME | |
84 | { MNTOPT_LAZYTIME, MS_LAZYTIME, ZS_COMMENT }, | |
d53368f6 | 85 | #endif |
11b9ec23 MT |
86 | { MNTOPT_CONTEXT, MS_COMMENT, ZS_COMMENT }, |
87 | { MNTOPT_FSCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
88 | { MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
89 | { MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_COMMENT }, | |
d53368f6 BB |
90 | #ifdef MS_I_VERSION |
91 | { MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT }, | |
92 | #endif | |
93 | #ifdef MS_MANDLOCK | |
94 | { MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT }, | |
95 | #endif | |
96 | /* Valid options not found in mount(8) */ | |
97 | { MNTOPT_BIND, MS_BIND, ZS_COMMENT }, | |
98 | #ifdef MS_REC | |
99 | { MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT }, | |
100 | #endif | |
101 | { MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT }, | |
102 | #ifdef MS_NOSUB | |
103 | { MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT }, | |
104 | #endif | |
105 | #ifdef MS_SILENT | |
106 | { MNTOPT_QUIET, MS_SILENT, ZS_COMMENT }, | |
107 | #endif | |
108 | /* Custom zfs options */ | |
2cf7f52b | 109 | { MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT }, |
d53368f6 BB |
110 | { MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT }, |
111 | { MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL }, | |
112 | { NULL, 0, 0 } }; | |
113 | ||
114 | /* | |
115 | * Break the mount option in to a name/value pair. The name is | |
116 | * validated against the option map and mount flags set accordingly. | |
117 | */ | |
118 | static int | |
119 | parse_option(char *mntopt, unsigned long *mntflags, | |
120 | unsigned long *zfsflags, int sloppy) | |
121 | { | |
122 | const option_map_t *opt; | |
123 | char *ptr, *name, *value = NULL; | |
124 | int error = 0; | |
125 | ||
126 | name = strdup(mntopt); | |
127 | if (name == NULL) | |
128 | return (ENOMEM); | |
129 | ||
130 | for (ptr = name; ptr && *ptr; ptr++) { | |
131 | if (*ptr == '=') { | |
132 | *ptr = '\0'; | |
133 | value = ptr+1; | |
03514b01 | 134 | VERIFY3P(value, !=, NULL); |
d53368f6 BB |
135 | break; |
136 | } | |
137 | } | |
138 | ||
139 | for (opt = option_map; opt->name != NULL; opt++) { | |
140 | if (strncmp(name, opt->name, strlen(name)) == 0) { | |
141 | *mntflags |= opt->mntmask; | |
142 | *zfsflags |= opt->zfsmask; | |
d53368f6 BB |
143 | error = 0; |
144 | goto out; | |
145 | } | |
146 | } | |
147 | ||
148 | if (!sloppy) | |
149 | error = ENOENT; | |
150 | out: | |
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 | */ | |
161 | static int | |
3aff7755 BB |
162 | parse_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 | ||
217 | out: | |
218 | free(opts); | |
219 | return (error); | |
220 | } | |
221 | ||
222 | /* | |
0c1171dc BB |
223 | * Return the pool/dataset to mount given the name passed to mount. This |
224 | * is expected to be of the form pool/dataset, however may also refer to | |
225 | * a block device if that device contains a valid zfs label. | |
d53368f6 BB |
226 | */ |
227 | static char * | |
228 | parse_dataset(char *dataset) | |
229 | { | |
230 | char cwd[PATH_MAX]; | |
0c1171dc BB |
231 | struct stat64 statbuf; |
232 | int error; | |
a6cba65c | 233 | int len; |
d53368f6 | 234 | |
0c1171dc BB |
235 | /* |
236 | * We expect a pool/dataset to be provided, however if we're | |
237 | * given a device which is a member of a zpool we attempt to | |
238 | * extract the pool name stored in the label. Given the pool | |
239 | * name we can mount the root dataset. | |
240 | */ | |
241 | error = stat64(dataset, &statbuf); | |
242 | if (error == 0) { | |
243 | nvlist_t *config; | |
244 | char *name; | |
245 | int fd; | |
246 | ||
247 | fd = open(dataset, O_RDONLY); | |
248 | if (fd < 0) | |
249 | goto out; | |
250 | ||
7d90f569 | 251 | error = zpool_read_label(fd, &config, NULL); |
0c1171dc BB |
252 | (void) close(fd); |
253 | if (error) | |
254 | goto out; | |
255 | ||
256 | error = nvlist_lookup_string(config, | |
257 | ZPOOL_CONFIG_POOL_NAME, &name); | |
c76955ea BB |
258 | if (error) { |
259 | nvlist_free(config); | |
260 | } else { | |
0c1171dc | 261 | dataset = strdup(name); |
c76955ea BB |
262 | nvlist_free(config); |
263 | return (dataset); | |
264 | } | |
0c1171dc BB |
265 | } |
266 | out: | |
267 | /* | |
268 | * If a file or directory in your current working directory is | |
269 | * named 'dataset' then mount(8) will prepend your current working | |
270 | * directory to the dataset. There is no way to prevent this | |
271 | * behavior so we simply check for it and strip the prepended | |
272 | * patch when it is added. | |
273 | */ | |
ec49a5f0 BB |
274 | if (getcwd(cwd, PATH_MAX) == NULL) |
275 | return (dataset); | |
276 | ||
a6cba65c BB |
277 | len = strlen(cwd); |
278 | ||
279 | /* Do not add one when cwd already ends in a trailing '/' */ | |
d1d7e268 | 280 | if (strncmp(cwd, dataset, len) == 0) |
a6cba65c | 281 | return (dataset + len + (cwd[len-1] != '/')); |
d53368f6 BB |
282 | |
283 | return (dataset); | |
284 | } | |
285 | ||
286 | /* | |
287 | * Update the mtab_* code to use the libmount library when it is commonly | |
288 | * available otherwise fallback to legacy mode. The mount(8) utility will | |
289 | * manage the lock file for us to prevent racing updates to /etc/mtab. | |
290 | */ | |
291 | static int | |
292 | mtab_is_writeable(void) | |
293 | { | |
294 | struct stat st; | |
295 | int error, fd; | |
296 | ||
79251738 | 297 | error = lstat("/etc/mtab", &st); |
d53368f6 BB |
298 | if (error || S_ISLNK(st.st_mode)) |
299 | return (0); | |
300 | ||
79251738 | 301 | fd = open("/etc/mtab", O_RDWR | O_CREAT, 0644); |
d53368f6 BB |
302 | if (fd < 0) |
303 | return (0); | |
304 | ||
305 | close(fd); | |
306 | return (1); | |
307 | } | |
308 | ||
309 | static int | |
310 | mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts) | |
311 | { | |
312 | struct mntent mnt; | |
313 | FILE *fp; | |
314 | int error; | |
315 | ||
316 | mnt.mnt_fsname = dataset; | |
317 | mnt.mnt_dir = mntpoint; | |
318 | mnt.mnt_type = type; | |
319 | mnt.mnt_opts = mntopts ? mntopts : ""; | |
320 | mnt.mnt_freq = 0; | |
321 | mnt.mnt_passno = 0; | |
322 | ||
79251738 | 323 | fp = setmntent("/etc/mtab", "a+"); |
d53368f6 BB |
324 | if (!fp) { |
325 | (void) fprintf(stderr, gettext( | |
79251738 | 326 | "filesystem '%s' was mounted, but /etc/mtab " |
d53368f6 | 327 | "could not be opened due to error %d\n"), |
79251738 | 328 | dataset, errno); |
d53368f6 BB |
329 | return (MOUNT_FILEIO); |
330 | } | |
331 | ||
332 | error = addmntent(fp, &mnt); | |
333 | if (error) { | |
334 | (void) fprintf(stderr, gettext( | |
79251738 | 335 | "filesystem '%s' was mounted, but /etc/mtab " |
d53368f6 | 336 | "could not be updated due to error %d\n"), |
79251738 | 337 | dataset, errno); |
d53368f6 BB |
338 | return (MOUNT_FILEIO); |
339 | } | |
340 | ||
341 | (void) endmntent(fp); | |
342 | ||
343 | return (MOUNT_SUCCESS); | |
344 | } | |
345 | ||
11b9ec23 | 346 | static void |
0282c413 BB |
347 | append_mntopt(const char *name, const char *val, char *mntopts, |
348 | char *mtabopt, boolean_t quote) | |
11b9ec23 MT |
349 | { |
350 | char tmp[MNT_LINE_MAX]; | |
351 | ||
0282c413 BB |
352 | snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val); |
353 | ||
354 | if (mntopts) | |
355 | strlcat(mntopts, tmp, MNT_LINE_MAX); | |
356 | ||
357 | if (mtabopt) | |
358 | strlcat(mtabopt, tmp, MNT_LINE_MAX); | |
11b9ec23 MT |
359 | } |
360 | ||
361 | static void | |
362 | zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name, | |
363 | char *mntopts, char *mtabopt) | |
364 | { | |
365 | char context[ZFS_MAXPROPLEN]; | |
366 | ||
367 | if (zfs_prop_get(zhp, zpt, context, sizeof (context), | |
368 | NULL, NULL, 0, B_FALSE) == 0) { | |
369 | if (strcmp(context, "none") != 0) | |
02730c33 | 370 | append_mntopt(name, context, mntopts, mtabopt, B_TRUE); |
11b9ec23 MT |
371 | } |
372 | } | |
373 | ||
d53368f6 BB |
374 | int |
375 | main(int argc, char **argv) | |
376 | { | |
377 | zfs_handle_t *zhp; | |
11b9ec23 | 378 | char prop[ZFS_MAXPROPLEN]; |
287be44f | 379 | uint64_t zfs_version = 0; |
d53368f6 BB |
380 | char mntopts[MNT_LINE_MAX] = { '\0' }; |
381 | char badopt[MNT_LINE_MAX] = { '\0' }; | |
3aff7755 | 382 | char mtabopt[MNT_LINE_MAX] = { '\0' }; |
33364b15 | 383 | char mntpoint[PATH_MAX]; |
384 | char *dataset; | |
c171ea71 | 385 | unsigned long mntflags = 0, zfsflags = 0, remount = 0; |
d53368f6 BB |
386 | int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0; |
387 | int error, c; | |
388 | ||
389 | (void) setlocale(LC_ALL, ""); | |
390 | (void) textdomain(TEXT_DOMAIN); | |
391 | ||
392 | opterr = 0; | |
393 | ||
394 | /* check options */ | |
d93b45ae | 395 | while ((c = getopt_long(argc, argv, "sfnvo:h?", 0, 0)) != -1) { |
d53368f6 BB |
396 | switch (c) { |
397 | case 's': | |
398 | sloppy = 1; | |
399 | break; | |
400 | case 'f': | |
401 | fake = 1; | |
402 | break; | |
403 | case 'n': | |
404 | nomtab = 1; | |
405 | break; | |
406 | case 'v': | |
407 | verbose++; | |
408 | break; | |
409 | case 'o': | |
410 | (void) strlcpy(mntopts, optarg, sizeof (mntopts)); | |
411 | break; | |
412 | case 'h': | |
413 | case '?': | |
414 | (void) fprintf(stderr, gettext("Invalid option '%c'\n"), | |
415 | optopt); | |
416 | (void) fprintf(stderr, gettext("Usage: mount.zfs " | |
417 | "[-sfnv] [-o options] <dataset> <mountpoint>\n")); | |
418 | return (MOUNT_USAGE); | |
419 | } | |
420 | } | |
421 | ||
422 | argc -= optind; | |
423 | argv += optind; | |
424 | ||
425 | /* check that we only have two arguments */ | |
426 | if (argc != 2) { | |
427 | if (argc == 0) | |
428 | (void) fprintf(stderr, gettext("missing dataset " | |
429 | "argument\n")); | |
430 | else if (argc == 1) | |
431 | (void) fprintf(stderr, | |
432 | gettext("missing mountpoint argument\n")); | |
433 | else | |
434 | (void) fprintf(stderr, gettext("too many arguments\n")); | |
435 | (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); | |
436 | return (MOUNT_USAGE); | |
437 | } | |
438 | ||
439 | dataset = parse_dataset(argv[0]); | |
33364b15 | 440 | |
441 | /* canonicalize the mount point */ | |
442 | if (realpath(argv[1], mntpoint) == NULL) { | |
443 | (void) fprintf(stderr, gettext("filesystem '%s' cannot be " | |
87bdc45c BB |
444 | "mounted at '%s' due to canonicalization error %d.\n"), |
445 | dataset, argv[1], errno); | |
33364b15 | 446 | return (MOUNT_SYSERR); |
447 | } | |
d53368f6 BB |
448 | |
449 | /* validate mount options and set mntflags */ | |
3aff7755 BB |
450 | error = parse_options(mntopts, &mntflags, &zfsflags, sloppy, |
451 | badopt, mtabopt); | |
d53368f6 BB |
452 | if (error) { |
453 | switch (error) { | |
454 | case ENOMEM: | |
455 | (void) fprintf(stderr, gettext("filesystem '%s' " | |
456 | "cannot be mounted due to a memory allocation " | |
3aff7755 | 457 | "failure.\n"), dataset); |
d53368f6 | 458 | return (MOUNT_SYSERR); |
3aff7755 | 459 | case ENOENT: |
d53368f6 | 460 | (void) fprintf(stderr, gettext("filesystem '%s' " |
758d3552 | 461 | "cannot be mounted due to invalid option " |
3aff7755 | 462 | "'%s'.\n"), dataset, badopt); |
d53368f6 BB |
463 | (void) fprintf(stderr, gettext("Use the '-s' option " |
464 | "to ignore the bad mount option.\n")); | |
465 | return (MOUNT_USAGE); | |
466 | default: | |
467 | (void) fprintf(stderr, gettext("filesystem '%s' " | |
3aff7755 | 468 | "cannot be mounted due to internal error %d.\n"), |
d53368f6 BB |
469 | dataset, error); |
470 | return (MOUNT_SOFTWARE); | |
471 | } | |
472 | } | |
473 | ||
d53368f6 BB |
474 | if (verbose) |
475 | (void) fprintf(stdout, gettext("mount.zfs:\n" | |
476 | " dataset: \"%s\"\n mountpoint: \"%s\"\n" | |
477 | " mountflags: 0x%lx\n zfsflags: 0x%lx\n" | |
3aff7755 BB |
478 | " mountopts: \"%s\"\n mtabopts: \"%s\"\n"), |
479 | dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt); | |
d53368f6 | 480 | |
c171ea71 | 481 | if (mntflags & MS_REMOUNT) { |
d53368f6 | 482 | nomtab = 1; |
c171ea71 BB |
483 | remount = 1; |
484 | } | |
093aa692 BB |
485 | |
486 | if (zfsflags & ZS_ZFSUTIL) | |
d53368f6 BB |
487 | zfsutil = 1; |
488 | ||
65037d9b BB |
489 | if ((g_zfs = libzfs_init()) == NULL) { |
490 | (void) fprintf(stderr, "%s", libzfs_error_init(errno)); | |
d53368f6 | 491 | return (MOUNT_SYSERR); |
65037d9b | 492 | } |
d53368f6 BB |
493 | |
494 | /* try to open the dataset to access the mount point */ | |
3613204c BB |
495 | if ((zhp = zfs_open(g_zfs, dataset, |
496 | ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) { | |
d53368f6 BB |
497 | (void) fprintf(stderr, gettext("filesystem '%s' cannot be " |
498 | "mounted, unable to open the dataset\n"), dataset); | |
499 | libzfs_fini(g_zfs); | |
500 | return (MOUNT_USAGE); | |
501 | } | |
502 | ||
11b9ec23 MT |
503 | /* |
504 | * Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists | |
505 | * if it does, create a tmp variable in case it's needed | |
506 | * checks to see if the selinux context is set to the default | |
507 | * if it is, allow the setting of the other context properties | |
508 | * this is needed because the 'context' property overrides others | |
509 | * if it is not the default, set the 'context' property | |
510 | */ | |
511 | if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop), | |
512 | NULL, NULL, 0, B_FALSE) == 0) { | |
513 | if (strcmp(prop, "none") == 0) { | |
514 | zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT, | |
515 | MNTOPT_FSCONTEXT, mntopts, mtabopt); | |
516 | zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT, | |
517 | MNTOPT_DEFCONTEXT, mntopts, mtabopt); | |
518 | zfs_selinux_setcontext(zhp, | |
519 | ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT, | |
520 | mntopts, mtabopt); | |
521 | } else { | |
0282c413 BB |
522 | append_mntopt(MNTOPT_CONTEXT, prop, |
523 | mntopts, mtabopt, B_TRUE); | |
11b9ec23 MT |
524 | } |
525 | } | |
526 | ||
0282c413 BB |
527 | /* A hint used to determine an auto-mounted snapshot mount point */ |
528 | append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE); | |
529 | ||
3613204c BB |
530 | /* treat all snapshots as legacy mount points */ |
531 | if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) | |
11b9ec23 | 532 | (void) strlcpy(prop, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN); |
3613204c | 533 | else |
11b9ec23 MT |
534 | (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop, |
535 | sizeof (prop), NULL, NULL, 0, B_FALSE); | |
d53368f6 | 536 | |
287be44f DS |
537 | /* |
538 | * Fetch the max supported zfs version in case we get ENOTSUP | |
539 | * back from the mount command, since we need the zfs handle | |
540 | * to do so. | |
541 | */ | |
542 | zfs_version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); | |
543 | if (zfs_version == 0) { | |
544 | fprintf(stderr, gettext("unable to fetch " | |
545 | "ZFS version for filesystem '%s'\n"), dataset); | |
546 | return (MOUNT_SYSERR); | |
547 | } | |
548 | ||
d53368f6 BB |
549 | zfs_close(zhp); |
550 | libzfs_fini(g_zfs); | |
551 | ||
552 | /* | |
553 | * Legacy mount points may only be mounted using 'mount', never using | |
554 | * 'zfs mount'. However, since 'zfs mount' actually invokes 'mount' | |
555 | * we differentiate the two cases using the 'zfsutil' mount option. | |
556 | * This mount option should only be supplied by the 'zfs mount' util. | |
093aa692 | 557 | * |
c171ea71 BB |
558 | * The only exception to the above rule is '-o remount' which is |
559 | * always allowed for non-legacy datasets. This is done because when | |
560 | * using zfs as your root file system both rc.sysinit/umountroot and | |
561 | * systemd depend on 'mount -o remount <mountpoint>' to work. | |
d53368f6 | 562 | */ |
11b9ec23 | 563 | if (zfsutil && (strcmp(prop, ZFS_MOUNTPOINT_LEGACY) == 0)) { |
d53368f6 BB |
564 | (void) fprintf(stderr, gettext( |
565 | "filesystem '%s' cannot be mounted using 'zfs mount'.\n" | |
566 | "Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n" | |
567 | "See zfs(8) for more information.\n"), | |
d1d7e268 | 568 | dataset, mntpoint, dataset, mntpoint); |
d53368f6 BB |
569 | return (MOUNT_USAGE); |
570 | } | |
571 | ||
462ee8e3 | 572 | if (!zfsutil && !(remount || fake) && |
11b9ec23 | 573 | strcmp(prop, ZFS_MOUNTPOINT_LEGACY)) { |
d53368f6 BB |
574 | (void) fprintf(stderr, gettext( |
575 | "filesystem '%s' cannot be mounted using 'mount'.\n" | |
576 | "Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n" | |
577 | "See zfs(8) for more information.\n"), | |
578 | dataset, "legacy", dataset); | |
579 | return (MOUNT_USAGE); | |
580 | } | |
581 | ||
582 | if (!fake) { | |
583 | error = mount(dataset, mntpoint, MNTTYPE_ZFS, | |
584 | mntflags, mntopts); | |
287be44f DS |
585 | } |
586 | ||
587 | if (error) { | |
588 | switch (errno) { | |
589 | case ENOENT: | |
590 | (void) fprintf(stderr, gettext("mount point " | |
591 | "'%s' does not exist\n"), mntpoint); | |
592 | return (MOUNT_SYSERR); | |
593 | case EBUSY: | |
594 | (void) fprintf(stderr, gettext("filesystem " | |
595 | "'%s' is already mounted\n"), dataset); | |
596 | return (MOUNT_BUSY); | |
597 | case ENOTSUP: | |
598 | if (zfs_version > ZPL_VERSION) { | |
599 | (void) fprintf(stderr, | |
600 | gettext("filesystem '%s' (v%d) is not " | |
601 | "supported by this implementation of " | |
602 | "ZFS (max v%d).\n"), dataset, | |
02730c33 | 603 | (int)zfs_version, (int)ZPL_VERSION); |
287be44f DS |
604 | } else { |
605 | (void) fprintf(stderr, | |
606 | gettext("filesystem '%s' mount " | |
607 | "failed for unknown reason.\n"), dataset); | |
d53368f6 | 608 | } |
287be44f | 609 | return (MOUNT_SYSERR); |
4070bfd8 BB |
610 | #ifdef MS_MANDLOCK |
611 | case EPERM: | |
612 | if (mntflags & MS_MANDLOCK) { | |
613 | (void) fprintf(stderr, gettext("filesystem " | |
614 | "'%s' has the 'nbmand=on' property set, " | |
615 | "this mount\noption may be disabled in " | |
616 | "your kernel. Use 'zfs set nbmand=off'\n" | |
617 | "to disable this option and try to " | |
618 | "mount the filesystem again.\n"), dataset); | |
619 | return (MOUNT_SYSERR); | |
620 | } | |
621 | /* fallthru */ | |
622 | #endif | |
287be44f DS |
623 | default: |
624 | (void) fprintf(stderr, gettext("filesystem " | |
4070bfd8 BB |
625 | "'%s' can not be mounted: %s\n"), dataset, |
626 | strerror(errno)); | |
287be44f | 627 | return (MOUNT_USAGE); |
d53368f6 BB |
628 | } |
629 | } | |
630 | ||
631 | if (!nomtab && mtab_is_writeable()) { | |
3aff7755 | 632 | error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt); |
d53368f6 BB |
633 | if (error) |
634 | return (error); | |
635 | } | |
636 | ||
637 | return (MOUNT_SUCCESS); | |
638 | } |