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; }
static int test_remount(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; if (argc < 3) return -1; mnt_fs_set_target(fs, argv[1]); mnt_fs_set_options(fs, argv[2]); rc = update(NULL, fs, MS_REMOUNT); mnt_free_fs(fs); return rc; }
static int test_move(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; if (argc < 3) return -1; mnt_fs_set_source(fs, argv[1]); mnt_fs_set_target(fs, argv[2]); rc = update(NULL, fs, MS_MOVE); mnt_free_fs(fs); return rc; }
static int test_add(struct libmnt_test *ts, int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; if (argc < 5 || !fs) return -1; mnt_fs_set_source(fs, argv[1]); mnt_fs_set_target(fs, argv[2]); mnt_fs_set_fstype(fs, argv[3]); mnt_fs_set_options(fs, argv[4]); rc = update(NULL, fs, 0); mnt_free_fs(fs); 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; }
/* 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; }
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; }