/* * 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; }
/* 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 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; }
/* * Deletes loop device */ int mnt_context_delete_loopdev(struct libmnt_context *cxt) { const char *src; int rc; assert(cxt); assert(cxt->fs); if (!cxt) return -EINVAL; src = mnt_fs_get_srcpath(cxt->fs); if (!src) return -EINVAL; if (cxt->loopdev_fd > -1) close(cxt->loopdev_fd); rc = loopdev_delete(src); cxt->flags &= ~MNT_FL_LOOPDEV_READY; cxt->loopdev_fd = -1; DBG(CXT, ul_debugobj(cxt, "loopdev deleted [rc=%d]", rc)); return rc; }
/* * 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; }
/** * mnt_fs_match_source: * @fs: filesystem * @source: tag or path (device or so) * @cache: tags/paths cache or NULL * * Possible are four attempts: * 1) compare @source with @fs->source * 2) compare realpath(@source) with @fs->source * 3) compare realpath(@source) with realpath(@fs->source) * 4) compare realpath(@source) with evaluated tag from @fs->source * * The 2nd, 3rd and 4th attempts are not performed when @cache is NULL. The * 2nd and 3rd attempts are not performed if @fs->source is tag. * * Returns: 1 if @fs source is equal to @source else 0. */ int mnt_fs_match_source(mnt_fs *fs, const char *source, mnt_cache *cache) { char *cn; const char *src, *t, *v; if (!fs || !source || !fs->source) return 0; /* 1) native paths/tags */ if (!strcmp(source, fs->source)) return 1; if (!cache) return 0; if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO)) return 0; cn = mnt_resolve_spec(source, cache); if (!cn) return 0; /* 2) canonicalized and native */ src = mnt_fs_get_srcpath(fs); if (src && !strcmp(cn, src)) return 1; /* 3) canonicalized and canonicalized */ if (src) { src = mnt_resolve_path(src, cache); if (src && !strcmp(cn, src)) return 1; } if (src || mnt_fs_get_tag(fs, &t, &v)) /* src path does not match and tag is not defined */ return 0; /* read @source's tags to the cache */ if (mnt_cache_read_tags(cache, cn) < 0) { if (errno == EACCES) { /* we don't have permissions to read TAGs from * @source, but can translate @fs tag to devname. * * (because libblkid uses udev symlinks and this is * accessible for non-root uses) */ char *x = mnt_resolve_tag(t, v, cache); if (x && !strcmp(x, cn)) return 1; } return 0; } /* 4) has the @source a tag that matches with tag from @fs ? */ if (mnt_cache_device_has_tag(cache, cn, t, v)) return 1; return 0; }
/* * Sets fs-root and fs-type to @upd->fs according to the @fs template and * @mountfalgs. For MS_BIND mountflag it reads information about source * filesystem from /proc/self/mountinfo. */ static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags) { struct libmnt_fs *src_fs; char *fsroot = NULL; const char *src; int rc = 0; DBG(UPDATE, mnt_debug("setting FS root")); assert(upd); assert(upd->fs); assert(fs); if (mountflags & MS_BIND) { if (!upd->mountinfo) upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); src = mnt_fs_get_srcpath(fs); if (src) { rc = mnt_fs_set_bindsrc(upd->fs, src); if (rc) goto err; } } src_fs = mnt_table_get_fs_root(upd->mountinfo, fs, mountflags, &fsroot); if (src_fs) { src = mnt_fs_get_srcpath(src_fs); rc = mnt_fs_set_source(upd->fs, src); if (rc) goto err; mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs)); } upd->fs->root = fsroot; return 0; err: free(fsroot); return rc; }
/** * mnt_context_prepare_umount: * @cxt: mount context * * Prepare context for umounting, unnecessary for mnt_context_umount(). * * Returns: 0 on success, and negative number in case of error. */ int mnt_context_prepare_umount(struct libmnt_context *cxt) { int rc; 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_context_get_source(cxt) && !mnt_context_get_target(cxt)) return -EINVAL; if (cxt->flags & MNT_FL_PREPARED) return 0; free(cxt->helper); /* be paranoid */ cxt->helper = NULL; cxt->action = MNT_ACT_UMOUNT; rc = lookup_umount_fs(cxt); if (!rc) rc = mnt_context_merge_mflags(cxt); if (!rc) rc = evaluate_permissions(cxt); if (!rc && !cxt->helper) { if (cxt->user_mountflags & MNT_MS_HELPER) /* on helper= mount option based helper */ rc = prepare_helper_from_options(cxt, "helper"); if (!rc && !cxt->helper) /* on fstype based helper */ rc = mnt_context_prepare_helper(cxt, "umount", NULL); } if (!rc && (cxt->user_mountflags & MNT_MS_LOOP)) /* loop option explicitly specified in mtab, detach this loop */ mnt_context_enable_loopdel(cxt, TRUE); if (!rc && mnt_context_is_loopdel(cxt) && cxt->fs) { const char *src = mnt_fs_get_srcpath(cxt->fs); if (src && (!is_loopdev(src) || loopdev_is_autoclear(src))) mnt_context_enable_loopdel(cxt, FALSE); } if (rc) { DBG(CXT, mnt_debug_h(cxt, "umount: preparing failed")); return rc; } cxt->flags |= MNT_FL_PREPARED; return rc; }
int mnt_context_is_loopdev(struct libmnt_context *cxt) { const char *type, *src; assert(cxt); /* The mount flags have to be merged, otherwise we have to use * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt->fs) return 0; src = mnt_fs_get_srcpath(cxt->fs); if (!src) return 0; /* backing file not set */ if (cxt->user_mountflags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) { DBG(LOOP, ul_debugobj(cxt, "loopdev specific options detected")); return 1; } if ((cxt->mountflags & (MS_BIND | MS_MOVE)) || mnt_context_propagation_only(cxt)) return 0; /* Automatically create a loop device from a regular file if a * filesystem is not specified or the filesystem is known for libblkid * (these filesystems work with block devices only). The file size * should be at least 1KiB, otherwise we will create an empty loopdev with * no mountable filesystem... * * Note that there is no restriction (on kernel side) that would prevent a regular * file as a mount(2) source argument. A filesystem that is able to mount * regular files could be implemented. */ type = mnt_fs_get_fstype(cxt->fs); if (mnt_fs_is_regular(cxt->fs) && (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { struct stat st; if (stat(src, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 1024) { DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option")); cxt->user_mountflags |= MNT_MS_LOOP; mnt_optstr_append_option(&cxt->fs->user_optstr, "loop", NULL); return 1; } } return 0; }
int mnt_context_is_loopdev(struct libmnt_context *cxt) { const char *type, *src; int fl; assert(cxt); /* The mount flags have to be merged, otherwise we have to use * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt->fs) return 0; src = mnt_fs_get_srcpath(cxt->fs); if (!src) return 0; /* backing file not set */ if (cxt->user_mountflags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) return 1; if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) return 0; /* Automatically create a loop device from a regular file if a filesystem * is not specified or the filesystem is known for libblkid (these * filesystems work with block devices only). * * Note that there is not a restriction (on kernel side) that prevents regular * file as a mount(2) source argument. A filesystem that is able to mount * regular files could be implemented. */ type = mnt_fs_get_fstype(cxt->fs); fl = __mnt_fs_get_flags(cxt->fs); if (!(fl && (MNT_FS_PSEUDO | MNT_FS_NET | MNT_FS_SWAP)) && (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { struct stat st; if (stat(src, &st) || !S_ISREG(st.st_mode)) return 0; } return 1; }
static int kernel_fs_postparse(struct libmnt_table *tb, struct libmnt_fs *fs, pid_t *tid, const char *filename) { int rc = 0; const char *src = mnt_fs_get_srcpath(fs); /* This is a filesystem description from /proc, so we're in some process * namespace. Let's remember the process PID. */ if (filename && *tid == -1) *tid = path_to_tid(filename); fs->tid = *tid; /* * Convert obscure /dev/root to something more usable */ if (src && strcmp(src, "/dev/root") == 0) { char *real = NULL; rc = mnt_guess_system_root(fs->devno, tb->cache, &real); if (rc < 0) return rc; if (rc == 0 && real) { DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real)); rc = __mnt_fs_set_source_ptr(fs, real); } else if (rc == 1) { /* mnt_guess_system_root() returns 1 if not able to conver to * the real devname; ignore this problem */ rc = 0; } } return rc; }
static int kernel_fs_postparse(struct libmnt_table *tb, struct libmnt_fs *fs, pid_t *tid, const char *filename) { int rc = 0; const char *src = mnt_fs_get_srcpath(fs); /* This is a filesystem description from /proc, so we're in some process * namespace. Let's remember the process PID. */ if (filename && *tid == -1) *tid = path_to_tid(filename); fs->tid = *tid; /* * Convert obscure /dev/root to something more usable */ if (src && strcmp(src, "/dev/root") == 0) { char *spec = mnt_get_kernel_cmdline_option("root="); char *real = NULL; DBG(TAB, mnt_debug_h(tb, "root FS: %s", spec)); if (spec) real = mnt_resolve_spec(spec, tb->cache); if (real) { DBG(TAB, mnt_debug_h(tb, "canonical root FS: %s", real)); rc = mnt_fs_set_source(fs, real); if (!tb->cache) free(real); } free(spec); } 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; }
/* * 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; }
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 has to be called before fix_optstr() */ static int evaluate_permissions(struct libmnt_context *cxt) { unsigned long u_flags = 0; assert(cxt); assert(cxt->fs); assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); if (!cxt) return -EINVAL; if (!cxt->fs) return 0; DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions")); mnt_context_get_user_mflags(cxt, &u_flags); if (!mnt_context_is_restricted(cxt)) { /* * superuser mount */ cxt->user_mountflags &= ~MNT_MS_OWNER; cxt->user_mountflags &= ~MNT_MS_GROUP; cxt->user_mountflags &= ~MNT_MS_USER; cxt->user_mountflags &= ~MNT_MS_USERS; } else { /* * user mount */ if (!(cxt->flags & MNT_FL_TAB_APPLIED)) { DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount")); return -EPERM; } /* * Note that MS_OWNERSECURE and MS_SECURE mount options * are applied by mnt_optstr_get_flags() from mnt_context_merge_mflags() */ /* * MS_OWNER: Allow owners to mount when fstab contains the * owner option. Note that this should never be used in a high * security environment, but may be useful to give people at * the console the possibility of mounting a floppy. MS_GROUP: * Allow members of device group to mount. (Martin Dickopp) */ if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) { struct stat sb; struct libmnt_cache *cache = NULL; char *xsrc = NULL; const char *srcpath = mnt_fs_get_srcpath(cxt->fs); if (!srcpath) { /* Ah... source is TAG */ cache = mnt_context_get_cache(cxt); xsrc = mnt_resolve_spec( mnt_context_get_source(cxt), cache); srcpath = xsrc; } if (!srcpath) { DBG(CXT, mnt_debug_h(cxt, "perms: src undefined")); return -EPERM; } if (strncmp(srcpath, "/dev/", 5) == 0 && stat(srcpath, &sb) == 0 && (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) || ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid)))) cxt->user_mountflags |= MNT_MS_USER; if (!cache) free(xsrc); } if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) { DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS")); return -EPERM; } } return 0; }
int mnt_context_setup_loopdev(struct libmnt_context *cxt) { const char *backing_file; char *loopdev = NULL; size_t len; struct loopdev_cxt lc; int rc, lo_flags = 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(CXT, mnt_debug_h(cxt, "trying to setup loopdev for %s", backing_file)); if (cxt->mountflags & MS_RDONLY) { DBG(CXT, mnt_debug_h(cxt, "enabling READ-ONLY flag")); lo_flags |= LO_FLAGS_READ_ONLY; } loopcxt_init(&lc, 0); if ((cxt->user_mountflags & MNT_MS_LOOP) && mnt_fs_get_option(cxt->fs, "loop", &loopdev, &len) == 0 && loopdev) { char *tmp = strndup(loopdev, len); if (!tmp) rc = -ENOMEM; else { rc = loopcxt_set_device(&lc, tmp); free(tmp); } } /* 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) || !cxt->mtab_writable) { DBG(CXT, mnt_debug_h(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, mnt_debug_h(cxt, "trying to use %s", loopcxt_get_device(&lc))); } /* set device attributes */ rc = loopcxt_set_backing_file(&lc, backing_file); if (rc) goto done; loopcxt_set_flags(&lc, lo_flags); /* setup the device */ rc = loopcxt_setup_device(&lc); if (!rc) break; /* success */ if (loopdev || rc != -EBUSY) { DBG(CXT, mnt_debug_h(cxt, "failed to setup device")); break; } DBG(CXT, mnt_debug_h(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 kernel, don't store * the "loop=" option to mtab. */ cxt->user_mountflags &= ~MNT_MS_LOOP; if (!(cxt->mountflags & MS_RDONLY) && loopcxt_is_readonly(&lc)) /* * mount planned read-write, but loopdev is read-only, * let's fix mount options... */ cxt->mountflags |= MS_RDONLY; /* we have to keep the device open until mount(1), * otherwise it will auto-cleared by kernel */ cxt->loopdev_fd = loopcxt_get_fd(&lc); loopcxt_set_fd(&lc, -1, 0); } done: loopcxt_deinit(&lc); 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; }
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; }
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; }
/** * mnt_update_set_fs: * @upd: update handler * @mountflags: MS_* flags * @target: umount target, must be NULL for mount * @fs: mount filesystem description, must be NULL for umount * * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary. */ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, const char *target, struct libmnt_fs *fs) { int rc; assert(upd); assert(target || fs); if (!upd) return -EINVAL; if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs))) return -EINVAL; if (target && fs) return -EINVAL; DBG(UPDATE, mnt_debug_h(upd, "reseting FS [fs=0x%p, target=%s, flags=0x%08lx]", fs, target, mountflags)); if (fs) { DBG(UPDATE, mnt_debug_h(upd, "FS template:")); DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); } mnt_free_fs(upd->fs); free(upd->target); upd->ready = FALSE; upd->fs = NULL; upd->target = NULL; upd->mountflags = 0; if (mountflags & MS_PROPAGATION) return 1; upd->mountflags = mountflags; rc = mnt_update_set_filename(upd, NULL, 0); if (rc) { DBG(UPDATE, mnt_debug_h(upd, "no writable file available [rc=%d]", rc)); return rc; /* error or no file available (rc = 1) */ } if (target) { upd->target = strdup(target); if (!upd->target) return -ENOMEM; } else if (fs) { if (upd->userspace_only && !(mountflags & MS_MOVE)) { int rc = utab_new_entry(upd, fs, mountflags); if (rc) return rc; } else { upd->fs = mnt_copy_mtab_fs(fs); if (!upd->fs) return -ENOMEM; } } DBG(UPDATE, mnt_debug_h(upd, "ready")); upd->ready = TRUE; return 0; }