/* * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs. * * The result can passed to free(). */ char *retrieve_mount_options(struct libmnt_fs *fs) { const char *opts; if (!fs) return NULL; opts = mnt_fs_get_attributes(fs); /* /dev/.mount/utab */ if (opts) return strdup(opts); return mnt_fs_strdup_options(fs); /* /etc/mtab */ }
/* * Converts already evalulated and fixed options to the form that is compatible * with /sbin/mount.type helpers. */ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr) { struct libmnt_optmap const *maps[1]; char *next, *name, *val; size_t namesz, valsz; int rc = 0; assert(cxt); assert(cxt->fs); assert(optstr); DBG(CXT, mnt_debug_h(cxt, "mount: generate heper mount options")); *optstr = mnt_fs_strdup_options(cxt->fs); if (!*optstr) return -ENOMEM; if (cxt->flags & MNT_FL_SAVED_USER) rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user); if (rc) goto err; /* remove userspace options with MNT_NOHLPS flag */ maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); next = *optstr; while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { const struct libmnt_optmap *ent; mnt_optmap_get_entry(maps, 1, name, namesz, &ent); if (ent && ent->id && (ent->mask & MNT_NOHLPS)) { next = name; rc = mnt_optstr_remove_option_at(optstr, name, val ? val + valsz : name + namesz); if (rc) goto err; } } return rc; err: free(*optstr); *optstr = NULL; return rc; }
/* * Parses one line from mountinfo file */ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) { int rc, end = 0; unsigned int maj, min; char *fstype = NULL, *src = NULL, *p; rc = sscanf(s, "%u " /* (1) id */ "%u " /* (2) parent */ "%u:%u " /* (3) maj:min */ UL_SCNsA" " /* (4) mountroot */ UL_SCNsA" " /* (5) target */ UL_SCNsA /* (6) vfs options (fs-independent) */ "%n", /* number of read bytes */ &fs->id, &fs->parent, &maj, &min, &fs->root, &fs->target, &fs->vfs_optstr, &end); if (rc >= 7 && end > 0) s += end; /* (7) optional fields, terminated by " - " */ p = strstr(s, " - "); if (!p) { DBG(TAB, mnt_debug("mountinfo parse error: not found separator")); return -EINVAL; } s = p + 3; rc += sscanf(s, UL_SCNsA" " /* (8) FS type */ UL_SCNsA" " /* (9) source */ UL_SCNsA, /* (10) fs options (fs specific) */ &fstype, &src, &fs->fs_optstr); if (rc >= 10) { fs->flags |= MNT_FS_KERNEL; fs->devno = makedev(maj, min); unmangle_string(fs->root); unmangle_string(fs->target); unmangle_string(fs->vfs_optstr); unmangle_string(fstype); unmangle_string(src); unmangle_string(fs->fs_optstr); rc = __mnt_fs_set_fstype_ptr(fs, fstype); if (!rc) { fstype = NULL; rc = __mnt_fs_set_source_ptr(fs, src); if (!rc) src = NULL; } /* merge VFS and FS options to the one string */ fs->optstr = mnt_fs_strdup_options(fs); if (!fs->optstr) rc = -ENOMEM; } else { free(fstype); free(src); DBG(TAB, mnt_debug( "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s)); rc = -EINVAL; } return rc; }
/* * this has to be called after mnt_context_evaluate_permissions() */ static int fix_optstr(struct libmnt_context *cxt) { int rc = 0; char *next; char *name, *val; size_t namesz, valsz; struct libmnt_fs *fs; #ifdef HAVE_LIBSELINUX int se_fix = 0, se_rem = 0; #endif assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt) return -EINVAL; if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED)) return 0; DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr")); fs = cxt->fs; /* The propagation flags should not be used together with any other * flags (except MS_REC and MS_SILENT) */ if (cxt->mountflags & MS_PROPAGATION) cxt->mountflags &= (MS_PROPAGATION | MS_REC | MS_SILENT); if (!mnt_optstr_get_option(fs->user_optstr, "user", &val, &valsz)) { if (val) { cxt->orig_user = strndup(val, valsz); if (!cxt->orig_user) { rc = -ENOMEM; goto done; } } cxt->flags |= MNT_FL_SAVED_USER; } /* * Sync mount options with mount flags */ rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags, mnt_get_builtin_optmap(MNT_LINUX_MAP)); if (rc) goto done; rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags, mnt_get_builtin_optmap(MNT_USERSPACE_MAP)); if (rc) goto done; next = fs->fs_optstr; #ifdef HAVE_LIBSELINUX if (!is_selinux_enabled()) /* Always remove SELinux garbage if SELinux disabled */ se_rem = 1; else if (cxt->mountflags & MS_REMOUNT) /* * Linux kernel < 2.6.39 does not allow to remount with any * selinux specific mount options. * * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65 * 026eb167ae77244458fa4b4b9fc171209c079ba7 * fix this odd behavior, so we don't have to care about it in * userspace. */ se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39); else /* For normal mount we have translate the contexts */ se_fix = 1; #endif while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { if (namesz == 3 && !strncmp(name, "uid", 3)) rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next); else if (namesz == 3 && !strncmp(name, "gid", 3)) rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next); #ifdef HAVE_LIBSELINUX else if ((se_rem || se_fix) && namesz >= 7 && (!strncmp(name, "context", 7) || !strncmp(name, "fscontext", 9) || !strncmp(name, "defcontext", 10) || !strncmp(name, "rootcontext", 11) || !strncmp(name, "seclabel", 8))) { if (se_rem) { /* remove context= option */ next = name; rc = mnt_optstr_remove_option_at(&fs->fs_optstr, name, val ? val + valsz : name + namesz); } else if (se_fix && val && valsz) /* translate selinux contexts */ rc = mnt_optstr_fix_secontext(&fs->fs_optstr, val, valsz, &next); } #endif if (rc) goto done; } if (!rc && cxt->user_mountflags & MNT_MS_USER) rc = mnt_optstr_fix_user(&fs->user_optstr); /* refresh merged optstr */ free(fs->optstr); fs->optstr = NULL; fs->optstr = mnt_fs_strdup_options(fs); done: cxt->flags |= MNT_FL_MOUNTOPTS_FIXED; DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: " "vfs: '%s' fs: '%s' user: '******', optstr: '%s'", rc, fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr)); if (rc) rc = -MNT_ERR_MOUNTOPT; return rc; }
/* * Parses one line from a mountinfo file */ static int mnt_parse_mountinfo_line(struct libmnt_fs *fs, char *s) { int rc, end = 0; unsigned int maj, min; char *fstype = NULL, *src = NULL, *p; #ifndef HAVE_SCANF_MS_MODIFIER size_t len = strlen(s) + 1; fs->root = malloc(len); fs->target = malloc(len); fs->vfs_optstr = malloc(len); fs->fs_optstr = malloc(len); fstype = malloc(len); src = malloc(len); #endif rc = sscanf(s, "%d " /* (1) id */ "%d " /* (2) parent */ "%u:%u " /* (3) maj:min */ UL_SCNsA" " /* (4) mountroot */ UL_SCNsA" " /* (5) target */ UL_SCNsA /* (6) vfs options (fs-independent) */ "%n", /* number of read bytes */ &fs->id, &fs->parent, &maj, &min, #ifdef HAVE_SCANF_MS_MODIFIER &fs->root, &fs->target, &fs->vfs_optstr, #else fs->root, fs->target, fs->vfs_optstr, #endif &end); if (rc >= 7 && end > 0) s += end; /* (7) optional fields, terminated by " - " */ p = strstr(s, " - "); if (!p) { DBG(TAB, ul_debug("mountinfo parse error: separator not found")); return -EINVAL; } if (p > s + 1) fs->opt_fields = strndup(s + 1, p - s - 1); s = p + 3; rc += sscanf(s, UL_SCNsA" " /* (8) FS type */ UL_SCNsA" " /* (9) source */ UL_SCNsA, /* (10) fs options (fs specific) */ #ifdef HAVE_SCANF_MS_MODIFIER &fstype, &src, &fs->fs_optstr); #else fstype, src, fs->fs_optstr); #endif if (rc >= 10) { size_t sz; fs->flags |= MNT_FS_KERNEL; fs->devno = makedev(maj, min); /* remove "(deleted)" suffix */ sz = strlen(fs->target); if (sz > PATH_DELETED_SUFFIX_SZ) { char *p = fs->target + (sz - PATH_DELETED_SUFFIX_SZ); if (strcmp(p, PATH_DELETED_SUFFIX) == 0) *p = '\0'; } unmangle_string(fs->root); unmangle_string(fs->target); unmangle_string(fs->vfs_optstr); unmangle_string(fstype); unmangle_string(src); unmangle_string(fs->fs_optstr); rc = __mnt_fs_set_fstype_ptr(fs, fstype); if (!rc) { fstype = NULL; rc = __mnt_fs_set_source_ptr(fs, src); if (!rc) src = NULL; } /* merge VFS and FS options to one string */ fs->optstr = mnt_fs_strdup_options(fs); if (!fs->optstr) rc = -ENOMEM; } else { free(fstype); free(src); DBG(TAB, ul_debug( "mountinfo parse error [sscanf rc=%d]: '%s'", rc, s)); rc = -EINVAL; } return rc; }
static int mount_main(struct libmnt_context *cxt, int argc, char **argv) { int rc, c; struct libmnt_fs *fs; char *spec = NULL, *mount_point = NULL, *opts = NULL; static const struct option longopts[] = { { "fake", 0, 0, 'f' }, { "help", 0, 0, 'h' }, { "no-mtab", 0, 0, 'n' }, { "read-only", 0, 0, 'r' }, { "ro", 0, 0, 'r' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { "read-write", 0, 0, 'w' }, { "rw", 0, 0, 'w' }, { "options", 1, 0, 'o' }, { "sloppy", 0, 0, 's' }, { NULL, 0, 0, 0 } }; mount_config_init(progname); mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0); while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) { rc = mnt_context_helper_setopt(cxt, c, optarg); if (rc == 0) /* valid option */ continue; if (rc < 0) /* error (probably ENOMEM) */ goto err; /* rc==1 means unknow option */ switch (c) { case 'V': printf("%s: ("PACKAGE_STRING")\n", progname); return EX_SUCCESS; case 'h': default: mount_usage(); return EX_USAGE; } } if (optind < argc) spec = argv[optind++]; if (optind < argc) mount_point = argv[optind++]; if (!mount_point) { nfs_error(_("%s: no mount point provided"), progname); goto err; } if (!spec) { nfs_error(_("%s: no mount spec provided"), progname); goto err; } if (geteuid() != 0) { nfs_error(_("%s: not installed setuid - " "\"user\" NFS mounts not supported."), progname); goto err; } verbose = mnt_context_is_verbose(cxt); sloppy = mnt_context_is_sloppy(cxt); nomtab = mnt_context_is_nomtab(cxt); if (strcmp(progname, "mount.nfs4") == 0) mnt_context_set_fstype(cxt, "nfs4"); else mnt_context_set_fstype(cxt, "nfs"); /* default */ rc = mnt_context_set_source(cxt, spec); if (!rc) mnt_context_set_target(cxt, mount_point); if (rc) { nfs_error(_("%s: failed to set spec or mountpoint: %s"), progname, strerror(errno)); goto err; } mount_point = mnt_resolve_path(mount_point, mnt_context_get_cache(cxt)); if (chk_mountpoint(mount_point)) goto err; /* * Concatenate mount options from the configuration file */ fs = mnt_context_get_fs(cxt); if (fs) { opts = mnt_fs_strdup_options(fs); opts = mount_config_opts(spec, mount_point, opts); mnt_fs_set_options(fs, opts); } rc = mnt_context_prepare_mount(cxt); if (rc) { nfs_error(_("%s: failed to prepare mount: %s\n"), progname, strerror(-rc)); goto err; } rc = try_mount(cxt, FOREGROUND); if (rc == EX_BG) { printf(_("%s: backgrounding \"%s\"\n"), progname, mnt_context_get_source(cxt)); printf(_("%s: mount options: \"%s\"\n"), progname, opts); fflush(stdout); if (daemon(0, 0)) { nfs_error(_("%s: failed to start " "background process: %s\n"), progname, strerror(errno)); exit(EX_FAIL); } rc = try_mount(cxt, BACKGROUND); if (verbose && rc) printf(_("%s: giving up \"%s\"\n"), progname, mnt_context_get_source(cxt)); } mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1); mnt_context_finalize_mount(cxt); /* mtab update */ return rc; err: return EX_FAIL; }