static int prepare_helper_from_options(struct libmnt_context *cxt, const char *name) { char *suffix = NULL; const char *opts; size_t valsz; if (mnt_context_is_nohelpers(cxt)) return 0; opts = mnt_fs_get_user_options(cxt->fs); if (!opts) return 0; if (mnt_optstr_get_option(opts, name, &suffix, &valsz)) return 0; suffix = strndup(suffix, valsz); if (!suffix) return -ENOMEM; DBG(CXT, mnt_debug_h(cxt, "umount: umount.%s %s requested", suffix, name)); return mnt_context_prepare_helper(cxt, "umount", suffix); }
/* check if @devname is loopdev and if the device is associated * with a source from @fstab_fs */ static int is_associated_fs(const char *devname, struct libmnt_fs *fs) { uintmax_t offset = 0; const char *src; char *val, *optstr; size_t valsz; int flags = 0; /* check if it begins with /dev/loop */ if (strncmp(devname, _PATH_DEV_LOOP, sizeof(_PATH_DEV_LOOP) - 1)) return 0; src = mnt_fs_get_srcpath(fs); if (!src) return 0; /* check for offset option in @fs */ optstr = (char *) mnt_fs_get_user_options(fs); if (optstr && mnt_optstr_get_option(optstr, "offset", &val, &valsz) == 0) { flags |= LOOPDEV_FL_OFFSET; if (mnt_parse_offset(val, valsz, &offset) != 0) return 0; } return loopdev_is_used(devname, src, offset, flags); }
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; }
/* * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs. * * Note that on systems without /etc/mtab the fs-specific options are not * managed by libmount at all. We have to use "mount attributes" that are * private for mount.<type> helpers. */ static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts) { char *o = NULL; mnt_fs_set_attributes(fs, nfs_opts); /* for non-mtab systems */ /* for mtab create a new options list */ mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL); mnt_optstr_append_option(&o, nfs_opts, NULL); mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL); mnt_fs_set_options(fs, o); free(o); }
/* * 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; }
/* * 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_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; }
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; }
/* * Allocates utab entry (upd->fs) for mount/remount. This function should be * called *before* mount(2) syscall. The @fs is used as a read-only template. * * Returns: 0 on success, negative number on error, 1 if utabs update is * unnecessary. */ static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags) { int rc = 0; const char *o = NULL, *a = NULL; char *u = NULL; assert(fs); assert(upd); assert(upd->fs == NULL); assert(!(mountflags & MS_MOVE)); DBG(UPDATE, mnt_debug("prepare utab entry")); o = mnt_fs_get_user_options(fs); a = mnt_fs_get_attributes(fs); upd->fs = NULL; if (o) { /* remove non-mtab options */ rc = mnt_optstr_get_options(o, &u, mnt_get_builtin_optmap(MNT_USERSPACE_MAP), MNT_NOMTAB); if (rc) goto err; } if (!u && !a) { DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)")); return 1; } /* allocate the entry */ upd->fs = mnt_copy_fs(NULL, fs); if (!upd->fs) { rc = -ENOMEM; goto err; } rc = mnt_fs_set_options(upd->fs, u); if (rc) goto err; rc = mnt_fs_set_attributes(upd->fs, a); if (rc) goto err; if (!(mountflags & MS_REMOUNT)) { rc = set_fs_root(upd, fs, mountflags); if (rc) goto err; } free(u); DBG(UPDATE, mnt_debug("utab entry OK")); return 0; err: free(u); mnt_free_fs(upd->fs); upd->fs = NULL; return rc; }