static int is_mounted(struct libmnt_fs *fs) { int rc; const char *src; src = mnt_fs_get_source(fs); if (!src) return 0; if (!mntcache) mntcache = mnt_new_cache(); if (!mtab) { mtab = mnt_new_table(); if (!mtab) err(FSCK_EX_ERROR, ("failed to initialize libmount table")); mnt_table_set_cache(mtab, mntcache); mnt_table_parse_mtab(mtab, NULL); } rc = mnt_table_find_source(mtab, src, MNT_ITER_BACKWARD) ? 1 : 0; if (verbose) { if (rc) printf(_("%s is mounted\n"), src); else printf(_("%s is not mounted\n"), src); } 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; }
/* Check, if there already exists a mounted loop device on the mountpoint node * with the same parameters. */ static int is_mounted_same_loopfile(struct libmnt_context *cxt, const char *target, const char *backing_file, uint64_t offset) { struct libmnt_table *tb; struct libmnt_iter itr; struct libmnt_fs *fs; struct libmnt_cache *cache; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!target || !backing_file || mnt_context_get_mtab(cxt, &tb)) return 0; DBG(CXT, mnt_debug_h(cxt, "checking if %s mounted on %s", backing_file, target)); cache = mnt_context_get_cache(cxt); mnt_reset_iter(&itr, MNT_ITER_BACKWARD); /* Search for mountpoint node in mtab, procceed if any of these has the * loop option set or the device is a loop device */ while (mnt_table_next_fs(tb, &itr, &fs) == 0) { const char *src = mnt_fs_get_source(fs); const char *opts = mnt_fs_get_user_options(fs); char *val; size_t len; int res = 0; if (!src || !mnt_fs_match_target(fs, target, cache)) continue; if (strncmp(src, "/dev/loop", 9) == 0) { res = loopdev_is_used((char *) src, backing_file, offset, LOOPDEV_FL_OFFSET); } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { val = strndup(val, len); res = loopdev_is_used((char *) val, backing_file, offset, LOOPDEV_FL_OFFSET); free(val); } if (res) { DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); return 1; } } return 0; }
/** * 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 swapoff_all(void) { int status = 0; struct libmnt_table *tb; struct libmnt_fs *fs; struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD); if (!itr) err(EXIT_FAILURE, _("failed to initialize libmount iterator")); /* * In case /proc/swaps exists, unswap stuff listed there. We are quiet * but report errors in status. Errors might mean that /proc/swaps * exists as ordinary file, not in procfs. do_swapoff() exits * immediately on EPERM. */ tb = get_swaps(); while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) status |= do_swapoff(mnt_fs_get_source(fs), QUIET, CANONIC); /* * Unswap stuff mentioned in /etc/fstab. Probably it was unmounted * already, so errors are not bad. Doing swapoff -a twice should not * give error messages. */ tb = get_fstab(); mnt_reset_iter(itr, MNT_ITER_FORWARD); while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) { if (!is_active_swap(mnt_fs_get_source(fs))) do_swapoff(mnt_fs_get_source(fs), QUIET, !CANONIC); } mnt_free_iter(itr); return status; }
/** * 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; }
/* * This function works like mnt_resolve_tag(), but it's able to read UUiD/LABEL * from regular swap files too (according to entries in /proc/swaps). Note that * mnt_resolve_tag() and mnt_resolve_spec() works with system visible block * devices only. */ static char *swapoff_resolve_tag(const char *name, const char *value, struct libmnt_cache *cache) { char *path; struct libmnt_table *tb; struct libmnt_iter *itr; struct libmnt_fs *fs; /* this is usual case for block devices (and it's really fast as it uses * udev /dev/disk/by-* symlinks by default */ path = mnt_resolve_tag(name, value, cache); if (path) return path; /* try regular files from /proc/swaps */ tb = get_swaps(); if (!tb) return NULL; itr = mnt_new_iter(MNT_ITER_BACKWARD); if (!itr) err(EXIT_FAILURE, _("failed to initialize libmount iterator")); while (tb && mnt_table_next_fs(tb, itr, &fs) == 0) { blkid_probe pr = NULL; const char *src = mnt_fs_get_source(fs); const char *type = mnt_fs_get_swaptype(fs); const char *data = NULL; if (!src || !type || strcmp(type, "file") != 0) continue; pr = get_swap_prober(src); if (!pr) continue; blkid_probe_lookup_value(pr, name, &data, NULL); if (data && strcmp(data, value) == 0) path = xstrdup(src); blkid_free_probe(pr); if (path) break; } mnt_free_iter(itr); return path; }
int main (int argc, char *argv[]) { struct libmnt_fs *fs; struct libmnt_table *tab; mnt_init_debug(0xffff); tab = mnt_new_table_from_file("/proc/self/mounts"); fs = mnt_table_find_target(tab, argv[2], MNT_ITER_BACKWARD); char *source = mnt_fs_get_source(fs); if (strcmp(source, argv[1]) == 0) printf("is mounted\n"); else printf("could not find fs in tab\n"); mnt_free_table(tab); #if 0 int rc; struct libmnt_fs *fs; struct libmnt_context *cxt = mnt_new_context(); mnt_init_debug(0xffff); mnt_context_set_source(cxt, argv[1]); mnt_context_set_target(cxt, argv[2]); fs = mnt_context_get_fs(cxt); if (fs != NULL) { if (mnt_context_is_fs_mounted(cxt, fs, &rc)) rc = 1; else printf("can't find fs in mtab\n"); } else printf("Can't get fs from mnt context\n"); mnt_free_context(cxt); #endif return 0; }
/* * fs from fstab might contains real device name as well as symlink, * LABEL or UUID, this function returns canonicalized result. */ static const char *fs_get_device(struct libmnt_fs *fs) { struct fsck_fs_data *data = mnt_fs_get_userdata(fs); if (!data || !data->eval_device) { const char *spec = mnt_fs_get_source(fs); if (!data) data = fs_create_data(fs); data->eval_device = 1; data->device = mnt_resolve_spec(spec, mnt_table_get_cache(fstab)); if (!data->device) data->device = xstrdup(spec); } return data->device; }
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; }
/* * 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; }
/* 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; }
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; }
/* * 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; }
/** * 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; 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; }