* SECTION: update
* @title: mtab (fstab) managment
* @short_description: userspace mount information management
- *
- * The libmount library allows to use more modes for mtab management:
- *
- * - /etc/mtab is regular file
- *
- * then libmount manages the file in classical way (all mounts are added to
- * the file). This mode is always used for /etc/fstab updates as well.
- *
- * - /etc/mtab is symlink
- *
- * then libmount ignores mtab at all
- *
- * - /etc/mtab is symlink and /var/run/mount/ directory exists
- *
- * then libmount stores userspace specific mount options to the
- * /var/run/mount/mountinfo file (the file format compatible to
- * /proc/self/mountinfo)
- *
- * Note that mnt_update_* interface does not manage mount options. It's callers
- * responsibility set the right mount options (for example merge options from
- * mtab with command line on MS_REMOUNT).
- *
- * The small exception is the /var/run/mount/mountinfo file where are store
- * userspace mount options only. This is done by mnt_prepare_update().
- *
- * The mtab is always updated in two steps. The first step is to prepare a new
- * update entry -- mnt_prepare_update(), this step has to be done before
- * mount(2) syscall. The second step is to update the file --
- * mnt_update_file(), this step should be done after mount(2) syscall.
- *
- * The mnt_update_file() behaviour is undefined if mnt_prepare_update() has
- * not been used.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "mangle.h"
#include "pathnames.h"
-
-
-/*
- * mtab update description
- */
struct _mnt_update {
- int action; /* MNT_ACT_{MOUNT,UMOUNT} */
- unsigned long mountflags; /* MS_* flags */
- char *filename; /* usually /etc/mtab or /var/run/mount/mountinfo */
- char *old_target; /* for MS_MOVE */
- int format; /* MNT_FMT_{MTAB,FSTAB,MOUNTINFO} */
- int nolock; /* don't alloca private mnt_lock */
- mnt_fs *fs; /* entry */
- mnt_lock *lc; /* lock or NULL */
- int pointless; /* update is unnecessary */
+ char *target;
+ mnt_fs *fs;
+ unsigned long mountflags;
+ int userspace_only;
+ int ready;
};
+static int utab_new_entry(mnt_fs *fs, unsigned long mountflags, mnt_fs **ent);
+static int get_fs_root(mnt_fs *fs, unsigned long mountflags, char **result);
+
/**
* mnt_new_update:
- * @action: MNT_ACT_{MOUNT,UMOUNT}
- * @mountflags: MS_{REMOUNT,BIND,MOVE}
- * @fs: FS description
+ * @userspace_only: TRUE/FALSE -- maintain userspace mount options only
*
- * Returns: newly allocated update description
+ * Returns: newly allocated update handler
*/
-mnt_update *mnt_new_update(int action, unsigned long mountflags, const mnt_fs *fs)
+mnt_update *mnt_new_update(int userspace_only)
{
mnt_update *upd;
if (!upd)
return NULL;
+ upd->userspace_only = userspace_only;
DBG(UPDATE, mnt_debug_h(upd, "allocate"));
- if (action)
- mnt_update_set_action(upd, action);
- if (mountflags)
- mnt_update_set_mountflags(upd, mountflags);
- if (fs)
- mnt_update_set_fs(upd, fs);
return upd;
}
DBG(UPDATE, mnt_debug_h(upd, "free"));
- mnt_free_lock(upd->lc);
- free(upd->filename);
- free(upd->old_target);
+ mnt_free_fs(upd->fs);
+ free(upd->target);
free(upd);
}
/**
- * mnt_update_set_filename:
- * @upd: update
- * @filename: path to update (default is /etc/update or /var/run/mount/mountinfo)
- *
- * Returns: 0 on success, -1 in case of error.
- */
-int mnt_update_set_filename(mnt_update *upd, const char *filename)
-{
- char *p = NULL;
-
- assert(upd);
- if (!upd)
- return -EINVAL;
- if (filename) {
- p = strdup(filename);
- if (!p)
- return -ENOMEM;
- }
- free(upd->filename);
- upd->filename = p;
- return 0;
-}
-
-/**
- * mnt_update_get_filename
- * @upd: update
+ * mnt_update_is_ready:
+ * @upd: update handler
*
- * Note that function returns NULL if the filename has not been defined (see
- * mnt_update_set_filename()) or mnt_prepare_update() has not been called.
- *
- * Returns: filename or NULL.
+ * Returns: 1 if entry described by @upd is successfully prepared and will be
+ * written to mtab/utab file.
*/
-const char *mnt_update_get_filename(mnt_update *upd)
+int mnt_update_is_ready(mnt_update *upd)
{
- return upd ? upd->filename : NULL;
-}
-
-/**
- * mnt_update_set_action:
- * @upd: update
- * @action: MNT_ACT_{MOUNT,UMOUNT}
- *
- * Overwrites the previously defined (usually by mnt_new_update()) action.
- *
- * Returns: 0 on success, -1 in case of error.
- */
-int mnt_update_set_action(mnt_update *upd, int action)
-{
- assert(upd);
- if (!upd)
- return -EINVAL;
- upd->action = action;
- return 0;
+ return upd ? upd->ready : FALSE;
}
/**
- * mnt_update_set_format:
- * @upd: update
- * @format: MNT_FMT_{MTAB,FSTAB,MOUNTINFO}
- *
- * Sets update file format, default is MNT_FMT_MTAB for paths that end with
- * "update", MNT_FMT_MOUNTINFO for paths that end with "mountinfo" and
- * MNT_FMT_FSTAB for paths that end with "fstab".
+ * mnt_update_is_userspace_only:
+ * @upd: update handler
*
- * Returns: 0 on success, -1 in case of error.
+ * Returns: 1 if @upd cares about userspace mount options only (see
+ * mnt_new_update().
*/
-int mnt_update_set_format(mnt_update *upd, int format)
+int mnt_update_is_userspace_only(mnt_update *upd)
{
- assert(upd);
- if (!upd)
- return -EINVAL;
- upd->format = format;
- return 0;
+ return upd ? upd->userspace_only : FALSE;
}
-/**
- * mnt_update_get_format
- * @upd: update
- *
- * Note that function returns zero if the format has not been defined (see
- * mnt_update_set_format()) or mnt_prepare_update() has not been called.
- *
- * Returns: MNT_FMT_{MTAB,FSTAB,MOUNTINFO} or 0.
- */
-int mnt_update_get_format(mnt_update *upd)
-{
- return upd ? upd->format : 0;
-}
/**
* mnt_update_set_fs:
- * @upd: update
- * @fs: filesystem to write to file
- *
- * This function replaces the current setting related to the current FS. Note that
- * format, old target and mountflags are not reseted.
+ * @upd: update handler
+ * @mountflags: MS_* flags
+ * @target: umount target or MS_MOVE source
+ * @fs: mount or MS_REMOUNT filesystem description, or MS_MOVE target
*
- * Returns; 0 on success, -1 in case of error.
+ * Returns: -1 in case on error, 0 on success, 1 if update is unnecessary.
*/
-int mnt_update_set_fs(mnt_update *upd, const mnt_fs *fs)
+int mnt_update_set_fs(mnt_update *upd, int mountflags,
+ const char *target, mnt_fs *fs)
{
- mnt_fs *x = NULL;
-
assert(upd);
- if (!upd)
- return -EINVAL;
- if (fs) {
- x = mnt_copy_fs(fs);
- if (!x)
- return -ENOMEM;
- }
+ assert(target || fs);
- mnt_free_fs(upd->fs);
- upd->fs = x;
- upd->pointless = FALSE;
- return 0;
-}
-
-/**
- * mnt_update_set_mountflags:
- * @upd: update
- * @flags: MS_{REMOUNT,MOVE,BIND,...}
- *
- * Sets mount flags for mount/umount action. The flags are also
- * extracted from mount options by mnt_prepare_update(). The mount flags
- * are used for mtab update to differentiate between move, remount, ...
- *
- * Returns: 0 on success, -1 in case of error.
- */
-int mnt_update_set_mountflags(mnt_update *upd, unsigned long flags)
-{
- assert(upd);
if (!upd)
return -EINVAL;
- upd->mountflags = flags;
- return 0;
-}
-/**
- * mnt_update_get_lock:
- * @upd: update
- *
- * This function should not be called before mnt_prepare_update(). The lock
- * is initialized when mtab update is required only.
- *
- * Note that after mnt_update_disable_lock(mt, TRUE) or after mnt_free_update()
- * the lock will be automatically deallocated.
- *
- * Returns: libmount lock handler or NULL if locking is disabled or update is
- * not prepared yet.
- */
-mnt_lock *mnt_update_get_lock(mnt_update *upd)
-{
- return upd ? upd->lc : NULL;
-}
+ DBG(UPDATE, mnt_debug_h(upd,
+ "reseting FS [fs=0x%p, target=%s, flags=0x%08x]",
+ fs, target, mountflags));
-/**
- * mnt_update_disable_lock:
- * @upd: update
- * @disable: TRUE/FALSE
- *
- * Enable or disable update locking, the locking is enabled by default.
- *
- * Returns: 0 on success, -1 in case of error.
- */
-int mnt_update_disable_lock(mnt_update *upd, int disable)
-{
- if (!upd)
- return -1;
- if (disable) {
- mnt_free_lock(upd->lc);
- upd->lc = NULL;
- }
- upd->nolock = disable;
- return 0;
-}
+ mnt_free_fs(upd->fs);
+ free(upd->target);
+ upd->ready = FALSE;
+ upd->fs = NULL;
+ upd->target = NULL;
+ upd->mountflags = mountflags;
-/**
- * mnt_update_set_old_target:
- * @upd: update
- * @target: old mountpoint
- *
- * Sets the original target for the MS_MOVE operation.
- *
- * Returns: 0 on success, -1 in case of error.
- */
-int mnt_update_set_old_target(mnt_update *upd, const char *target)
-{
- char *p = NULL;
+ if (fs) {
+ if (upd->userspace_only && !(mountflags & MS_MOVE)) {
+ int rc = utab_new_entry(fs, mountflags, &upd->fs);
+ if (rc)
+ return rc;
+ } else {
+ upd->fs = mnt_copy_fs(fs);
+ if (!upd->fs)
+ return -ENOMEM;
+ }
+ }
- if (!upd)
- return -EINVAL;
if (target) {
- p = strdup(target);
- if (!p)
- return -1;
+ upd->target = strdup(target);
+ if (!upd->target)
+ return -ENOMEM;
}
- free(upd->old_target);
- upd->old_target = p;
+
+ DBG(UPDATE, mnt_debug_h(upd, "ready"));
+ upd->ready = TRUE;
return 0;
}
+
/*
- * The format is same as /proc/self/mountinfo, but it contains userspace
- * mount options and some unnecessary fields are ignored.
+ * Allocates (but does not write) utab entry for mount/remount. This function
+ * should be called *before* mount(2) syscall.
+ *
+ * Returns: 0 on success, negative number on error, 1 if utabs update is
+ * unnecessary.
*/
-static int fprintf_mountinfo_fs(FILE *f, mnt_fs *fs)
+static int utab_new_entry(mnt_fs *fs, unsigned long mountflags, mnt_fs **ent)
{
- char *root = NULL, *target = NULL, *optstr = NULL,
- *fstype = NULL, *source = NULL;
- int rc = -1;
- dev_t devno;
-
- assert(fs);
- assert(f);
-
- if (!fs || !f)
- return -EINVAL;
- devno = mnt_fs_get_devno(fs);
- source = mangle(mnt_fs_get_source(fs));
- root = mangle(mnt_fs_get_root(fs));
- target = mangle(mnt_fs_get_target(fs));
- fstype = mangle(mnt_fs_get_fstype(fs));
- optstr = mangle(mnt_fs_get_optstr(fs));
-
- if (!root || !target || !optstr)
- goto done;
-
- rc = fprintf(f, "%i %i %u:%u %s %s %s - %s %s %s\n",
- mnt_fs_get_id(fs),
- mnt_fs_get_parent_id(fs),
- major(devno), minor(devno),
- root,
- target,
- optstr,
- fstype ? fstype : "auto",
- source ? source : "none",
- "none");
- rc = 0;
-done:
- free(root);
- free(target);
- free(optstr);
- free(fstype);
- free(source);
- return rc;
-}
-
-/* mtab and fstab update */
-static int fprintf_mtab_fs(FILE *f, mnt_fs *fs)
-{
- char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
- int rc = -1;
+ int rc = 0;
+ const char *o = NULL;
+ char *u = NULL;
assert(fs);
- assert(f);
-
- if (!fs || !f)
- return -EINVAL;
-
- m1 = mangle(mnt_fs_get_source(fs));
- m2 = mangle(mnt_fs_get_target(fs));
- m3 = mangle(mnt_fs_get_fstype(fs));
- m4 = mangle(mnt_fs_get_optstr(fs));
-
- if (!m1 || !m2 || !m3 || !m4)
- goto done;
-
- rc = fprintf(f, "%s %s %s %s %d %d\n",
- m1, m2, m3, m4,
- mnt_fs_get_freq(fs),
- mnt_fs_get_passno(fs));
- rc = 0;
-done:
- free(m1);
- free(m2);
- free(m3);
- free(m4);
-
- return rc;
-}
+ assert(ent);
+ assert(!(mountflags & MS_MOVE));
-static int update_file(const char *filename, int fmt, mnt_tab *tb)
-{
- mnt_iter itr;
- mnt_fs *fs;
- FILE *f = NULL;
- char tmpname[PATH_MAX];
- struct stat st;
- int fd;
- int (*line_fn)(FILE *, mnt_fs *) = fprintf_mountinfo_fs;
-
- assert(tb);
- if (!tb)
+ if (!fs || !ent)
return -EINVAL;
+ *ent = NULL;
- DBG(UPDATE, mnt_debug("%s: update from tab %p", filename, tb));
-
- if (snprintf(tmpname, sizeof(tmpname), "%s.tmp", filename)
- >= sizeof(tmpname))
- goto error;
-
- f = fopen(tmpname, "w");
- if (!f)
- goto error;
-
-
- if (fmt == MNT_FMT_MTAB || fmt == MNT_FMT_FSTAB)
- line_fn = fprintf_mtab_fs;
+ DBG(UPDATE, mnt_debug("prepare utab entry"));
- mnt_reset_iter(&itr, MNT_ITER_FORWARD);
- while(mnt_tab_next_fs(tb, &itr, &fs) == 0)
- line_fn(f, fs);
-
- fd = fileno(f);
-
- if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
- goto error;
-
- /* Copy uid/gid from the present file before renaming. */
- if (stat(filename, &st) == 0) {
- if (fchown(fd, st.st_uid, st.st_gid) < 0)
- goto error;
+ o = mnt_fs_get_optstr(fs);
+ if (o) {
+ rc = mnt_split_optstr(o, &u, NULL, NULL, MNT_NOMTAB, 0);
+ if (rc)
+ return rc; /* parse error or so... */
+ }
+ /* TODO
+ if (extra_opts) {
+ rc = mnt_optstr_append_option(&u, extra_opts, NULL);
+ if (rc)
+ goto err;
+ }
+ */
+ if (!u)
+ return 1; /* don't have mount options */
+
+ /* allocate the entry */
+ *ent = mnt_copy_fs(fs);
+ if (!*ent) {
+ rc = -ENOMEM;
+ goto err;
}
- fclose(f);
- f = NULL;
+ rc = __mnt_fs_set_optstr_ptr(*ent, u, FALSE);
+ if (rc)
+ goto err;
+ u = NULL;
- if (rename(tmpname, filename) < 0)
- goto error;
+ if (!(mountflags & MS_REMOUNT)) {
+ rc = get_fs_root(fs, mountflags, &(*ent)->root);
+ if (rc)
+ goto err;
+ }
+ DBG(UPDATE, mnt_debug("utab entry OK"));
return 0;
-error:
- DBG(UPDATE, mnt_debug("%s: update from tab %p failed", filename, tb));
- if (f)
- fclose(f);
- return errno ? -errno : -1;
+err:
+ free(u);
+ mnt_free_fs(*ent);
+ return rc;
}
-static int set_fs_root(mnt_update *upd, mnt_fs *fs)
+static int get_fs_root(mnt_fs *fs, unsigned long mountflags, char **result)
{
char *root = NULL, *mnt = NULL;
- const char *fstype;
- char *optstr;
+ const char *fstype, *optstr;
mnt_tab *tb = NULL;
+ int rc = -ENOMEM;
- if (upd->mountflags & MS_REMOUNT)
- return 0;
+ assert(fs);
+ assert(result);
+
+ DBG(UPDATE, mnt_debug("setting FS root"));
- optstr = (char *) mnt_fs_get_optstr(fs);
+ optstr = mnt_fs_get_optstr(fs);
fstype = mnt_fs_get_fstype(fs);
/*
* bind-mount -- get fs-root and source device for the source filesystem
*/
- if (upd->mountflags & MS_BIND) {
+ if (mountflags & MS_BIND) {
const char *src, *src_root;
mnt_fs *src_fs;
src = mnt_fs_get_srcpath(fs);
- if (!src)
- goto err;
- mnt = mnt_get_mountpoint(src);
- if (!mnt)
+ if (src)
+ mnt = mnt_get_mountpoint(src);
+ if (!mnt) {
+ rc = -EINVAL;
goto err;
+ }
root = mnt_get_fs_root(src, mnt);
tb = mnt_new_tab_from_file(_PATH_PROC_MOUNTINFO);
char *vol = NULL, *p;
size_t sz, volsz = 0;
- if (mnt_optstr_get_option(optstr, "subvol", &vol, &volsz))
+ // TODO: remove this stupid cast...
+ if (mnt_optstr_get_option((char *) optstr, "subvol", &vol, &volsz))
goto dflt;
sz = volsz;
memcpy(p, vol, volsz);
*(root + sz) = '\0';
}
-
dflt:
mnt_free_tab(tb);
- if (!root)
+ if (!root) {
root = strdup("/");
- if (!root)
- goto err;
- fs->root = root;
+ if (!root)
+ goto err;
+ }
+ *result = root;
free(mnt);
return 0;
err:
free(root);
free(mnt);
- return -1;
-}
-
-/**
- * mnt_update_is_pointless:
- * @upd: update
- *
- * This function returns 1 if the previous mnt_prepare_update() call returned 1
- * too.
- *
- * Returns: status of the update.
- */
-int mnt_update_is_pointless(mnt_update *upd)
-{
- return upd ? upd->pointless : 0;
+ return rc;
}
-/**
- * mnt_prepare_update:
- * @upd: update
- *
- * Prepares internal data for mtab update:
- * - set filename if mnt_update_set_filename() wasn't called
- * - set file format if mnt_update_set_format() wasn't called
- * - bitwise-OR mountflags from mount options
- * - for /var/run/mount/mountinfo:
- * * evaluate if the update is necessary
- * * set fs root and devname for bind mount and btrfs subvolumes
- * * removes unnecessary mount options
- * - allocate update_lock if necessary
- *
- * This function has to be always called before mount(2). The mnt_update_file()
- * should not be called if mnt_prepare_update() returns non-zero value.
- *
- * Returns: 0 on success, 1 if update is unnecessary, negative in case of error
- */
-int mnt_prepare_update(mnt_update *upd)
+/* mtab and fstab update */
+static int fprintf_mtab_fs(FILE *f, mnt_fs *fs)
{
- char *u = NULL;
- const char *o = NULL;
- int rc = 0;
-
- assert(upd);
- assert(upd->fs);
-
- if (!upd || !upd->fs)
- return -EINVAL;
+ char *m1, *m2, *m3, *m4;
+ int rc;
- DBG(UPDATE, mnt_debug_h(upd,
- "prepare update (target=%s, source=%s, optstr=%s)",
- mnt_fs_get_target(upd->fs),
- mnt_fs_get_source(upd->fs),
- mnt_fs_get_optstr(upd->fs)));
-
- if (!upd->filename) {
- const char *p = mnt_get_writable_mtab_path();
-
- if (!p) {
- if (errno) {
- rc = -errno;
- goto err; /* EACCES? */
- }
- goto nothing; /* no mtab */
- }
- upd->filename = strdup(p);
- if (!upd->filename) {
- rc = -ENOMEM;
- goto err;
- }
- }
+ assert(fs);
+ assert(f);
- DBG(UPDATE, mnt_debug_h(upd, "filename: %s", upd->filename));
+ m1 = mangle(mnt_fs_get_source(fs));
+ m2 = mangle(mnt_fs_get_target(fs));
+ m3 = mangle(mnt_fs_get_fstype(fs));
+ m4 = mangle(mnt_fs_get_optstr(fs));
- if (!upd->format) {
- if (endswith(upd->filename, "mountinfo"))
- upd->format = MNT_FMT_MOUNTINFO;
- else if (endswith(upd->filename, "fstab"))
- upd->format = MNT_FMT_FSTAB;
- else
- upd->format = MNT_FMT_MTAB;
- }
+ if (m1 && m2 && m3 && m4)
+ rc = !fprintf(f, "%s %s %s %s %d %d\n",
+ m1, m2, m3, m4,
+ mnt_fs_get_freq(fs),
+ mnt_fs_get_passno(fs));
+ else
+ rc = -ENOMEM;
- DBG(UPDATE, mnt_debug_h(upd, "format: %s",
- upd->format == MNT_FMT_MOUNTINFO ? "mountinfo" :
- upd->format == MNT_FMT_FSTAB ? "fstab" : "mtab"));
+ free(m1);
+ free(m2);
+ free(m3);
+ free(m4);
- /* TODO: cannonicalize source and target paths on mnt->fs */
+ return rc;
+}
- if (upd->format != MNT_FMT_FSTAB) {
- unsigned long fl = 0;
+static int fprintf_utab_fs(FILE *f, mnt_fs *fs)
+{
+ char *root = NULL, *target = NULL, *optstr = NULL,
+ *fstype = NULL, *source = NULL;
+ int rc = -1;
+ dev_t devno;
- o = mnt_fs_get_optstr(upd->fs);
- if (o && !mnt_optstr_get_mountflags(o, &fl))
- upd->mountflags |= fl;
- }
+ assert(fs);
+ assert(f);
- /* umount */
- if (upd->action == MNT_ACT_UMOUNT)
- goto done;
+ if (!fs || !f)
+ return -EINVAL;
+ devno = mnt_fs_get_devno(fs);
+ source = mangle(mnt_fs_get_source(fs));
+ root = mangle(mnt_fs_get_root(fs));
+ target = mangle(mnt_fs_get_target(fs));
+ fstype = mangle(mnt_fs_get_fstype(fs));
+ optstr = mangle(mnt_fs_get_optstr(fs));
- /*
- * A) classic /etc/mtab or /etc/fstab update
- */
- if (upd->format != MNT_FMT_MOUNTINFO)
+ if (!root || !target || !optstr)
goto done;
- /*
- * B) /var/run/mount/mountinfo
- * - remove all non-userspace mount options
- */
- if (upd->mountflags & MS_REMOUNT) {
- rc = mnt_split_optstr(o, &u, NULL, NULL, MNT_NOMTAB, 0);
- if (rc)
- goto err;
- rc = __mnt_fs_set_optstr_ptr(upd->fs, u, FALSE);
- if (rc)
- goto err;
- u = NULL;
- } else {
- if (!o)
- goto nothing; /* no options */
- rc = mnt_split_optstr(o, &u, NULL, NULL, MNT_NOMTAB, 0);
- if (rc)
- goto err;
- if (!u)
- goto nothing; /* no userpsace options */
- rc = set_fs_root(upd, upd->fs);
- if (rc)
- goto err;
- rc = __mnt_fs_set_optstr_ptr(upd->fs, u, FALSE);
- if (rc)
- goto err;
- u = NULL;
- }
-
+ rc = fprintf(f, "%i %i %u:%u %s %s %s - %s %s %s\n",
+ mnt_fs_get_id(fs),
+ mnt_fs_get_parent_id(fs),
+ major(devno), minor(devno),
+ root,
+ target,
+ optstr,
+ fstype ? fstype : "auto",
+ source ? source : "none",
+ "none");
+ rc = 0;
done:
- if (!upd->nolock && !upd->lc) {
- upd->lc = mnt_new_lock(upd->filename, 0);
- if (!upd->lc) {
- rc = -ENOMEM;
- goto err;
- }
- }
-
- DBG(UPDATE, mnt_debug_h(upd, "prepare update: success"));
- return 0;
-err:
- free(u);
- DBG(UPDATE, mnt_debug_h(upd, "prepare update: failed"));
+ free(root);
+ free(target);
+ free(optstr);
+ free(fstype);
+ free(source);
return rc;
-nothing:
- upd->pointless = TRUE;
- DBG(UPDATE, mnt_debug_h(upd, "prepare update: pointless"));
- return 1;
}
-static int add_entry(mnt_update *upd)
+static int update_tab(mnt_update *upd, const char *filename, mnt_tab *tb)
{
FILE *f;
- int rc = -1;
+ int rc, fd;
+ char *uq = NULL;
- assert(upd);
+ if (!tb || !filename)
+ return -EINVAL;
- DBG(UPDATE, mnt_debug_h(upd, "add entry"));
+ DBG(UPDATE, mnt_debug_h(upd, "%s: updating", filename));
- if (upd->lc)
- mnt_lock_file(upd->lc);
- f = fopen(upd->filename, "a+");
+ fd = mnt_open_uniq_filename(filename, &uq, O_WRONLY);
+ if (fd < 0)
+ return fd; /* error */
+
+ f = fdopen(fd, "w");
if (f) {
- if (upd->format == MNT_FMT_MOUNTINFO)
- rc = fprintf_mountinfo_fs(f, upd->fs);
- else
- rc = fprintf_mtab_fs(f, upd->fs);
+ struct stat st;
+ mnt_iter itr;
+ mnt_fs *fs;
+ int fd;
+
+ mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+ while(mnt_tab_next_fs(tb, &itr, &fs) == 0) {
+ if (upd->userspace_only)
+ fprintf_utab_fs(f, fs);
+ else
+ fprintf_mtab_fs(f, fs);
+ }
+ fd = fileno(f);
+ 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);
+ rc = rename(uq, filename) ? -errno : 0;
+ } else {
+ rc = -errno;
+ close(fd);
}
- if (upd->lc)
- mnt_unlock_file(upd->lc);
+
+ unlink(uq); /* be paranoid */
+ free(uq);
return rc;
}
-static int remove_entry(mnt_update *upd)
+static int utab_lock(const char *filename)
{
- const char *target;
- mnt_tab *tb = NULL;
- mnt_fs *fs = NULL;
- int rc = -1;
+ char *lfile;
+ int fd;
- assert(upd);
- assert(upd->filename);
+ assert(filename);
- target = mnt_fs_get_target(upd->fs);
- assert(target);
+ if (asprintf(&lfile, "%s.lock", filename) == -1)
+ return -1;
- DBG(UPDATE, mnt_debug_h(upd, "remove entry (target %s)", target));
+ DBG(UPDATE, mnt_debug("%s: locking", lfile));
- if (upd->lc)
- mnt_lock_file(upd->lc);
- tb = mnt_new_tab_from_file(upd->filename);
- if (!tb)
- goto done;
- fs = mnt_tab_find_target(tb, target, MNT_ITER_BACKWARD);
- if (!fs) {
- rc = 0; /* no error if the file does not contain the target */
- goto done;
+ fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
+ free(lfile);
+
+ if (fd < 0)
+ return -errno;
+ if (flock(fd, LOCK_EX)) {
+ int errsv = errno;
+ close(fd);
+ return -errsv;
}
- mnt_tab_remove_fs(tb, fs);
+ return fd;
+}
- if (!update_file(upd->filename, upd->format, tb))
- rc = 0;
-done:
- if (upd->lc)
- mnt_unlock_file(upd->lc);
- mnt_free_tab(tb);
- mnt_free_fs(fs);
- return rc;
+static void utab_unlock(int fd)
+{
+ if (fd >= 0) {
+ DBG(UPDATE, mnt_debug("unlocking utab"));
+ close(fd);
+ }
}
-static int modify_target(mnt_update *upd)
+static int update_add_entry(mnt_update *upd, const char *filename, mnt_lock *lc)
{
- mnt_tab *tb = NULL;
- mnt_fs *fs = NULL;
- int rc = -1;
+ FILE *f;
+ int rc = 0, u_lc = -1;
assert(upd);
- assert(upd->old_target);
- assert(upd->filename);
- assert(mnt_fs_get_target(upd->fs));
+ assert(upd->fs);
- if (!upd->old_target)
- return -1;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", filename));
- DBG(UPDATE, mnt_debug_h(upd, "modify target (%s->%s)",
- upd->old_target, mnt_fs_get_target(upd->fs)));
+ if (lc)
+ mnt_lock_file(lc);
+ else if (upd->userspace_only)
+ u_lc = utab_lock(filename);
- if (upd->lc)
- mnt_lock_file(upd->lc);
- tb = mnt_new_tab_from_file(upd->filename);
- if (!tb)
- goto done;
- fs = mnt_tab_find_target(tb, upd->old_target, MNT_ITER_BACKWARD);
- if (!fs) {
- rc = 0; /* no error if the file does not contain the target */
- goto done;
+ f = fopen(filename, "a+");
+ if (f) {
+ rc = upd->userspace_only ? fprintf_utab_fs(f, upd->fs) :
+ fprintf_mtab_fs(f, upd->fs);
+ DBG(UPDATE, mnt_debug_h(upd, "%s: add [rc=%d]", filename, rc));
+ fclose(f);
+ } else {
+ DBG(UPDATE, mnt_debug_h(upd, "%s: failed: %m", filename));
+ rc = -errno;
}
+ if (lc)
+ mnt_unlock_file(lc);
+ else if (u_lc != -1)
+ utab_unlock(u_lc);
+ return rc;
+}
- mnt_fs_set_target(fs, mnt_fs_get_target(upd->fs));
+static int update_remove_entry(mnt_update *upd, const char *filename, mnt_lock *lc)
+{
+ mnt_tab *tb;
+ int rc = -EINVAL, u_lc = -1;
- if (!update_file(upd->filename, upd->format, tb))
- rc = 0;
-done:
- if (upd->lc)
- mnt_unlock_file(upd->lc);
- mnt_free_tab(tb);
+ assert(upd);
+ assert(upd->target);
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", filename));
+
+ if (lc)
+ mnt_lock_file(lc);
+ else if (upd->userspace_only)
+ u_lc = utab_lock(filename);
+
+ tb = mnt_new_tab_from_file(filename);
+ if (tb) {
+ mnt_fs *rem = mnt_tab_find_target(tb, upd->target, MNT_ITER_BACKWARD);
+ if (rem) {
+ mnt_tab_remove_fs(tb, rem);
+ rc = update_tab(upd, filename, tb);
+ mnt_free_fs(rem);
+ }
+ mnt_free_tab(tb);
+ }
+ if (lc)
+ mnt_unlock_file(lc);
+ else if (u_lc != -1)
+ utab_unlock(u_lc);
return rc;
}
-static int modify_options(mnt_update *upd)
+static int update_modify_target(mnt_update *upd, const char *filename, mnt_lock *lc)
{
mnt_tab *tb = NULL;
- mnt_fs *fs = NULL, *rem_fs = NULL;
- int rc = -1;
- const char *target = mnt_fs_get_target(upd->fs);
+ int rc = -EINVAL, u_lc = -1;
+
+ DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", filename));
+
+ if (lc)
+ mnt_lock_file(lc);
+ else if (upd->userspace_only)
+ u_lc = utab_lock(filename);
+
+ tb = mnt_new_tab_from_file(filename);
+ if (tb) {
+ mnt_fs *cur = mnt_tab_find_target(tb, upd->target, MNT_ITER_BACKWARD);
+ if (cur) {
+ rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs));
+ if (!rc)
+ rc = update_tab(upd, filename, tb);
+ }
+ mnt_free_tab(tb);
+ }
+ if (lc)
+ mnt_unlock_file(lc);
+ else if (u_lc != -1)
+ utab_unlock(u_lc);
+ return rc;
+}
- assert(target);
- assert(upd->filename);
+static int update_modify_options(mnt_update *upd, const char *filename, mnt_lock *lc)
+{
+ mnt_tab *tb = NULL;
+ int rc = -EINVAL, u_lc = -1;
- DBG(UPDATE, mnt_debug_h(upd, "modify options (target %s)", target));
+ assert(upd);
+ assert(upd->fs);
- if (upd->lc)
- mnt_lock_file(upd->lc);
- tb = mnt_new_tab_from_file(upd->filename);
- if (!tb)
- goto done;
- fs = mnt_tab_find_target(tb, target, MNT_ITER_BACKWARD);
- if (!fs) {
- rc = 0; /* no error if the file does not contain the target */
- goto done;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", filename));
+
+ if (lc)
+ mnt_lock_file(lc);
+ else if (upd->userspace_only)
+ u_lc = utab_lock(filename);
+
+ tb = mnt_new_tab_from_file(filename);
+ if (tb) {
+ mnt_fs *cur = mnt_tab_find_target(tb,
+ mnt_fs_get_target(upd->fs),
+ MNT_ITER_BACKWARD);
+ if (cur) {
+ rc = mnt_fs_set_optstr(cur, mnt_fs_get_optstr(upd->fs));
+ if (!rc)
+ rc = update_tab(upd, filename, tb);
+ }
+ mnt_free_tab(tb);
}
- if (upd->format == MNT_FMT_MOUNTINFO && !mnt_fs_get_optstr(upd->fs)) {
- mnt_tab_remove_fs(tb, fs);
- rem_fs = fs;
- } else
- rc = __mnt_fs_set_optstr(fs, mnt_fs_get_optstr(upd->fs), FALSE);
+ if (lc)
+ mnt_unlock_file(lc);
+ else if (u_lc != -1)
+ utab_unlock(u_lc);
- if (!update_file(upd->filename, upd->format, tb))
- rc = 0;
-done:
- if (upd->lc)
- mnt_unlock_file(upd->lc);
- mnt_free_tab(tb);
- mnt_free_fs(rem_fs);
return rc;
}
/**
- * mnt_update_file:
- * @upd: update
+ * mnt_update_tab:
+ * @filename: mtab of utab filename
+ * @lc: lock
*
- * Updates the update file. The behavior of this function is undefined if
- * mnt_prepare_update() has not been called. The request to update file will
- * be ignored for pointless updates (see mnt_update_is_pointless()).
+ * High-level API to update /etc/mtab or /dev/.mount/utab.
*
- * Returns: 0 on success, -1 in case of error.
+ * Returns: 0 on success, negative number on error.
*/
-int mnt_update_file(mnt_update *upd)
+int mnt_update_tab(mnt_update *upd, const char *filename, mnt_lock *lc)
{
+ int rc = -EINVAL;
+
assert(upd);
- assert(upd->filename);
- assert(upd->format);
- assert(upd->fs);
+ assert(filename);
- if (!upd || !upd->fs)
- return -1;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", filename));
- if (upd->pointless) {
- DBG(UPDATE, mnt_debug_h(upd, "ingnore update requiest (pointless)"));
+ if (!filename || !upd)
+ return -EINVAL;
+ if (!upd->ready)
return 0;
- }
-
- DBG(UPDATE, mnt_debug_h(upd, "update (target %s)",
- mnt_fs_get_target(upd->fs)));
- /*
- * umount
- */
- if (upd->action == MNT_ACT_UMOUNT)
- return remove_entry(upd);
- /*
- * mount
- */
- if (upd->action == MNT_ACT_MOUNT) {
- if (upd->mountflags & MS_REMOUNT)
- return modify_options(upd);
- if (upd->mountflags & MS_MOVE)
- return modify_target(upd);
-
- return add_entry(upd); /* mount */
- }
- return -1;
+ if (!upd->fs && upd->target)
+ rc = update_remove_entry(upd, filename, lc); /* umount */
+ else if (upd->mountflags & MS_MOVE)
+ rc = update_modify_target(upd, filename, lc); /* move */
+ else if (upd->mountflags & MS_REMOUNT)
+ rc = update_modify_options(upd, filename, lc); /* remount */
+ else if (upd->fs)
+ rc = update_add_entry(upd, filename, lc); /* mount */
+
+ upd->ready = FALSE;
+ DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]", filename, rc));
+ return rc;
}
#ifdef TEST_PROGRAM
mnt_unlock_file(lock);
}
-static int update(mnt_update *upd)
+static int update(const char *target, mnt_fs *fs, unsigned long mountflags)
{
- int rc;
+ int rc, writable = 0;
+ const char *filename = NULL;
+ mnt_update *upd;
+ mnt_lock *lock = NULL;
- /*
- * Note that mount(2) syscal should be called *after*
- * mnt_prepare_update() and *before* mnt_update_file()
- */
- rc = mnt_prepare_update(upd);
- if (!rc) {
- /* setup lock fallback */
- int rc;
+ DBG(UPDATE, mnt_debug("update test"));
- lock = mnt_update_get_lock(upd);
- atexit(lock_fallback);
+ rc = mnt_has_regular_mtab(&filename, &writable);
+ if (rc && writable) {
+ upd = mnt_new_update(FALSE);
+ lock = mnt_new_lock(filename, 0);
- rc = mnt_update_file(upd);
- lock = NULL;
- return rc;
- }
- if (rc == 1)
- return 0;
- fprintf(stderr, "update: failed to prepare update\n");
- return -1;
-}
+ /* note that proper solution is to call mnt_unlock_file() from
+ * signal handler. The atexit() could be ignore if program ends
+ * by _exit(). The _exit() function is usually used in signal
+ * handlers.
+ */
+ atexit(lock_fallback);
-int test_add(struct mtest *ts, int argc, char *argv[])
-{
- mnt_fs *fs = mnt_new_fs();
- mnt_update *upd;
- int rc = -1;
+ } else {
+ filename = NULL;
+ rc = mnt_has_regular_utab(&filename, &writable);
- 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_optstr(fs, argv[4]);
+ if (rc && writable)
+ upd = mnt_new_update(TRUE);
+ else
+ return -EACCES;
+ }
- upd = mnt_new_update(MNT_ACT_MOUNT, 0, fs);
- if (!upd)
- return -1;
+ rc = mnt_update_set_fs(upd, mountflags, target, fs);
+ if (rc)
+ goto done;
+ if (rc == 1) {
+ fprintf(stderr, "update is unnecessary\n");
+ rc = 0;
+ goto done;
+ }
- rc = update(upd);
+ /* [... here should be mount(2) call ...] */
- mnt_free_update(upd);
- mnt_free_fs(fs);
+ rc = mnt_update_tab(upd, filename, lock);
+done:
return rc;
}
-int test_add_fstab(struct mtest *ts, int argc, char *argv[])
+static int test_add(struct mtest *ts, int argc, char *argv[])
{
mnt_fs *fs = mnt_new_fs();
- mnt_update *upd;
- int rc = -1;
+ int rc;
- if (argc < 7 || !fs)
+ 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_optstr(fs, argv[4]);
- mnt_fs_set_freq(fs, atoi(argv[5]));
- mnt_fs_set_passno(fs, atoi(argv[6]));
-
- /* this is tricky -- to add to fstab use "MNT_ACT_MOUNT" */
- upd = mnt_new_update(MNT_ACT_MOUNT, 0, fs);
- if (!upd)
- return -1;
-
- mnt_update_disable_lock(upd, TRUE); /* lock is unnecessary */
-
- mnt_update_set_filename(upd, mnt_get_fstab_path());
- mnt_update_set_format(upd, MNT_FMT_FSTAB);
-
- rc = update(upd);
- mnt_free_update(upd);
+ rc = update(NULL, fs, 0);
mnt_free_fs(fs);
return rc;
}
-int test_remove(struct mtest *ts, int argc, char *argv[])
+static int test_remove(struct mtest *ts, int argc, char *argv[])
{
- mnt_fs *fs = mnt_new_fs();
- mnt_update *upd;
- int rc = -1;
-
- if (argc < 2 || !fs)
- return -1;
- mnt_fs_set_target(fs, argv[1]);
-
- upd = mnt_new_update(MNT_ACT_UMOUNT, 0, fs);
- if (!upd)
+ if (argc < 2)
return -1;
-
- rc = update(upd);
-
- mnt_free_update(upd);
- mnt_free_fs(fs);
- return rc;
+ return update(argv[1], NULL, 0);
}
-int test_move(struct mtest *ts, int argc, char *argv[])
+static int test_move(struct mtest *ts, int argc, char *argv[])
{
mnt_fs *fs = mnt_new_fs();
- mnt_update *upd;
- int rc = -1;
+ int rc;
- if (argc < 3 || !fs)
+ if (argc < 3)
return -1;
mnt_fs_set_target(fs, argv[2]);
+ rc = update(argv[1], fs, MS_MOVE);
- upd = mnt_new_update(MNT_ACT_MOUNT, MS_MOVE, fs);
- if (!upd)
- return -1;
- mnt_update_set_old_target(upd, argv[1]);
-
- rc = update(upd);
-
- mnt_free_update(upd);
mnt_free_fs(fs);
return rc;
}
-int test_remount(struct mtest *ts, int argc, char *argv[])
+static int test_remount(struct mtest *ts, int argc, char *argv[])
{
mnt_fs *fs = mnt_new_fs();
- mnt_update *upd;
- int rc = -1;
+ int rc;
- if (argc < 3 || !fs)
+ if (argc < 3)
return -1;
-
mnt_fs_set_target(fs, argv[1]);
mnt_fs_set_optstr(fs, argv[2]);
- upd = mnt_new_update(MNT_ACT_MOUNT, MS_REMOUNT, fs);
- if (!upd)
- return -1;
-
- rc = update(upd);
-
- mnt_free_update(upd);
+ rc = update(NULL, fs, MS_REMOUNT);
mnt_free_fs(fs);
return rc;
}
{ "--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" },
-
- { "--add-fstab", test_add_fstab, "<src> <target> <type> <options> <freq> <passno> add line to fstab" },
-
{ NULL }
};