]> err.no Git - util-linux/commitdiff
libmount: rewrite update
authorKarel Zak <kzak@redhat.com>
Mon, 8 Nov 2010 10:14:44 +0000 (11:14 +0100)
committerKarel Zak <kzak@redhat.com>
Mon, 3 Jan 2011 11:28:46 +0000 (12:28 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/context.c
shlibs/mount/src/context_mount.c
shlibs/mount/src/context_umount.c
shlibs/mount/src/lock.c
shlibs/mount/src/mount.h.in
shlibs/mount/src/mountP.h
shlibs/mount/src/tab_update.c
shlibs/mount/src/utils.c

index 21a5d8a1d029d33902c9257505975a25458e3fcd..b4662d89e593697629f8808466b182895ae8ffce 100644 (file)
@@ -39,6 +39,12 @@ mnt_context *mnt_new_context()
        DBG(CXT, mnt_debug_h(cxt, "allocate %s",
                                cxt->restricted ? "[RESTRICTED]" : ""));
 
+       mnt_has_regular_mtab(&cxt->mtab_path, &cxt->mtab_writable);
+
+       if (!cxt->mtab_writable)
+               /* use /dev/.mount/utab if /etc/mtab is useless */
+               mnt_has_regular_mtab(&cxt->utab_path, &cxt->utab_writable);
+
        return cxt;
 }
 
@@ -53,22 +59,20 @@ void mnt_free_context(mnt_context *cxt)
        if (!cxt)
                return;
 
+       mnt_reset_context(cxt);
+
        DBG(CXT, mnt_debug_h(cxt, "free"));
 
        free(cxt->fstype_pattern);
        free(cxt->optstr_pattern);
-       free(cxt->helper);
-       free(cxt->orig_user);
 
-       if (!(cxt->flags & MNT_FL_EXTERN_FS))
-               mnt_free_fs(cxt->fs);
        if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
                mnt_free_tab(cxt->fstab);
        if (!(cxt->flags & MNT_FL_EXTERN_CACHE))
                mnt_free_cache(cxt->cache);
 
+       mnt_free_lock(cxt->lock);
        mnt_free_update(cxt->update);
-       mnt_free_tab(cxt->mtab);
 
        free(cxt);
 }
@@ -101,22 +105,18 @@ int mnt_reset_context(mnt_context *cxt)
 
        fl = cxt->flags;
 
-       if (cxt->update)
-               mnt_update_set_fs(cxt->update, NULL);
-
        if (!(cxt->flags & MNT_FL_EXTERN_FS))
                mnt_free_fs(cxt->fs);
 
        mnt_free_tab(cxt->mtab);
 
-       cxt->fs = NULL;
-       cxt->mtab = NULL;
-
        free(cxt->helper);
        free(cxt->orig_user);
 
+       cxt->fs = NULL;
+       cxt->mtab = NULL;
        cxt->helper = NULL;
-
+       cxt->orig_user = NULL;
        cxt->mountflags = 0;
        cxt->user_mountflags = 0;
        cxt->mountdata = NULL;
@@ -585,7 +585,7 @@ int mnt_context_get_mtab(mnt_context *cxt, mnt_tab **tb)
                cxt->mtab = mnt_new_tab();
                if (!cxt->mtab)
                        return -ENOMEM;
-               rc = mnt_tab_parse_mtab(cxt->mtab, NULL);
+               rc = mnt_tab_parse_mtab(cxt->mtab, cxt->mtab_path);
                if (rc)
                        return rc;
        }
@@ -650,9 +650,6 @@ mnt_cache *mnt_context_get_cache(mnt_context *cxt)
  * mnt_context_get_lock:
  * @cxt: mount context
  *
- * The lock is available after mnt_context_prepare_mount() or
- * mnt_context_prepare_umount().
- *
  * The application that uses libmount context does not have to care about
  * mtab locking, but with a small exceptions: the application has to be able to
  * remove the lock file when interrupted by signal. It means that properly written
@@ -661,16 +658,20 @@ mnt_cache *mnt_context_get_cache(mnt_context *cxt)
  * See also mnt_unlock_file(), mnt_context_disable_lock() and
  * mnt_context_disable_mtab().
  *
- * It's not error if this function returns NULL (it usually means that the
- * context is not prepared yet, or mtab update is unnecessary).
+ * This function returns NULL if mtab file is not writable or nolock or nomtab
+ * flags is enabled.
  *
- * Returns: pointer to lock struct.
+ * Returns: pointer to lock struct or NULL.
  */
 mnt_lock *mnt_context_get_lock(mnt_context *cxt)
 {
-       if (!cxt || !cxt->update || (cxt->flags & (MNT_FL_NOMTAB | MNT_FL_NOLOCK)))
+       if (!cxt || (cxt->flags & MNT_FL_NOMTAB) || !cxt->mtab_writable)
                return NULL;
-       return mnt_update_get_lock(cxt->update);
+
+       if (!cxt->lock && cxt->mtab_path)
+               cxt->lock = mnt_new_lock(cxt->mtab_path, 0);
+
+       return cxt->lock;
 }
 
 /**
@@ -1025,48 +1026,63 @@ int mnt_context_merge_mountflags(mnt_context *cxt)
 }
 
 /*
- * Prepare /etc/mtab or /var/run/mount/mountinfo update
+ * Prepare /etc/mtab or /dev/.mount/utab
  */
-int mnt_context_prepare_update(mnt_context *cxt, int act)
+int mnt_context_prepare_update(mnt_context *cxt)
 {
        int rc;
-       const char *tgt = cxt->fs ? mnt_fs_get_target(cxt->fs) : NULL;
+       const char *target;
 
        assert(cxt);
        assert(cxt->fs);
+       assert(cxt->action);
        assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
 
-       if (act == MNT_ACT_UMOUNT && tgt && !strcmp(tgt, "/"))
+       target = mnt_fs_get_target(cxt->fs);
+
+       if (cxt->action == MNT_ACT_UMOUNT && target && !strcmp(target, "/"))
                /* Don't try to touch mtab if umounting root FS */
                cxt->flags |= MNT_FL_NOMTAB;
 
        if ((cxt->flags & MNT_FL_NOMTAB) || cxt->helper)
                return 0;
+       if (!cxt->mtab_writable && !cxt->utab_writable)
+               return 0;
 
        if (!cxt->update) {
-               cxt->update = mnt_new_update(act, cxt->mountflags, cxt->fs);
+               cxt->update = mnt_new_update(!cxt->mtab_writable);
                if (!cxt->update)
                        return -ENOMEM;
-       } else {
-               rc = mnt_update_set_action(cxt->update, act);
-               if (!rc)
-                       rc = mnt_update_set_mountflags(cxt->update, cxt->mountflags);
-               if (!rc)
-                       rc = mnt_update_set_fs(cxt->update, cxt->fs);
-               if (rc)
-                       return rc;
        }
 
-       if (cxt->flags & MNT_FL_NOLOCK)
-               mnt_update_disable_lock(cxt->update, TRUE);
+       rc = mnt_update_set_fs(cxt->update, cxt->mountflags,
+                         mnt_fs_get_target(cxt->fs),
+                         cxt->action == MNT_ACT_UMOUNT ? NULL : cxt->fs);
 
-       rc = mnt_prepare_update(cxt->update);
+       return rc < 0 ? rc : 0;
+}
 
-       if (rc == 1)
-               /* mtab update is unnecessary for this system */
-               rc = 0;
+int mnt_context_update_tabs(mnt_context *cxt)
+{
+       const char *filename;
+       mnt_lock *lock = NULL;
 
-       return rc;
+       assert(cxt);
+
+       if (cxt->flags & MNT_FL_NOMTAB)
+               return 0;
+       if (!cxt->update || !mnt_update_is_ready(cxt->update))
+               return 0;
+
+       if (mnt_update_is_userspace_only(cxt->update))
+               filename = cxt->utab_path;
+       else {
+               filename = cxt->mtab_path;
+               lock = mnt_context_get_lock(cxt);
+       }
+       assert(filename);
+
+       return mnt_update_tab(cxt->update, filename, lock);
 }
 
 static int is_remount(mnt_context *cxt)
@@ -1199,6 +1215,19 @@ err:
        return rc;
 }
 
+/**
+ * mnt_context_strerror
+ * @cxt: context
+ * @buf: buffer
+ * @bufsiz: size of the buffer
+ *
+ * Returns: 0 or negative number in case of error.
+ */
+int mnt_context_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
+{
+       /* TODO: based on cxt->syscall_errno or cxt->helper_status */
+       return 0;
+}
 
 #ifdef TEST_PROGRAM
 
index e12e0a933f687bfeed653d4332a154df6bf11c46..de6557df42ee3298ca6936dd817a61f275b9c884 100644 (file)
@@ -363,32 +363,30 @@ static int do_mount(mnt_context *cxt, const char *try_type)
 }
 
 /**
- * mnt_context_prepare_mount:
+ * mnt_context_do_mount:
  * @cxt: mount context
  *
- * This function:
- *     - read information from fstab (if necessary)
- *     - cleanup mount options
- *     - check premissions
- *     - prepare device (e.g. loop device)
- *     - detect FS type (if necessary)
- *     - generate mount flags and mount data (if not set yet)
- *     - prepare for mtab update (if necessary)
+ * Mount filesystem by mount(2) or fork()+exec(/sbin/mount.<type>).
  *
- * It's strongly recommended to use this function before mnt_context_mount_fs().
+ * See also mnt_context_disable_helpers().
  *
  * Returns: 0 on success, and negative number in case of error.
  */
-int mnt_context_prepare_mount(mnt_context *cxt)
+int mnt_context_do_mount(mnt_context *cxt)
 {
-       int rc = 0;
+       int rc = -EINVAL;
+       const char *type;
 
-       if (!cxt)
-               return -EINVAL;
+       assert(cxt);
+       assert(cxt->fs);
+       assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
 
-       if (!cxt->fs || (!mnt_fs_get_source(cxt->fs) &&
-                        !mnt_fs_get_target(cxt->fs)))
+       if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
                return -EINVAL;
+       if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
+               return -EINVAL;
+
+       cxt->action = MNT_ACT_MOUNT;
 
        DBG(CXT, mnt_debug_h(cxt, "mount: preparing"));
 
@@ -406,39 +404,13 @@ int mnt_context_prepare_mount(mnt_context *cxt)
        if (!rc)
                rc = mnt_context_prepare_helper(cxt, "mount", NULL);
        if (!rc)
-               rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT);
+               rc = mnt_context_prepare_update(cxt);
 
-       if (!rc) {
-               DBG(CXT, mnt_debug_h(cxt, "sucessfully prepared"));
-               return 0;
+       if (rc) {
+               DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed"));
+               return rc;
        }
 
-       DBG(CXT, mnt_debug_h(cxt, "prepare failed"));
-       return rc;
-}
-
-/**
- * mnt_context_do_mount:
- * @cxt: mount context
- *
- * Mount filesystem by mount(2) or fork()+exec(/sbin/mount.<type>).
- *
- * See also mnt_context_disable_helpers().
- *
- * Returns: 0 on success, and negative number in case of error.
- */
-int mnt_context_do_mount(mnt_context *cxt)
-{
-       int rc = -EINVAL;
-       const char *type;
-
-       assert(cxt);
-       assert(cxt->fs);
-       assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
-
-       if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
-               return -EINVAL;
-
        DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));
 
        if (!(cxt->flags & MNT_FL_MOUNTDATA))
@@ -456,55 +428,10 @@ int mnt_context_do_mount(mnt_context *cxt)
 
        /* TODO: try all filesystems from /proc/filesystems and /etc/filesystems */
 
-       return rc;
-}
-
-/**
- * mnt_context_post_mount:
- * @cxt: mount context
- *
- * Updates mtab, etc. This function should be always called after
- * mnt_context_do_mount().
- *
- * Returns: 0 on success, and negative number in case of error.
- */
-int mnt_context_post_mount(mnt_context *cxt)
-{
-       int rc = 0;
-
-       assert(cxt);
-       assert(cxt->fs);
-       assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
-
-       if (!cxt)
-               return -EINVAL;
-       /*
-        * Update /etc/mtab or /var/run/mount/mountinfo
+       /* TODO: if mtab update is expected then check if the
+        * target is really mounted read-write to avoid 'ro' in
+        * mtab and 'rw' in /proc/mounts.
         */
-       if (!cxt->syscall_errno && !cxt->helper &&
-           !(cxt->flags & MNT_FL_NOMTAB) &&
-           cxt->update && !mnt_update_is_pointless(cxt->update)) {
-
-               /* TODO: if mtab update is expected then checkif the target is really
-                *       mounted read-write to avoid 'ro' in mtab and 'rw' in /proc/mounts.
-                */
-               rc = mnt_update_file(cxt->update);
-       }
-
-       return rc;
-}
-
-/**
- * mnt_context_mount_strerror
- * @cxt: mount context
- * @buf: buffer
- * @bufsiz: size of the buffer
- *
- * Returns: 0 or negative number in case of error.
- */
-int mnt_context_mount_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
-{
-       /* TODO: based on cxt->syscall_errno or cxt->helper_status */
-       return 0;
+       return mnt_context_update_tabs(cxt);
 }
 
index b2590be945df86ae97f745f3b69706a26fbbb95a..476ee515c23dd45231fbf6fb12e9650291485826 100644 (file)
@@ -413,32 +413,29 @@ static int do_umount(mnt_context *cxt)
 }
 
 /**
- * mnt_context_prepare_umount:
+ * mnt_context_do_umount:
  * @cxt: mount context
  *
- * This function:
- *     - read information from fstab/mtab (if necessary)
- *     - check premissions
- *     - prepare for mtab update (if necessary)
+ * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.<type>).
  *
- * It's strongly recommended to use this function before mnt_context_do_umount().
+ * See also mnt_context_disable_helpers().
  *
  * Returns: 0 on success, and negative number in case of error.
  */
-int mnt_context_prepare_umount(mnt_context *cxt)
+int mnt_context_do_umount(mnt_context *cxt)
 {
-       int rc = 0;
+       int rc;
 
-       if (!cxt)
+       if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
                return -EINVAL;
-
-       if (!cxt->fs || (!mnt_fs_get_source(cxt->fs) &&
-                        !mnt_fs_get_target(cxt->fs)))
+       if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
                return -EINVAL;
 
        free(cxt->helper);      /* be paranoid */
        cxt->helper = NULL;
 
+       cxt->action = MNT_ACT_UMOUNT;
+
        rc = lookup_umount_fs(cxt);
        if (!rc)
                rc = mnt_context_merge_mountflags(cxt);
@@ -452,51 +449,15 @@ int mnt_context_prepare_umount(mnt_context *cxt)
                cxt->flags &= ~MNT_FL_LOOPDEL;
 */
        if (!rc)
-               rc = mnt_context_prepare_update(cxt, MNT_ACT_UMOUNT);
-       if (!rc) {
-               DBG(CXT, mnt_debug_h(cxt, "umount sucessfully prepared"));
-               return 0;
+               rc = mnt_context_prepare_update(cxt);
+       if (rc) {
+               DBG(CXT, mnt_debug_h(cxt, "prepared umount failed"));
+               return rc;
        }
 
-       DBG(CXT, mnt_debug_h(cxt, "umount prepare failed"));
-       return rc;
-}
-
-/**
- * mnt_context_do_umount:
- * @cxt: mount context
- *
- * Umount filesystem by umount(2) or fork()+exec(/sbin/umount.<type>).
- *
- * See also mnt_context_disable_helpers().
- *
- * Returns: 0 on success, and negative number in case of error.
- */
-int mnt_context_do_umount(mnt_context *cxt)
-{
-       if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
-               return -EINVAL;
-
-       return do_umount(cxt);
-}
-
-/**
- * mnt_context_post_umount:
- * @cxt: mount context
- *
- * Updates mtab and detroy loopdev etc. This function should be always called after
- * mnt_context_do_umount().
- *
- * Returns: 0 on success, and negative number in case of error.
- */
-int mnt_context_post_umount(mnt_context *cxt)
-{
-       int rc = 0;
-
-       if (!cxt)
-               return -EINVAL;
-       if (cxt->syscall_errno || cxt->helper)
-               return 0;
+       rc = do_umount(cxt);
+       if (rc)
+               return rc;
 /* TODO
        if (cxt->flags & MNT_FL_LOOPDEL)
                rc = mnt_loopdev_clean(mnt_fs_get_source(cxt->fs));
@@ -507,34 +468,23 @@ int mnt_context_post_umount(mnt_context *cxt)
        if ((cxt->flags & MNT_FL_RDONLY_UMOUNT) &&
            (cxt->mountflags & (MS_RDONLY | MS_REMOUNT))) {
                /*
-                * refresh update to handle remount to read-only
+                * update options to handle remount to read-only
                 */
-               rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT);
-               if (rc)
-                       return rc;
-       }
+               const char *o = mnt_fs_get_optstr(cxt->fs);
+               char *n = o ? strdup(o) : NULL;
 
-       /*
-        * Update /etc/mtab or /var/run/mount/mountinfo
-        */
-       if (cxt->update && !mnt_update_is_pointless(cxt->update)) {
-               rc = mnt_update_file(cxt->update);
+               if (n)
+                       mnt_optstr_remove_option(&n, "rw");
+               rc = mnt_optstr_prepend_option(&n, "ro", NULL);
                if (!rc)
-                       return rc;
+                       rc = __mnt_fs_set_optstr_ptr(cxt->fs, n, FALSE);
+
+               if (!rc && cxt->update &&
+                   !mnt_update_is_userspace_only(cxt->update))
+                       /* refresh options in /etc/mtab as well*/
+                       rc = mnt_update_set_fs(cxt->update,
+                                              cxt->mountflags, NULL, cxt->fs);
        }
-       return rc;
-}
 
-/**
- * mnt_context_umount_strerror
- * @cxt: mount context
- * @buf: buffer
- * @bufsiz: size of the buffer
- *
- * Returns: 0 or negative number in case of error.
- */
-int mnt_context_umount_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
-{
-       /* TODO: based on cxt->syscall_errno or cxt->helper_status */
-       return 0;
+       return rc ? : mnt_context_update_tabs(cxt);
 }
index d8043121d852f07417c836bbc2c45025d8ec7c8c..b4fa7f7d19328497665d4a5cc2291e6e535e9fec 100644 (file)
@@ -424,7 +424,7 @@ int mnt_lock_file(mnt_lock *ml)
                }
        }
        DBG(LOCKS, mnt_debug_h(ml,
-                       "LOCK: %s: (%d) successfully locked\n",
+                       "%s: (%d) successfully locked\n",
                        lockfile, getpid()));
        unlink(linkfile);
        return 0;
index 01ba5a6d6624c909a66608c49728b533798047e7..8bb44c7b45902021722713c92a2db1884cdef516 100644 (file)
@@ -89,7 +89,7 @@ typedef struct _mnt_tab mnt_tab;
 /**
  * mnt_update
  *
- * /etc/mtab or /var/run/mountinfo update description
+ * /etc/mtab or /dev/.mount/utab update description
  */
 typedef struct _mnt_update mnt_update;
 
@@ -131,9 +131,12 @@ extern int mnt_fstype_is_netfs(const char *type);
 extern int mnt_fstype_is_pseudofs(const char *type);
 extern int mnt_match_fstype(const char *type, const char *pattern);
 extern int mnt_match_options(const char *optstr, const char *pattern);
-extern const char *mnt_get_writable_mtab_path(void);
 extern const char *mnt_get_fstab_path(void);
 extern const char *mnt_get_mtab_path(void);
+extern const char *mnt_get_utab_path(void);
+
+extern int mnt_has_regular_mtab(const char **mtab, int *writable);
+extern int mnt_has_regular_utab(const char **utab, int *writable);
 
 /* cache.c */
 extern mnt_cache *mnt_new_cache(void);
@@ -251,6 +254,7 @@ extern int mnt_fs_print_debug(mnt_fs *ent, FILE *file);
 
 /* tab-parse.c */
 extern mnt_tab *mnt_new_tab_from_file(const char *filename);
+extern mnt_tab *mnt_new_tab_from_dir(const char *dirname);
 extern int mnt_tab_parse_stream(mnt_tab *tb, FILE *f, const char *filename);
 extern int mnt_tab_parse_file(mnt_tab *tb, const char *filename);
 extern int mnt_tab_parse_fstab(mnt_tab *tb, const char *filename);
@@ -286,22 +290,14 @@ extern int mnt_tab_find_next_fs(mnt_tab *tb, mnt_iter *itr,
                        mnt_fs **fs);
 
 /* tab_update.c */
-extern mnt_update *mnt_new_update(int action, unsigned long mountflags, const mnt_fs *fs);
+extern mnt_update *mnt_new_update(int userspace_only);
 extern void mnt_free_update(mnt_update *upd);
-extern int mnt_update_set_filename(mnt_update *upd, const char *filename);
-extern const char *mnt_update_get_filename(mnt_update *upd);
-extern int mnt_update_get_format(mnt_update *upd);
-extern int mnt_update_set_action(mnt_update *upd, int action);
-extern int mnt_update_set_format(mnt_update *upd, int format);
-extern int mnt_update_set_mountflags(mnt_update *upd, unsigned long flags);
-extern mnt_lock *mnt_update_get_lock(mnt_update *upd);
-extern int mnt_update_disable_lock(mnt_update *upd, int disable);
-extern int mnt_update_set_old_target(mnt_update *upd, const char *target);
-extern int mnt_update_set_fs(mnt_update *upd, const mnt_fs *fs);
-
-extern int mnt_prepare_update(mnt_update *upd);
-extern int mnt_update_file(mnt_update *upd);
-extern int mnt_update_is_pointless(mnt_update *upd);
+extern int mnt_update_is_ready(mnt_update *upd);
+extern int mnt_update_set_fs(mnt_update *upd, int mountflags,
+                             const char *target, mnt_fs *fs);
+extern int mnt_update_tab(mnt_update *upd, const char *filename, mnt_lock *lc);
+extern int mnt_update_is_userspace_only(mnt_update *upd);;
+
 
 /* context.c */
 
index 2a418758361b96399dc6698f23623b73987ce912..0f1395e5ac17a491fabc4b3041045519fab72de7 100644 (file)
@@ -92,8 +92,7 @@ mnt_debug_h(void *handler, const char *mesg, ...)
 #define MNT_MNTTABDIR_EXT      ".fstab"
 
 /* library private paths */
-#define MNT_PATH_RUNDIR                "/var/run/mount"
-#define MNT_PATH_MOUNTINFO     MNT_PATH_RUNDIR "/mountinfo"
+#define MNT_PATH_UTAB  "/dev/.mount/utab"
 
 #ifdef TEST_PROGRAM
 struct mtest {
@@ -116,10 +115,9 @@ extern int mnt_get_uid(const char *username, uid_t *uid);
 extern int mnt_get_gid(const char *groupname, gid_t *gid);
 extern int mnt_in_group(gid_t gid);
 
-extern int mnt_has_regular_mtab(void);
-
 extern char *mnt_get_mountpoint(const char *path);
 extern char *mnt_get_fs_root(const char *path, const char *mountpoint);
+extern int mnt_open_uniq_filename(const char *filename, char **name, int flags);
 
 /*
  * Generic iterator
@@ -207,7 +205,6 @@ struct _mnt_tab {
 struct _mnt_context
 {
        int     action;         /* MNT_ACT_{MOUNT,UMOUNT} */
-
        int     restricted;     /* root or not? */
 
        char    *fstype_pattern;        /* for mnt_match_fstype() */
@@ -224,8 +221,15 @@ struct _mnt_context
 
        unsigned long   user_mountflags;        /* MNT_MS_* (loop=, user=, ...) */
 
-       mnt_update      *update;        /* mtab update */
-       mnt_cache       *cache;         /* paths cache */
+       mnt_cache       *cache; /* paths cache */
+       mnt_lock        *lock;  /* mtab lock */
+       mnt_update      *update;/* mtab/utab update */
+
+       const char      *mtab_path; /* writable mtab */
+       int             mtab_writable; /* ismtab writeable */
+
+       const char      *utab_path; /* writable mtab */
+       int             utab_writable; /* ismtab writeable */
 
        int     flags;          /* private context flags */
        int     ambi;           /* libblkid returns ambivalent result */
@@ -285,8 +289,9 @@ extern int __mnt_fs_set_optstr(mnt_fs *fs, const char *optstr, int split);
 extern int mnt_context_prepare_srcpath(mnt_context *cxt);
 extern int mnt_context_guess_fstype(mnt_context *cxt);
 extern int mnt_context_prepare_helper(mnt_context *cxt, const char *name, const char *type);
-extern int mnt_context_prepare_update(mnt_context *cxt, int act);
+extern int mnt_context_prepare_update(mnt_context *cxt);
 extern mnt_fs *mnt_context_get_fs(mnt_context *cxt);
 extern int mnt_context_merge_mountflags(mnt_context *cxt);
+extern int mnt_context_update_tabs(mnt_context *cxt);
 
 #endif /* _LIBMOUNT_PRIVATE_H */
index 946cdc09001eaa577c0f24e417ddfcc3d312a91b..09c9cdfa02e6f4822fcd75c2c9a6cb145afa1684 100644 (file)
@@ -9,44 +9,14 @@
  * 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;
 
@@ -89,14 +51,9 @@ mnt_update *mnt_new_update(int action, unsigned long mountflags, const mnt_fs *f
        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;
 }
 
@@ -113,378 +70,183 @@ void mnt_free_update(mnt_update *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);
@@ -525,7 +287,8 @@ static int set_fs_root(mnt_update *upd, mnt_fs *fs)
                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;
@@ -540,358 +303,338 @@ static int set_fs_root(mnt_update *upd, mnt_fs *fs)
                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
@@ -906,150 +649,102 @@ static void lock_fallback(void)
                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;
 }
@@ -1061,9 +756,6 @@ int main(int argc, char *argv[])
        { "--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 }
        };
 
index 9242849658d7c485a343f633c8587b3c5874d9bf..410028fa7394bfef438cc9249ec377a16654f2f5 100644 (file)
@@ -87,6 +87,18 @@ int startswith(const char *s, const char *sx)
         return !strncmp(s, sx, off);
 }
 
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+static char *stripoff_last_component(char *path)
+{
+       char *p = path ? strrchr(path, '/') : NULL;
+
+       if (!p)
+               return NULL;
+       *p = '\0';
+       return ++p;
+}
+
 /**
  * mnt_mangle:
  * @str: string
@@ -404,32 +416,116 @@ done:
        return rc;
 }
 
-/*
- * Returns 1 if /etc/mtab is a reqular file.
+static int try_write(const char *filename)
+{
+       int fd;
+
+       if (!filename)
+               return -EINVAL;
+
+       fd = open(filename, O_RDWR, 0644);
+       if (fd >= 0) {
+               close(fd);
+               return 0;
+       }
+       return -errno;
+}
+
+/**
+ * mnt_has_regular_mtab:
+ * @mtab: returns path to mtab
+ * @writable: returns 1 if the file is writable
+ *
+ * If the file does not exist and @writable argument is not NULL then it will
+ * try to create the file
+ *
+ * Returns: 1 if /etc/mtab is a reqular file, and 0 in case of error (check
+ *          errno for more details).
  */
-int mnt_has_regular_mtab(const char **mtab, int *writeable)
+int mnt_has_regular_mtab(const char **mtab, int *writable)
 {
        struct stat st;
        int rc;
-       const char *x = mtab && *mtab ? *mtab : mnt_get_mtab_path();
+       const char *filename = mtab && *mtab ? *mtab : mnt_get_mtab_path();
 
        if (mtab && !*mtab)
-               *mtab = x;
-       rc = (lstat(x, &st) == 0 && S_ISREG(st.st_mode));
+               *mtab = filename;
+
+       DBG(UTILS, mnt_debug("mtab: %s", filename));
 
+       rc = lstat(filename, &st);
+
+       if (rc == 0) {
+               /* file exist */
+               if (S_ISREG(st.st_mode)) {
+                       if (writable)
+                               *writable = !try_write(filename);
+                       return 1;
+               }
+               return 0;       /* it's not regular file */
+       }
+
+       /* try to create the file */
        if (writable) {
-               if (rc) {
-                       /* TODO: use utimensat() */
-                       int fd = open(path, O_RDWR, 0644);
-                       if (fd >= 0) {
-                               close(fd);
-                               *writable = 1;
-                               return rc;
-                       }
+               *writable = !try_write(filename);
+               return *writable;
+       }
+
+       return 0;
+}
+/**
+ *
+ * mnt_has_regular_utab:
+ * @utab: returns path to utab (usually /dev/.mount/utab)
+ * @writable: returns 1 if the file is writable
+ *
+ * If the file does not exist and @writable argument is not NULL then it will
+ * try to create the directory (e.g. /dev/.mount) and the file.
+ *
+ * Returns: 1 if /etc/utab is a reqular file, and 0 in case of error (check
+ *          errno for more details).
+ */
+
+int mnt_has_regular_utab(const char **utab, int *writable)
+{
+       struct stat st;
+       int rc;
+       const char *filename = utab && *utab ? *utab : mnt_get_utab_path();
+
+       if (utab && !*utab)
+               *utab = filename;
+
+       DBG(UTILS, mnt_debug("utab: %s", filename));
+
+       rc = lstat(filename, &st);
+
+       if (rc == 0) {
+               /* file exist */
+               if (S_ISREG(st.st_mode)) {
+                       if (writable)
+                               *writable = try_write(filename);
+                       return 1;
                }
-               *writable = 0;
+               return 0;       /* it's not regular file */
        }
-       return rc;
+
+       if (writable) {
+               char *dirname = strdup(filename);
+
+               if (!dirname)
+                       return 0;
+
+               stripoff_last_component(dirname);       /* remove filename */
+
+               rc = mkdir(dirname, 755);
+               free(dirname);
+               if (rc && errno != EEXIST)
+                       return 0;                       /* probably EACCES */
+
+               *writable = !try_write(filename);
+               return *writable;
+       }
+       return 0;
 }
 
 /**
@@ -496,18 +592,6 @@ int mnt_open_uniq_filename(const char *filename, char **name, int flags)
        return fd < 0 ? -errno : fd;
 }
 
-/* returns basename and keeps dirname in the @path, if @path is "/" (root)
- * then returns empty string */
-static char *stripoff_last_component(char *path)
-{
-       char *p = strrchr(path, '/');
-
-       if (!p)
-               return NULL;
-       *p = '\0';
-       return ++p;
-}
-
 char *mnt_get_mountpoint(const char *path)
 {
        char *mnt = strdup(path);