]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - shlibs/mount/src/tab_update.c
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
10 * @title: mtab managment
11 * @short_description: userspace mount information management.
13 * The struct libmnt_update provides abstraction to manage mount options in userspace independently on
14 * system configuration. This low-level API works on system with and without /etc/mtab. On
15 * systems without the regular /etc/mtab file are userspace mount options (e.g. user=)
16 * stored to the /dev/.mount/utab file.
18 * It's recommended to use high-level struct libmnt_context API.
23 #include <sys/types.h>
34 #include "pathnames.h"
36 struct libmnt_update
{
40 unsigned long mountflags
;
45 static int utab_new_entry(struct libmnt_fs
*fs
, unsigned long mountflags
, struct libmnt_fs
**ent
);
46 static int set_fs_root(struct libmnt_fs
*result
, struct libmnt_fs
*fs
, unsigned long mountflags
);
51 * Returns: newly allocated update handler
53 struct libmnt_update
*mnt_new_update(void)
55 struct libmnt_update
*upd
;
57 upd
= calloc(1, sizeof(*upd
));
61 DBG(UPDATE
, mnt_debug_h(upd
, "allocate"));
70 * Deallocates struct libmnt_update handler.
72 void mnt_free_update(struct libmnt_update
*upd
)
77 DBG(UPDATE
, mnt_debug_h(upd
, "free"));
86 * Returns 0 on success, 1 if not file available, -1 in case of error.
88 int mnt_update_set_filename(struct libmnt_update
*upd
, const char *filename
, int userspace_only
)
90 const char *path
= NULL
;
95 /* filename explicitly defined */
97 char *p
= strdup(filename
);
101 upd
->userspace_only
= userspace_only
;
109 /* detect tab filename -- /etc/mtab or /dev/.mount/utab
111 mnt_has_regular_mtab(&path
, &rw
);
114 mnt_has_regular_utab(&path
, &rw
);
117 upd
->userspace_only
= TRUE
;
119 upd
->filename
= strdup(path
);
127 * mnt_update_get_filename:
130 * This function returns file name (e.g. /etc/mtab) if the update
131 * should be covered by mnt_lock, otherwise returne NULL.
133 * Returns: pointer to filename that will be updated or NULL in case of error.
135 const char *mnt_update_get_filename(struct libmnt_update
*upd
)
137 return upd
&& !upd
->userspace_only
? upd
->filename
: NULL
;
141 * mnt_update_is_ready:
142 * @upd: update handler
144 * Returns: 1 if entry described by @upd is successfully prepared and will be
145 * written to mtab/utab file.
147 int mnt_update_is_ready(struct libmnt_update
*upd
)
149 return upd
? upd
->ready
: FALSE
;
154 * @upd: update handler
155 * @mountflags: MS_* flags
156 * @target: umount target, must be num for mount
157 * @fs: mount filesystem description, must be NULL for umount
159 * Returns: -1 in case on error, 0 on success, 1 if update is unnecessary.
161 int mnt_update_set_fs(struct libmnt_update
*upd
, unsigned long mountflags
,
162 const char *target
, struct libmnt_fs
*fs
)
167 assert(target
|| fs
);
171 if ((mountflags
& MS_MOVE
) && (!fs
|| !mnt_fs_get_srcpath(fs
)))
176 DBG(UPDATE
, mnt_debug_h(upd
,
177 "reseting FS [fs=0x%p, target=%s, flags=0x%08lx]",
178 fs
, target
, mountflags
));
180 DBG(UPDATE
, mnt_debug_h(upd
, "FS template:"));
181 DBG(UPDATE
, mnt_fs_print_debug(fs
, stderr
));
184 mnt_free_fs(upd
->fs
);
191 if (mountflags
& MS_PROPAGATION
)
194 upd
->mountflags
= mountflags
;
196 rc
= mnt_update_set_filename(upd
, NULL
, 0);
198 return rc
; /* error or no file available (rc = 1) */
201 upd
->target
= strdup(target
);
206 if (upd
->userspace_only
&& !(mountflags
& MS_MOVE
)) {
207 int rc
= utab_new_entry(fs
, mountflags
, &upd
->fs
);
211 upd
->fs
= mnt_copy_mtab_fs(fs
);
219 DBG(UPDATE
, mnt_debug_h(upd
, "ready"));
228 * Returns: update filesystem entry or NULL
230 struct libmnt_fs
*mnt_update_get_fs(struct libmnt_update
*upd
)
232 return upd
? upd
->fs
: NULL
;
236 * mnt_update_get_mflags:
239 * Returns: mount flags as was set by mnt_update_set_fs()
241 unsigned long mnt_update_get_mflags(struct libmnt_update
*upd
)
243 return upd
? upd
->mountflags
: 0;
247 * mnt_update_force_rdonly:
249 * @rdonly: is read-only?
251 * Returns: 0 on success and negative number in case of error.
253 int mnt_update_force_rdonly(struct libmnt_update
*upd
, int rdonly
)
257 if (!upd
|| !upd
->fs
)
260 if (rdonly
&& (upd
->mountflags
& MS_RDONLY
))
262 if (!rdonly
&& !(upd
->mountflags
& MS_RDONLY
))
265 if (!upd
->userspace_only
) {
266 /* /etc/mtab -- we care about VFS options there */
267 const char *o
= mnt_fs_get_vfs_options(upd
->fs
);
268 char *n
= o
? strdup(o
) : NULL
;
271 mnt_optstr_remove_option(&n
, rdonly
? "rw" : "ro");
272 if (!mnt_optstr_prepend_option(&n
, rdonly
? "ro" : "rw", NULL
))
273 rc
= mnt_fs_set_vfs_options(upd
->fs
, n
);
279 upd
->mountflags
&= ~MS_RDONLY
;
281 upd
->mountflags
|= MS_RDONLY
;
287 * Allocates (but does not write) utab entry for mount/remount. This function
288 * should be called *before* mount(2) syscall.
290 * Returns: 0 on success, negative number on error, 1 if utabs update is
293 static int utab_new_entry(struct libmnt_fs
*fs
, unsigned long mountflags
, struct libmnt_fs
**ent
)
296 const char *o
= NULL
, *a
= NULL
;
301 assert(!(mountflags
& MS_MOVE
));
307 DBG(UPDATE
, mnt_debug("prepare utab entry"));
309 o
= mnt_fs_get_user_options(fs
);
310 a
= mnt_fs_get_attributes(fs
);
313 /* remove non-mtab options */
314 rc
= mnt_optstr_get_options(o
, &u
,
315 mnt_get_builtin_optmap(MNT_USERSPACE_MAP
),
322 DBG(UPDATE
, mnt_debug("utab entry unnecessary (no options)"));
326 /* allocate the entry */
327 *ent
= mnt_copy_fs(NULL
, fs
);
333 rc
= mnt_fs_set_user_options(*ent
, u
);
336 rc
= mnt_fs_set_attributes(*ent
, a
);
340 if (!(mountflags
& MS_REMOUNT
)) {
341 rc
= set_fs_root(*ent
, fs
, mountflags
);
347 DBG(UPDATE
, mnt_debug("utab entry OK"));
356 static int set_fs_root(struct libmnt_fs
*result
, struct libmnt_fs
*fs
, unsigned long mountflags
)
358 char *root
= NULL
, *mnt
= NULL
;
360 struct libmnt_table
*tb
= NULL
;
366 DBG(UPDATE
, mnt_debug("setting FS root"));
368 fstype
= mnt_fs_get_fstype(fs
);
371 * bind-mount -- get fs-root and source device for the source filesystem
373 if (mountflags
& MS_BIND
) {
374 const char *src
, *src_root
;
375 struct libmnt_fs
*src_fs
;
377 DBG(UPDATE
, mnt_debug("setting FS root: bind"));
379 src
= mnt_fs_get_srcpath(fs
);
381 rc
= mnt_fs_set_bindsrc(result
, src
);
384 mnt
= mnt_get_mountpoint(src
);
390 root
= mnt_get_fs_root(src
, mnt
);
392 tb
= __mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
, MNT_FMT_MOUNTINFO
);
394 DBG(UPDATE
, mnt_debug("failed to parse mountinfo -- using default"));
397 src_fs
= mnt_table_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
399 DBG(UPDATE
, mnt_debug("not found '%s' in mountinfo -- using default", mnt
));
403 /* set device name and fs */
404 src
= mnt_fs_get_srcpath(src_fs
);
405 rc
= mnt_fs_set_source(result
, src
);
409 mnt_fs_set_fstype(result
, mnt_fs_get_fstype(src_fs
));
411 /* on btrfs the subvolume is used as fs-root in
412 * /proc/self/mountinfo, so we have get the original subvolume
413 * name from src_fs and prepend the subvolume name to the
416 src_root
= mnt_fs_get_root(src_fs
);
417 if (src_root
&& !startswith(root
, src_root
)) {
418 size_t sz
= strlen(root
) + strlen(src_root
) + 1;
419 char *tmp
= malloc(sz
);
423 snprintf(tmp
, sz
, "%s%s", src_root
, root
);
430 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
432 else if (fstype
&& !strcmp(fstype
, "btrfs")) {
433 char *vol
= NULL
, *p
;
434 size_t sz
, volsz
= 0;
436 if (mnt_fs_get_option(fs
, "subvol", &vol
, &volsz
))
439 DBG(UPDATE
, mnt_debug("setting FS root: btrfs subvol"));
444 root
= malloc(sz
+ 1);
450 memcpy(p
, vol
, volsz
);
462 DBG(UPDATE
, mnt_debug("FS root result: %s", root
));
472 /* mtab and fstab update */
473 static int fprintf_mtab_fs(FILE *f
, struct libmnt_fs
*fs
)
476 char *m1
, *m2
, *m3
, *m4
;
482 o
= mnt_fs_strdup_options(fs
);
486 m1
= mangle(mnt_fs_get_source(fs
));
487 m2
= mangle(mnt_fs_get_target(fs
));
488 m3
= mangle(mnt_fs_get_fstype(fs
));
491 if (m1
&& m2
&& m3
&& m4
)
492 rc
= !fprintf(f
, "%s %s %s %s %d %d\n",
495 mnt_fs_get_passno(fs
));
508 static int fprintf_utab_fs(FILE *f
, struct libmnt_fs
*fs
)
518 p
= mangle(mnt_fs_get_source(fs
));
520 fprintf(f
, "SRC=%s ", p
);
523 p
= mangle(mnt_fs_get_target(fs
));
525 fprintf(f
, "TARGET=%s ", p
);
528 p
= mangle(mnt_fs_get_root(fs
));
530 fprintf(f
, "ROOT=%s ", p
);
533 p
= mangle(mnt_fs_get_bindsrc(fs
));
535 fprintf(f
, "BINDSRC=%s ", p
);
538 p
= mangle(mnt_fs_get_attributes(fs
));
540 fprintf(f
, "ATTRS=%s ", p
);
543 p
= mangle(mnt_fs_get_user_options(fs
));
545 fprintf(f
, "OPTS=%s", p
);
553 static int update_table(struct libmnt_update
*upd
, struct libmnt_table
*tb
)
559 if (!tb
|| !upd
->filename
)
562 DBG(UPDATE
, mnt_debug_h(upd
, "%s: updating", upd
->filename
));
564 fd
= mnt_open_uniq_filename(upd
->filename
, &uq
, O_WRONLY
);
566 return fd
; /* error */
571 struct libmnt_iter itr
;
572 struct libmnt_fs
*fs
;
575 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
576 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
577 if (upd
->userspace_only
)
578 fprintf_utab_fs(f
, fs
);
580 fprintf_mtab_fs(f
, fs
);
583 rc
= fchmod(fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) ? -errno
: 0;
585 if (!rc
&& stat(upd
->filename
, &st
) == 0)
586 /* Copy uid/gid from the present file before renaming. */
587 rc
= fchown(fd
, st
.st_uid
, st
.st_gid
) ? -errno
: 0;
590 rc
= rename(uq
, upd
->filename
) ? -errno
: 0;
596 unlink(uq
); /* be paranoid */
601 static int utab_lock(const char *filename
)
608 if (asprintf(&lfile
, "%s.lock", filename
) == -1)
611 DBG(UPDATE
, mnt_debug("%s: locking", lfile
));
613 fd
= open(lfile
, O_RDONLY
|O_CREAT
|O_CLOEXEC
, S_IWUSR
|
614 S_IRUSR
|S_IRGRP
|S_IROTH
);
619 if (flock(fd
, LOCK_EX
)) {
627 static void utab_unlock(int fd
)
630 DBG(UPDATE
, mnt_debug("unlocking utab"));
635 static int update_add_entry(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
638 int rc
= 0, u_lc
= -1;
643 DBG(UPDATE
, mnt_debug_h(upd
, "%s: add entry", upd
->filename
));
647 else if (upd
->userspace_only
)
648 u_lc
= utab_lock(upd
->filename
);
650 f
= fopen(upd
->filename
, "a+");
652 rc
= upd
->userspace_only
? fprintf_utab_fs(f
, upd
->fs
) :
653 fprintf_mtab_fs(f
, upd
->fs
);
654 DBG(UPDATE
, mnt_debug_h(upd
, "%s: add [rc=%d]", upd
->filename
, rc
));
657 DBG(UPDATE
, mnt_debug_h(upd
, "%s: failed: %m", upd
->filename
));
667 static int update_remove_entry(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
669 struct libmnt_table
*tb
;
670 int rc
= 0, u_lc
= -1;
675 DBG(UPDATE
, mnt_debug_h(upd
, "%s: remove entry", upd
->filename
));
679 else if (upd
->userspace_only
)
680 u_lc
= utab_lock(upd
->filename
);
682 tb
= __mnt_new_table_from_file(upd
->filename
,
683 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
685 struct libmnt_fs
*rem
= mnt_table_find_target(tb
, upd
->target
, MNT_ITER_BACKWARD
);
687 mnt_table_remove_fs(tb
, rem
);
688 rc
= update_table(upd
, tb
);
700 static int update_modify_target(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
702 struct libmnt_table
*tb
= NULL
;
703 int rc
= 0, u_lc
= -1;
705 DBG(UPDATE
, mnt_debug_h(upd
, "%s: modify target", upd
->filename
));
709 else if (upd
->userspace_only
)
710 u_lc
= utab_lock(upd
->filename
);
712 tb
= __mnt_new_table_from_file(upd
->filename
,
713 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
715 struct libmnt_fs
*cur
= mnt_table_find_target(tb
,
716 mnt_fs_get_srcpath(upd
->fs
), MNT_ITER_BACKWARD
);
718 rc
= mnt_fs_set_target(cur
, mnt_fs_get_target(upd
->fs
));
720 rc
= update_table(upd
, tb
);
731 static int update_modify_options(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
733 struct libmnt_table
*tb
= NULL
;
734 int rc
= 0, u_lc
= -1;
735 struct libmnt_fs
*fs
;
740 DBG(UPDATE
, mnt_debug_h(upd
, "%s: modify options", upd
->filename
));
746 else if (upd
->userspace_only
)
747 u_lc
= utab_lock(upd
->filename
);
749 tb
= __mnt_new_table_from_file(upd
->filename
,
750 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
752 struct libmnt_fs
*cur
= mnt_table_find_target(tb
,
753 mnt_fs_get_target(fs
),
756 if (upd
->userspace_only
)
757 rc
= mnt_fs_set_attributes(cur
, mnt_fs_get_attributes(fs
));
758 if (!rc
&& !upd
->userspace_only
)
759 rc
= mnt_fs_set_vfs_options(cur
, mnt_fs_get_vfs_options(fs
));
760 if (!rc
&& !upd
->userspace_only
)
761 rc
= mnt_fs_set_fs_options(cur
, mnt_fs_get_fs_options(fs
));
763 rc
= mnt_fs_set_user_options(cur
,
764 mnt_fs_get_user_options(fs
));
766 rc
= update_table(upd
, tb
);
783 * High-level API to update /etc/mtab (or private /dev/.mount/utab file).
785 * Returns: 0 on success, negative number on error.
787 int mnt_update_table(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
793 if (!upd
->filename
|| !upd
)
798 DBG(UPDATE
, mnt_debug_h(upd
, "%s: update tab", upd
->filename
));
800 DBG(UPDATE
, mnt_fs_print_debug(upd
->fs
, stderr
));
803 if (!upd
->fs
&& upd
->target
)
804 rc
= update_remove_entry(upd
, lc
); /* umount */
805 else if (upd
->mountflags
& MS_MOVE
)
806 rc
= update_modify_target(upd
, lc
); /* move */
807 else if (upd
->mountflags
& MS_REMOUNT
)
808 rc
= update_modify_options(upd
, lc
); /* remount */
810 rc
= update_add_entry(upd
, lc
); /* mount */
813 DBG(UPDATE
, mnt_debug_h(upd
, "%s: update tab: done [rc=%d]",
820 struct libmnt_lock
*lock
;
822 static void lock_fallback(void)
825 mnt_unlock_file(lock
);
828 static int update(const char *target
, struct libmnt_fs
*fs
, unsigned long mountflags
)
831 struct libmnt_update
*upd
;
832 const char *filename
;
834 DBG(UPDATE
, mnt_debug("update test"));
836 upd
= mnt_new_update();
840 rc
= mnt_update_set_fs(upd
, mountflags
, target
, fs
);
842 /* update is unnecessary */
847 fprintf(stderr
, "failed to set FS\n");
851 /* [... here should be mount(2) call ...] */
853 filename
= mnt_update_get_filename(upd
);
855 lock
= mnt_new_lock(filename
, 0);
857 atexit(lock_fallback
);
859 rc
= mnt_update_table(upd
, lock
);
864 static int test_add(struct libmnt_test
*ts
, int argc
, char *argv
[])
866 struct libmnt_fs
*fs
= mnt_new_fs();
871 mnt_fs_set_source(fs
, argv
[1]);
872 mnt_fs_set_target(fs
, argv
[2]);
873 mnt_fs_set_fstype(fs
, argv
[3]);
874 mnt_fs_set_options(fs
, argv
[4]);
876 rc
= update(NULL
, fs
, 0);
881 static int test_remove(struct libmnt_test
*ts
, int argc
, char *argv
[])
885 return update(argv
[1], NULL
, 0);
888 static int test_move(struct libmnt_test
*ts
, int argc
, char *argv
[])
890 struct libmnt_fs
*fs
= mnt_new_fs();
895 mnt_fs_set_source(fs
, argv
[1]);
896 mnt_fs_set_target(fs
, argv
[2]);
898 rc
= update(NULL
, fs
, MS_MOVE
);
904 static int test_remount(struct libmnt_test
*ts
, int argc
, char *argv
[])
906 struct libmnt_fs
*fs
= mnt_new_fs();
911 mnt_fs_set_target(fs
, argv
[1]);
912 mnt_fs_set_options(fs
, argv
[2]);
914 rc
= update(NULL
, fs
, MS_REMOUNT
);
919 int main(int argc
, char *argv
[])
921 struct libmnt_test tss
[] = {
922 { "--add", test_add
, "<src> <target> <type> <options> add line to mtab" },
923 { "--remove", test_remove
, "<target> MS_REMOUNT mtab change" },
924 { "--move", test_move
, "<old_target> <target> MS_MOVE mtab change" },
925 { "--remount",test_remount
, "<target> <options> MS_REMOUNT mtab change" },
929 return mnt_run_test(tss
, argc
, argv
);
932 #endif /* TEST_PROGRAM */