/** * mnt_context_do_mount * @cxt: context * * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount(). * * Note that this function could be called only once. If you want to mount * another source or target than you have to call mnt_reset_context(). * * If you want to call mount(2) for the same source and target with a diffrent * mount flags or fstype then call mnt_context_reset_status() and then try * again mnt_context_do_mount(). * * WARNING: non-zero return code does not mean that mount(2) syscall or * mount.type helper wasn't successfully called. * * Check mnt_context_get_status() after error! * * Returns: 0 on success; * >0 in case of mount(2) error (returns syscall errno), * <0 in case of other errors. */ int mnt_context_do_mount(struct libmnt_context *cxt) { const char *type; int res; assert(cxt); assert(cxt->fs); assert(cxt->helper_exec_status == 1); assert(cxt->syscall_status == 1); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); assert((cxt->flags & MNT_FL_PREPARED)); assert((cxt->action == MNT_ACT_MOUNT)); DBG(CXT, mnt_debug_h(cxt, "mount: do mount")); if (!(cxt->flags & MNT_FL_MOUNTDATA)) cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs); type = mnt_fs_get_fstype(cxt->fs); if (type) res = do_mount(cxt, NULL); else res = do_mount_by_pattern(cxt, cxt->fstype_pattern); if (mnt_context_get_status(cxt) && !mnt_context_is_fake(cxt) && !cxt->helper) { /* * Mounted by mount(2), do some post-mount checks * * Kernel allows to use MS_RDONLY for bind mounts, but the * read-only request could be silently ignored. Check it to * avoid 'ro' in mtab and 'rw' in /proc/mounts. */ if ((cxt->mountflags & MS_BIND) && (cxt->mountflags & MS_RDONLY) && !mnt_is_readonly(mnt_context_get_target(cxt))) mnt_context_set_mflags(cxt, cxt->mountflags & ~MS_RDONLY); /* Kernel can silently add MS_RDONLY flag when mounting file * system that does not have write support. Check this to avoid * 'ro' in /proc/mounts and 'rw' in mtab. */ if (!(cxt->mountflags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE)) && mnt_is_readonly(mnt_context_get_target(cxt))) mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY); } return res; }
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; }
int mnt_context_setup_loopdev(struct libmnt_context *cxt) { const char *backing_file, *optstr, *loopdev = NULL; char *val = NULL, *loopval = NULL; size_t len; struct loopdev_cxt lc; int rc = 0, lo_flags = 0; uint64_t offset = 0, sizelimit = 0; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); backing_file = mnt_fs_get_srcpath(cxt->fs); if (!backing_file) return -EINVAL; DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file)); if (cxt->mountflags & MS_RDONLY) { DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag")); lo_flags |= LO_FLAGS_READ_ONLY; } optstr = mnt_fs_get_user_options(cxt->fs); /* * loop= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) && mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) { loopval = strndup(val, len); rc = loopval ? 0 : -ENOMEM; } /* * offset= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) && mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) { rc = mnt_parse_offset(val, len, &offset); if (rc) { DBG(LOOP, ul_debugobj(cxt, "failed to parse offset=")); rc = -MNT_ERR_MOUNTOPT; } } /* * sizelimit= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) && mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) { rc = mnt_parse_offset(val, len, &sizelimit); if (rc) { DBG(LOOP, ul_debugobj(cxt, "failed to parse sizelimit=")); rc = -MNT_ERR_MOUNTOPT; } } /* * encryption= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) && mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) { DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported")); rc = -MNT_ERR_MOUNTOPT; } if (rc == 0 && is_mounted_same_loopfile(cxt, mnt_context_get_target(cxt), backing_file, offset)) rc = -EBUSY; if (rc) goto done_no_deinit; /* It is possible to mount the same file more times. If we set more * than one loop device referring to the same file, kernel has no * mechanism to detect it. To prevent data corruption, the same loop * device has to be recycled. */ if (backing_file) { rc = loopcxt_init(&lc, 0); if (rc) goto done_no_deinit; rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit); switch (rc) { case 0: /* not found */ DBG(LOOP, ul_debugobj(cxt, "not found overlaping loopdev")); loopcxt_deinit(&lc); break; case 1: /* overlap */ DBG(LOOP, ul_debugobj(cxt, "overlaping %s detected", loopcxt_get_device(&lc))); rc = -MNT_ERR_LOOPOVERLAP; goto done; case 2: /* overlap -- full size and offset match (reuse) */ { uint32_t lc_encrypt_type; DBG(LOOP, ul_debugobj(cxt, "re-using existing loop device %s", loopcxt_get_device(&lc))); /* Once a loop is initialized RO, there is no * way to change its parameters. */ if (loopcxt_is_readonly(&lc) && !(lo_flags & LO_FLAGS_READ_ONLY)) { DBG(LOOP, ul_debugobj(cxt, "%s is read-only", loopcxt_get_device(&lc))); rc = -EROFS; goto done; } /* This is no more supported, but check to be safe. */ if (loopcxt_get_encrypt_type(&lc, &lc_encrypt_type) == 0 && lc_encrypt_type != LO_CRYPT_NONE) { DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported for device %s", loopcxt_get_device(&lc))); rc = -MNT_ERR_LOOPOVERLAP; goto done; } rc = 0; goto success; } default: /* error */ goto done; } } DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device")); rc = loopcxt_init(&lc, 0); if (rc) goto done_no_deinit; if (loopval) { rc = loopcxt_set_device(&lc, loopval); if (rc == 0) loopdev = loopcxt_get_device(&lc); } if (rc) goto done; /* since 2.6.37 we don't have to store backing filename to mtab * because kernel provides the name in /sys. */ if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || !mnt_context_mtab_writable(cxt)) { DBG(LOOP, ul_debugobj(cxt, "enabling AUTOCLEAR flag")); lo_flags |= LO_FLAGS_AUTOCLEAR; } do { /* found free device */ if (!loopdev) { rc = loopcxt_find_unused(&lc); if (rc) goto done; DBG(LOOP, ul_debugobj(cxt, "trying to use %s", loopcxt_get_device(&lc))); } /* set device attributes * -- note that loopcxt_find_unused() resets "lc" */ rc = loopcxt_set_backing_file(&lc, backing_file); if (!rc && offset) rc = loopcxt_set_offset(&lc, offset); if (!rc && sizelimit) rc = loopcxt_set_sizelimit(&lc, sizelimit); if (!rc) loopcxt_set_flags(&lc, lo_flags); if (rc) { DBG(LOOP, ul_debugobj(cxt, "failed to set loop attributes")); goto done; } /* setup the device */ rc = loopcxt_setup_device(&lc); if (!rc) break; /* success */ if (loopdev || rc != -EBUSY) { DBG(LOOP, ul_debugobj(cxt, "failed to setup device")); rc = -MNT_ERR_LOOPDEV; goto done; } DBG(LOOP, ul_debugobj(cxt, "device stolen...trying again")); } while (1); success: if (!rc) rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); if (!rc) { /* success */ cxt->flags |= MNT_FL_LOOPDEV_READY; if ((cxt->user_mountflags & MNT_MS_LOOP) && loopcxt_is_autoclear(&lc)) { /* * autoclear flag accepted by the kernel, don't store * the "loop=" option to mtab. */ DBG(LOOP, ul_debugobj(cxt, "removing unnecessary loop= from mtab")); cxt->user_mountflags &= ~MNT_MS_LOOP; mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop"); } if (!(cxt->mountflags & MS_RDONLY) && loopcxt_is_readonly(&lc)) /* * mount planned read-write, but loopdev is read-only, * let's fix mount options... */ mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY); /* we have to keep the device open until mount(1), * otherwise it will be auto-cleared by kernel */ cxt->loopdev_fd = loopcxt_get_fd(&lc); if (cxt->loopdev_fd < 0) { DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD")); rc = -errno; } else loopcxt_set_fd(&lc, -1, 0); } done: loopcxt_deinit(&lc); done_no_deinit: free(loopval); return rc; }
int mnt_context_setup_loopdev(struct libmnt_context *cxt) { const char *backing_file, *optstr, *loopdev = NULL; char *val = NULL; size_t len; struct loopdev_cxt lc; int rc = 0, lo_flags = 0; uint64_t offset = 0, sizelimit = 0; assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt) return -EINVAL; backing_file = mnt_fs_get_srcpath(cxt->fs); if (!backing_file) return -EINVAL; DBG(CXT, ul_debugobj(cxt, "trying to setup loopdev for %s", backing_file)); if (cxt->mountflags & MS_RDONLY) { DBG(CXT, ul_debugobj(cxt, "enabling READ-ONLY flag")); lo_flags |= LO_FLAGS_READ_ONLY; } rc = loopcxt_init(&lc, 0); if (rc) return rc; ON_DBG(CXT, loopcxt_enable_debug(&lc, 1)); optstr = mnt_fs_get_user_options(cxt->fs); /* * loop= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) && mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) { val = strndup(val, len); rc = val ? loopcxt_set_device(&lc, val) : -ENOMEM; free(val); if (rc == 0) loopdev = loopcxt_get_device(&lc); } /* * offset= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) && mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) { rc = mnt_parse_offset(val, len, &offset); if (rc) { DBG(CXT, ul_debugobj(cxt, "failed to parse offset=")); rc = -MNT_ERR_MOUNTOPT; } } /* * sizelimit= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) && mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) { rc = mnt_parse_offset(val, len, &sizelimit); if (rc) { DBG(CXT, ul_debugobj(cxt, "failed to parse sizelimit=")); rc = -MNT_ERR_MOUNTOPT; } } /* * encryption= */ if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) && mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) { DBG(CXT, ul_debugobj(cxt, "encryption no longer supported")); rc = -MNT_ERR_MOUNTOPT; } if (rc == 0 && is_mounted_same_loopfile(cxt, mnt_context_get_target(cxt), backing_file, offset)) rc = -EBUSY; if (rc) goto done; /* since 2.6.37 we don't have to store backing filename to mtab * because kernel provides the name in /sys. */ if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || !mnt_context_mtab_writable(cxt)) { DBG(CXT, ul_debugobj(cxt, "enabling AUTOCLEAR flag")); lo_flags |= LO_FLAGS_AUTOCLEAR; } do { /* found free device */ if (!loopdev) { rc = loopcxt_find_unused(&lc); if (rc) goto done; DBG(CXT, ul_debugobj(cxt, "trying to use %s", loopcxt_get_device(&lc))); } /* set device attributes * -- note that loopcxt_find_unused() resets "lc" */ rc = loopcxt_set_backing_file(&lc, backing_file); if (!rc && offset) rc = loopcxt_set_offset(&lc, offset); if (!rc && sizelimit) rc = loopcxt_set_sizelimit(&lc, sizelimit); if (!rc) loopcxt_set_flags(&lc, lo_flags); if (rc) { DBG(CXT, ul_debugobj(cxt, "failed to set loopdev attributes")); goto done; } /* setup the device */ rc = loopcxt_setup_device(&lc); if (!rc) break; /* success */ if (loopdev || rc != -EBUSY) { DBG(CXT, ul_debugobj(cxt, "failed to setup device")); rc = -MNT_ERR_LOOPDEV; goto done; } DBG(CXT, ul_debugobj(cxt, "loopdev stolen...trying again")); } while (1); if (!rc) rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); if (!rc) { /* success */ cxt->flags |= MNT_FL_LOOPDEV_READY; if ((cxt->user_mountflags & MNT_MS_LOOP) && loopcxt_is_autoclear(&lc)) { /* * autoclear flag accepted by the kernel, don't store * the "loop=" option to mtab. */ cxt->user_mountflags &= ~MNT_MS_LOOP; mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop"); } if (!(cxt->mountflags & MS_RDONLY) && loopcxt_is_readonly(&lc)) /* * mount planned read-write, but loopdev is read-only, * let's fix mount options... */ mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY); /* we have to keep the device open until mount(1), * otherwise it will be auto-cleared by kernel */ cxt->loopdev_fd = loopcxt_get_fd(&lc); loopcxt_set_fd(&lc, -1, 0); } done: loopcxt_deinit(&lc); return rc; }