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