From 1d0cd73f3f8929132e731a821ceb07fa6193c00c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 8 Nov 2010 11:14:44 +0100 Subject: [PATCH] libmount: rewrite update Signed-off-by: Karel Zak --- shlibs/mount/src/context.c | 111 ++- shlibs/mount/src/context_mount.c | 117 +-- shlibs/mount/src/context_umount.c | 110 +-- shlibs/mount/src/lock.c | 2 +- shlibs/mount/src/mount.h.in | 30 +- shlibs/mount/src/mountP.h | 21 +- shlibs/mount/src/tab_update.c | 1172 +++++++++++------------------ shlibs/mount/src/utils.c | 140 +++- 8 files changed, 693 insertions(+), 1010 deletions(-) diff --git a/shlibs/mount/src/context.c b/shlibs/mount/src/context.c index 21a5d8a1..b4662d89 100644 --- a/shlibs/mount/src/context.c +++ b/shlibs/mount/src/context.c @@ -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 diff --git a/shlibs/mount/src/context_mount.c b/shlibs/mount/src/context_mount.c index e12e0a93..de6557df 100644 --- a/shlibs/mount/src/context_mount.c +++ b/shlibs/mount/src/context_mount.c @@ -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.). * - * 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.). - * - * 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); } diff --git a/shlibs/mount/src/context_umount.c b/shlibs/mount/src/context_umount.c index b2590be9..476ee515 100644 --- a/shlibs/mount/src/context_umount.c +++ b/shlibs/mount/src/context_umount.c @@ -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.). * - * 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.). - * - * 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); } diff --git a/shlibs/mount/src/lock.c b/shlibs/mount/src/lock.c index d8043121..b4fa7f7d 100644 --- a/shlibs/mount/src/lock.c +++ b/shlibs/mount/src/lock.c @@ -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; diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in index 01ba5a6d..8bb44c7b 100644 --- a/shlibs/mount/src/mount.h.in +++ b/shlibs/mount/src/mount.h.in @@ -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 */ diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index 2a418758..0f1395e5 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -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 */ diff --git a/shlibs/mount/src/tab_update.c b/shlibs/mount/src/tab_update.c index 946cdc09..09c9cdfa 100644 --- a/shlibs/mount/src/tab_update.c +++ b/shlibs/mount/src/tab_update.c @@ -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 #include #include #include +#include +#include #include #include #include @@ -56,32 +26,24 @@ #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, " MS_REMOUNT mtab change" }, { "--move", test_move, " MS_MOVE mtab change" }, { "--remount",test_remount, " MS_REMOUNT mtab change" }, - - { "--add-fstab", test_add_fstab, " add line to fstab" }, - { NULL } }; diff --git a/shlibs/mount/src/utils.c b/shlibs/mount/src/utils.c index 92428496..410028fa 100644 --- a/shlibs/mount/src/utils.c +++ b/shlibs/mount/src/utils.c @@ -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); -- 2.39.5