diff options
Diffstat (limited to 'libmount/src/tab_update.c')
-rw-r--r-- | libmount/src/tab_update.c | 1100 |
1 files changed, 1100 insertions, 0 deletions
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c new file mode 100644 index 0000000..3a22e71 --- /dev/null +++ b/libmount/src/tab_update.c @@ -0,0 +1,1100 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2011-2018 Karel Zak <kzak@redhat.com> + * + * libmount is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + */ + +/** + * SECTION: update + * @title: Tables update + * @short_description: userspace mount information management + * + * The struct libmnt_update provides an abstraction to manage mount options in + * userspace independently of system configuration. This low-level API works on + * systems both with and without /etc/mtab. On systems without the regular /etc/mtab + * file, the userspace mount options (e.g. user=) are stored in the /run/mount/utab + * file. + * + * It's recommended to use high-level struct libmnt_context API. + */ +#include <sys/file.h> +#include <fcntl.h> +#include <signal.h> + +#include "mountP.h" +#include "mangle.h" +#include "pathnames.h" +#include "strutils.h" + +struct libmnt_update { + char *target; + struct libmnt_fs *fs; + char *filename; + unsigned long mountflags; + int userspace_only; + int ready; + + struct libmnt_table *mountinfo; +}; + +static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags); +static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags); + +/** + * mnt_new_update: + * + * Returns: newly allocated update handler + */ +struct libmnt_update *mnt_new_update(void) +{ + struct libmnt_update *upd; + + upd = calloc(1, sizeof(*upd)); + if (!upd) + return NULL; + + DBG(UPDATE, ul_debugobj(upd, "allocate")); + return upd; +} + +/** + * mnt_free_update: + * @upd: update + * + * Deallocates struct libmnt_update handler. + */ +void mnt_free_update(struct libmnt_update *upd) +{ + if (!upd) + return; + + DBG(UPDATE, ul_debugobj(upd, "free")); + + mnt_unref_fs(upd->fs); + mnt_unref_table(upd->mountinfo); + free(upd->target); + free(upd->filename); + free(upd); +} + +/* + * Returns 0 on success, <0 in case of error. + */ +int mnt_update_set_filename(struct libmnt_update *upd, const char *filename, + int userspace_only) +{ + const char *path = NULL; + int rw = 0; + + if (!upd) + return -EINVAL; + + /* filename explicitly defined */ + if (filename) { + char *p = strdup(filename); + if (!p) + return -ENOMEM; + + upd->userspace_only = userspace_only; + free(upd->filename); + upd->filename = p; + } + + if (upd->filename) + return 0; + + /* detect tab filename -- /etc/mtab or /run/mount/utab + */ +#ifdef USE_LIBMOUNT_SUPPORT_MTAB + mnt_has_regular_mtab(&path, &rw); +#endif + if (!rw) { + path = NULL; + mnt_has_regular_utab(&path, &rw); + if (!rw) + return -EACCES; + upd->userspace_only = TRUE; + } + upd->filename = strdup(path); + if (!upd->filename) + return -ENOMEM; + + return 0; +} + +/** + * mnt_update_get_filename: + * @upd: update + * + * This function returns the file name (e.g. /etc/mtab) of the up-dated file. + * + * Returns: pointer to filename that will be updated or NULL in case of error. + */ +const char *mnt_update_get_filename(struct libmnt_update *upd) +{ + return upd ? upd->filename : NULL; +} + +/** + * mnt_update_is_ready: + * @upd: update handler + * + * Returns: 1 if entry described by @upd is successfully prepared and will be + * written to the mtab/utab file. + */ +int mnt_update_is_ready(struct libmnt_update *upd) +{ + return upd ? upd->ready : FALSE; +} + +/** + * 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; + + if (!upd) + return -EINVAL; + if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs))) + return -EINVAL; + if (target && fs) + return -EINVAL; + + DBG(UPDATE, ul_debugobj(upd, + "resetting FS [target=%s, flags=0x%08lx]", + target, mountflags)); + if (fs) { + DBG(UPDATE, ul_debugobj(upd, "FS template:")); + DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); + } + + mnt_unref_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, ul_debugobj(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)) { + 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, ul_debugobj(upd, "ready")); + upd->ready = TRUE; + return 0; +} + +/** + * mnt_update_get_fs: + * @upd: update + * + * Returns: update filesystem entry or NULL + */ +struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd) +{ + return upd ? upd->fs : NULL; +} + +/** + * mnt_update_get_mflags: + * @upd: update + * + * Returns: mount flags as was set by mnt_update_set_fs() + */ +unsigned long mnt_update_get_mflags(struct libmnt_update *upd) +{ + return upd ? upd->mountflags : 0; +} + +/** + * mnt_update_force_rdonly: + * @upd: update + * @rdonly: is read-only? + * + * Returns: 0 on success and negative number in case of error. + */ +int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly) +{ + int rc = 0; + + if (!upd || !upd->fs) + return -EINVAL; + + if (rdonly && (upd->mountflags & MS_RDONLY)) + return 0; + if (!rdonly && !(upd->mountflags & MS_RDONLY)) + return 0; + + if (!upd->userspace_only) { + /* /etc/mtab -- we care about VFS options there */ + const char *o = mnt_fs_get_options(upd->fs); + char *n = o ? strdup(o) : NULL; + + if (n) + mnt_optstr_remove_option(&n, rdonly ? "rw" : "ro"); + if (!mnt_optstr_prepend_option(&n, rdonly ? "ro" : "rw", NULL)) + rc = mnt_fs_set_options(upd->fs, n); + + free(n); + } + + if (rdonly) + upd->mountflags &= ~MS_RDONLY; + else + upd->mountflags |= MS_RDONLY; + + return rc; +} + + +/* + * Allocates an 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 utab's 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, *a; + char *u = NULL; + + assert(fs); + assert(upd); + assert(upd->fs == NULL); + assert(!(mountflags & MS_MOVE)); + + DBG(UPDATE, ul_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, ul_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, ul_debug("utab entry OK")); + return 0; +err: + free(u); + mnt_unref_fs(upd->fs); + upd->fs = NULL; + return rc; +} + +/* + * Sets fs-root and fs-type to @upd->fs according to the @fs template and + * @mountfalgs. For MS_BIND mountflag it reads information about the 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, *fstype; + int rc = 0; + + DBG(UPDATE, ul_debug("setting FS root")); + + assert(upd); + assert(upd->fs); + assert(fs); + + fstype = mnt_fs_get_fstype(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; + } + + } else if (fstype && (strcmp(fstype, "btrfs") == 0 || strcmp(fstype, "auto") == 0)) { + if (!upd->mountinfo) + upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); + } + + 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; +} + +/* mtab and fstab update -- returns zero on success + */ +static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs) +{ + const char *o, *src, *fstype, *comm; + char *m1, *m2, *m3, *m4; + int rc; + + assert(fs); + assert(f); + + comm = mnt_fs_get_comment(fs); + 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) { + if (comm) + fputs(comm, f); + 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 fprintf_utab_fs(FILE *f, struct libmnt_fs *fs) +{ + char *p; + int rc = 0; + + 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; +} + +static int update_table(struct libmnt_update *upd, struct libmnt_table *tb) +{ + FILE *f; + int rc, fd; + char *uq = NULL; + + if (!tb || !upd->filename) + return -EINVAL; + + DBG(UPDATE, ul_debugobj(upd, "%s: updating", upd->filename)); + + fd = mnt_open_uniq_filename(upd->filename, &uq); + if (fd < 0) + return fd; /* error */ + + f = fdopen(fd, "w" UL_CLOEXECSTR); + if (f) { + struct stat st; + struct libmnt_iter itr; + struct libmnt_fs *fs; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + if (tb->comms && mnt_table_get_intro_comment(tb)) + fputs(mnt_table_get_intro_comment(tb), f); + + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + if (upd->userspace_only) + rc = fprintf_utab_fs(f, fs); + else + rc = fprintf_mtab_fs(f, fs); + if (rc) { + DBG(UPDATE, ul_debugobj(upd, + "%s: write entry failed: %m", uq)); + goto leave; + } + } + if (tb->comms && mnt_table_get_trailing_comment(tb)) + fputs(mnt_table_get_trailing_comment(tb), f); + + if (fflush(f) != 0) { + rc = -errno; + DBG(UPDATE, ul_debugobj(upd, "%s: fflush failed: %m", uq)); + goto leave; + } + + rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; + + if (!rc && stat(upd->filename, &st) == 0) + /* Copy uid/gid from the present file before renaming. */ + rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0; + + fclose(f); + f = NULL; + + if (!rc) + rc = rename(uq, upd->filename) ? -errno : 0; + } else { + rc = -errno; + close(fd); + } + +leave: + if (f) + fclose(f); + + unlink(uq); /* be paranoid */ + free(uq); + DBG(UPDATE, ul_debugobj(upd, "%s: done [rc=%d]", upd->filename, rc)); + return rc; +} + +/** + * mnt_table_write_file + * @tb: parsed file (e.g. fstab) + * @file: target + * + * This function writes @tb to @file. + * + * Returns: 0 on success, negative number on error. + */ +int mnt_table_write_file(struct libmnt_table *tb, FILE *file) +{ + int rc = 0; + struct libmnt_iter itr; + struct libmnt_fs *fs; + + if (tb->comms && mnt_table_get_intro_comment(tb)) + fputs(mnt_table_get_intro_comment(tb), file); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_table_next_fs(tb, &itr, &fs) == 0) { + rc = fprintf_mtab_fs(file, fs); + if (rc) + return rc; + } + if (tb->comms && mnt_table_get_trailing_comment(tb)) + fputs(mnt_table_get_trailing_comment(tb), file); + + if (fflush(file) != 0) + rc = -errno; + + DBG(TAB, ul_debugobj(tb, "write file done [rc=%d]", rc)); + return rc; +} + +/** + * mnt_table_replace_file + * @tb: parsed file (e.g. fstab) + * @filename: target + * + * This function replaces @file by the new content from @tb. + * + * Returns: 0 on success, negative number on error. + */ +int mnt_table_replace_file(struct libmnt_table *tb, const char *filename) +{ + int fd, rc = 0; + FILE *f; + char *uq = NULL; + + DBG(TAB, ul_debugobj(tb, "%s: replacing", filename)); + + fd = mnt_open_uniq_filename(filename, &uq); + if (fd < 0) + return fd; /* error */ + + f = fdopen(fd, "w" UL_CLOEXECSTR); + if (f) { + struct stat st; + + mnt_table_write_file(tb, f); + + if (fflush(f) != 0) { + rc = -errno; + DBG(UPDATE, ul_debug("%s: fflush failed: %m", uq)); + goto leave; + } + + rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; + + if (!rc && stat(filename, &st) == 0) + /* Copy uid/gid from the present file before renaming. */ + rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0; + + fclose(f); + f = NULL; + + if (!rc) + rc = rename(uq, filename) ? -errno : 0; + } else { + rc = -errno; + close(fd); + } + +leave: + if (f) + fclose(f); + unlink(uq); + free(uq); + + DBG(TAB, ul_debugobj(tb, "replace done [rc=%d]", rc)); + return rc; +} + +static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd) +{ + struct libmnt_fs *fs; + + assert(upd); + + fs = mnt_copy_fs(NULL, upd->fs); + if (!fs) + return -ENOMEM; + + mnt_table_add_fs(tb, fs); + mnt_unref_fs(fs); + + return update_table(upd, tb); +} + +static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb; + int rc = 0; + + assert(upd); + assert(upd->fs); + + DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return -MNT_ERR_LOCK; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) + rc = add_file_entry(tb, upd); + if (lc) + mnt_unlock_file(lc); + + mnt_unref_table(tb); + return rc; +} + +static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb; + int rc = 0; + + assert(upd); + assert(upd->target); + + DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return -MNT_ERR_LOCK; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD); + if (rem) { + mnt_table_remove_fs(tb, rem); + rc = update_table(upd, tb); + } + } + if (lc) + mnt_unlock_file(lc); + + mnt_unref_table(tb); + return rc; +} + +static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb = NULL; + int rc = 0; + + assert(upd); + DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename)); + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return -MNT_ERR_LOCK; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + const char *upd_source = mnt_fs_get_srcpath(upd->fs); + const char *upd_target = mnt_fs_get_target(upd->fs); + struct libmnt_iter itr; + struct libmnt_fs *fs; + char *cn_target = mnt_resolve_path(upd_target, NULL); + + if (!cn_target) { + rc = -ENOMEM; + goto done; + } + + mnt_reset_iter(&itr, MNT_ITER_BACKWARD); + while (mnt_table_next_fs(tb, &itr, &fs) == 0) { + char *p; + const char *e; + + e = startswith(mnt_fs_get_target(fs), upd_source); + if (!e || (*e && *e != '/')) + continue; + if (*e == '/') + e++; /* remove extra '/' */ + + /* no subdirectory, replace entire path */ + if (!*e) + rc = mnt_fs_set_target(fs, cn_target); + + /* update start of the path, keep subdirectory */ + else if (asprintf(&p, "%s/%s", cn_target, e) > 0) { + rc = mnt_fs_set_target(fs, p); + free(p); + } else + rc = -ENOMEM; + + if (rc < 0) + break; + } + + if (!rc) + rc = update_table(upd, tb); + free(cn_target); + } + +done: + if (lc) + mnt_unlock_file(lc); + + mnt_unref_table(tb); + return rc; +} + +static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb = NULL; + int rc = 0; + struct libmnt_fs *fs; + + assert(upd); + assert(upd->fs); + + DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename)); + + fs = upd->fs; + + if (lc) + rc = mnt_lock_file(lc); + if (rc) + return -MNT_ERR_LOCK; + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_target(fs), + MNT_ITER_BACKWARD); + if (cur) { + if (upd->userspace_only) + rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs)); + if (!rc) + rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs)); + if (!rc) + rc = update_table(upd, tb); + } else + rc = add_file_entry(tb, upd); /* not found, add new */ + } + + if (lc) + mnt_unlock_file(lc); + + mnt_unref_table(tb); + return rc; +} + +/** + * mnt_update_table: + * @upd: update + * @lc: lock or NULL + * + * High-level API to update /etc/mtab (or private /run/mount/utab file). + * + * The @lc lock is optional and will be created if necessary. Note that + * an automatically created lock blocks all signals. + * + * See also mnt_lock_block_signals() and mnt_context_get_lock(). + * + * Returns: 0 on success, negative number on error. + */ +int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_lock *lc0 = lc; + int rc = -EINVAL; + + if (!upd || !upd->filename) + return -EINVAL; + if (!upd->ready) + return 0; + + DBG(UPDATE, ul_debugobj(upd, "%s: update tab", upd->filename)); + if (upd->fs) { + DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr)); + } + if (!lc) { + lc = mnt_new_lock(upd->filename, 0); + if (lc) + mnt_lock_block_signals(lc, TRUE); + } + if (lc && upd->userspace_only) + mnt_lock_use_simplelock(lc, TRUE); /* use flock */ + + if (!upd->fs && upd->target) + rc = update_remove_entry(upd, lc); /* umount */ + else if (upd->mountflags & MS_MOVE) + rc = update_modify_target(upd, lc); /* move */ + else if (upd->mountflags & MS_REMOUNT) + rc = update_modify_options(upd, lc); /* remount */ + else if (upd->fs) + rc = update_add_entry(upd, lc); /* mount */ + + upd->ready = FALSE; + DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]", + upd->filename, rc)); + if (lc != lc0) + mnt_free_lock(lc); + return rc; +} + +int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + struct libmnt_table *tb = NULL; + struct libmnt_lock *lc0 = lc; + int rc = 0; + + if (!upd || !upd->filename || (!upd->fs && !upd->target)) + return -EINVAL; + + DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename)); + + if (!lc) { + lc = mnt_new_lock(upd->filename, 0); + if (lc) + mnt_lock_block_signals(lc, TRUE); + } + if (lc && upd->userspace_only) + mnt_lock_use_simplelock(lc, TRUE); /* use flock */ + if (lc) { + rc = mnt_lock_file(lc); + if (rc) { + rc = -MNT_ERR_LOCK; + goto done; + } + } + + tb = __mnt_new_table_from_file(upd->filename, + upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB, 1); + if (lc) + mnt_unlock_file(lc); + if (!tb) + goto done; + + if (upd->fs) { + /* mount */ + const char *tgt = mnt_fs_get_target(upd->fs); + const char *src = mnt_fs_get_bindsrc(upd->fs) ? + mnt_fs_get_bindsrc(upd->fs) : + mnt_fs_get_source(upd->fs); + + if (mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD)) { + DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s", + upd->filename, src, tgt)); + rc = 1; + } + } else if (upd->target) { + /* umount */ + if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) { + DBG(UPDATE, ul_debugobj(upd, "%s: not-found (umounted) %s", + upd->filename, upd->target)); + rc = 1; + } + } + + mnt_unref_table(tb); +done: + if (lc && lc != lc0) + mnt_free_lock(lc); + DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]", + upd->filename, rc)); + return rc; +} + + +#ifdef TEST_PROGRAM + +static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags) +{ + int rc; + struct libmnt_update *upd; + + DBG(UPDATE, ul_debug("update test")); + + upd = mnt_new_update(); + if (!upd) + return -ENOMEM; + + rc = mnt_update_set_fs(upd, mountflags, target, fs); + if (rc == 1) { + /* update is unnecessary */ + rc = 0; + goto done; + } + if (rc) { + fprintf(stderr, "failed to set FS\n"); + goto done; + } + + /* [... mount(2) call should be here...] */ + + rc = mnt_update_table(upd, NULL); +done: + mnt_free_update(upd); + 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_unref_fs(fs); + return rc; +} + +static int test_remove(struct libmnt_test *ts, int argc, char *argv[]) +{ + if (argc < 2) + return -1; + return update(argv[1], NULL, 0); +} + +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_unref_fs(fs); + 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_unref_fs(fs); + return rc; +} + +static int test_replace(struct libmnt_test *ts, int argc, char *argv[]) +{ + struct libmnt_fs *fs = mnt_new_fs(); + struct libmnt_table *tb = mnt_new_table(); + int rc; + + if (argc < 3) + return -1; + + mnt_table_enable_comments(tb, TRUE); + mnt_table_parse_fstab(tb, NULL); + + mnt_fs_set_source(fs, argv[1]); + mnt_fs_set_target(fs, argv[2]); + mnt_fs_append_comment(fs, "# this is new filesystem\n"); + + mnt_table_add_fs(tb, fs); + mnt_unref_fs(fs); + + rc = mnt_table_replace_file(tb, mnt_get_fstab_path()); + mnt_unref_table(tb); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_test tss[] = { + { "--add", test_add, "<src> <target> <type> <options> add a line to mtab" }, + { "--remove", test_remove, "<target> MS_REMOUNT mtab change" }, + { "--move", test_move, "<old_target> <target> MS_MOVE mtab change" }, + { "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" }, + { "--replace",test_replace, "<src> <target> Add a line to LIBMOUNT_FSTAB and replace the original file" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ |