/** * mnt_optstr_get_options * @optstr: string with comma separated list of options * @subset: returns newly allocated string with options * @map: options map * @ignore: mask of the options that should be ignored * * Extracts options from @optstr that belongs to the @map, for example: * * mnt_optstr_get_options(optstr, &p, * mnt_get_builtin_optmap(MNT_LINUX_MAP), * MNT_NOMTAB); * * the 'p' returns all VFS options, the options that does not belong to mtab * are ignored. * * Returns: 0 on success, or negative number in case of error. */ int mnt_optstr_get_options(const char *optstr, char **subset, const struct libmnt_optmap *map, int ignore) { struct libmnt_optmap const *maps[1]; char *name, *val, *str = (char *) optstr; size_t namesz, valsz; if (!optstr || !subset) return -EINVAL; maps[0] = map; *subset = NULL; while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { int rc = 0; const struct libmnt_optmap *ent; mnt_optmap_get_entry(maps, 1, name, namesz, &ent); if (!ent || !ent->id) continue; /* ignore undefined options (comments) */ if (ignore && (ent->mask & ignore)) continue; rc = __mnt_optstr_append_option(subset, name, namesz, val, valsz); if (rc) { free(*subset); return rc; } } return 0; }
/** * mnt_split_optstr: * @optstr: string with comma separated list of options * @user: returns newly allocated string with userspace options * @vfs: returns newly allocated string with VFS options * @fs: returns newly allocated string with FS options * @ignore_user: option mask for options that should be ignored * @ignore_vfs: option mask for options that should be ignored * * For example: * * mnt_split_optstr(optstr, &u, NULL, NULL, MNT_NOMTAB, 0); * * returns all userspace options, the options that does not belong to * mtab are ignored. * * Note that FS options are all options that are undefined in MNT_USERSPACE_MAP * or MNT_LINUX_MAP. * * Returns: 0 on success, or negative number in case of error. */ int mnt_split_optstr(const char *optstr, char **user, char **vfs, char **fs, int ignore_user, int ignore_vfs) { char *name, *val, *str = (char *) optstr; size_t namesz, valsz; struct libmnt_optmap const *maps[2]; assert(optstr); if (!optstr) return -EINVAL; maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP); maps[1] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); if (vfs) *vfs = NULL; if (fs) *fs = NULL; if (user) *user = NULL; while(!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { int rc = 0; const struct libmnt_optmap *ent = NULL; const struct libmnt_optmap *m = mnt_optmap_get_entry(maps, 2, name, namesz, &ent); if (ent && !ent->id) continue; /* ignore undefined options (comments) */ if (ent && m && m == maps[0] && vfs) { if (ignore_vfs && (ent->mask & ignore_vfs)) continue; rc = __mnt_optstr_append_option(vfs, name, namesz, val, valsz); } else if (ent && m && m == maps[1] && user) { if (ignore_user && (ent->mask & ignore_user)) continue; rc = __mnt_optstr_append_option(user, name, namesz, val, valsz); } else if (!m && fs) rc = __mnt_optstr_append_option(fs, name, namesz, val, valsz); if (rc) { if (vfs) free(*vfs); if (fs) free(*fs); if (user) free(*user); return rc; } } return 0; }
/** * mnt_optstr_get_flags: * @optstr: string with comma separated list of options * @flags: returns mount flags * @map: options map * * Returns in @flags IDs of options from @optstr as defined in the @map. * * For example: * * "bind,exec,foo,bar" --returns-> MS_BIND * * "bind,noexec,foo,bar" --returns-> MS_BIND|MS_NOEXEC * * Note that @flags are not zeroized by this function! This function sets/unsets * bits in the @flags only. * * Returns: 0 on success or negative number in case of error */ int mnt_optstr_get_flags(const char *optstr, unsigned long *flags, const struct libmnt_optmap *map) { struct libmnt_optmap const *maps[2]; char *name, *str = (char *) optstr; size_t namesz = 0, valsz = 0; int nmaps = 0; assert(optstr); if (!optstr || !flags || !map) return -EINVAL; maps[nmaps++] = map; if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) /* * Add userspace map -- the "user" is interpreted as * MS_NO{EXEC,SUID,DEV}. */ maps[nmaps++] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); while(!mnt_optstr_next_option(&str, &name, &namesz, NULL, &valsz)) { const struct libmnt_optmap *ent; const struct libmnt_optmap *m; m = mnt_optmap_get_entry(maps, nmaps, name, namesz, &ent); if (!m || !ent || !ent->id) continue; /* ignore name=<value> if options map expects <name> only */ if (valsz && mnt_optmap_entry_novalue(ent)) continue; if (m == map) { /* requested map */ if (ent->mask & MNT_INVERT) *flags &= ~ent->id; else *flags |= ent->id; } else if (nmaps == 2 && m == maps[1] && valsz == 0) { /* * Special case -- translate "user" (but no user=) to * MS_ options */ if (ent->mask & MNT_INVERT) continue; if (ent->id & (MNT_MS_OWNER | MNT_MS_GROUP)) *flags |= MS_OWNERSECURE; else if (ent->id & (MNT_MS_USER | MNT_MS_USERS)) *flags |= MS_SECURE; } } return 0; }
/* * 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; }
/* * 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; }
/** * mnt_optstr_apply_flags: * @optstr: string with comma separated list of options * @flags: returns mount flags * @map: options map * * Removes/adds options to the @optstr according to flags. For example: * * MS_NOATIME and "foo,bar,noexec" --returns-> "foo,bar,noatime" * * Returns: 0 on success or negative number in case of error. */ int mnt_optstr_apply_flags(char **optstr, unsigned long flags, const struct libmnt_optmap *map) { struct libmnt_optmap const *maps[1]; char *name, *next, *val; size_t namesz = 0, valsz = 0; unsigned long fl; int rc = 0; assert(optstr); if (!optstr || !map) return -EINVAL; DBG(CXT, mnt_debug("applying 0x%08lu flags to '%s'", flags, *optstr)); maps[0] = map; next = *optstr; fl = flags; /* * There is a convention that 'rw/ro' flags are always at the beginning of * the string (although the 'rw' is unnecessary). */ if (map == mnt_get_builtin_optmap(MNT_LINUX_MAP)) { const char *o = (fl & MS_RDONLY) ? "ro" : "rw"; if (next && (!strncmp(next, "rw", 2) || !strncmp(next, "ro", 2)) && (*(next + 2) == '\0' || *(next + 2) == ',')) { /* already set, be paranoid and fix it */ memcpy(next, o, 2); } else { rc = mnt_optstr_prepend_option(optstr, o, NULL); if (rc) goto err; next = *optstr; /* because realloc() */ } fl &= ~MS_RDONLY; next += 2; if (*next == ',') next++; } if (next && *next) { /* * scan @optstr and remove options that are missing in * @flags */ while(!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) { const struct libmnt_optmap *ent; if (mnt_optmap_get_entry(maps, 1, name, namesz, &ent)) { /* * remove unwanted option (rw/ro is already set) */ if (!ent || !ent->id) continue; /* ignore name=<value> if options map expects <name> only */ if (valsz && mnt_optmap_entry_novalue(ent)) continue; if (ent->id == MS_RDONLY || (ent->mask & MNT_INVERT) || (fl & ent->id) != (unsigned long) ent->id) { char *end = val ? val + valsz : name + namesz; next = name; rc = mnt_optstr_remove_option_at( optstr, name, end); if (rc) goto err; } if (!(ent->mask & MNT_INVERT)) fl &= ~ent->id; } } } /* add missing options */ if (fl) { const struct libmnt_optmap *ent; char *p; for (ent = map; ent && ent->name; ent++) { if ((ent->mask & MNT_INVERT) || ent->id == 0 || (fl & ent->id) != (unsigned long) ent->id) continue; /* don't add options which require values (e.g. offset=%d) */ p = strchr(ent->name, '='); if (p) { if (p > ent->name && *(p - 1) == '[') p--; /* name[=] */ else continue; /* name= */ p = strndup(ent->name, p - ent->name); if (!p) { rc = -ENOMEM; goto err; } mnt_optstr_append_option(optstr, p, NULL); free(p); } else mnt_optstr_append_option(optstr, ent->name, NULL); } } DBG(CXT, mnt_debug("new optstr '%s'", *optstr)); return rc; err: DBG(CXT, mnt_debug("failed to apply flags [rc=%d]", rc)); return rc; }