/* * This function uses @uf to found corresponding record in @tb, then the record * from @tb is updated (user specific mount options are added). * * Note that @uf must contain only user specific mount options instead of * VFS options (note that FS options are ignored). * * Returns modified filesystem (from @tb) or NULL. */ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct libmnt_fs *uf) { struct libmnt_fs *fs; struct libmnt_iter itr; const char *optstr, *src, *target, *root, *attrs; assert(tb); assert(uf); if (!tb || !uf) return NULL; DBG(TAB, mnt_debug_h(tb, "merging user fs")); src = mnt_fs_get_srcpath(uf); target = mnt_fs_get_target(uf); optstr = mnt_fs_get_user_options(uf); attrs = mnt_fs_get_attributes(uf); root = mnt_fs_get_root(uf); if (!src || !target || !root || (!attrs && !optstr)) return NULL; mnt_reset_iter(&itr, MNT_ITER_BACKWARD); while(mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *s = mnt_fs_get_srcpath(fs), *t = mnt_fs_get_target(fs), *r = mnt_fs_get_root(fs); if (fs->flags & MNT_FS_MERGED) continue; if (s && t && r && !strcmp(t, target) && !strcmp(s, src) && !strcmp(r, root)) break; } if (fs) { DBG(TAB, mnt_debug_h(tb, "found fs -- appending user optstr")); mnt_fs_append_options(fs, optstr); mnt_fs_append_attributes(fs, attrs); mnt_fs_set_bindsrc(fs, mnt_fs_get_bindsrc(uf)); fs->flags |= MNT_FS_MERGED; DBG(TAB, mnt_debug_h(tb, "found fs:")); DBG(TAB, mnt_fs_print_debug(fs, stderr)); } return fs; }
static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc) { struct libmnt_table *tb = NULL; int rc = 0; DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename)); if (lc) rc = mnt_lock_file(lc); if (rc) return rc; tb = __mnt_new_table_from_file(upd->filename, upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); if (tb) { struct libmnt_fs *cur = mnt_table_find_target(tb, mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD); if (cur) { rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs)); if (!rc) rc = update_table(upd, tb); } } if (lc) mnt_unlock_file(lc); mnt_free_table(tb); return rc; }
/** * mnt_context_next_umount: * @cxt: context * @itr: iterator * @fs: returns the current filesystem * @mntrc: returns the return code from mnt_context_umount() * @ignored: returns 1 for not matching * * This function tries to umount the next filesystem from mtab (as returned by * mnt_context_get_mtab()). * * You can filter out filesystems by: * mnt_context_set_options_pattern() to simulate umount -a -O pattern * mnt_context_set_fstype_pattern() to simulate umount -a -t pattern * * If the filesystem is not mounted or does not match the defined criteria, * then the function mnt_context_next_umount() returns zero, but the @ignored is * non-zero. Note that the root filesystem is always ignored. * * If umount(2) syscall or umount.type helper failed, then the * mnt_context_next_umount() function returns zero, but the @mntrc is non-zero. * Use also mnt_context_get_status() to check if the filesystem was * successfully umounted. * * Returns: 0 on success, * <0 in case of error (!= umount(2) errors) * 1 at the end of the list. */ int mnt_context_next_umount(struct libmnt_context *cxt, struct libmnt_iter *itr, struct libmnt_fs **fs, int *mntrc, int *ignored) { struct libmnt_table *mtab; const char *tgt; int rc; if (ignored) *ignored = 0; if (mntrc) *mntrc = 0; if (!cxt || !fs || !itr) return -EINVAL; rc = mnt_context_get_mtab(cxt, &mtab); cxt->mtab = NULL; /* do not reset mtab */ mnt_reset_context(cxt); cxt->mtab = mtab; if (rc) return rc; do { rc = mnt_table_next_fs(mtab, itr, fs); if (rc != 0) return rc; /* no more filesystems (or error) */ tgt = mnt_fs_get_target(*fs); } while (!tgt); DBG(CXT, ul_debugobj(cxt, "next-umount: trying %s [fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", tgt, mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern)); /* ignore filesystems which don't match options patterns */ if ((cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || /* ignore filesystems which don't match type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) *ignored = 1; DBG(CXT, ul_debugobj(cxt, "next-umount: not-match")); return 0; } rc = mnt_context_set_fs(cxt, *fs); if (rc) return rc; rc = mnt_context_umount(cxt); if (mntrc) *mntrc = rc; return 0; }
/* * The default is to use fstype from cxt->fs, this could be overwritten by * @try_type argument. * * Returns: 0 on success, * >0 in case of mount(2) error (returns syscall errno), * <0 in case of other errors. */ static int do_mount(struct libmnt_context *cxt, const char *try_type) { int rc = 0; const char *src, *target, *type; unsigned long flags; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (try_type && !cxt->helper) { rc = mnt_context_prepare_helper(cxt, "mount", try_type); if (!rc) return rc; } if (cxt->helper) return exec_helper(cxt); flags = cxt->mountflags; src = mnt_fs_get_srcpath(cxt->fs); target = mnt_fs_get_target(cxt->fs); if (!src || !target) return -EINVAL; type = try_type ? : mnt_fs_get_fstype(cxt->fs); if (!(flags & MS_MGC_MSK)) flags |= MS_MGC_VAL; DBG(CXT, mnt_debug_h(cxt, "%smount(2) " "[source=%s, target=%s, type=%s, " " mountflags=0x%08lx, mountdata=%s]", (cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "", src, target, type, flags, cxt->mountdata ? "yes" : "<none>")); if (cxt->flags & MNT_FL_FAKE) cxt->syscall_status = 0; else { if (mount(src, target, type, flags, cxt->mountdata)) { cxt->syscall_status = -errno; DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]", -cxt->syscall_status)); return -cxt->syscall_status; } DBG(CXT, mnt_debug_h(cxt, "mount(2) success")); cxt->syscall_status = 0; } if (try_type && cxt->update) { struct libmnt_fs *fs = mnt_update_get_fs(cxt->update); if (fs) rc = mnt_fs_set_fstype(fs, try_type); } return rc; }
static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs) { char *p; int rc = 0; assert(fs); assert(f); if (!fs || !f) return -EINVAL; p = mangle(mnt_fs_get_source(fs)); if (p) { rc = fprintf(f, "SRC=%s ", p); free(p); } if (rc >= 0) { p = mangle(mnt_fs_get_target(fs)); if (p) { rc = fprintf(f, "TARGET=%s ", p); free(p); } } if (rc >= 0) { p = mangle(mnt_fs_get_root(fs)); if (p) { rc = fprintf(f, "ROOT=%s ", p); free(p); } } if (rc >= 0) { p = mangle(mnt_fs_get_bindsrc(fs)); if (p) { rc = fprintf(f, "BINDSRC=%s ", p); free(p); } } if (rc >= 0) { p = mangle(mnt_fs_get_attributes(fs)); if (p) { rc = fprintf(f, "ATTRS=%s ", p); free(p); } } if (rc >= 0) { p = mangle(mnt_fs_get_user_options(fs)); if (p) { rc = fprintf(f, "OPTS=%s", p); free(p); } } if (rc >= 0) rc = fprintf(f, "\n"); if (rc > 0) rc = 0; /* success */ return rc; }
static void verify_mesg(struct verify_context *vfy, char type, const char *fmt, va_list ap) { if (!vfy->target_printed) { fprintf(stdout, "%s\n", mnt_fs_get_target(vfy->fs)); vfy->target_printed = 1; } fprintf(stdout, " [%c] ", type); vfprintf(stdout, fmt, ap); fputc('\n', stdout); }
/** * mnt_table_next_child_fs: * @tb: mountinfo file (/proc/self/mountinfo) * @itr: iterator * @parent: parental FS * @chld: returns the next child filesystem * * Note that filesystems are returned in the order how was mounted (according to * IDs in /proc/self/mountinfo). * * Returns: 0 on success, negative number in case of error or 1 at end of list. */ int mnt_table_next_child_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct libmnt_fs *parent, struct libmnt_fs **chld) { struct libmnt_fs *fs; int parent_id, lastchld_id = 0, chld_id = 0; if (!tb || !itr || !parent) return -EINVAL; DBG(TAB, mnt_debug_h(tb, "lookup next child of '%s'", mnt_fs_get_target(parent))); parent_id = mnt_fs_get_id(parent); if (!parent_id) return -EINVAL; /* get ID of the previously returned child */ if (itr->head && itr->p != itr->head) { MNT_ITER_ITERATE(itr, fs, struct libmnt_fs, ents); lastchld_id = mnt_fs_get_id(fs); } *chld = NULL; mnt_reset_iter(itr, MNT_ITER_FORWARD); while(mnt_table_next_fs(tb, itr, &fs) == 0) { int id; if (mnt_fs_get_parent_id(fs) != parent_id) continue; id = mnt_fs_get_id(fs); /* avoid infinite loop. This only happens in rare cases * such as in early userspace when the rootfs is its own parent */ if (id == parent_id) continue; if ((!lastchld_id || id > lastchld_id) && (!*chld || id < chld_id)) { *chld = fs; chld_id = id; } } if (!chld_id) return 1; /* end of iterator */ /* set the iterator to the @chld for the next call */ mnt_table_set_iter(tb, itr, *chld); return 0; }
static int dir_to_device(const char *spec, dev_t *dev) { struct libmnt_table *tb = mnt_new_table_from_file("/proc/self/mountinfo"); struct libmnt_fs *fs; struct libmnt_cache *cache; int rc = -1; if (!tb) { /* * Fallback. Traditional way to detect mountpoints. This way * is independent on /proc, but not able to detect bind mounts. */ struct stat pst, st; char buf[PATH_MAX], *cn; int len; if (stat(spec, &st) != 0) return -1; cn = mnt_resolve_path(spec, NULL); /* canonicalize */ len = snprintf(buf, sizeof(buf), "%s/..", cn ? cn : spec); free(cn); if (len < 0 || (size_t) len + 1 > sizeof(buf)) return -1; if (stat(buf, &pst) !=0) return -1; if ((st.st_dev != pst.st_dev) || (st.st_dev == pst.st_dev && st.st_ino == pst.st_ino)) { *dev = st.st_dev; return 0; } return -1; } /* to canonicalize all necessary paths */ cache = mnt_new_cache(); mnt_table_set_cache(tb, cache); fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD); if (fs && mnt_fs_get_target(fs)) { *dev = mnt_fs_get_devno(fs); rc = 0; } mnt_free_table(tb); mnt_free_cache(cache); return rc; }
/** * mnt_table_add_fs: * @tb: tab pointer * @fs: new entry * * Adds a new entry to tab and increment @fs reference counter. Don't forget to * use mnt_unref_fs() after mnt_table_add_fs() you want to keep the @fs * referenced by the table only. * * Returns: 0 on success or negative number in case of error. */ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { if (!tb || !fs) return -EINVAL; mnt_ref_fs(fs); list_add_tail(&fs->ents, &tb->ents); tb->nents++; DBG(TAB, ul_debugobj(tb, "add entry: %s %s", mnt_fs_get_source(fs), mnt_fs_get_target(fs))); return 0; }
static int dir_to_device(struct mountpoint_control *ctl) { struct libmnt_table *tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); struct libmnt_fs *fs; struct libmnt_cache *cache; int rc = -1; if (!tb) { /* * Fallback. Traditional way to detect mountpoints. This way * is independent on /proc, but not able to detect bind mounts. */ struct stat pst; char buf[PATH_MAX], *cn; int len; cn = mnt_resolve_path(ctl->path, NULL); /* canonicalize */ len = snprintf(buf, sizeof(buf), "%s/..", cn ? cn : ctl->path); free(cn); if (len < 0 || (size_t) len >= sizeof(buf)) return -1; if (stat(buf, &pst) !=0) return -1; if ((ctl->st.st_dev != pst.st_dev) || (ctl->st.st_dev == pst.st_dev && ctl->st.st_ino == pst.st_ino)) { ctl->dev = ctl->st.st_dev; return 0; } return -1; } /* to canonicalize all necessary paths */ cache = mnt_new_cache(); mnt_table_set_cache(tb, cache); mnt_unref_cache(cache); fs = mnt_table_find_target(tb, ctl->path, MNT_ITER_BACKWARD); if (fs && mnt_fs_get_target(fs)) { ctl->dev = mnt_fs_get_devno(fs); rc = 0; } mnt_unref_table(tb); return rc; }
/** * mnt_table_add_fs: * @tb: tab pointer * @fs: new entry * * Adds a new entry to tab. * * Returns: 0 on success or negative number in case of error. */ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs) { assert(tb); assert(fs); if (!tb || !fs) return -EINVAL; list_add_tail(&fs->ents, &tb->ents); DBG(TAB, mnt_debug_h(tb, "add entry: %s %s", mnt_fs_get_source(fs), mnt_fs_get_target(fs))); tb->nents++; return 0; }
static char *dir_to_device(const char *spec) { struct libmnt_table *tb = mnt_new_table_from_file("/proc/self/mountinfo"); struct libmnt_fs *fs; char *res = NULL; if (!tb) return NULL; fs = mnt_table_find_target(tb, spec, MNT_ITER_BACKWARD); if (fs && mnt_fs_get_target(fs)) res = xstrdup(mnt_fs_get_source(fs)); mnt_free_table(tb); return res; }
int main(const int argc, const char* argv[]) { if (argc == 2) { if (strcmp(argv[1], "--verbose") == 0) { verbose_mode = 1; } } prepare(); clearSupplementaryGroups(); selfCheck(); struct libmnt_table* mt = mnt_new_table_from_file("/opt/xware_desktop/mounts"); if (mt == NULL) { fprintf(stderr, "mnt_new_table_from_file failed.\n"); exit(EXIT_FAILURE); } struct libmnt_iter* itr = mnt_new_iter(MNT_ITER_FORWARD); struct libmnt_fs* fs = NULL; while(1) { int tmp = mnt_table_find_next_fs(mt, itr, &matchAll, NULL, &fs); if (tmp < 0) { // error fprintf(stderr, "mnt_table_find_next_fs failed.\n"); break; } else { if (tmp == 1) { // reach EOF break; } const char* target = mnt_fs_get_target(fs); if (target == NULL) { fprintf(stderr, "mnt_fs_get_target failed.\n"); } else { printf("%s%s%s\n", BOLD, target, NOSTYLE); printf("================================\n"); if (checkDirXPermissions(target)) { checkTargetDirPermissions(target); } printf("\n"); } } } printf("%s", NOSTYLE); mnt_free_fs(fs); mnt_free_iter(itr); mnt_free_table(mt); return 0; }
static int try_mount(struct libmnt_context *cxt, int bg) { struct libmnt_fs *fs; const char *p; char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL; unsigned long flags = 0; int fake, ret = 0; fs = mnt_context_get_fs(cxt); /* libmount returns read-only pointers (const char) * so, reallocate for nfsmount() functions. */ if ((p = mnt_fs_get_source(fs))) /* spec */ src = strdup(p); if ((p = mnt_fs_get_target(fs))) /* mountpoint */ tgt = strdup(p); if ((p = mnt_fs_get_fstype(fs))) /* FS type */ type = strdup(p); if ((p = mnt_fs_get_fs_options(fs))) /* mount options */ opts = strdup(p); mnt_context_get_mflags(cxt, &flags); /* mount(2) flags */ fake = mnt_context_is_fake(cxt); if (string) ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg); else if (strcmp(type, "nfs4") == 0) ret = nfs4mount(src, tgt, flags, &opts, fake, bg); else ret = nfsmount(src, tgt, flags, &opts, fake, bg); /* Store mount options if not called with mount --no-mtab */ if (!ret && !mnt_context_is_nomtab(cxt)) store_mount_options(fs, opts); free(src); free(tgt); free(type); free(opts); return ret; }
/** * mnt_context_prepare_mount: * @cxt: context * * Prepare context for mounting, unnecessary for mnt_context_mount(). * * Returns: negative number on error, zero on success */ int mnt_context_prepare_mount(struct libmnt_context *cxt) { int rc = -EINVAL; assert(cxt); assert(cxt->fs); assert(cxt->helper_exec_status == 1); assert(cxt->syscall_status == 1); if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs)) return -EINVAL; if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs)) return -EINVAL; if (cxt->flags & MNT_FL_PREPARED) return 0; cxt->action = MNT_ACT_MOUNT; DBG(CXT, mnt_debug_h(cxt, "mount: preparing")); /* TODO: fstab is unnecessary for MS_{MOVE,BIND,STARED,...} */ rc = mnt_context_apply_fstab(cxt); if (!rc) rc = mnt_context_merge_mflags(cxt); if (!rc) rc = evaluate_permissions(cxt); if (!rc) rc = fix_optstr(cxt); if (!rc) rc = mnt_context_prepare_srcpath(cxt); if (!rc) rc = mnt_context_prepare_target(cxt); if (!rc) rc = mnt_context_guess_fstype(cxt); if (!rc) rc = mnt_context_prepare_helper(cxt, "mount", NULL); if (rc) { DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed")); return rc; } cxt->flags |= MNT_FL_PREPARED; return rc; }
/* * See if device has been mounted by looking in mount table. If so, set * device name and mount point name, and return 1, otherwise return 0. */ static int device_get_mountpoint(char **devname, char **mnt) { struct libmnt_fs *fs; int rc; *mnt = NULL; if (!mtab) { mtab = mnt_new_table(); if (!mtab) err(EXIT_FAILURE, _("failed to initialize libmount table")); cache = mnt_new_cache(); mnt_table_set_cache(mtab, cache); if (p_option) rc = mnt_table_parse_file(mtab, _PATH_PROC_MOUNTINFO); else rc = mnt_table_parse_mtab(mtab, NULL); if (rc) err(EXIT_FAILURE, _("failed to parse mount table")); } fs = mnt_table_find_source(mtab, *devname, MNT_ITER_BACKWARD); if (!fs) { /* maybe 'devname' is mountpoint rather than a real device */ fs = mnt_table_find_target(mtab, *devname, MNT_ITER_BACKWARD); if (fs) { free(*devname); *devname = xstrdup(mnt_fs_get_source(fs)); } } if (fs) { *mnt = xstrdup(mnt_fs_get_target(fs)); /* We'll call umount(), so remove the filesystem from the table * to avoid duplicate results in the next device_get_mountpoint() * call */ mnt_free_fs(fs); } return *mnt ? 0 : -1; }
static int get_debugfs(char **path) { struct libmnt_context *cxt; struct libmnt_table *tb; struct libmnt_iter *itr = NULL; struct libmnt_fs *fs; int found = 0, ret; cxt = mnt_new_context(); if (!cxt) err(EXIT_FAILURE, "libmount context allocation failed"); itr = mnt_new_iter(MNT_ITER_FORWARD); if (!itr) err(EXIT_FAILURE, "failed to initialize libmount iterator"); if (mnt_context_get_mtab(cxt, &tb)) err(EXIT_FAILURE, "failed to read mtab"); while (mnt_table_next_fs(tb, itr, &fs) == 0) { const char *type = mnt_fs_get_fstype(fs); if (!strcmp(type, "debugfs")) { found = 1; break; } } if (found) { ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); if (ret < 0) err(EXIT_FAILURE, "failed to format string"); } mnt_free_iter(itr); mnt_free_context(cxt); if (!found) return -1; return 0; }
/* * See if device has been mounted by looking in mount table. If so, set * device name and mount point name, and return 1, otherwise return 0. */ static int device_get_mountpoint(char **devname, char **mnt) { struct libmnt_fs *fs; int rc; *mnt = NULL; if (!mtab) { struct libmnt_cache *cache; mtab = mnt_new_table(); if (!mtab) err(EXIT_FAILURE, _("failed to initialize libmount table")); cache = mnt_new_cache(); mnt_table_set_cache(mtab, cache); mnt_unref_cache(cache); if (p_option) rc = mnt_table_parse_file(mtab, _PATH_PROC_MOUNTINFO); else rc = mnt_table_parse_mtab(mtab, NULL); if (rc) err(EXIT_FAILURE, _("failed to parse mount table")); } fs = mnt_table_find_source(mtab, *devname, MNT_ITER_BACKWARD); if (!fs) { /* maybe 'devname' is mountpoint rather than a real device */ fs = mnt_table_find_target(mtab, *devname, MNT_ITER_BACKWARD); if (fs) { free(*devname); *devname = xstrdup(mnt_fs_get_source(fs)); } } if (fs) *mnt = xstrdup(mnt_fs_get_target(fs)); return *mnt ? 0 : -1; }
static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc) { struct libmnt_table *tb = NULL; int rc = 0; struct libmnt_fs *fs; assert(upd); assert(upd->fs); DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename)); fs = upd->fs; if (lc) rc = mnt_lock_file(lc); if (rc) return rc; tb = __mnt_new_table_from_file(upd->filename, upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); if (tb) { struct libmnt_fs *cur = mnt_table_find_target(tb, mnt_fs_get_target(fs), MNT_ITER_BACKWARD); if (cur) { if (upd->userspace_only) rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs)); if (!rc) rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs)); if (!rc) rc = update_table(upd, tb); } } if (lc) mnt_unlock_file(lc); mnt_free_table(tb); return rc; }
/* mtab and fstab update -- returns zero on success */ static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs) { const char *o, *src, *fstype; char *m1, *m2, *m3, *m4; int rc; assert(fs); assert(f); src = mnt_fs_get_source(fs); fstype = mnt_fs_get_fstype(fs); o = mnt_fs_get_options(fs); m1 = src ? mangle(src) : "none"; m2 = mangle(mnt_fs_get_target(fs)); m3 = fstype ? mangle(fstype) : "none"; m4 = o ? mangle(o) : "rw"; if (m1 && m2 && m3 && m4) { rc = fprintf(f, "%s %s %s %s %d %d\n", m1, m2, m3, m4, mnt_fs_get_freq(fs), mnt_fs_get_passno(fs)); if (rc > 0) rc = 0; } else rc = -ENOMEM; if (src) free(m1); free(m2); if (fstype) free(m3); if (o) free(m4); return rc; }
/** * mnt_context_find_umount_fs: * @cxt: mount context * @tgt: mountpoint, device, ... * @pfs: returns point to filesystem * * Returns: 0 on success, <0 on error, 1 if target filesystem not found */ int mnt_context_find_umount_fs(struct libmnt_context *cxt, const char *tgt, struct libmnt_fs **pfs) { int rc; struct libmnt_table *mtab = NULL; struct libmnt_fs *fs; char *loopdev = NULL; if (pfs) *pfs = NULL; if (!cxt || !tgt || !pfs) return -EINVAL; DBG(CXT, ul_debugobj(cxt, "umount: lookup FS for '%s'", tgt)); if (!*tgt) return 1; /* empty string is not an error */ /* * The mount table may be huge, and on systems with utab we have to merge * userspace mount options into /proc/self/mountinfo. This all is * expensive. The tab filter allows to filter out entries, then * a mount table and utab are very tiny files. * * *but*... the filter uses mnt_fs_streq_{target,srcpath} functions * where LABEL, UUID or symlinks are canonicalized. It means that * it's usable only for canonicalized stuff (e.g. kernel mountinfo). */ if (!mnt_context_mtab_writable(cxt) && *tgt == '/' && !mnt_context_is_force(cxt) && !mnt_context_is_lazy(cxt)) rc = mnt_context_get_mtab_for_target(cxt, &mtab, tgt); else rc = mnt_context_get_mtab(cxt, &mtab); if (rc) { DBG(CXT, ul_debugobj(cxt, "umount: failed to read mtab")); return rc; } if (mnt_table_get_nents(mtab) == 0) { DBG(CXT, ul_debugobj(cxt, "umount: mtab empty")); return 1; } try_loopdev: fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD); if (!fs && mnt_context_is_swapmatch(cxt)) { /* * Maybe the option is source rather than target (sometimes * people use e.g. "umount /dev/sda1") */ fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD); if (fs) { struct libmnt_fs *fs1 = mnt_table_find_target(mtab, mnt_fs_get_target(fs), MNT_ITER_BACKWARD); if (!fs1) { DBG(CXT, ul_debugobj(cxt, "mtab is broken?!?!")); rc = -EINVAL; goto err; } if (fs != fs1) { /* Something was stacked over `file' on the * same mount point. */ DBG(CXT, ul_debugobj(cxt, "umount: %s: %s is mounted " "over it on the same point", tgt, mnt_fs_get_source(fs1))); rc = -EINVAL; goto err; } } } if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) { /* * Maybe the option is /path/file.img, try to convert to /dev/loopN */ struct stat st; if (mnt_stat_mountpoint(tgt, &st) == 0 && S_ISREG(st.st_mode)) { int count; struct libmnt_cache *cache = mnt_context_get_cache(cxt); const char *bf = cache ? mnt_resolve_path(tgt, cache) : tgt; count = loopdev_count_by_backing_file(bf, &loopdev); if (count == 1) { DBG(CXT, ul_debugobj(cxt, "umount: %s --> %s (retry)", tgt, loopdev)); tgt = loopdev; goto try_loopdev; } else if (count > 1) DBG(CXT, ul_debugobj(cxt, "umount: warning: %s is associated " "with more than one loopdev", tgt)); } } if (pfs) *pfs = fs; free(loopdev); DBG(CXT, ul_debugobj(cxt, "umount fs: %s", fs ? mnt_fs_get_target(fs) : "<not found>")); return fs ? 0 : 1; err: free(loopdev); return rc; }
static int lookup_umount_fs(struct libmnt_context *cxt) { int rc, loopdev = 0; const char *tgt; struct libmnt_table *mtab = NULL; struct libmnt_fs *fs; struct libmnt_cache *cache = NULL; char *cn_tgt = NULL; assert(cxt); assert(cxt->fs); DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS")); tgt = mnt_fs_get_target(cxt->fs); if (!tgt) { DBG(CXT, mnt_debug_h(cxt, "umount: undefined target")); return -EINVAL; } /* * The mtab file maybe huge and on systems with utab we have to merge * userspace mount options into /proc/self/mountinfo. This all is * expensive. The mtab filter allows to filter out entries, then * mtab and utab are very tiny files. * * *but*... the filter uses mnt_fs_streq_{target,srcpath} functions * where LABEL, UUID or symlinks are to canonicalized. It means that * it's usable only for canonicalized stuff (e.g. kernel mountinfo). */ if (!cxt->mtab_writable && *tgt == '/' && !mnt_context_is_force(cxt) && !mnt_context_is_lazy(cxt)) { struct stat st; if (stat(tgt, &st) == 0 && S_ISDIR(st.st_mode)) { /* we'll canonicalized /proc/self/mountinfo */ cache = mnt_context_get_cache(cxt); cn_tgt = mnt_resolve_path(tgt, cache); if (cn_tgt) mnt_context_set_tabfilter(cxt, mtab_filter, cn_tgt); } } rc = mnt_context_get_mtab(cxt, &mtab); if (cn_tgt) { mnt_context_set_tabfilter(cxt, NULL, NULL); if (!cache) free(cn_tgt); } if (rc) { DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab")); return rc; } try_loopdev: fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD); if (!fs && mnt_context_is_swapmatch(cxt)) { /* * Maybe the option is source rather than target (sometimes * people use e.g. "umount /dev/sda1") */ fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD); if (fs) { struct libmnt_fs *fs1 = mnt_table_find_target(mtab, mnt_fs_get_target(fs), MNT_ITER_BACKWARD); if (!fs1) { DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!")); return -EINVAL; } if (fs != fs1) { /* Something was stacked over `file' on the * same mount point. */ DBG(CXT, mnt_debug_h(cxt, "umount: %s: %s is mounted " "over it on the same point", tgt, mnt_fs_get_source(fs1))); return -EINVAL; } } } if (!fs && !loopdev && mnt_context_is_swapmatch(cxt)) { /* * Maybe the option is /path/file.img, try to convert to /dev/loopN */ struct stat st; if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) { char *dev = NULL; int count = loopdev_count_by_backing_file(tgt, &dev); if (count == 1) { DBG(CXT, mnt_debug_h(cxt, "umount: %s --> %s (retry)", tgt, dev)); mnt_fs_set_source(cxt->fs, tgt); mnt_fs_set_target(cxt->fs, dev); free(dev); tgt = mnt_fs_get_target(cxt->fs); loopdev = 1; /* to avoid endless loop */ goto try_loopdev; } else if (count > 1) DBG(CXT, mnt_debug_h(cxt, "umount: warning: %s is associated " "with more than one loopdev", tgt)); } } if (!fs) { DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt)); return 0; } if (fs != cxt->fs) { /* copy from mtab to our FS description */ mnt_fs_set_source(cxt->fs, NULL); mnt_fs_set_target(cxt->fs, NULL); if (!mnt_copy_fs(cxt->fs, fs)) { DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS")); return -errno; } DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied")); } cxt->flags |= MNT_FL_TAB_APPLIED; return rc; }
static int do_umount(struct libmnt_context *cxt) { int rc = 0, flags = 0; const char *src, *target; char *tgtbuf = NULL; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); assert(cxt->syscall_status == 1); if (cxt->helper) return exec_helper(cxt); src = mnt_fs_get_srcpath(cxt->fs); target = mnt_fs_get_target(cxt->fs); if (!target) return -EINVAL; DBG(CXT, mnt_debug_h(cxt, "do umount")); if (cxt->restricted && !mnt_context_is_fake(cxt)) { /* * extra paranoia for non-root users * -- chdir to the parent of the mountpoint and use NOFOLLOW * flag to avoid races and symlink attacks. */ if (umount_nofollow_support()) flags |= UMOUNT_NOFOLLOW; rc = mnt_chdir_to_parent(target, &tgtbuf); if (rc) return rc; target = tgtbuf; } if (mnt_context_is_lazy(cxt)) flags |= MNT_DETACH; else if (mnt_context_is_force(cxt)) flags |= MNT_FORCE; DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]%s", target, flags, mnt_context_is_fake(cxt) ? " (FAKE)" : "")); if (mnt_context_is_fake(cxt)) rc = 0; else { rc = flags ? umount2(target, flags) : umount(target); if (rc < 0) cxt->syscall_status = -errno; free(tgtbuf); } /* * try remount read-only */ if (rc < 0 && cxt->syscall_status == -EBUSY && mnt_context_is_rdonly_umount(cxt) && src) { mnt_context_set_mflags(cxt, (cxt->mountflags | MS_REMOUNT | MS_RDONLY)); mnt_context_enable_loopdel(cxt, FALSE); DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d] -- trying to remount read-only", -cxt->syscall_status)); rc = mount(src, mnt_fs_get_target(cxt->fs), NULL, MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); if (rc < 0) { cxt->syscall_status = -errno; DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) failed [errno=%d]", -cxt->syscall_status)); return -cxt->syscall_status; } cxt->syscall_status = 0; DBG(CXT, mnt_debug_h(cxt, "read-only re-mount(2) success")); return 0; } if (rc < 0) { DBG(CXT, mnt_debug_h(cxt, "umount(2) failed [errno=%d]", -cxt->syscall_status)); return -cxt->syscall_status; } cxt->syscall_status = 0; DBG(CXT, mnt_debug_h(cxt, "umount(2) success")); return 0; }
static int exec_helper(struct libmnt_context *cxt) { int rc; assert(cxt); assert(cxt->fs); assert(cxt->helper); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); assert(cxt->helper_exec_status == 1); DBG_FLUSH; switch (fork()) { case 0: { const char *args[10], *type; int i = 0; if (setgid(getgid()) < 0) exit(EXIT_FAILURE); if (setuid(getuid()) < 0) exit(EXIT_FAILURE); type = mnt_fs_get_fstype(cxt->fs); args[i++] = cxt->helper; /* 1 */ args[i++] = mnt_fs_get_target(cxt->fs); /* 2 */ if (mnt_context_is_nomtab(cxt)) args[i++] = "-n"; /* 3 */ if (mnt_context_is_lazy(cxt)) args[i++] = "-l"; /* 4 */ if (mnt_context_is_force(cxt)) args[i++] = "-f"; /* 5 */ if (mnt_context_is_verbose(cxt)) args[i++] = "-v"; /* 6 */ if (mnt_context_is_rdonly_umount(cxt)) args[i++] = "-r"; /* 7 */ if (type && !endswith(cxt->helper, type)) { args[i++] = "-t"; /* 8 */ args[i++] = (char *) type; /* 9 */ } args[i] = NULL; /* 10 */ #ifdef CONFIG_LIBMOUNT_DEBUG for (i = 0; args[i]; i++) DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", i, args[i])); #endif DBG_FLUSH; execv(cxt->helper, (char * const *) args); exit(EXIT_FAILURE); } default: { int st; wait(&st); cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", cxt->helper, cxt->helper_status)); cxt->helper_exec_status = rc = 0; break; } case -1: cxt->helper_exec_status = rc = -errno; DBG(CXT, mnt_debug_h(cxt, "fork() failed")); break; } return rc; }
/* * Note that cxt->fs contains relevant mtab entry! */ static int evaluate_permissions(struct libmnt_context *cxt) { struct libmnt_table *fstab; unsigned long u_flags = 0; const char *tgt, *src, *optstr; int rc, ok = 0; struct libmnt_fs *fs; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt || !cxt->fs) return -EINVAL; if (!mnt_context_is_restricted(cxt)) return 0; /* superuser mount */ DBG(CXT, mnt_debug_h(cxt, "umount: evaluating permissions")); if (!(cxt->flags & MNT_FL_TAB_APPLIED)) { DBG(CXT, mnt_debug_h(cxt, "cannot find %s in mtab and you are not root", mnt_fs_get_target(cxt->fs))); goto eperm; } if (cxt->user_mountflags & MNT_MS_UHELPER) { /* on uhelper= mount option based helper */ rc = prepare_helper_from_options(cxt, "uhelper"); if (rc) return rc; if (cxt->helper) return 0; /* we'll call /sbin/umount.<uhelper> */ } /* * User mounts has to be in /etc/fstab */ rc = mnt_context_get_fstab(cxt, &fstab); if (rc) return rc; tgt = mnt_fs_get_target(cxt->fs); src = mnt_fs_get_source(cxt->fs); if (mnt_fs_get_bindsrc(cxt->fs)) { src = mnt_fs_get_bindsrc(cxt->fs); DBG(CXT, mnt_debug_h(cxt, "umount: using bind source: %s", src)); } /* If fstab contains the two lines * /dev/sda1 /mnt/zip auto user,noauto 0 0 * /dev/sda4 /mnt/zip auto user,noauto 0 0 * then "mount /dev/sda4" followed by "umount /mnt/zip" used to fail. * So, we must not look for file, but for the pair (dev,file) in fstab. */ fs = mnt_table_find_pair(fstab, src, tgt, MNT_ITER_FORWARD); if (!fs) { /* * It's possible that there is /path/file.img in fstab and * /dev/loop0 in mtab -- then we have to check releation * between loopdev and the file. */ fs = mnt_table_find_target(fstab, tgt, MNT_ITER_FORWARD); if (fs) { const char *dev = mnt_fs_get_srcpath(cxt->fs); /* devname from mtab */ if (!dev || !is_associated_fs(dev, fs)) fs = NULL; } if (!fs) { DBG(CXT, mnt_debug_h(cxt, "umount %s: mtab disagrees with fstab", tgt)); goto eperm; } } /* * User mounting and unmounting is allowed only if fstab contains one * of the options `user', `users' or `owner' or `group'. * * The option `users' allows arbitrary users to mount and unmount - * this may be a security risk. * * The options `user', `owner' and `group' only allow unmounting by the * user that mounted (visible in mtab). */ optstr = mnt_fs_get_user_options(fs); /* FSTAB mount options! */ if (!optstr) goto eperm; if (mnt_optstr_get_flags(optstr, &u_flags, mnt_get_builtin_optmap(MNT_USERSPACE_MAP))) goto eperm; if (u_flags & MNT_MS_USERS) { DBG(CXT, mnt_debug_h(cxt, "umount: promiscuous setting ('users') in fstab")); return 0; } /* * Check user=<username> setting from mtab if there is user, owner or * group option in /etc/fstab */ if (u_flags & (MNT_MS_USER | MNT_MS_OWNER | MNT_MS_GROUP)) { char *curr_user = NULL; char *mtab_user = NULL; size_t sz; DBG(CXT, mnt_debug_h(cxt, "umount: checking user=<username> from mtab")); curr_user = mnt_get_username(getuid()); if (!curr_user) { DBG(CXT, mnt_debug_h(cxt, "umount %s: cannot " "convert %d to username", tgt, getuid())); goto eperm; } /* get options from mtab */ optstr = mnt_fs_get_user_options(cxt->fs); if (optstr && !mnt_optstr_get_option(optstr, "user", &mtab_user, &sz) && sz) ok = !strncmp(curr_user, mtab_user, sz); } if (ok) { DBG(CXT, mnt_debug_h(cxt, "umount %s is allowed", tgt)); return 0; } eperm: DBG(CXT, mnt_debug_h(cxt, "umount is not allowed for you")); return -EPERM; }
static int lookup_umount_fs(struct libmnt_context *cxt) { int rc, loopdev = 0; const char *tgt; struct libmnt_table *mtab = NULL; struct libmnt_fs *fs; assert(cxt); assert(cxt->fs); DBG(CXT, mnt_debug_h(cxt, "umount: lookup FS")); tgt = mnt_fs_get_target(cxt->fs); if (!tgt) { DBG(CXT, mnt_debug_h(cxt, "umount: undefined target")); return -EINVAL; } rc = mnt_context_get_mtab(cxt, &mtab); if (rc) { DBG(CXT, mnt_debug_h(cxt, "umount: failed to read mtab")); return rc; } try_loopdev: fs = mnt_table_find_target(mtab, tgt, MNT_ITER_BACKWARD); if (!fs) { /* maybe the option is source rather than target (mountpoint) */ fs = mnt_table_find_source(mtab, tgt, MNT_ITER_BACKWARD); if (fs) { struct libmnt_fs *fs1 = mnt_table_find_target(mtab, mnt_fs_get_target(fs), MNT_ITER_BACKWARD); if (!fs1) { DBG(CXT, mnt_debug_h(cxt, "mtab is broken?!?!")); return -EINVAL; } if (fs != fs1) { /* Something was stacked over `file' on the * same mount point. */ DBG(CXT, mnt_debug_h(cxt, "umount: %s: %s is mounted " "over it on the same point", tgt, mnt_fs_get_source(fs1))); return -EINVAL; } } } if (!fs && !loopdev) { /* * Maybe target is /path/file.img, try to convert to /dev/loopN */ struct stat st; if (stat(tgt, &st) == 0 && S_ISREG(st.st_mode)) { char *dev = NULL; int count = loopdev_count_by_backing_file(tgt, &dev); if (count == 1) { DBG(CXT, mnt_debug_h(cxt, "umount: %s --> %s (retry)", tgt, dev)); mnt_fs_set_source(cxt->fs, tgt); mnt_fs_set_target(cxt->fs, dev); free(dev); tgt = mnt_fs_get_target(cxt->fs); loopdev = 1; /* to avoid endless loop */ goto try_loopdev; } else if (count > 1) DBG(CXT, mnt_debug_h(cxt, "umount: warning: %s is associated " "with more than one loodev", tgt)); } } if (!fs) { DBG(CXT, mnt_debug_h(cxt, "umount: cannot find %s in mtab", tgt)); return 0; } if (fs != cxt->fs) { /* copy from mtab to our FS description */ mnt_fs_set_source(cxt->fs, NULL); mnt_fs_set_target(cxt->fs, NULL); if (!mnt_copy_fs(cxt->fs, fs)) { DBG(CXT, mnt_debug_h(cxt, "umount: failed to copy FS")); return -errno; } DBG(CXT, mnt_debug_h(cxt, "umount: mtab applied")); } cxt->flags |= MNT_FL_TAB_APPLIED; return rc; }
static int exec_helper(struct libmnt_context *cxt) { char *o = NULL; int rc; assert(cxt); assert(cxt->fs); assert(cxt->helper); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper)); rc = generate_helper_optstr(cxt, &o); if (rc) return -EINVAL; DBG_FLUSH; switch (fork()) { case 0: { const char *args[12], *type; int i = 0; if (setgid(getgid()) < 0) exit(EXIT_FAILURE); if (setuid(getuid()) < 0) exit(EXIT_FAILURE); type = mnt_fs_get_fstype(cxt->fs); args[i++] = cxt->helper; /* 1 */ args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */ args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */ /* * TODO: remove the exception for "nfs", -s is documented * for years should be usable everywhere. */ if (mnt_context_is_sloppy(cxt) && type && startswith(type, "nfs")) args[i++] = "-s"; /* 4 */ if (mnt_context_is_fake(cxt)) args[i++] = "-f"; /* 5 */ if (mnt_context_is_nomtab(cxt)) args[i++] = "-n"; /* 6 */ if (mnt_context_is_verbose(cxt)) args[i++] = "-v"; /* 7 */ if (o) { args[i++] = "-o"; /* 8 */ args[i++] = o; /* 9 */ } if (type && !endswith(cxt->helper, type)) { args[i++] = "-t"; /* 10 */ args[i++] = type; /* 11 */ } args[i] = NULL; /* 12 */ #ifdef CONFIG_LIBMOUNT_DEBUG for (i = 0; args[i]; i++) DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"", i, args[i])); #endif DBG_FLUSH; execv(cxt->helper, (char * const *) args); exit(EXIT_FAILURE); } default: { int st; wait(&st); cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1; DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]", cxt->helper, cxt->helper_status)); cxt->helper_exec_status = rc = 0; break; } case -1: cxt->helper_exec_status = rc = -errno; DBG(CXT, mnt_debug_h(cxt, "fork() failed")); break; } return rc; }
/* this is umount replacement to mnt_context_apply_fstab(), use * mnt_context_tab_applied() to check result. */ static int lookup_umount_fs(struct libmnt_context *cxt) { const char *tgt; struct stat st; struct libmnt_fs *fs = NULL; int rc = 0; assert(cxt); assert(cxt->fs); tgt = mnt_fs_get_target(cxt->fs); if (!tgt) { DBG(CXT, ul_debugobj(cxt, "umount: undefined target")); return -EINVAL; } /* * Let's try to avoid mountinfo usage at all to minimize performance * degradation. Don't forget that kernel has to compose *whole* * mountinfo about all mountpoints although we look for only one entry. * * All we need is fstype and to check if there is no userspace mount * options for the target (e.g. helper=udisks to call /sbin/umount.udisks). * * So, let's use statfs() if possible (it's bad idea for --lazy/--force * umounts as target is probably unreachable NFS, also for --detach-loop * as this additionally needs to know the name of the loop device). */ if (!mnt_context_is_restricted(cxt) && *tgt == '/' && !(cxt->flags & MNT_FL_HELPER) && !mnt_context_mtab_writable(cxt) && !mnt_context_is_force(cxt) && !mnt_context_is_lazy(cxt) && !mnt_context_is_loopdel(cxt) && mnt_stat_mountpoint(tgt, &st) == 0 && S_ISDIR(st.st_mode) && !has_utab_entry(cxt, tgt)) { const char *type = mnt_fs_get_fstype(cxt->fs); /* !mnt_context_mtab_writable(cxt) && has_utab_entry() verified that there * is no stuff in utab, so disable all mtab/utab related actions */ mnt_context_disable_mtab(cxt, TRUE); if (!type) { struct statfs vfs; if (statfs(tgt, &vfs) == 0) type = mnt_statfs_get_fstype(&vfs); if (type) { rc = mnt_fs_set_fstype(cxt->fs, type); if (rc) return rc; } } if (type) { DBG(CXT, ul_debugobj(cxt, "umount: mountinfo unnecessary [type=%s]", type)); return 0; } } rc = mnt_context_find_umount_fs(cxt, tgt, &fs); if (rc < 0) return rc; if (rc == 1 || !fs) { DBG(CXT, ul_debugobj(cxt, "umount: cannot find '%s' in mtab", tgt)); return 0; /* this is correct! */ } if (fs != cxt->fs) { /* copy from mtab to our FS description */ mnt_fs_set_source(cxt->fs, NULL); mnt_fs_set_target(cxt->fs, NULL); if (!mnt_copy_fs(cxt->fs, fs)) { DBG(CXT, ul_debugobj(cxt, "umount: failed to copy FS")); return -errno; } DBG(CXT, ul_debugobj(cxt, "umount: mtab applied")); } cxt->flags |= MNT_FL_TAB_APPLIED; return rc; }
/** * mnt_context_next_mount: * @cxt: context * @itr: iterator * @fs: returns the current filesystem * @mntrc: returns the return code from mnt_context_mount() * @ignored: returns 1 for not matching and 2 for already mounted filesystems * * This function tries to mount the next filesystem from fstab (as returned by * mnt_context_get_fstab()). See also mnt_context_set_fstab(). * * You can filter out filesystems by: * mnt_context_set_options_pattern() to simulate mount -a -O pattern * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern * * If the filesystem is already mounted or does not match defined criteria, * then the mnt_context_next_mount() function returns zero, but the @ignored is * non-zero. Note that the root filesystem and filesystems with "noauto" option * are always ignored. * * If mount(2) syscall or mount.type helper failed, then the * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero. * Use also mnt_context_get_status() to check if the filesystem was * successfully mounted. * * Returns: 0 on success, * <0 in case of error (!= mount(2) errors) * 1 at the end of the list. */ int mnt_context_next_mount(struct libmnt_context *cxt, struct libmnt_iter *itr, struct libmnt_fs **fs, int *mntrc, int *ignored) { struct libmnt_table *fstab, *mtab; const char *o, *tgt; int rc, mounted = 0; if (ignored) *ignored = 0; if (mntrc) *mntrc = 0; if (!cxt || !fs || !itr) return -EINVAL; mtab = cxt->mtab; cxt->mtab = NULL; /* do not reset mtab */ mnt_reset_context(cxt); cxt->mtab = mtab; rc = mnt_context_get_fstab(cxt, &fstab); if (rc) return rc; rc = mnt_table_next_fs(fstab, itr, fs); if (rc != 0) return rc; /* more filesystems (or error) */ o = mnt_fs_get_user_options(*fs); tgt = mnt_fs_get_target(*fs); DBG(CXT, mnt_debug_h(cxt, "next-mount: trying %s", tgt)); /* ignore swap */ if (mnt_fs_is_swaparea(*fs) || /* ignore root filesystem */ (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) || /* ignore noauto filesystems */ (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) || /* ignore filesystems not match with options patterns */ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs, cxt->fstype_pattern)) || /* ignore filesystems not match with type patterns */ (cxt->optstr_pattern && !mnt_fs_match_options(*fs, cxt->optstr_pattern))) { if (ignored) *ignored = 1; DBG(CXT, mnt_debug_h(cxt, "next-mount: not-match " "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]", mnt_fs_get_fstype(*fs), cxt->fstype_pattern, mnt_fs_get_options(*fs), cxt->optstr_pattern)); return 0; } /* ignore already mounted filesystems */ rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted); if (rc) return rc; if (mounted) { if (ignored) *ignored = 2; return 0; } if (mnt_context_is_fork(cxt)) { rc = mnt_fork_context(cxt); if (rc) return rc; /* fork error */ if (mnt_context_is_parent(cxt)) { return 0; /* parent */ } } /* child or non-forked */ rc = mnt_context_set_fs(cxt, *fs); if (!rc) { rc = mnt_context_mount(cxt); if (mntrc) *mntrc = rc; } if (mnt_context_is_child(cxt)) { DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc)); DBG_FLUSH; exit(rc); } return 0; }
/* * The default is to use fstype from cxt->fs, this could be overwritten by * @try_type argument. * * Returns: 0 on success, * >0 in case of mount(2) error (returns syscall errno), * <0 in case of other errors. */ static int do_mount(struct libmnt_context *cxt, const char *try_type) { int rc = 0; const char *src, *target, *type; unsigned long flags; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (try_type && !cxt->helper) { rc = mnt_context_prepare_helper(cxt, "mount", try_type); if (rc) return rc; } if (cxt->helper) return exec_helper(cxt); flags = cxt->mountflags; src = mnt_fs_get_srcpath(cxt->fs); target = mnt_fs_get_target(cxt->fs); if (!target) return -EINVAL; if (!src) { /* unnecessary, should be already resolved in * mnt_context_prepare_srcpath(), but for sure... */ DBG(CXT, mnt_debug_h(cxt, "WARNING: source is NULL -- using \"none\"!")); src = "none"; } type = try_type ? : mnt_fs_get_fstype(cxt->fs); if (!(flags & MS_MGC_MSK)) flags |= MS_MGC_VAL; DBG(CXT, mnt_debug_h(cxt, "%smount(2) " "[source=%s, target=%s, type=%s, " " mountflags=0x%08lx, mountdata=%s]", mnt_context_is_fake(cxt) ? "(FAKE) " : "", src, target, type, flags, cxt->mountdata ? "yes" : "<none>")); if (mnt_context_is_fake(cxt)) cxt->syscall_status = 0; else { if (mount(src, target, type, flags, cxt->mountdata)) { cxt->syscall_status = -errno; DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]", -cxt->syscall_status)); return -cxt->syscall_status; } DBG(CXT, mnt_debug_h(cxt, "mount(2) success")); cxt->syscall_status = 0; } if (try_type && cxt->update) { struct libmnt_fs *fs = mnt_update_get_fs(cxt->update); if (fs) rc = mnt_fs_set_fstype(fs, try_type); } return rc; }