]>
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 (fstab) managment
11 * @short_description: userspace mount information management
13 * The libmount library allows to use more modes for mtab management:
15 * - /etc/mtab is regular file
17 * then libmount manages the file in classical way (all mounts are added to
18 * the file). This mode is always used for /etc/fstab updates as well.
20 * - /etc/mtab is symlink
22 * then libmount ignores mtab at all
24 * - /etc/mtab is symlink and /var/run/mount/ directory exists
26 * then libmount stores userspace specific mount options to the
27 * /var/run/mount/mountinfo file (the file format compatible to
28 * /proc/self/mountinfo)
31 * The mtab is always updated in two steps. The first step is to prepare a new
32 * update entry -- mnt_prepare_update(), this step has to be done before
33 * mount(2) syscall. The second step is to update the file --
34 * mnt_update_file(), this step should be done after mount(2) syscall.
36 * The mnt_update_file() behaviour is undefined if mnt_prepare_update() has
42 #include <sys/types.h>
51 #include "pathnames.h"
56 * mtab update description
59 int action
; /* MNT_ACT_{MOUNT,UMOUNT} */
60 unsigned long mountflags
; /* MS_* flags */
61 char *filename
; /* usually /etc/mtab or /var/run/mount/mountinfo */
62 char *old_target
; /* for MS_MOVE */
63 int format
; /* MNT_FMT_{MTAB,FSTAB,MOUNTINFO} */
64 int nolock
; /* don't alloca private mnt_lock */
65 mnt_fs
*fs
; /* entry */
66 mnt_lock
*lc
; /* lock or NULL */
71 * @action: MNT_ACT_{MOUNT,UMOUNT}
72 * @mountflags: MS_{REMOUNT,BIND,MOVE}
75 * Returns: newly allocated update description
77 mnt_update
*mnt_new_update(int action
, unsigned long mountflags
, const mnt_fs
*fs
)
81 upd
= calloc(1, sizeof(struct _mnt_update
));
85 DBG(UPDATE
, mnt_debug_h(upd
, "allocate"));
88 mnt_update_set_action(upd
, action
);
90 mnt_update_set_mountflags(upd
, mountflags
);
92 mnt_update_set_fs(upd
, fs
);
100 * Deallocates mnt_update handler.
102 void mnt_free_update(mnt_update
*upd
)
107 DBG(UPDATE
, mnt_debug_h(upd
, "free"));
109 mnt_free_lock(upd
->lc
);
111 free(upd
->old_target
);
116 * mnt_update_set_filename:
118 * @filename: path to update (default is /etc/update or /var/run/mount/mountinfo)
120 * Returns: 0 on success, -1 in case of error.
122 int mnt_update_set_filename(mnt_update
*upd
, const char *filename
)
130 p
= strdup(filename
);
140 * mnt_update_set_action:
142 * @action: MNT_ACT_{MOUNT,UMOUNT}
144 * Overwrites the previously defined (usually by mnt_new_update()) action.
146 * Returns: 0 on success, -1 in case of error.
148 int mnt_update_set_action(mnt_update
*upd
, int action
)
153 upd
->action
= action
;
158 * mnt_update_set_format:
160 * @format: MNT_FMT_{MTAB,FSTAB,MOUNTINFO}
162 * Sets update file format, default is MNT_FMT_MTAB for paths that end with
163 * "update", MNT_FMT_MOUNTINFO for paths that end with "mountinfo" and
164 * MNT_FMT_FSTAB for paths that end with "fstab".
166 * Returns: 0 on success, -1 in case of error.
168 int mnt_update_set_format(mnt_update
*upd
, int format
)
173 upd
->format
= format
;
180 * @fs: filesystem to write to file
182 * Returns; 0 on success, -1 in case of error.
184 int mnt_update_set_fs(mnt_update
*upd
, const mnt_fs
*fs
)
197 mnt_free_fs(upd
->fs
);
203 * mnt_update_set_mountflags:
205 * @flags: MS_{REMOUNT,MOVE,BIND,...}
207 * Sets mount flags for mount/umount action. The flags are also
208 * extracted from mount options by mnt_prepare_update(). The mount flags
209 * are used for mtab update to differentiate between move, remount, ...
211 * Returns: 0 on success, -1 in case of error.
213 int mnt_update_set_mountflags(mnt_update
*upd
, unsigned long flags
)
218 upd
->mountflags
= flags
;
223 * mnt_update_get_lock:
226 * This function should not be called before mnt_prepare_update(). The lock
227 * is initialized when mtab update is required only.
229 * Note that after mnt_update_disable_lock(mt, TRUE) or after mnt_free_update()
230 * the lock will be automatically deallocated.
232 * Returns: libmount lock handler or NULL if locking is disabled.
234 mnt_lock
*mnt_update_get_lock(mnt_update
*upd
)
236 return upd
? upd
->lc
: NULL
;
240 * mnt_update_disable_lock:
242 * @disable: TRUE/FALSE
244 * Enable or disable update locking, the locking is enabled by default.
246 * Returns: 0 on success, -1 in case of error.
248 int mnt_update_disable_lock(mnt_update
*upd
, int disable
)
253 mnt_free_lock(upd
->lc
);
256 upd
->nolock
= disable
;
261 * mnt_update_set_old_target:
263 * @target: old mountpoint
265 * Sets the original target for the MS_MOVE operation.
267 * Returns: 0 on success, -1 in case of error.
269 int mnt_update_set_old_target(mnt_update
*upd
, const char *target
)
280 free(upd
->old_target
);
286 * The format is same as /proc/self/mountinfo, but it contains userspace
287 * mount options and some unnecessary fields are ignored.
289 static int fprintf_mountinfo_fs(FILE *f
, mnt_fs
*fs
)
291 char *root
= NULL
, *target
= NULL
, *optstr
= NULL
,
292 *fstype
= NULL
, *source
= NULL
;
301 devno
= mnt_fs_get_devno(fs
);
302 source
= mangle(mnt_fs_get_source(fs
));
303 root
= mangle(mnt_fs_get_root(fs
));
304 target
= mangle(mnt_fs_get_target(fs
));
305 fstype
= mangle(mnt_fs_get_fstype(fs
));
306 optstr
= mangle(mnt_fs_get_optstr(fs
));
308 if (!root
|| !target
|| !optstr
)
311 rc
= fprintf(f
, "%i %i %u:%u %s %s %s - %s %s %s\n",
313 mnt_fs_get_parent_id(fs
),
314 major(devno
), minor(devno
),
318 fstype
? fstype
: "auto",
319 source
? source
: "none",
331 /* mtab and fstab update */
332 static int fprintf_mtab_fs(FILE *f
, mnt_fs
*fs
)
334 char *m1
= NULL
, *m2
= NULL
, *m3
= NULL
, *m4
= NULL
;
343 m1
= mangle(mnt_fs_get_source(fs
));
344 m2
= mangle(mnt_fs_get_target(fs
));
345 m3
= mangle(mnt_fs_get_fstype(fs
));
346 m4
= mangle(mnt_fs_get_optstr(fs
));
348 if (!m1
|| !m2
|| !m3
|| !m4
)
351 rc
= fprintf(f
, "%s %s %s %s %d %d\n",
354 mnt_fs_get_passno(fs
));
365 static int update_file(const char *filename
, int fmt
, mnt_tab
*tb
)
370 char tmpname
[PATH_MAX
];
373 int (*line_fn
)(FILE *, mnt_fs
*) = fprintf_mountinfo_fs
;
379 DBG(UPDATE
, mnt_debug("%s: update from tab %p", filename
, tb
));
381 if (snprintf(tmpname
, sizeof(tmpname
), "%s.tmp", filename
)
385 f
= fopen(tmpname
, "w");
390 if (fmt
== MNT_FMT_MTAB
|| fmt
== MNT_FMT_FSTAB
)
391 line_fn
= fprintf_mtab_fs
;
393 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
394 while(mnt_tab_next_fs(tb
, &itr
, &fs
) == 0)
399 if (fchmod(fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) < 0)
402 /* Copy uid/gid from the present file before renaming. */
403 if (stat(filename
, &st
) == 0) {
404 if (fchown(fd
, st
.st_uid
, st
.st_gid
) < 0)
411 if (rename(tmpname
, filename
) < 0)
416 DBG(UPDATE
, mnt_debug("%s: update from tab %p failed", filename
, tb
));
422 static int set_fs_root(mnt_update
*upd
, mnt_fs
*fs
)
424 char *root
= NULL
, *mnt
= NULL
;
429 if (upd
->mountflags
& MS_REMOUNT
)
432 optstr
= (char *) mnt_fs_get_optstr(fs
);
433 fstype
= mnt_fs_get_fstype(fs
);
436 * bind-mount -- get fs-root and source device for the source filesystem
438 if (upd
->mountflags
& MS_BIND
) {
439 const char *src
, *src_root
;
442 src
= mnt_fs_get_srcpath(fs
);
445 mnt
= mnt_get_mountpoint(src
);
448 root
= mnt_get_fs_root(src
, mnt
);
450 tb
= mnt_new_tab_from_file(_PATH_PROC_MOUNTINFO
);
453 src_fs
= mnt_tab_find_target(tb
, mnt
, MNT_ITER_BACKWARD
);
457 /* set device name and fs */
458 src
= mnt_fs_get_srcpath(src_fs
);
459 mnt_fs_set_source(fs
, src
);
461 mnt_fs_set_fstype(fs
, mnt_fs_get_fstype(src_fs
));
463 /* on btrfs the subvolume is used as fs-root in
464 * /proc/self/mountinfo, so we have get the original subvolume
465 * name from src_fs and prepend the subvolume name to the
468 src_root
= mnt_fs_get_root(src_fs
);
469 if (src_root
&& !startswith(root
, src_root
)) {
470 size_t sz
= strlen(root
) + strlen(src_root
) + 1;
471 char *tmp
= malloc(sz
);
475 snprintf(tmp
, sz
, "%s%s", src_root
, root
);
482 * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path
484 else if (fstype
&& !strcmp(fstype
, "btrfs")) {
485 char *vol
= NULL
, *p
;
486 size_t sz
, volsz
= 0;
488 if (mnt_optstr_get_option(optstr
, "subvol", &vol
, &volsz
))
494 root
= malloc(sz
+ 1);
500 memcpy(p
, vol
, volsz
);
520 * mnt_prepare_update:
523 * Prepares internal data for mtab update:
524 * - set filename if mnt_update_set_filename() wasn't called
525 * - set file format if mnt_update_set_format() wasn't called
526 * - bitwise-OR mountflags from mount options
527 * - for /var/run/mount/mountinfo:
528 * * evaluate if the update is necessary
529 * * set fs root and devname for bind mount and btrfs subvolumes
530 * * removes unnecessary mount options
531 * - allocate update_lock if necessary
533 * This function has to be always called before mount(2). The mnt_update_file()
534 * should not be called if mnt_prepare_update() returns non-zero value.
536 * Returns: 0 on success, 1 if update is unnecessary, negative in case of error
538 int mnt_prepare_update(mnt_update
*upd
)
541 const char *o
= NULL
;
547 if (!upd
|| !upd
->fs
)
550 DBG(UPDATE
, mnt_debug_h(upd
,
551 "prepare update (target %s, source %s, optstr %s)",
552 mnt_fs_get_target(upd
->fs
),
553 mnt_fs_get_source(upd
->fs
),
554 mnt_fs_get_optstr(upd
->fs
)));
556 if (!upd
->filename
) {
557 const char *p
= mnt_get_writable_mtab_path();
561 goto err
; /* EACCES? */
563 goto nothing
; /* no mtab */
565 upd
->filename
= strdup(p
);
566 if (!upd
->filename
) {
572 if (endswith(upd
->filename
, "mountinfo"))
573 upd
->format
= MNT_FMT_MOUNTINFO
;
574 else if (endswith(upd
->filename
, "fstab"))
575 upd
->format
= MNT_FMT_FSTAB
;
577 upd
->format
= MNT_FMT_MTAB
;
580 /* TODO: cannonicalize source and target paths on mnt->fs */
582 if (upd
->format
!= MNT_FMT_FSTAB
) {
583 unsigned long fl
= 0;
585 o
= mnt_fs_get_optstr(upd
->fs
);
586 if (o
&& !mnt_optstr_get_mountflags(o
, &fl
))
587 upd
->mountflags
|= fl
;
591 if (upd
->action
== MNT_ACT_UMOUNT
)
595 * A) classic /etc/mtab or /etc/fstab update
597 if (upd
->format
!= MNT_FMT_MOUNTINFO
)
601 * B) /var/run/mount/mountinfo
602 * - remove all non-userspace mount options
604 if (upd
->mountflags
& MS_REMOUNT
) {
605 rc
= mnt_split_optstr(o
, &u
, NULL
, NULL
, MNT_NOMTAB
, 0);
608 rc
= __mnt_fs_set_optstr_ptr(upd
->fs
, u
, FALSE
);
614 goto nothing
; /* no options */
615 rc
= mnt_split_optstr(o
, &u
, NULL
, NULL
, MNT_NOMTAB
, 0);
619 goto nothing
; /* no userpsace options */
620 rc
= set_fs_root(upd
, upd
->fs
);
623 rc
= __mnt_fs_set_optstr_ptr(upd
->fs
, u
, FALSE
);
629 if (!upd
->nolock
&& !upd
->lc
) {
630 upd
->lc
= mnt_new_lock(upd
->filename
, 0);
637 DBG(UPDATE
, mnt_debug_h(upd
, "prepare update: success"));
641 DBG(UPDATE
, mnt_debug_h(upd
, "prepare update: failed"));
647 static int add_entry(mnt_update
*upd
)
654 DBG(UPDATE
, mnt_debug_h(upd
, "add entry"));
657 mnt_lock_file(upd
->lc
);
658 f
= fopen(upd
->filename
, "a+");
660 if (upd
->format
== MNT_FMT_MOUNTINFO
)
661 rc
= fprintf_mountinfo_fs(f
, upd
->fs
);
663 rc
= fprintf_mtab_fs(f
, upd
->fs
);
667 mnt_unlock_file(upd
->lc
);
671 static int remove_entry(mnt_update
*upd
)
679 assert(upd
->filename
);
681 target
= mnt_fs_get_target(upd
->fs
);
684 DBG(UPDATE
, mnt_debug_h(upd
, "remove entry (target %s)", target
));
687 mnt_lock_file(upd
->lc
);
688 tb
= mnt_new_tab_from_file(upd
->filename
);
691 fs
= mnt_tab_find_target(tb
, target
, MNT_ITER_BACKWARD
);
693 rc
= 0; /* no error if the file does not contain the target */
696 mnt_tab_remove_fs(tb
, fs
);
698 if (!update_file(upd
->filename
, upd
->format
, tb
))
702 mnt_unlock_file(upd
->lc
);
708 static int modify_target(mnt_update
*upd
)
715 assert(upd
->old_target
);
716 assert(upd
->filename
);
717 assert(mnt_fs_get_target(upd
->fs
));
719 if (!upd
->old_target
)
722 DBG(UPDATE
, mnt_debug_h(upd
, "modify target (%s->%s)",
723 upd
->old_target
, mnt_fs_get_target(upd
->fs
)));
726 mnt_lock_file(upd
->lc
);
727 tb
= mnt_new_tab_from_file(upd
->filename
);
730 fs
= mnt_tab_find_target(tb
, upd
->old_target
, MNT_ITER_BACKWARD
);
732 rc
= 0; /* no error if the file does not contain the target */
736 mnt_fs_set_target(fs
, mnt_fs_get_target(upd
->fs
));
738 if (!update_file(upd
->filename
, upd
->format
, tb
))
742 mnt_unlock_file(upd
->lc
);
747 static int modify_options(mnt_update
*upd
)
750 mnt_fs
*fs
= NULL
, *rem_fs
= NULL
;
752 const char *target
= mnt_fs_get_target(upd
->fs
);
755 assert(upd
->filename
);
757 DBG(UPDATE
, mnt_debug_h(upd
, "modify options (target %s)", target
));
760 mnt_lock_file(upd
->lc
);
761 tb
= mnt_new_tab_from_file(upd
->filename
);
764 fs
= mnt_tab_find_target(tb
, target
, MNT_ITER_BACKWARD
);
766 rc
= 0; /* no error if the file does not contain the target */
769 if (upd
->format
== MNT_FMT_MOUNTINFO
&& !mnt_fs_get_optstr(upd
->fs
)) {
770 mnt_tab_remove_fs(tb
, fs
);
773 rc
= __mnt_fs_set_optstr(fs
, mnt_fs_get_optstr(upd
->fs
), FALSE
);
775 if (!update_file(upd
->filename
, upd
->format
, tb
))
779 mnt_unlock_file(upd
->lc
);
789 * Updates the update file. The behavior of this function is undefined if
790 * mnt_prepare_update() has not been called.
792 * Returns: 0 on success, -1 in case of error.
794 int mnt_update_file(mnt_update
*upd
)
797 assert(upd
->filename
);
801 if (!upd
|| !upd
->fs
)
804 DBG(UPDATE
, mnt_debug_h(upd
, "update (target %s)",
805 mnt_fs_get_target(upd
->fs
)));
809 if (upd
->action
== MNT_ACT_UMOUNT
)
810 return remove_entry(upd
);
814 if (upd
->action
== MNT_ACT_MOUNT
) {
815 if (upd
->mountflags
& MS_REMOUNT
)
816 return modify_options(upd
);
818 if (upd
->mountflags
& MS_MOVE
)
819 return modify_target(upd
);
821 return add_entry(upd
); /* mount */
832 static void lock_fallback(void)
835 mnt_unlock_file(lock
);
838 static int update(mnt_update
*upd
)
843 * Note that mount(2) syscal should be called *after*
844 * mnt_prepare_update() and *before* mnt_update_file()
846 rc
= mnt_prepare_update(upd
);
848 /* setup lock fallback */
849 lock
= mnt_update_get_lock(upd
);
850 atexit(lock_fallback
);
852 return mnt_update_file(upd
);
855 printf("update: update is not reuquired\n");
858 fprintf(stderr
, "update: failed to prepare update\n");
862 int test_add(struct mtest
*ts
, int argc
, char *argv
[])
864 mnt_fs
*fs
= mnt_new_fs();
870 mnt_fs_set_source(fs
, argv
[1]);
871 mnt_fs_set_target(fs
, argv
[2]);
872 mnt_fs_set_fstype(fs
, argv
[3]);
873 mnt_fs_set_optstr(fs
, argv
[4]);
875 upd
= mnt_new_update(MNT_ACT_MOUNT
, 0, fs
);
881 mnt_free_update(upd
);
886 int test_add_fstab(struct mtest
*ts
, int argc
, char *argv
[])
888 mnt_fs
*fs
= mnt_new_fs();
894 mnt_fs_set_source(fs
, argv
[1]);
895 mnt_fs_set_target(fs
, argv
[2]);
896 mnt_fs_set_fstype(fs
, argv
[3]);
897 mnt_fs_set_optstr(fs
, argv
[4]);
898 mnt_fs_set_freq(fs
, atoi(argv
[5]));
899 mnt_fs_set_passno(fs
, atoi(argv
[6]));
901 /* this is tricky -- to add to fstab use "MNT_ACT_MOUNT" */
902 upd
= mnt_new_update(MNT_ACT_MOUNT
, 0, fs
);
906 mnt_update_disable_lock(upd
, TRUE
); /* lock is unnecessary */
907 mnt_update_set_filename(upd
, _PATH_MNTTAB
); /* fstab */
908 mnt_update_set_format(upd
, MNT_FMT_FSTAB
);
912 mnt_free_update(upd
);
917 int test_remove(struct mtest
*ts
, int argc
, char *argv
[])
919 mnt_fs
*fs
= mnt_new_fs();
925 mnt_fs_set_target(fs
, argv
[1]);
927 upd
= mnt_new_update(MNT_ACT_UMOUNT
, 0, fs
);
933 mnt_free_update(upd
);
938 int test_move(struct mtest
*ts
, int argc
, char *argv
[])
940 mnt_fs
*fs
= mnt_new_fs();
946 mnt_fs_set_target(fs
, argv
[2]);
948 upd
= mnt_new_update(MNT_ACT_MOUNT
, MS_MOVE
, fs
);
951 mnt_update_set_old_target(upd
, argv
[1]);
955 mnt_free_update(upd
);
960 int test_remount(struct mtest
*ts
, int argc
, char *argv
[])
962 mnt_fs
*fs
= mnt_new_fs();
969 mnt_fs_set_target(fs
, argv
[1]);
970 mnt_fs_set_optstr(fs
, argv
[2]);
972 upd
= mnt_new_update(MNT_ACT_MOUNT
, MS_REMOUNT
, fs
);
978 mnt_free_update(upd
);
983 int main(int argc
, char *argv
[])
985 struct mtest tss
[] = {
986 { "--add", test_add
, "<src> <target> <type> <options> add line to mtab" },
987 { "--remove", test_remove
, "<target> MS_REMOUNT mtab change" },
988 { "--move", test_move
, "<old_target> <target> MS_MOVE mtab change" },
989 { "--remount",test_remount
, "<target> <options> MS_REMOUNT mtab change" },
991 { "--add-fstab", test_add_fstab
, "<src> <target> <type> <options> <freq> <passno> add line to fstab" },
996 return mnt_run_test(tss
, argc
, argv
);
999 #endif /* TEST_PROGRAM */