]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab_update.c
9603f77ecde4d23517f1e39ee596404b5af0a283
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: Tables update
11 * @short_description: userspace mount information management
13 * The struct libmnt_update provides an abstraction to manage mount options in
14 * userspace independently of system configuration. This low-level API works on
15 * systems both with and without /etc/mtab. On systems without the regular /etc/mtab
16 * file, the userspace mount options (e.g. user=) are stored in the /run/mount/utab
19 * It's recommended to use high-level struct libmnt_context API.
27 #include "pathnames.h"
29 struct libmnt_update
{
33 unsigned long mountflags
;
37 struct libmnt_table
*mountinfo
;
40 static int set_fs_root(struct libmnt_update
*upd
, struct libmnt_fs
*fs
, unsigned long mountflags
);
41 static int utab_new_entry(struct libmnt_update
*upd
, struct libmnt_fs
*fs
, unsigned long mountflags
);
46 * Returns: newly allocated update handler
48 struct libmnt_update
*mnt_new_update(void)
50 struct libmnt_update
*upd
;
52 upd
= calloc(1, sizeof(*upd
));
56 DBG(UPDATE
, mnt_debug_h(upd
, "allocate"));
64 * Deallocates struct libmnt_update handler.
66 void mnt_free_update(struct libmnt_update
*upd
)
71 DBG(UPDATE
, mnt_debug_h(upd
, "free"));
74 mnt_free_table(upd
->mountinfo
);
81 * Returns 0 on success, <0 in case of error.
83 int mnt_update_set_filename(struct libmnt_update
*upd
, const char *filename
,
86 const char *path
= NULL
;
93 /* filename explicitly defined */
95 char *p
= strdup(filename
);
99 upd
->userspace_only
= userspace_only
;
107 /* detect tab filename -- /etc/mtab or /run/mount/utab
109 mnt_has_regular_mtab(&path
, &rw
);
112 mnt_has_regular_utab(&path
, &rw
);
115 upd
->userspace_only
= TRUE
;
117 upd
->filename
= strdup(path
);
125 * mnt_update_get_filename:
128 * This function returns the file name (e.g. /etc/mtab) of the up-dated file.
130 * Returns: pointer to filename that will be updated or NULL in case of error.
132 const char *mnt_update_get_filename(struct libmnt_update
*upd
)
135 return upd
? upd
->filename
: NULL
;
139 * mnt_update_is_ready:
140 * @upd: update handler
142 * Returns: 1 if entry described by @upd is successfully prepared and will be
143 * written to the mtab/utab file.
145 int mnt_update_is_ready(struct libmnt_update
*upd
)
148 return upd
? upd
->ready
: FALSE
;
153 * @upd: update handler
154 * @mountflags: MS_* flags
155 * @target: umount target, must be NULL for mount
156 * @fs: mount filesystem description, must be NULL for umount
158 * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
160 int mnt_update_set_fs(struct libmnt_update
*upd
, unsigned long mountflags
,
161 const char *target
, struct libmnt_fs
*fs
)
166 assert(target
|| fs
);
170 if ((mountflags
& MS_MOVE
) && (!fs
|| !mnt_fs_get_srcpath(fs
)))
175 DBG(UPDATE
, mnt_debug_h(upd
,
176 "resetting FS [fs=0x%p, target=%s, flags=0x%08lx]",
177 fs
, target
, mountflags
));
179 DBG(UPDATE
, mnt_debug_h(upd
, "FS template:"));
180 DBG(UPDATE
, mnt_fs_print_debug(fs
, stderr
));
183 mnt_free_fs(upd
->fs
);
190 if (mountflags
& MS_PROPAGATION
)
193 upd
->mountflags
= mountflags
;
195 rc
= mnt_update_set_filename(upd
, NULL
, 0);
197 DBG(UPDATE
, mnt_debug_h(upd
, "no writable file available [rc=%d]", rc
));
198 return rc
; /* error or no file available (rc = 1) */
201 upd
->target
= strdup(target
);
206 if (upd
->userspace_only
&& !(mountflags
& MS_MOVE
)) {
207 rc
= utab_new_entry(upd
, fs
, mountflags
);
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
)
233 return upd
? upd
->fs
: NULL
;
237 * mnt_update_get_mflags:
240 * Returns: mount flags as was set by mnt_update_set_fs()
242 unsigned long mnt_update_get_mflags(struct libmnt_update
*upd
)
245 return upd
? upd
->mountflags
: 0;
249 * mnt_update_force_rdonly:
251 * @rdonly: is read-only?
253 * Returns: 0 on success and negative number in case of error.
255 int mnt_update_force_rdonly(struct libmnt_update
*upd
, int rdonly
)
260 if (!upd
|| !upd
->fs
)
263 if (rdonly
&& (upd
->mountflags
& MS_RDONLY
))
265 if (!rdonly
&& !(upd
->mountflags
& MS_RDONLY
))
268 if (!upd
->userspace_only
) {
269 /* /etc/mtab -- we care about VFS options there */
270 const char *o
= mnt_fs_get_options(upd
->fs
);
271 char *n
= o
? strdup(o
) : NULL
;
274 mnt_optstr_remove_option(&n
, rdonly
? "rw" : "ro");
275 if (!mnt_optstr_prepend_option(&n
, rdonly
? "ro" : "rw", NULL
))
276 rc
= mnt_fs_set_options(upd
->fs
, n
);
282 upd
->mountflags
&= ~MS_RDONLY
;
284 upd
->mountflags
|= MS_RDONLY
;
290 * Allocates an utab entry (upd->fs) for mount/remount. This function should be
291 * called *before* mount(2) syscall. The @fs is used as a read-only template.
293 * Returns: 0 on success, negative number on error, 1 if utab's update is
296 static int utab_new_entry(struct libmnt_update
*upd
, struct libmnt_fs
*fs
,
297 unsigned long mountflags
)
300 const char *o
= NULL
, *a
= NULL
;
305 assert(upd
->fs
== NULL
);
306 assert(!(mountflags
& MS_MOVE
));
308 DBG(UPDATE
, mnt_debug("prepare utab entry"));
310 o
= mnt_fs_get_user_options(fs
);
311 a
= mnt_fs_get_attributes(fs
);
315 /* remove non-mtab options */
316 rc
= mnt_optstr_get_options(o
, &u
,
317 mnt_get_builtin_optmap(MNT_USERSPACE_MAP
),
324 DBG(UPDATE
, mnt_debug("utab entry unnecessary (no options)"));
328 /* allocate the entry */
329 upd
->fs
= mnt_copy_fs(NULL
, fs
);
335 rc
= mnt_fs_set_options(upd
->fs
, u
);
338 rc
= mnt_fs_set_attributes(upd
->fs
, a
);
342 if (!(mountflags
& MS_REMOUNT
)) {
343 rc
= set_fs_root(upd
, fs
, mountflags
);
349 DBG(UPDATE
, mnt_debug("utab entry OK"));
353 mnt_free_fs(upd
->fs
);
359 * Sets fs-root and fs-type to @upd->fs according to the @fs template and
360 * @mountfalgs. For MS_BIND mountflag it reads information about the source
361 * filesystem from /proc/self/mountinfo.
363 static int set_fs_root(struct libmnt_update
*upd
, struct libmnt_fs
*fs
,
364 unsigned long mountflags
)
366 struct libmnt_fs
*src_fs
;
371 DBG(UPDATE
, mnt_debug("setting FS root"));
377 if (mountflags
& MS_BIND
) {
379 upd
->mountinfo
= mnt_new_table_from_file(_PATH_PROC_MOUNTINFO
);
381 src
= mnt_fs_get_srcpath(fs
);
383 rc
= mnt_fs_set_bindsrc(upd
->fs
, src
);
389 src_fs
= mnt_table_get_fs_root(upd
->mountinfo
, fs
,
390 mountflags
, &fsroot
);
392 src
= mnt_fs_get_srcpath(src_fs
);
393 rc
= mnt_fs_set_source(upd
->fs
, src
);
397 mnt_fs_set_fstype(upd
->fs
, mnt_fs_get_fstype(src_fs
));
400 upd
->fs
->root
= fsroot
;
407 /* mtab and fstab update -- returns zero on success
409 static int fprintf_mtab_fs(FILE *f
, struct libmnt_fs
*fs
)
411 const char *o
, *src
, *fstype
, *comm
;
412 char *m1
, *m2
, *m3
, *m4
;
418 comm
= mnt_fs_get_comment(fs
);
419 src
= mnt_fs_get_source(fs
);
420 fstype
= mnt_fs_get_fstype(fs
);
421 o
= mnt_fs_get_options(fs
);
423 m1
= src
? mangle(src
) : "none";
424 m2
= mangle(mnt_fs_get_target(fs
));
425 m3
= fstype
? mangle(fstype
) : "none";
426 m4
= o
? mangle(o
) : "rw";
428 if (m1
&& m2
&& m3
&& m4
) {
431 rc
= fprintf(f
, "%s %s %s %s %d %d\n",
434 mnt_fs_get_passno(fs
));
451 static int fprintf_utab_fs(FILE *f
, struct libmnt_fs
*fs
)
462 p
= mangle(mnt_fs_get_source(fs
));
464 rc
= fprintf(f
, "SRC=%s ", p
);
468 p
= mangle(mnt_fs_get_target(fs
));
470 rc
= fprintf(f
, "TARGET=%s ", p
);
475 p
= mangle(mnt_fs_get_root(fs
));
477 rc
= fprintf(f
, "ROOT=%s ", p
);
482 p
= mangle(mnt_fs_get_bindsrc(fs
));
484 rc
= fprintf(f
, "BINDSRC=%s ", p
);
489 p
= mangle(mnt_fs_get_attributes(fs
));
491 rc
= fprintf(f
, "ATTRS=%s ", p
);
496 p
= mangle(mnt_fs_get_user_options(fs
));
498 rc
= fprintf(f
, "OPTS=%s", p
);
503 rc
= fprintf(f
, "\n");
506 rc
= 0; /* success */
510 static int update_table(struct libmnt_update
*upd
, struct libmnt_table
*tb
)
517 if (!tb
|| !upd
->filename
)
520 DBG(UPDATE
, mnt_debug_h(upd
, "%s: updating", upd
->filename
));
522 fd
= mnt_open_uniq_filename(upd
->filename
, &uq
);
524 return fd
; /* error */
526 f
= fdopen(fd
, "w" UL_CLOEXECSTR
);
529 struct libmnt_iter itr
;
530 struct libmnt_fs
*fs
;
532 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
534 if (tb
->comms
&& mnt_table_get_intro_comment(tb
))
535 fputs(mnt_table_get_intro_comment(tb
), f
);
537 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
538 if (upd
->userspace_only
)
539 rc
= fprintf_utab_fs(f
, fs
);
541 rc
= fprintf_mtab_fs(f
, fs
);
543 DBG(UPDATE
, mnt_debug_h(upd
,
544 "%s: write entry failed: %m", uq
));
548 if (tb
->comms
&& mnt_table_get_trailing_comment(tb
))
549 fputs(mnt_table_get_trailing_comment(tb
), f
);
551 if (fflush(f
) != 0) {
553 DBG(UPDATE
, mnt_debug_h(upd
, "%s: fflush failed: %m", uq
));
557 rc
= fchmod(fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) ? -errno
: 0;
559 if (!rc
&& stat(upd
->filename
, &st
) == 0)
560 /* Copy uid/gid from the present file before renaming. */
561 rc
= fchown(fd
, st
.st_uid
, st
.st_gid
) ? -errno
: 0;
567 rc
= rename(uq
, upd
->filename
) ? -errno
: 0;
577 unlink(uq
); /* be paranoid */
583 * mnt_table_write_file
584 * @tb: parsed file (e.g. fstab)
587 * This function writes @tb to @file.
589 * Returns: 0 on success, negative number on error.
591 int mnt_table_write_file(struct libmnt_table
*tb
, FILE *file
)
594 struct libmnt_iter itr
;
595 struct libmnt_fs
*fs
;
597 if (tb
->comms
&& mnt_table_get_intro_comment(tb
))
598 fputs(mnt_table_get_intro_comment(tb
), file
);
600 mnt_reset_iter(&itr
, MNT_ITER_FORWARD
);
601 while(mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
602 rc
= fprintf_mtab_fs(file
, fs
);
606 if (tb
->comms
&& mnt_table_get_trailing_comment(tb
))
607 fputs(mnt_table_get_trailing_comment(tb
), file
);
609 if (fflush(file
) != 0)
612 DBG(TAB
, mnt_debug_h(tb
, "write file done [rc=%d]", rc
));
617 * mnt_table_replace_file
618 * @tb: parsed file (e.g. fstab)
621 * This function replaces @file by the new content from @tb.
623 * Returns: 0 on success, negative number on error.
625 int mnt_table_replace_file(struct libmnt_table
*tb
, const char *filename
)
631 DBG(TAB
, mnt_debug_h(tb
, "%s: replacing", filename
));
633 fd
= mnt_open_uniq_filename(filename
, &uq
);
635 return fd
; /* error */
637 f
= fdopen(fd
, "w" UL_CLOEXECSTR
);
641 mnt_table_write_file(tb
, f
);
643 rc
= fchmod(fd
, S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
) ? -errno
: 0;
645 if (!rc
&& stat(filename
, &st
) == 0)
646 /* Copy uid/gid from the present file before renaming. */
647 rc
= fchown(fd
, st
.st_uid
, st
.st_gid
) ? -errno
: 0;
651 rename(uq
, filename
);
660 DBG(TAB
, mnt_debug_h(tb
, "replace done [rc=%d]", rc
));
663 static int add_file_entry(struct libmnt_table
*tb
, struct libmnt_update
*upd
)
665 struct libmnt_fs
*fs
;
669 fs
= mnt_copy_fs(NULL
, upd
->fs
);
673 mnt_table_add_fs(tb
, fs
);
674 return update_table(upd
, tb
);
677 static int update_add_entry(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
679 struct libmnt_table
*tb
;
685 DBG(UPDATE
, mnt_debug_h(upd
, "%s: add entry", upd
->filename
));
688 rc
= mnt_lock_file(lc
);
692 tb
= __mnt_new_table_from_file(upd
->filename
,
693 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
695 rc
= add_file_entry(tb
, upd
);
703 static int update_remove_entry(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
705 struct libmnt_table
*tb
;
711 DBG(UPDATE
, mnt_debug_h(upd
, "%s: remove entry", upd
->filename
));
714 rc
= mnt_lock_file(lc
);
718 tb
= __mnt_new_table_from_file(upd
->filename
,
719 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
721 struct libmnt_fs
*rem
= mnt_table_find_target(tb
, upd
->target
, MNT_ITER_BACKWARD
);
723 mnt_table_remove_fs(tb
, rem
);
724 rc
= update_table(upd
, tb
);
735 static int update_modify_target(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
737 struct libmnt_table
*tb
= NULL
;
741 DBG(UPDATE
, mnt_debug_h(upd
, "%s: modify target", upd
->filename
));
744 rc
= mnt_lock_file(lc
);
748 tb
= __mnt_new_table_from_file(upd
->filename
,
749 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
751 struct libmnt_fs
*cur
= mnt_table_find_target(tb
,
752 mnt_fs_get_srcpath(upd
->fs
), MNT_ITER_BACKWARD
);
754 rc
= mnt_fs_set_target(cur
, mnt_fs_get_target(upd
->fs
));
756 rc
= update_table(upd
, tb
);
767 static int update_modify_options(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
769 struct libmnt_table
*tb
= NULL
;
771 struct libmnt_fs
*fs
;
776 DBG(UPDATE
, mnt_debug_h(upd
, "%s: modify options", upd
->filename
));
781 rc
= mnt_lock_file(lc
);
785 tb
= __mnt_new_table_from_file(upd
->filename
,
786 upd
->userspace_only
? MNT_FMT_UTAB
: MNT_FMT_MTAB
);
788 struct libmnt_fs
*cur
= mnt_table_find_target(tb
,
789 mnt_fs_get_target(fs
),
792 if (upd
->userspace_only
)
793 rc
= mnt_fs_set_attributes(cur
, mnt_fs_get_attributes(fs
));
795 rc
= mnt_fs_set_options(cur
, mnt_fs_get_options(fs
));
797 rc
= update_table(upd
, tb
);
799 rc
= add_file_entry(tb
, upd
); /* not found, add new */
814 * High-level API to update /etc/mtab (or private /run/mount/utab file).
816 * The @lc lock is optional and will be created if necessary. Note that
817 * an automatically created lock blocks all signals.
819 * See also mnt_lock_block_signals() and mnt_context_get_lock().
821 * Returns: 0 on success, negative number on error.
823 int mnt_update_table(struct libmnt_update
*upd
, struct libmnt_lock
*lc
)
825 struct libmnt_lock
*lc0
= lc
;
829 if (!upd
|| !upd
->filename
)
834 DBG(UPDATE
, mnt_debug_h(upd
, "%s: update tab", upd
->filename
));
836 DBG(UPDATE
, mnt_fs_print_debug(upd
->fs
, stderr
));
839 lc
= mnt_new_lock(upd
->filename
, 0);
841 mnt_lock_block_signals(lc
, TRUE
);
843 if (lc
&& upd
->userspace_only
)
844 mnt_lock_use_simplelock(lc
, TRUE
); /* use flock */
846 if (!upd
->fs
&& upd
->target
)
847 rc
= update_remove_entry(upd
, lc
); /* umount */
848 else if (upd
->mountflags
& MS_MOVE
)
849 rc
= update_modify_target(upd
, lc
); /* move */
850 else if (upd
->mountflags
& MS_REMOUNT
)
851 rc
= update_modify_options(upd
, lc
); /* remount */
853 rc
= update_add_entry(upd
, lc
); /* mount */
856 DBG(UPDATE
, mnt_debug_h(upd
, "%s: update tab: done [rc=%d]",
865 static int update(const char *target
, struct libmnt_fs
*fs
, unsigned long mountflags
)
868 struct libmnt_update
*upd
;
870 DBG(UPDATE
, mnt_debug("update test"));
872 upd
= mnt_new_update();
876 rc
= mnt_update_set_fs(upd
, mountflags
, target
, fs
);
878 /* update is unnecessary */
883 fprintf(stderr
, "failed to set FS\n");
887 /* [... mount(2) call should be here...] */
889 rc
= mnt_update_table(upd
, NULL
);
891 mnt_free_update(upd
);
895 static int test_add(struct libmnt_test
*ts
, int argc
, char *argv
[])
897 struct libmnt_fs
*fs
= mnt_new_fs();
902 mnt_fs_set_source(fs
, argv
[1]);
903 mnt_fs_set_target(fs
, argv
[2]);
904 mnt_fs_set_fstype(fs
, argv
[3]);
905 mnt_fs_set_options(fs
, argv
[4]);
907 rc
= update(NULL
, fs
, 0);
912 static int test_remove(struct libmnt_test
*ts
, int argc
, char *argv
[])
916 return update(argv
[1], NULL
, 0);
919 static int test_move(struct libmnt_test
*ts
, int argc
, char *argv
[])
921 struct libmnt_fs
*fs
= mnt_new_fs();
926 mnt_fs_set_source(fs
, argv
[1]);
927 mnt_fs_set_target(fs
, argv
[2]);
929 rc
= update(NULL
, fs
, MS_MOVE
);
935 static int test_remount(struct libmnt_test
*ts
, int argc
, char *argv
[])
937 struct libmnt_fs
*fs
= mnt_new_fs();
942 mnt_fs_set_target(fs
, argv
[1]);
943 mnt_fs_set_options(fs
, argv
[2]);
945 rc
= update(NULL
, fs
, MS_REMOUNT
);
950 static int test_replace(struct libmnt_test
*ts
, int argc
, char *argv
[])
952 struct libmnt_fs
*fs
= mnt_new_fs();
953 struct libmnt_table
*tb
= mnt_new_table();
959 mnt_table_enable_comments(tb
, TRUE
);
960 mnt_table_parse_fstab(tb
, NULL
);
962 mnt_fs_set_source(fs
, argv
[1]);
963 mnt_fs_set_target(fs
, argv
[2]);
964 mnt_fs_append_comment(fs
, "# this is new filesystem\n");
965 mnt_table_add_fs(tb
, fs
);
967 rc
= mnt_table_replace_file(tb
, mnt_get_fstab_path());
972 int main(int argc
, char *argv
[])
974 struct libmnt_test tss
[] = {
975 { "--add", test_add
, "<src> <target> <type> <options> add a line to mtab" },
976 { "--remove", test_remove
, "<target> MS_REMOUNT mtab change" },
977 { "--move", test_move
, "<old_target> <target> MS_MOVE mtab change" },
978 { "--remount",test_remount
, "<target> <options> MS_REMOUNT mtab change" },
979 { "--replace",test_replace
, "<src> <target> Add a line to LIBMOUNT_FSTAB and replace the original file" },
983 return mnt_run_test(tss
, argc
, argv
);
986 #endif /* TEST_PROGRAM */