summaryrefslogtreecommitdiffstats
path: root/libmount/src/tab_update.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src/tab_update.c')
-rw-r--r--libmount/src/tab_update.c318
1 files changed, 255 insertions, 63 deletions
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c
index f5e5d30..87512af 100644
--- a/libmount/src/tab_update.c
+++ b/libmount/src/tab_update.c
@@ -35,9 +35,15 @@ struct libmnt_update {
struct libmnt_fs *fs;
char *filename;
unsigned long mountflags;
- int ready;
+
+ int act_fd;
+ char *act_filename;
+
+ unsigned int ready : 1,
+ missing_options : 1;
struct libmnt_table *mountinfo;
+ struct libmnt_lock *lock;
};
static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
@@ -56,6 +62,7 @@ struct libmnt_update *mnt_new_update(void)
if (!upd)
return NULL;
+ upd->act_fd = -1;
DBG(UPDATE, ul_debugobj(upd, "allocate"));
return upd;
}
@@ -73,10 +80,14 @@ void mnt_free_update(struct libmnt_update *upd)
DBG(UPDATE, ul_debugobj(upd, "free"));
+ mnt_unref_lock(upd->lock);
mnt_unref_fs(upd->fs);
mnt_unref_table(upd->mountinfo);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
free(upd->target);
free(upd->filename);
+ free(upd->act_filename);
free(upd);
}
@@ -173,7 +184,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
mnt_unref_fs(upd->fs);
free(upd->target);
- upd->ready = FALSE;
+ upd->ready = 0;
upd->fs = NULL;
upd->target = NULL;
upd->mountflags = 0;
@@ -206,7 +217,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
}
DBG(UPDATE, ul_debugobj(upd, "ready"));
- upd->ready = TRUE;
+ upd->ready = 1;
return 0;
}
@@ -666,43 +677,42 @@ static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
return update_table(upd, tb);
}
-static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_add_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
if (tb)
rc = add_file_entry(tb, upd);
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_remove_entry(struct libmnt_update *upd)
{
struct libmnt_table *tb;
int rc = 0;
assert(upd);
assert(upd->target);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -714,23 +724,22 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc
rc = update_table(upd, tb);
}
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
+static int update_modify_target(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
assert(upd);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename));
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -779,14 +788,13 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l
}
done:
- if (lc)
- mnt_unlock_file(lc);
-
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
}
-static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
+/* replaces option in the table entry by new options from @udp */
+static int update_modify_options(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
int rc = 0;
@@ -794,13 +802,13 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
assert(upd);
assert(upd->fs);
+ assert(upd->lock);
DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename));
fs = upd->fs;
- if (lc)
- rc = mnt_lock_file(lc);
+ rc = mnt_lock_file(upd->lock);
if (rc)
return -MNT_ERR_LOCK;
@@ -819,11 +827,77 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
rc = add_file_entry(tb, upd); /* not found, add new */
}
- if (lc)
- mnt_unlock_file(lc);
+ mnt_unlock_file(upd->lock);
+ mnt_unref_table(tb);
+ return rc;
+}
+
+/* add user options missing in the table entry by new options from @udp */
+static int update_add_options(struct libmnt_update *upd)
+{
+ struct libmnt_table *tb = NULL;
+ int rc = 0;
+ struct libmnt_fs *fs;
+
+ assert(upd);
+ assert(upd->fs);
+ assert(upd->lock);
+
+ if (!upd->fs->user_optstr)
+ return 0;
+ DBG(UPDATE, ul_debugobj(upd, "%s: add options", upd->filename));
+
+ fs = upd->fs;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
+ if (tb) {
+ struct libmnt_fs *cur = mnt_table_find_target(tb,
+ mnt_fs_get_target(fs),
+ MNT_ITER_BACKWARD);
+ if (cur) {
+ char *u = NULL;
+
+ rc = mnt_optstr_get_missing(cur->user_optstr, upd->fs->user_optstr, &u);
+ if (!rc && u) {
+ DBG(UPDATE, ul_debugobj(upd, " add missing: %s", u));
+ rc = mnt_optstr_append_option(&cur->user_optstr, u, NULL);
+ }
+ if (!rc && u)
+ rc = update_table(upd, tb);
+
+ if (rc == 1) /* nothing is missing */
+ rc = 0;
+ } else
+ rc = add_file_entry(tb, upd); /* not found, add new */
+ }
+
+ mnt_unlock_file(upd->lock);
mnt_unref_table(tb);
return rc;
+
+}
+
+static int update_init_lock(struct libmnt_update *upd, struct libmnt_lock *lc)
+{
+ assert(upd);
+
+ if (lc) {
+ mnt_unref_lock(upd->lock);
+ mnt_ref_lock(lc);
+ upd->lock = lc;
+ } else if (!upd->lock) {
+ upd->lock = mnt_new_lock(upd->filename, 0);
+ if (!upd->lock)
+ return -ENOMEM;
+ mnt_lock_block_signals(upd->lock, TRUE);
+ }
+
+ return 0;
}
/**
@@ -842,7 +916,6 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *
*/
int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
{
- struct libmnt_lock *lc0 = lc;
int rc = -EINVAL;
if (!upd || !upd->filename)
@@ -854,33 +927,32 @@ int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
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);
- }
+
+ rc = update_init_lock(upd, lc);
+ if (rc)
+ goto done;
if (!upd->fs && upd->target)
- rc = update_remove_entry(upd, lc); /* umount */
+ rc = update_remove_entry(upd); /* umount */
else if (upd->mountflags & MS_MOVE)
- rc = update_modify_target(upd, lc); /* move */
+ rc = update_modify_target(upd); /* move */
else if (upd->mountflags & MS_REMOUNT)
- rc = update_modify_options(upd, lc); /* remount */
+ rc = update_modify_options(upd); /* remount */
+ else if (upd->fs && upd->missing_options)
+ rc = update_add_options(upd); /* mount by externel helper */
else if (upd->fs)
- rc = update_add_entry(upd, lc); /* mount */
+ rc = update_add_entry(upd); /* mount */
- upd->ready = FALSE;
+ upd->ready = 1;
+done:
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)
+int mnt_update_already_done(struct libmnt_update *upd)
{
struct libmnt_table *tb = NULL;
- struct libmnt_lock *lc0 = lc;
int rc = 0;
if (!upd || !upd->filename || (!upd->fs && !upd->target))
@@ -888,37 +960,32 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
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) {
- rc = mnt_lock_file(lc);
- if (rc) {
- rc = -MNT_ERR_LOCK;
- goto done;
- }
- }
-
tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
- if (lc)
- mnt_unlock_file(lc);
if (!tb)
goto done;
if (upd->fs) {
/* mount */
+ struct libmnt_fs *fs;
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)) {
+ fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
+ if (fs) {
DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s",
upd->filename, src, tgt));
+
+ /* Check if utab entry (probably writen by /sbin/mount.<type>
+ * helper) contains all options expected by this update */
+ if (mnt_optstr_get_missing(fs->user_optstr, upd->fs->user_optstr, NULL) == 0) {
+ upd->missing_options = 1;
+ DBG(UPDATE, ul_debugobj(upd, " missing options detected"));
+ }
+ } else
rc = 1;
- }
+
} else if (upd->target) {
/* umount */
if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) {
@@ -930,13 +997,133 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
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;
}
+int mnt_update_emit_event(struct libmnt_update *upd)
+{
+ char *filename;
+ int fd;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (asprintf(&filename, "%s.event", upd->filename) <= 0)
+ return -ENOMEM;
+
+ DBG(UPDATE, ul_debugobj(upd, "emitting utab event"));
+
+ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC,
+ S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
+ free(filename);
+ if (fd < 0)
+ return -errno;
+ close(fd);
+ return 0;
+}
+
+/*
+ * Let's use /run/mount/utab.act file to report to libmount monitor that
+ * libmount is working with utab. In this case, the monitor can ignore all
+ * events from kernel until entire mount (with all steps) is done.
+ *
+ * For example mount NFS with x-* options, means
+ * - create utab.act and mark it as used (by LOCK_SH)
+ * - exec /sbin/mount.nfs
+ * - call mount(2) (kernel event on /proc/self/mounts)
+ * - utab update (NFS stuff)
+ * - utab update (add x-* userspace options)
+ * - unlink utab.act (if not use anyone else)
+ * - release event by /run/mount/utab.event
+ *
+ * Note, this is used only when utab is in the game (x-* options).
+ */
+int mnt_update_start(struct libmnt_update *upd)
+{
+ int rc = 0;
+ mode_t oldmask;
+
+ if (!upd || !upd->filename)
+ return -EINVAL;
+
+ if (!upd->act_filename &&
+ asprintf(&upd->act_filename, "%s.act", upd->filename) <= 0)
+ return -ENOMEM;
+
+ /* Use exclusive lock to avoid some other proces will remove the the
+ * file before it's marked as used by LOCK_SH (below) */
+ rc = update_init_lock(upd, NULL);
+ if (rc)
+ return rc;
+
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ DBG(UPDATE, ul_debugobj(upd, "creating act file"));
+
+ oldmask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+ upd->act_fd = open(upd->act_filename, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
+ umask(oldmask);
+
+ if (upd->act_fd < 0) {
+ rc = -errno;
+ goto fail;
+ }
+
+ /* mark the file as used */
+ rc = flock(upd->act_fd, LOCK_SH);
+ if (rc) {
+ rc = -errno;
+ goto fail;
+ }
+
+ mnt_unlock_file(upd->lock);
+ return 0;
+fail:
+ DBG(UPDATE, ul_debugobj(upd, "act file failed [rc=%d]", rc));
+ mnt_unlock_file(upd->lock);
+ unlink(upd->act_filename);
+ if (upd->act_fd >= 0)
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return rc;
+}
+
+int mnt_update_end(struct libmnt_update *upd)
+{
+ int rc;
+
+ if (!upd || upd->act_fd < 0)
+ return -EINVAL;
+
+ DBG(UPDATE, ul_debugobj(upd, "removing act file"));
+
+ /* make sure nobody else will use the file */
+ rc = mnt_lock_file(upd->lock);
+ if (rc)
+ return -MNT_ERR_LOCK;
+
+ /* mark the file as unused */
+ flock(upd->act_fd, LOCK_UN);
+ errno = 0;
+
+ /* check if nobody else need the file (if yes, then the file is under LOCK_SH) )*/
+ if (flock(upd->act_fd, LOCK_EX | LOCK_NB) != 0) {
+ if (errno == EWOULDBLOCK)
+ DBG(UPDATE, ul_debugobj(upd, "act file used, no unlink"));
+ } else {
+ DBG(UPDATE, ul_debugobj(upd, "unlinking act file"));
+ unlink(upd->act_filename);
+ }
+
+ mnt_unlock_file(upd->lock);
+ close(upd->act_fd);
+ upd->act_fd = -1;
+ return 0;
+}
#ifdef TEST_PROGRAM
@@ -970,7 +1157,8 @@ done:
return rc;
}
-static int test_add(struct libmnt_test *ts, int argc, char *argv[])
+static int test_add(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -987,14 +1175,16 @@ static int test_add(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remove(struct libmnt_test *ts __attribute__((unused)),
+ 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[])
+static int test_move(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1010,7 +1200,8 @@ static int test_move(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
+static int test_remount(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
int rc;
@@ -1025,7 +1216,8 @@ static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
-static int test_replace(struct libmnt_test *ts, int argc, char *argv[])
+static int test_replace(struct libmnt_test *ts __attribute__((unused)),
+ int argc, char *argv[])
{
struct libmnt_fs *fs = mnt_new_fs();
struct libmnt_table *tb = mnt_new_table();