]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab_update.c
9603f77ecde4d23517f1e39ee596404b5af0a283
[thirdparty/util-linux.git] / libmount / src / tab_update.c
1 /*
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
7
8 /**
9 * SECTION: update
10 * @title: Tables update
11 * @short_description: userspace mount information management
12 *
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
17 * file.
18 *
19 * It's recommended to use high-level struct libmnt_context API.
20 */
21 #include <sys/file.h>
22 #include <fcntl.h>
23 #include <signal.h>
24
25 #include "mountP.h"
26 #include "mangle.h"
27 #include "pathnames.h"
28
29 struct libmnt_update {
30 char *target;
31 struct libmnt_fs *fs;
32 char *filename;
33 unsigned long mountflags;
34 int userspace_only;
35 int ready;
36
37 struct libmnt_table *mountinfo;
38 };
39
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);
42
43 /**
44 * mnt_new_update:
45 *
46 * Returns: newly allocated update handler
47 */
48 struct libmnt_update *mnt_new_update(void)
49 {
50 struct libmnt_update *upd;
51
52 upd = calloc(1, sizeof(*upd));
53 if (!upd)
54 return NULL;
55
56 DBG(UPDATE, mnt_debug_h(upd, "allocate"));
57 return upd;
58 }
59
60 /**
61 * mnt_free_update:
62 * @upd: update
63 *
64 * Deallocates struct libmnt_update handler.
65 */
66 void mnt_free_update(struct libmnt_update *upd)
67 {
68 if (!upd)
69 return;
70
71 DBG(UPDATE, mnt_debug_h(upd, "free"));
72
73 mnt_free_fs(upd->fs);
74 mnt_free_table(upd->mountinfo);
75 free(upd->target);
76 free(upd->filename);
77 free(upd);
78 }
79
80 /*
81 * Returns 0 on success, <0 in case of error.
82 */
83 int mnt_update_set_filename(struct libmnt_update *upd, const char *filename,
84 int userspace_only)
85 {
86 const char *path = NULL;
87 int rw = 0;
88
89 assert(upd);
90 if (!upd)
91 return -EINVAL;
92
93 /* filename explicitly defined */
94 if (filename) {
95 char *p = strdup(filename);
96 if (!p)
97 return -ENOMEM;
98
99 upd->userspace_only = userspace_only;
100 free(upd->filename);
101 upd->filename = p;
102 }
103
104 if (upd->filename)
105 return 0;
106
107 /* detect tab filename -- /etc/mtab or /run/mount/utab
108 */
109 mnt_has_regular_mtab(&path, &rw);
110 if (!rw) {
111 path = NULL;
112 mnt_has_regular_utab(&path, &rw);
113 if (!rw)
114 return -EACCES;
115 upd->userspace_only = TRUE;
116 }
117 upd->filename = strdup(path);
118 if (!upd->filename)
119 return -ENOMEM;
120
121 return 0;
122 }
123
124 /**
125 * mnt_update_get_filename:
126 * @upd: update
127 *
128 * This function returns the file name (e.g. /etc/mtab) of the up-dated file.
129 *
130 * Returns: pointer to filename that will be updated or NULL in case of error.
131 */
132 const char *mnt_update_get_filename(struct libmnt_update *upd)
133 {
134 assert(upd);
135 return upd ? upd->filename : NULL;
136 }
137
138 /**
139 * mnt_update_is_ready:
140 * @upd: update handler
141 *
142 * Returns: 1 if entry described by @upd is successfully prepared and will be
143 * written to the mtab/utab file.
144 */
145 int mnt_update_is_ready(struct libmnt_update *upd)
146 {
147 assert(upd);
148 return upd ? upd->ready : FALSE;
149 }
150
151 /**
152 * mnt_update_set_fs:
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
157 *
158 * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
159 */
160 int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
161 const char *target, struct libmnt_fs *fs)
162 {
163 int rc;
164
165 assert(upd);
166 assert(target || fs);
167
168 if (!upd)
169 return -EINVAL;
170 if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs)))
171 return -EINVAL;
172 if (target && fs)
173 return -EINVAL;
174
175 DBG(UPDATE, mnt_debug_h(upd,
176 "resetting FS [fs=0x%p, target=%s, flags=0x%08lx]",
177 fs, target, mountflags));
178 if (fs) {
179 DBG(UPDATE, mnt_debug_h(upd, "FS template:"));
180 DBG(UPDATE, mnt_fs_print_debug(fs, stderr));
181 }
182
183 mnt_free_fs(upd->fs);
184 free(upd->target);
185 upd->ready = FALSE;
186 upd->fs = NULL;
187 upd->target = NULL;
188 upd->mountflags = 0;
189
190 if (mountflags & MS_PROPAGATION)
191 return 1;
192
193 upd->mountflags = mountflags;
194
195 rc = mnt_update_set_filename(upd, NULL, 0);
196 if (rc) {
197 DBG(UPDATE, mnt_debug_h(upd, "no writable file available [rc=%d]", rc));
198 return rc; /* error or no file available (rc = 1) */
199 }
200 if (target) {
201 upd->target = strdup(target);
202 if (!upd->target)
203 return -ENOMEM;
204
205 } else if (fs) {
206 if (upd->userspace_only && !(mountflags & MS_MOVE)) {
207 rc = utab_new_entry(upd, fs, mountflags);
208 if (rc)
209 return rc;
210 } else {
211 upd->fs = mnt_copy_mtab_fs(fs);
212 if (!upd->fs)
213 return -ENOMEM;
214
215 }
216 }
217
218
219 DBG(UPDATE, mnt_debug_h(upd, "ready"));
220 upd->ready = TRUE;
221 return 0;
222 }
223
224 /**
225 * mnt_update_get_fs:
226 * @upd: update
227 *
228 * Returns: update filesystem entry or NULL
229 */
230 struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd)
231 {
232 assert(upd);
233 return upd ? upd->fs : NULL;
234 }
235
236 /**
237 * mnt_update_get_mflags:
238 * @upd: update
239 *
240 * Returns: mount flags as was set by mnt_update_set_fs()
241 */
242 unsigned long mnt_update_get_mflags(struct libmnt_update *upd)
243 {
244 assert(upd);
245 return upd ? upd->mountflags : 0;
246 }
247
248 /**
249 * mnt_update_force_rdonly:
250 * @upd: update
251 * @rdonly: is read-only?
252 *
253 * Returns: 0 on success and negative number in case of error.
254 */
255 int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly)
256 {
257 int rc = 0;
258
259 assert(upd);
260 if (!upd || !upd->fs)
261 return -EINVAL;
262
263 if (rdonly && (upd->mountflags & MS_RDONLY))
264 return 0;
265 if (!rdonly && !(upd->mountflags & MS_RDONLY))
266 return 0;
267
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;
272
273 if (n)
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);
277
278 free(n);
279 }
280
281 if (rdonly)
282 upd->mountflags &= ~MS_RDONLY;
283 else
284 upd->mountflags |= MS_RDONLY;
285
286 return rc;
287 }
288
289 /*
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.
292 *
293 * Returns: 0 on success, negative number on error, 1 if utab's update is
294 * unnecessary.
295 */
296 static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs,
297 unsigned long mountflags)
298 {
299 int rc = 0;
300 const char *o = NULL, *a = NULL;
301 char *u = NULL;
302
303 assert(fs);
304 assert(upd);
305 assert(upd->fs == NULL);
306 assert(!(mountflags & MS_MOVE));
307
308 DBG(UPDATE, mnt_debug("prepare utab entry"));
309
310 o = mnt_fs_get_user_options(fs);
311 a = mnt_fs_get_attributes(fs);
312 upd->fs = NULL;
313
314 if (o) {
315 /* remove non-mtab options */
316 rc = mnt_optstr_get_options(o, &u,
317 mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
318 MNT_NOMTAB);
319 if (rc)
320 goto err;
321 }
322
323 if (!u && !a) {
324 DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)"));
325 return 1;
326 }
327
328 /* allocate the entry */
329 upd->fs = mnt_copy_fs(NULL, fs);
330 if (!upd->fs) {
331 rc = -ENOMEM;
332 goto err;
333 }
334
335 rc = mnt_fs_set_options(upd->fs, u);
336 if (rc)
337 goto err;
338 rc = mnt_fs_set_attributes(upd->fs, a);
339 if (rc)
340 goto err;
341
342 if (!(mountflags & MS_REMOUNT)) {
343 rc = set_fs_root(upd, fs, mountflags);
344 if (rc)
345 goto err;
346 }
347
348 free(u);
349 DBG(UPDATE, mnt_debug("utab entry OK"));
350 return 0;
351 err:
352 free(u);
353 mnt_free_fs(upd->fs);
354 upd->fs = NULL;
355 return rc;
356 }
357
358 /*
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.
362 */
363 static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs,
364 unsigned long mountflags)
365 {
366 struct libmnt_fs *src_fs;
367 char *fsroot = NULL;
368 const char *src;
369 int rc = 0;
370
371 DBG(UPDATE, mnt_debug("setting FS root"));
372
373 assert(upd);
374 assert(upd->fs);
375 assert(fs);
376
377 if (mountflags & MS_BIND) {
378 if (!upd->mountinfo)
379 upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
380
381 src = mnt_fs_get_srcpath(fs);
382 if (src) {
383 rc = mnt_fs_set_bindsrc(upd->fs, src);
384 if (rc)
385 goto err;
386 }
387 }
388
389 src_fs = mnt_table_get_fs_root(upd->mountinfo, fs,
390 mountflags, &fsroot);
391 if (src_fs) {
392 src = mnt_fs_get_srcpath(src_fs);
393 rc = mnt_fs_set_source(upd->fs, src);
394 if (rc)
395 goto err;
396
397 mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs));
398 }
399
400 upd->fs->root = fsroot;
401 return 0;
402 err:
403 free(fsroot);
404 return rc;
405 }
406
407 /* mtab and fstab update -- returns zero on success
408 */
409 static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
410 {
411 const char *o, *src, *fstype, *comm;
412 char *m1, *m2, *m3, *m4;
413 int rc;
414
415 assert(fs);
416 assert(f);
417
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);
422
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";
427
428 if (m1 && m2 && m3 && m4) {
429 if (comm)
430 fputs(comm, f);
431 rc = fprintf(f, "%s %s %s %s %d %d\n",
432 m1, m2, m3, m4,
433 mnt_fs_get_freq(fs),
434 mnt_fs_get_passno(fs));
435 if (rc > 0)
436 rc = 0;
437 } else
438 rc = -ENOMEM;
439
440 if (src)
441 free(m1);
442 free(m2);
443 if (fstype)
444 free(m3);
445 if (o)
446 free(m4);
447
448 return rc;
449 }
450
451 static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
452 {
453 char *p;
454 int rc = 0;
455
456 assert(fs);
457 assert(f);
458
459 if (!fs || !f)
460 return -EINVAL;
461
462 p = mangle(mnt_fs_get_source(fs));
463 if (p) {
464 rc = fprintf(f, "SRC=%s ", p);
465 free(p);
466 }
467 if (rc >= 0) {
468 p = mangle(mnt_fs_get_target(fs));
469 if (p) {
470 rc = fprintf(f, "TARGET=%s ", p);
471 free(p);
472 }
473 }
474 if (rc >= 0) {
475 p = mangle(mnt_fs_get_root(fs));
476 if (p) {
477 rc = fprintf(f, "ROOT=%s ", p);
478 free(p);
479 }
480 }
481 if (rc >= 0) {
482 p = mangle(mnt_fs_get_bindsrc(fs));
483 if (p) {
484 rc = fprintf(f, "BINDSRC=%s ", p);
485 free(p);
486 }
487 }
488 if (rc >= 0) {
489 p = mangle(mnt_fs_get_attributes(fs));
490 if (p) {
491 rc = fprintf(f, "ATTRS=%s ", p);
492 free(p);
493 }
494 }
495 if (rc >= 0) {
496 p = mangle(mnt_fs_get_user_options(fs));
497 if (p) {
498 rc = fprintf(f, "OPTS=%s", p);
499 free(p);
500 }
501 }
502 if (rc >= 0)
503 rc = fprintf(f, "\n");
504
505 if (rc > 0)
506 rc = 0; /* success */
507 return rc;
508 }
509
510 static int update_table(struct libmnt_update *upd, struct libmnt_table *tb)
511 {
512 FILE *f;
513 int rc, fd;
514 char *uq = NULL;
515
516 assert(upd);
517 if (!tb || !upd->filename)
518 return -EINVAL;
519
520 DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename));
521
522 fd = mnt_open_uniq_filename(upd->filename, &uq);
523 if (fd < 0)
524 return fd; /* error */
525
526 f = fdopen(fd, "w" UL_CLOEXECSTR);
527 if (f) {
528 struct stat st;
529 struct libmnt_iter itr;
530 struct libmnt_fs *fs;
531
532 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
533
534 if (tb->comms && mnt_table_get_intro_comment(tb))
535 fputs(mnt_table_get_intro_comment(tb), f);
536
537 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
538 if (upd->userspace_only)
539 rc = fprintf_utab_fs(f, fs);
540 else
541 rc = fprintf_mtab_fs(f, fs);
542 if (rc) {
543 DBG(UPDATE, mnt_debug_h(upd,
544 "%s: write entry failed: %m", uq));
545 goto leave;
546 }
547 }
548 if (tb->comms && mnt_table_get_trailing_comment(tb))
549 fputs(mnt_table_get_trailing_comment(tb), f);
550
551 if (fflush(f) != 0) {
552 rc = -errno;
553 DBG(UPDATE, mnt_debug_h(upd, "%s: fflush failed: %m", uq));
554 goto leave;
555 }
556
557 rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
558
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;
562
563 fclose(f);
564 f = NULL;
565
566 if (!rc)
567 rc = rename(uq, upd->filename) ? -errno : 0;
568 } else {
569 rc = -errno;
570 close(fd);
571 }
572
573 leave:
574 if (f)
575 fclose(f);
576
577 unlink(uq); /* be paranoid */
578 free(uq);
579 return rc;
580 }
581
582 /**
583 * mnt_table_write_file
584 * @tb: parsed file (e.g. fstab)
585 * @file: target
586 *
587 * This function writes @tb to @file.
588 *
589 * Returns: 0 on success, negative number on error.
590 */
591 int mnt_table_write_file(struct libmnt_table *tb, FILE *file)
592 {
593 int rc = 0;
594 struct libmnt_iter itr;
595 struct libmnt_fs *fs;
596
597 if (tb->comms && mnt_table_get_intro_comment(tb))
598 fputs(mnt_table_get_intro_comment(tb), file);
599
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);
603 if (rc)
604 return rc;
605 }
606 if (tb->comms && mnt_table_get_trailing_comment(tb))
607 fputs(mnt_table_get_trailing_comment(tb), file);
608
609 if (fflush(file) != 0)
610 rc = -errno;
611
612 DBG(TAB, mnt_debug_h(tb, "write file done [rc=%d]", rc));
613 return rc;
614 }
615
616 /**
617 * mnt_table_replace_file
618 * @tb: parsed file (e.g. fstab)
619 * @filename: target
620 *
621 * This function replaces @file by the new content from @tb.
622 *
623 * Returns: 0 on success, negative number on error.
624 */
625 int mnt_table_replace_file(struct libmnt_table *tb, const char *filename)
626 {
627 int fd, rc = 0;
628 FILE *f;
629 char *uq = NULL;
630
631 DBG(TAB, mnt_debug_h(tb, "%s: replacing", filename));
632
633 fd = mnt_open_uniq_filename(filename, &uq);
634 if (fd < 0)
635 return fd; /* error */
636
637 f = fdopen(fd, "w" UL_CLOEXECSTR);
638 if (f) {
639 struct stat st;
640
641 mnt_table_write_file(tb, f);
642
643 rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
644
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;
648
649 fclose(f);
650 if (!rc)
651 rename(uq, filename);
652 } else {
653 rc = -errno;
654 close(fd);
655 }
656
657 unlink(uq);
658 free(uq);
659
660 DBG(TAB, mnt_debug_h(tb, "replace done [rc=%d]", rc));
661 return rc;
662 }
663 static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
664 {
665 struct libmnt_fs *fs;
666
667 assert(upd);
668
669 fs = mnt_copy_fs(NULL, upd->fs);
670 if (!fs)
671 return -ENOMEM;
672
673 mnt_table_add_fs(tb, fs);
674 return update_table(upd, tb);
675 }
676
677 static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
678 {
679 struct libmnt_table *tb;
680 int rc = 0;
681
682 assert(upd);
683 assert(upd->fs);
684
685 DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", upd->filename));
686
687 if (lc)
688 rc = mnt_lock_file(lc);
689 if (rc)
690 return rc;
691
692 tb = __mnt_new_table_from_file(upd->filename,
693 upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
694 if (tb)
695 rc = add_file_entry(tb, upd);
696 if (lc)
697 mnt_unlock_file(lc);
698
699 mnt_free_table(tb);
700 return rc;
701 }
702
703 static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
704 {
705 struct libmnt_table *tb;
706 int rc = 0;
707
708 assert(upd);
709 assert(upd->target);
710
711 DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", upd->filename));
712
713 if (lc)
714 rc = mnt_lock_file(lc);
715 if (rc)
716 return rc;
717
718 tb = __mnt_new_table_from_file(upd->filename,
719 upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
720 if (tb) {
721 struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD);
722 if (rem) {
723 mnt_table_remove_fs(tb, rem);
724 rc = update_table(upd, tb);
725 mnt_free_fs(rem);
726 }
727 }
728 if (lc)
729 mnt_unlock_file(lc);
730
731 mnt_free_table(tb);
732 return rc;
733 }
734
735 static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
736 {
737 struct libmnt_table *tb = NULL;
738 int rc = 0;
739
740 assert(upd);
741 DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename));
742
743 if (lc)
744 rc = mnt_lock_file(lc);
745 if (rc)
746 return rc;
747
748 tb = __mnt_new_table_from_file(upd->filename,
749 upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
750 if (tb) {
751 struct libmnt_fs *cur = mnt_table_find_target(tb,
752 mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD);
753 if (cur) {
754 rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs));
755 if (!rc)
756 rc = update_table(upd, tb);
757 }
758 }
759
760 if (lc)
761 mnt_unlock_file(lc);
762
763 mnt_free_table(tb);
764 return rc;
765 }
766
767 static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
768 {
769 struct libmnt_table *tb = NULL;
770 int rc = 0;
771 struct libmnt_fs *fs;
772
773 assert(upd);
774 assert(upd->fs);
775
776 DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename));
777
778 fs = upd->fs;
779
780 if (lc)
781 rc = mnt_lock_file(lc);
782 if (rc)
783 return rc;
784
785 tb = __mnt_new_table_from_file(upd->filename,
786 upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB);
787 if (tb) {
788 struct libmnt_fs *cur = mnt_table_find_target(tb,
789 mnt_fs_get_target(fs),
790 MNT_ITER_BACKWARD);
791 if (cur) {
792 if (upd->userspace_only)
793 rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs));
794 if (!rc)
795 rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs));
796 if (!rc)
797 rc = update_table(upd, tb);
798 } else
799 rc = add_file_entry(tb, upd); /* not found, add new */
800 }
801
802 if (lc)
803 mnt_unlock_file(lc);
804
805 mnt_free_table(tb);
806 return rc;
807 }
808
809 /**
810 * mnt_update_table:
811 * @upd: update
812 * @lc: lock or NULL
813 *
814 * High-level API to update /etc/mtab (or private /run/mount/utab file).
815 *
816 * The @lc lock is optional and will be created if necessary. Note that
817 * an automatically created lock blocks all signals.
818 *
819 * See also mnt_lock_block_signals() and mnt_context_get_lock().
820 *
821 * Returns: 0 on success, negative number on error.
822 */
823 int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
824 {
825 struct libmnt_lock *lc0 = lc;
826 int rc = -EINVAL;
827
828 assert(upd);
829 if (!upd || !upd->filename)
830 return -EINVAL;
831 if (!upd->ready)
832 return 0;
833
834 DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", upd->filename));
835 if (upd->fs) {
836 DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
837 }
838 if (!lc) {
839 lc = mnt_new_lock(upd->filename, 0);
840 if (lc)
841 mnt_lock_block_signals(lc, TRUE);
842 }
843 if (lc && upd->userspace_only)
844 mnt_lock_use_simplelock(lc, TRUE); /* use flock */
845
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 */
852 else if (upd->fs)
853 rc = update_add_entry(upd, lc); /* mount */
854
855 upd->ready = FALSE;
856 DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]",
857 upd->filename, rc));
858 if (lc != lc0)
859 mnt_free_lock(lc);
860 return rc;
861 }
862
863 #ifdef TEST_PROGRAM
864
865 static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags)
866 {
867 int rc;
868 struct libmnt_update *upd;
869
870 DBG(UPDATE, mnt_debug("update test"));
871
872 upd = mnt_new_update();
873 if (!upd)
874 return -ENOMEM;
875
876 rc = mnt_update_set_fs(upd, mountflags, target, fs);
877 if (rc == 1) {
878 /* update is unnecessary */
879 rc = 0;
880 goto done;
881 }
882 if (rc) {
883 fprintf(stderr, "failed to set FS\n");
884 goto done;
885 }
886
887 /* [... mount(2) call should be here...] */
888
889 rc = mnt_update_table(upd, NULL);
890 done:
891 mnt_free_update(upd);
892 return rc;
893 }
894
895 static int test_add(struct libmnt_test *ts, int argc, char *argv[])
896 {
897 struct libmnt_fs *fs = mnt_new_fs();
898 int rc;
899
900 if (argc < 5 || !fs)
901 return -1;
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]);
906
907 rc = update(NULL, fs, 0);
908 mnt_free_fs(fs);
909 return rc;
910 }
911
912 static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
913 {
914 if (argc < 2)
915 return -1;
916 return update(argv[1], NULL, 0);
917 }
918
919 static int test_move(struct libmnt_test *ts, int argc, char *argv[])
920 {
921 struct libmnt_fs *fs = mnt_new_fs();
922 int rc;
923
924 if (argc < 3)
925 return -1;
926 mnt_fs_set_source(fs, argv[1]);
927 mnt_fs_set_target(fs, argv[2]);
928
929 rc = update(NULL, fs, MS_MOVE);
930
931 mnt_free_fs(fs);
932 return rc;
933 }
934
935 static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
936 {
937 struct libmnt_fs *fs = mnt_new_fs();
938 int rc;
939
940 if (argc < 3)
941 return -1;
942 mnt_fs_set_target(fs, argv[1]);
943 mnt_fs_set_options(fs, argv[2]);
944
945 rc = update(NULL, fs, MS_REMOUNT);
946 mnt_free_fs(fs);
947 return rc;
948 }
949
950 static int test_replace(struct libmnt_test *ts, int argc, char *argv[])
951 {
952 struct libmnt_fs *fs = mnt_new_fs();
953 struct libmnt_table *tb = mnt_new_table();
954 int rc;
955
956 if (argc < 3)
957 return -1;
958
959 mnt_table_enable_comments(tb, TRUE);
960 mnt_table_parse_fstab(tb, NULL);
961
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);
966
967 rc = mnt_table_replace_file(tb, mnt_get_fstab_path());
968 mnt_free_table(tb);
969 return rc;
970 }
971
972 int main(int argc, char *argv[])
973 {
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" },
980 { NULL }
981 };
982
983 return mnt_run_test(tss, argc, argv);
984 }
985
986 #endif /* TEST_PROGRAM */