]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/tab_update.c
3cb5b30f956c327cfeb65cefaeb3371eddc9fac3
[thirdparty/util-linux.git] / libmount / src / tab_update.c
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
39 int act_fd;
40 char *act_filename;
41
42 unsigned int ready : 1,
43 missing_options : 1;
44
45 struct libmnt_table *mountinfo;
46 struct libmnt_lock *lock;
47 };
48
49 static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
50 static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
51
52 /**
53 * mnt_new_update:
54 *
55 * Returns: newly allocated update handler
56 */
57 struct libmnt_update *mnt_new_update(void)
58 {
59 struct libmnt_update *upd;
60
61 upd = calloc(1, sizeof(*upd));
62 if (!upd)
63 return NULL;
64
65 upd->act_fd = -1;
66 DBG(UPDATE, ul_debugobj(upd, "allocate"));
67 return upd;
68 }
69
70 /**
71 * mnt_free_update:
72 * @upd: update
73 *
74 * Deallocates struct libmnt_update handler.
75 */
76 void mnt_free_update(struct libmnt_update *upd)
77 {
78 if (!upd)
79 return;
80
81 DBG(UPDATE, ul_debugobj(upd, "free"));
82
83 mnt_unref_lock(upd->lock);
84 mnt_unref_fs(upd->fs);
85 mnt_unref_table(upd->mountinfo);
86 if (upd->act_fd >= 0)
87 close(upd->act_fd);
88 free(upd->target);
89 free(upd->filename);
90 free(upd->act_filename);
91 free(upd);
92 }
93
94 /*
95 * Returns 0 on success, <0 in case of error.
96 */
97 int mnt_update_set_filename(struct libmnt_update *upd, const char *filename)
98 {
99 const char *path = NULL;
100 int rw = 0;
101
102 if (!upd)
103 return -EINVAL;
104
105 /* filename explicitly defined */
106 if (filename) {
107 char *p = strdup(filename);
108 if (!p)
109 return -ENOMEM;
110
111 free(upd->filename);
112 upd->filename = p;
113 }
114
115 if (upd->filename)
116 return 0;
117
118 /* detect tab filename -- /run/mount/utab
119 */
120 path = NULL;
121 mnt_has_regular_utab(&path, &rw);
122 if (!rw)
123 return -EACCES;
124 upd->filename = strdup(path);
125 if (!upd->filename)
126 return -ENOMEM;
127
128 return 0;
129 }
130
131 /**
132 * mnt_update_get_filename:
133 * @upd: update
134 *
135 * This function returns the file name of the up-dated file.
136 *
137 * Returns: pointer to filename that will be updated or NULL in case of error.
138 */
139 const char *mnt_update_get_filename(struct libmnt_update *upd)
140 {
141 return upd ? upd->filename : NULL;
142 }
143
144 /**
145 * mnt_update_is_ready:
146 * @upd: update handler
147 *
148 * Returns: 1 if entry described by @upd is successfully prepared and will be
149 * written to the utab file.
150 */
151 int mnt_update_is_ready(struct libmnt_update *upd)
152 {
153 return upd ? upd->ready : FALSE;
154 }
155
156 /**
157 * mnt_update_set_fs:
158 * @upd: update handler
159 * @mountflags: MS_* flags
160 * @target: umount target, must be NULL for mount
161 * @fs: mount filesystem description, must be NULL for umount
162 *
163 * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
164 */
165 int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
166 const char *target, struct libmnt_fs *fs)
167 {
168 int rc;
169
170 if (!upd)
171 return -EINVAL;
172 if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs)))
173 return -EINVAL;
174 if (target && fs)
175 return -EINVAL;
176
177 DBG(UPDATE, ul_debugobj(upd,
178 "resetting FS [target=%s, flags=0x%08lx]",
179 target, mountflags));
180 if (fs) {
181 DBG(UPDATE, ul_debugobj(upd, "FS template:"));
182 DBG(UPDATE, mnt_fs_print_debug(fs, stderr));
183 }
184
185 mnt_unref_fs(upd->fs);
186 free(upd->target);
187 upd->ready = 0;
188 upd->fs = NULL;
189 upd->target = NULL;
190 upd->mountflags = 0;
191
192 if (mountflags & MS_PROPAGATION)
193 return 1;
194
195 upd->mountflags = mountflags;
196
197 rc = mnt_update_set_filename(upd, NULL);
198 if (rc) {
199 DBG(UPDATE, ul_debugobj(upd, "no writable file available [rc=%d]", rc));
200 return rc; /* error or no file available (rc = 1) */
201 }
202 if (target) {
203 upd->target = strdup(target);
204 if (!upd->target)
205 return -ENOMEM;
206
207 } else if (fs) {
208 if (!(mountflags & MS_MOVE)) {
209 rc = utab_new_entry(upd, fs, mountflags);
210 if (rc)
211 return rc;
212 } else {
213 upd->fs = mnt_copy_mtab_fs(fs);
214 if (!upd->fs)
215 return -ENOMEM;
216 }
217 }
218
219 DBG(UPDATE, ul_debugobj(upd, "ready"));
220 upd->ready = 1;
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 return upd ? upd->fs : NULL;
233 }
234
235 /**
236 * mnt_update_get_mflags:
237 * @upd: update
238 *
239 * Returns: mount flags as was set by mnt_update_set_fs()
240 */
241 unsigned long mnt_update_get_mflags(struct libmnt_update *upd)
242 {
243 return upd ? upd->mountflags : 0;
244 }
245
246 /**
247 * mnt_update_force_rdonly:
248 * @upd: update
249 * @rdonly: is read-only?
250 *
251 * Returns: 0 on success and negative number in case of error.
252 */
253 int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly)
254 {
255 int rc = 0;
256
257 if (!upd || !upd->fs)
258 return -EINVAL;
259
260 if (rdonly && (upd->mountflags & MS_RDONLY))
261 return 0;
262 if (!rdonly && !(upd->mountflags & MS_RDONLY))
263 return 0;
264
265 if (rdonly)
266 upd->mountflags &= ~MS_RDONLY;
267 else
268 upd->mountflags |= MS_RDONLY;
269
270 return rc;
271 }
272
273
274 /*
275 * Allocates an utab entry (upd->fs) for mount/remount. This function should be
276 * called *before* mount(2) syscall. The @fs is used as a read-only template.
277 *
278 * Returns: 0 on success, negative number on error, 1 if utab's update is
279 * unnecessary.
280 */
281 static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs,
282 unsigned long mountflags)
283 {
284 int rc = 0;
285 const char *o, *a;
286 char *u = NULL;
287
288 assert(fs);
289 assert(upd);
290 assert(upd->fs == NULL);
291 assert(!(mountflags & MS_MOVE));
292
293 DBG(UPDATE, ul_debug("prepare utab entry"));
294
295 o = mnt_fs_get_user_options(fs);
296 a = mnt_fs_get_attributes(fs);
297 upd->fs = NULL;
298
299 if (o) {
300 /* remove non-mtab options */
301 rc = mnt_optstr_get_options(o, &u,
302 mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
303 MNT_NOMTAB);
304 if (rc)
305 goto err;
306 }
307
308 if (!u && !a) {
309 DBG(UPDATE, ul_debug("utab entry unnecessary (no options)"));
310 return 1;
311 }
312
313 /* allocate the entry */
314 upd->fs = mnt_copy_fs(NULL, fs);
315 if (!upd->fs) {
316 rc = -ENOMEM;
317 goto err;
318 }
319
320 rc = mnt_fs_set_options(upd->fs, u);
321 if (rc)
322 goto err;
323 rc = mnt_fs_set_attributes(upd->fs, a);
324 if (rc)
325 goto err;
326
327 if (!(mountflags & MS_REMOUNT)) {
328 rc = set_fs_root(upd, fs, mountflags);
329 if (rc)
330 goto err;
331 }
332
333 free(u);
334 DBG(UPDATE, ul_debug("utab entry OK"));
335 return 0;
336 err:
337 free(u);
338 mnt_unref_fs(upd->fs);
339 upd->fs = NULL;
340 return rc;
341 }
342
343 /*
344 * Sets fs-root and fs-type to @upd->fs according to the @fs template and
345 * @mountfalgs. For MS_BIND mountflag it reads information about the source
346 * filesystem from /proc/self/mountinfo.
347 */
348 static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs,
349 unsigned long mountflags)
350 {
351 struct libmnt_fs *src_fs;
352 char *fsroot = NULL;
353 const char *src, *fstype;
354 int rc = 0;
355
356 DBG(UPDATE, ul_debug("setting FS root"));
357
358 assert(upd);
359 assert(upd->fs);
360 assert(fs);
361
362 fstype = mnt_fs_get_fstype(fs);
363
364 if (mountflags & MS_BIND) {
365 if (!upd->mountinfo)
366 upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
367 src = mnt_fs_get_srcpath(fs);
368 if (src) {
369 rc = mnt_fs_set_bindsrc(upd->fs, src);
370 if (rc)
371 goto err;
372 }
373
374 } else if (fstype && (strcmp(fstype, "btrfs") == 0 || strcmp(fstype, "auto") == 0)) {
375 if (!upd->mountinfo)
376 upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
377 }
378
379 src_fs = mnt_table_get_fs_root(upd->mountinfo, fs,
380 mountflags, &fsroot);
381 if (src_fs) {
382 src = mnt_fs_get_srcpath(src_fs);
383 rc = mnt_fs_set_source(upd->fs, src);
384 if (rc)
385 goto err;
386
387 mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs));
388 }
389
390 upd->fs->root = fsroot;
391 return 0;
392 err:
393 free(fsroot);
394 return rc;
395 }
396
397 /* mtab and fstab update -- returns zero on success
398 */
399 static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
400 {
401 const char *o, *src, *fstype, *comm;
402 char *m1, *m2, *m3, *m4;
403 int rc;
404
405 assert(fs);
406 assert(f);
407
408 comm = mnt_fs_get_comment(fs);
409 src = mnt_fs_get_source(fs);
410 fstype = mnt_fs_get_fstype(fs);
411 o = mnt_fs_get_options(fs);
412
413 m1 = src ? mangle(src) : "none";
414 m2 = mangle(mnt_fs_get_target(fs));
415 m3 = fstype ? mangle(fstype) : "none";
416 m4 = o ? mangle(o) : "rw";
417
418 if (m1 && m2 && m3 && m4) {
419 if (comm)
420 fputs(comm, f);
421 rc = fprintf(f, "%s %s %s %s %d %d\n",
422 m1, m2, m3, m4,
423 mnt_fs_get_freq(fs),
424 mnt_fs_get_passno(fs));
425 if (rc > 0)
426 rc = 0;
427 } else
428 rc = -ENOMEM;
429
430 if (src)
431 free(m1);
432 free(m2);
433 if (fstype)
434 free(m3);
435 if (o)
436 free(m4);
437
438 return rc;
439 }
440
441 static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
442 {
443 char *p;
444 int rc = 0;
445
446 if (!fs || !f)
447 return -EINVAL;
448
449 if (mnt_fs_get_id(fs) > 0)
450 rc = fprintf(f, "ID=%d ", mnt_fs_get_id(fs));
451 if (mnt_fs_get_uniq_id(fs) > 0)
452 rc = fprintf(f, "UNIQID=%" PRIu64, mnt_fs_get_uniq_id(fs));
453
454 if (rc >= 0) {
455 p = mangle(mnt_fs_get_source(fs));
456 if (p) {
457 rc = fprintf(f, "SRC=%s ", p);
458 free(p);
459 }
460 }
461 if (rc >= 0) {
462 p = mangle(mnt_fs_get_target(fs));
463 if (p) {
464 rc = fprintf(f, "TARGET=%s ", p);
465 free(p);
466 }
467 }
468 if (rc >= 0) {
469 p = mangle(mnt_fs_get_root(fs));
470 if (p) {
471 rc = fprintf(f, "ROOT=%s ", p);
472 free(p);
473 }
474 }
475 if (rc >= 0) {
476 p = mangle(mnt_fs_get_bindsrc(fs));
477 if (p) {
478 rc = fprintf(f, "BINDSRC=%s ", p);
479 free(p);
480 }
481 }
482 if (rc >= 0) {
483 p = mangle(mnt_fs_get_attributes(fs));
484 if (p) {
485 rc = fprintf(f, "ATTRS=%s ", p);
486 free(p);
487 }
488 }
489 if (rc >= 0) {
490 p = mangle(mnt_fs_get_user_options(fs));
491 if (p) {
492 rc = fprintf(f, "OPTS=%s", p);
493 free(p);
494 }
495 }
496 if (rc >= 0)
497 rc = fprintf(f, "\n");
498
499 if (rc > 0)
500 rc = 0; /* success */
501 return rc;
502 }
503
504 static int update_table(struct libmnt_update *upd, struct libmnt_table *tb)
505 {
506 FILE *f;
507 int rc, fd;
508 char *uq = NULL;
509
510 if (!tb || !upd->filename)
511 return -EINVAL;
512
513 DBG(UPDATE, ul_debugobj(upd, "%s: updating", upd->filename));
514
515 fd = mnt_open_uniq_filename(upd->filename, &uq);
516 if (fd < 0)
517 return fd; /* error */
518
519 f = fdopen(fd, "w" UL_CLOEXECSTR);
520 if (f) {
521 struct stat st;
522 struct libmnt_iter itr;
523 struct libmnt_fs *fs;
524
525 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
526
527 if (tb->comms && mnt_table_get_intro_comment(tb))
528 fputs(mnt_table_get_intro_comment(tb), f);
529
530 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
531 rc = fprintf_utab_fs(f, fs);
532 if (rc) {
533 DBG(UPDATE, ul_debugobj(upd,
534 "%s: write entry failed: %m", uq));
535 goto leave;
536 }
537 }
538 if (tb->comms && mnt_table_get_trailing_comment(tb))
539 fputs(mnt_table_get_trailing_comment(tb), f);
540
541 if (fflush(f) != 0) {
542 rc = -errno;
543 DBG(UPDATE, ul_debugobj(upd, "%s: fflush failed: %m", uq));
544 goto leave;
545 }
546
547 rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
548
549 if (!rc && stat(upd->filename, &st) == 0)
550 /* Copy uid/gid from the present file before renaming. */
551 rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
552
553 fclose(f);
554 f = NULL;
555
556 if (!rc)
557 rc = rename(uq, upd->filename) ? -errno : 0;
558 } else {
559 rc = -errno;
560 close(fd);
561 }
562
563 leave:
564 if (f)
565 fclose(f);
566
567 unlink(uq); /* be paranoid */
568 free(uq);
569 DBG(UPDATE, ul_debugobj(upd, "%s: done [rc=%d]", upd->filename, rc));
570 return rc;
571 }
572
573 /**
574 * mnt_table_write_file
575 * @tb: parsed file (e.g. fstab)
576 * @file: target
577 *
578 * This function writes @tb to @file.
579 *
580 * Returns: 0 on success, negative number on error.
581 */
582 int mnt_table_write_file(struct libmnt_table *tb, FILE *file)
583 {
584 int rc = 0;
585 struct libmnt_iter itr;
586 struct libmnt_fs *fs;
587
588 if (tb->comms && mnt_table_get_intro_comment(tb))
589 fputs(mnt_table_get_intro_comment(tb), file);
590
591 mnt_reset_iter(&itr, MNT_ITER_FORWARD);
592 while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
593 rc = fprintf_mtab_fs(file, fs);
594 if (rc)
595 return rc;
596 }
597 if (tb->comms && mnt_table_get_trailing_comment(tb))
598 fputs(mnt_table_get_trailing_comment(tb), file);
599
600 if (fflush(file) != 0)
601 rc = -errno;
602
603 DBG(TAB, ul_debugobj(tb, "write file done [rc=%d]", rc));
604 return rc;
605 }
606
607 /**
608 * mnt_table_replace_file
609 * @tb: parsed file (e.g. fstab)
610 * @filename: target
611 *
612 * This function replaces @file by the new content from @tb.
613 *
614 * Returns: 0 on success, negative number on error.
615 */
616 int mnt_table_replace_file(struct libmnt_table *tb, const char *filename)
617 {
618 int fd, rc = 0;
619 FILE *f;
620 char *uq = NULL;
621
622 DBG(TAB, ul_debugobj(tb, "%s: replacing", filename));
623
624 fd = mnt_open_uniq_filename(filename, &uq);
625 if (fd < 0)
626 return fd; /* error */
627
628 f = fdopen(fd, "w" UL_CLOEXECSTR);
629 if (f) {
630 struct stat st;
631
632 mnt_table_write_file(tb, f);
633
634 if (fflush(f) != 0) {
635 rc = -errno;
636 DBG(UPDATE, ul_debug("%s: fflush failed: %m", uq));
637 goto leave;
638 }
639
640 rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
641
642 if (!rc && stat(filename, &st) == 0)
643 /* Copy uid/gid from the present file before renaming. */
644 rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
645
646 fclose(f);
647 f = NULL;
648
649 if (!rc)
650 rc = rename(uq, filename) ? -errno : 0;
651 } else {
652 rc = -errno;
653 close(fd);
654 }
655
656 leave:
657 if (f)
658 fclose(f);
659 unlink(uq);
660 free(uq);
661
662 DBG(TAB, ul_debugobj(tb, "replace done [rc=%d]", rc));
663 return rc;
664 }
665
666 static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
667 {
668 struct libmnt_fs *fs;
669
670 assert(upd);
671
672 fs = mnt_copy_fs(NULL, upd->fs);
673 if (!fs)
674 return -ENOMEM;
675
676 mnt_table_add_fs(tb, fs);
677 mnt_unref_fs(fs);
678
679 return update_table(upd, tb);
680 }
681
682 static int update_add_entry(struct libmnt_update *upd)
683 {
684 struct libmnt_table *tb;
685 int rc = 0;
686
687 assert(upd);
688 assert(upd->fs);
689 assert(upd->lock);
690
691 DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename));
692
693 rc = mnt_lock_file(upd->lock);
694 if (rc)
695 return -MNT_ERR_LOCK;
696
697 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
698 if (tb)
699 rc = add_file_entry(tb, upd);
700
701 mnt_unlock_file(upd->lock);
702 mnt_unref_table(tb);
703 return rc;
704 }
705
706 static int update_remove_entry(struct libmnt_update *upd)
707 {
708 struct libmnt_table *tb;
709 int rc = 0;
710
711 assert(upd);
712 assert(upd->target);
713 assert(upd->lock);
714
715 DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename));
716
717 rc = mnt_lock_file(upd->lock);
718 if (rc)
719 return -MNT_ERR_LOCK;
720
721 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
722 if (tb) {
723 struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD);
724 if (rem) {
725 mnt_table_remove_fs(tb, rem);
726 rc = update_table(upd, tb);
727 }
728 }
729
730 mnt_unlock_file(upd->lock);
731 mnt_unref_table(tb);
732 return rc;
733 }
734
735 static int update_modify_target(struct libmnt_update *upd)
736 {
737 struct libmnt_table *tb = NULL;
738 int rc = 0;
739
740 assert(upd);
741 assert(upd->lock);
742 DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename));
743
744 rc = mnt_lock_file(upd->lock);
745 if (rc)
746 return -MNT_ERR_LOCK;
747
748 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
749 if (tb) {
750 const char *upd_source = mnt_fs_get_srcpath(upd->fs);
751 const char *upd_target = mnt_fs_get_target(upd->fs);
752 struct libmnt_iter itr;
753 struct libmnt_fs *fs;
754 char *cn_target = mnt_resolve_path(upd_target, NULL);
755
756 if (!cn_target) {
757 rc = -ENOMEM;
758 goto done;
759 }
760
761 mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
762 while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
763 char *p;
764 const char *e;
765
766 e = ul_startswith(mnt_fs_get_target(fs), upd_source);
767 if (!e || (*e && *e != '/'))
768 continue;
769 if (*e == '/')
770 e++; /* remove extra '/' */
771
772 /* no subdirectory, replace entire path */
773 if (!*e)
774 rc = mnt_fs_set_target(fs, cn_target);
775
776 /* update start of the path, keep subdirectory */
777 else if (asprintf(&p, "%s/%s", cn_target, e) > 0) {
778 rc = mnt_fs_set_target(fs, p);
779 free(p);
780 } else
781 rc = -ENOMEM;
782
783 if (rc < 0)
784 break;
785 }
786
787 if (!rc)
788 rc = update_table(upd, tb);
789 free(cn_target);
790 }
791
792 done:
793 mnt_unlock_file(upd->lock);
794 mnt_unref_table(tb);
795 return rc;
796 }
797
798 /* replaces option in the table entry by new options from @udp */
799 static int update_modify_options(struct libmnt_update *upd)
800 {
801 struct libmnt_table *tb = NULL;
802 int rc = 0;
803 struct libmnt_fs *fs;
804
805 assert(upd);
806 assert(upd->fs);
807 assert(upd->lock);
808
809 DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename));
810
811 fs = upd->fs;
812
813 rc = mnt_lock_file(upd->lock);
814 if (rc)
815 return -MNT_ERR_LOCK;
816
817 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
818 if (tb) {
819 struct libmnt_fs *cur = mnt_table_find_target(tb,
820 mnt_fs_get_target(fs),
821 MNT_ITER_BACKWARD);
822 if (cur) {
823 rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs));
824 if (!rc)
825 rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs));
826 if (!rc)
827 rc = update_table(upd, tb);
828 } else
829 rc = add_file_entry(tb, upd); /* not found, add new */
830 }
831
832 mnt_unlock_file(upd->lock);
833 mnt_unref_table(tb);
834 return rc;
835 }
836
837 /* add user options missing in the table entry by new options from @udp */
838 static int update_add_options(struct libmnt_update *upd)
839 {
840 struct libmnt_table *tb = NULL;
841 int rc = 0;
842 struct libmnt_fs *fs;
843
844 assert(upd);
845 assert(upd->fs);
846 assert(upd->lock);
847
848 if (!upd->fs->user_optstr)
849 return 0;
850
851 DBG(UPDATE, ul_debugobj(upd, "%s: add options", upd->filename));
852
853 fs = upd->fs;
854
855 rc = mnt_lock_file(upd->lock);
856 if (rc)
857 return -MNT_ERR_LOCK;
858
859 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
860 if (tb) {
861 struct libmnt_fs *cur = mnt_table_find_target(tb,
862 mnt_fs_get_target(fs),
863 MNT_ITER_BACKWARD);
864 if (cur) {
865 char *u = NULL;
866
867 rc = mnt_optstr_get_missing(cur->user_optstr, upd->fs->user_optstr, &u);
868 if (!rc && u) {
869 DBG(UPDATE, ul_debugobj(upd, " add missing: %s", u));
870 rc = mnt_optstr_append_option(&cur->user_optstr, u, NULL);
871 }
872 if (!rc && u)
873 rc = update_table(upd, tb);
874
875 if (rc == 1) /* nothing is missing */
876 rc = 0;
877 } else
878 rc = add_file_entry(tb, upd); /* not found, add new */
879 }
880
881 mnt_unlock_file(upd->lock);
882 mnt_unref_table(tb);
883 return rc;
884
885 }
886
887 static int update_init_lock(struct libmnt_update *upd, struct libmnt_lock *lc)
888 {
889 assert(upd);
890
891 if (lc) {
892 mnt_unref_lock(upd->lock);
893 mnt_ref_lock(lc);
894 upd->lock = lc;
895 } else if (!upd->lock) {
896 upd->lock = mnt_new_lock(upd->filename, 0);
897 if (!upd->lock)
898 return -ENOMEM;
899 mnt_lock_block_signals(upd->lock, TRUE);
900 }
901
902 return 0;
903 }
904
905 /**
906 * mnt_update_table:
907 * @upd: update
908 * @lc: lock or NULL
909 *
910 * High-level API to update /etc/mtab (or private /run/mount/utab file).
911 *
912 * The @lc lock is optional and will be created if necessary. Note that
913 * an automatically created lock blocks all signals.
914 *
915 * See also mnt_lock_block_signals() and mnt_context_get_lock().
916 *
917 * Returns: 0 on success, negative number on error.
918 */
919 int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
920 {
921 int rc = -EINVAL;
922
923 if (!upd || !upd->filename)
924 return -EINVAL;
925 if (!upd->ready)
926 return 0;
927
928 DBG(UPDATE, ul_debugobj(upd, "%s: update tab", upd->filename));
929 if (upd->fs) {
930 DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
931 }
932
933 rc = update_init_lock(upd, lc);
934 if (rc)
935 goto done;
936
937 if (!upd->fs && upd->target)
938 rc = update_remove_entry(upd); /* umount */
939 else if (upd->mountflags & MS_MOVE)
940 rc = update_modify_target(upd); /* move */
941 else if (upd->mountflags & MS_REMOUNT)
942 rc = update_modify_options(upd); /* remount */
943 else if (upd->fs && upd->missing_options)
944 rc = update_add_options(upd); /* mount by external helper */
945 else if (upd->fs)
946 rc = update_add_entry(upd); /* mount */
947
948 upd->ready = 1;
949 done:
950 DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]",
951 upd->filename, rc));
952 return rc;
953 }
954
955 int mnt_update_already_done(struct libmnt_update *upd)
956 {
957 struct libmnt_table *tb = NULL;
958 int rc = 0;
959
960 if (!upd || !upd->filename || (!upd->fs && !upd->target))
961 return -EINVAL;
962
963 DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename));
964
965 tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
966 if (!tb)
967 goto done;
968
969 if (upd->fs) {
970 /* mount */
971 struct libmnt_fs *fs;
972 const char *tgt = mnt_fs_get_target(upd->fs);
973 const char *src = mnt_fs_get_bindsrc(upd->fs) ?
974 mnt_fs_get_bindsrc(upd->fs) :
975 mnt_fs_get_source(upd->fs);
976
977 fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
978 if (fs) {
979 DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s",
980 upd->filename, src, tgt));
981
982 /* Check if utab entry (probably written by /sbin/mount.<type>
983 * helper) contains all options expected by this update */
984 if (mnt_optstr_get_missing(fs->user_optstr, upd->fs->user_optstr, NULL) == 0) {
985 upd->missing_options = 1;
986 DBG(UPDATE, ul_debugobj(upd, " missing options detected"));
987 } else
988 rc = 1;
989 }
990
991 } else if (upd->target) {
992 /* umount */
993 if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) {
994 DBG(UPDATE, ul_debugobj(upd, "%s: not-found (umounted) %s",
995 upd->filename, upd->target));
996 rc = 1;
997 }
998 }
999
1000 mnt_unref_table(tb);
1001 done:
1002 DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]",
1003 upd->filename, rc));
1004 return rc;
1005 }
1006
1007 int mnt_update_emit_event(struct libmnt_update *upd)
1008 {
1009 char *filename;
1010 int fd;
1011
1012 if (!upd || !upd->filename)
1013 return -EINVAL;
1014
1015 if (asprintf(&filename, "%s.event", upd->filename) <= 0)
1016 return -ENOMEM;
1017
1018 DBG(UPDATE, ul_debugobj(upd, "emitting utab event"));
1019
1020 fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC,
1021 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
1022 free(filename);
1023 if (fd < 0)
1024 return -errno;
1025 close(fd);
1026 return 0;
1027 }
1028
1029 /*
1030 * Let's use /run/mount/utab.act file to report to libmount monitor that
1031 * libmount is working with utab. In this case, the monitor can ignore all
1032 * events from kernel until entire mount (with all steps) is done.
1033 *
1034 * For example mount NFS with x-* options, means
1035 * - create utab.act and mark it as used (by LOCK_SH)
1036 * - exec /sbin/mount.nfs
1037 * - call mount(2) (kernel event on /proc/self/mounts)
1038 * - utab update (NFS stuff)
1039 * - utab update (add x-* userspace options)
1040 * - unlink utab.act (if not use anyone else)
1041 * - release event by /run/mount/utab.event
1042 *
1043 * Note, this is used only when utab is in the game (x-* options).
1044 */
1045 int mnt_update_start(struct libmnt_update *upd)
1046 {
1047 int rc = 0;
1048 mode_t oldmask;
1049
1050 if (!upd || !upd->filename)
1051 return -EINVAL;
1052
1053 if (!upd->act_filename &&
1054 asprintf(&upd->act_filename, "%s.act", upd->filename) <= 0)
1055 return -ENOMEM;
1056
1057 /* Use exclusive lock to avoid some other process will remove the the
1058 * file before it's marked as used by LOCK_SH (below) */
1059 rc = update_init_lock(upd, NULL);
1060 if (rc)
1061 return rc;
1062
1063 rc = mnt_lock_file(upd->lock);
1064 if (rc)
1065 return -MNT_ERR_LOCK;
1066
1067 DBG(UPDATE, ul_debugobj(upd, "creating act file"));
1068
1069 oldmask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
1070 upd->act_fd = open(upd->act_filename, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
1071 umask(oldmask);
1072
1073 if (upd->act_fd < 0) {
1074 rc = -errno;
1075 goto fail;
1076 }
1077
1078 /* mark the file as used */
1079 rc = flock(upd->act_fd, LOCK_SH);
1080 if (rc) {
1081 rc = -errno;
1082 goto fail;
1083 }
1084
1085 mnt_unlock_file(upd->lock);
1086 return 0;
1087 fail:
1088 DBG(UPDATE, ul_debugobj(upd, "act file failed [rc=%d]", rc));
1089 mnt_unlock_file(upd->lock);
1090 unlink(upd->act_filename);
1091 if (upd->act_fd >= 0)
1092 close(upd->act_fd);
1093 upd->act_fd = -1;
1094 return rc;
1095 }
1096
1097 int mnt_update_end(struct libmnt_update *upd)
1098 {
1099 int rc;
1100
1101 if (!upd || upd->act_fd < 0)
1102 return -EINVAL;
1103
1104 DBG(UPDATE, ul_debugobj(upd, "removing act file"));
1105
1106 /* make sure nobody else will use the file */
1107 rc = mnt_lock_file(upd->lock);
1108 if (rc)
1109 return -MNT_ERR_LOCK;
1110
1111 /* mark the file as unused */
1112 flock(upd->act_fd, LOCK_UN);
1113 errno = 0;
1114
1115 /* check if nobody else need the file (if yes, then the file is under LOCK_SH) )*/
1116 if (flock(upd->act_fd, LOCK_EX | LOCK_NB) != 0) {
1117 if (errno == EWOULDBLOCK)
1118 DBG(UPDATE, ul_debugobj(upd, "act file used, no unlink"));
1119 } else {
1120 DBG(UPDATE, ul_debugobj(upd, "unlinking act file"));
1121 unlink(upd->act_filename);
1122 }
1123
1124 mnt_unlock_file(upd->lock);
1125 close(upd->act_fd);
1126 upd->act_fd = -1;
1127 return 0;
1128 }
1129
1130 #ifdef TEST_PROGRAM
1131
1132 static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags)
1133 {
1134 int rc;
1135 struct libmnt_update *upd;
1136
1137 DBG(UPDATE, ul_debug("update test"));
1138
1139 upd = mnt_new_update();
1140 if (!upd)
1141 return -ENOMEM;
1142
1143 rc = mnt_update_set_fs(upd, mountflags, target, fs);
1144 if (rc == 1) {
1145 /* update is unnecessary */
1146 rc = 0;
1147 goto done;
1148 }
1149 if (rc) {
1150 fprintf(stderr, "failed to set FS\n");
1151 goto done;
1152 }
1153
1154 /* [... mount(2) call should be here...] */
1155
1156 rc = mnt_update_table(upd, NULL);
1157 done:
1158 mnt_free_update(upd);
1159 return rc;
1160 }
1161
1162 static int test_add(struct libmnt_test *ts __attribute__((unused)),
1163 int argc, char *argv[])
1164 {
1165 struct libmnt_fs *fs = mnt_new_fs();
1166 int rc;
1167
1168 if (argc < 5 || !fs)
1169 return -1;
1170 mnt_fs_set_source(fs, argv[1]);
1171 mnt_fs_set_target(fs, argv[2]);
1172 mnt_fs_set_fstype(fs, argv[3]);
1173 mnt_fs_set_options(fs, argv[4]);
1174
1175 rc = update(NULL, fs, 0);
1176 mnt_unref_fs(fs);
1177 return rc;
1178 }
1179
1180 static int test_remove(struct libmnt_test *ts __attribute__((unused)),
1181 int argc, char *argv[])
1182 {
1183 if (argc < 2)
1184 return -1;
1185 return update(argv[1], NULL, 0);
1186 }
1187
1188 static int test_move(struct libmnt_test *ts __attribute__((unused)),
1189 int argc, char *argv[])
1190 {
1191 struct libmnt_fs *fs = mnt_new_fs();
1192 int rc;
1193
1194 if (argc < 3)
1195 return -1;
1196 mnt_fs_set_source(fs, argv[1]);
1197 mnt_fs_set_target(fs, argv[2]);
1198
1199 rc = update(NULL, fs, MS_MOVE);
1200
1201 mnt_unref_fs(fs);
1202 return rc;
1203 }
1204
1205 static int test_remount(struct libmnt_test *ts __attribute__((unused)),
1206 int argc, char *argv[])
1207 {
1208 struct libmnt_fs *fs = mnt_new_fs();
1209 int rc;
1210
1211 if (argc < 3)
1212 return -1;
1213 mnt_fs_set_target(fs, argv[1]);
1214 mnt_fs_set_options(fs, argv[2]);
1215
1216 rc = update(NULL, fs, MS_REMOUNT);
1217 mnt_unref_fs(fs);
1218 return rc;
1219 }
1220
1221 static int test_replace(struct libmnt_test *ts __attribute__((unused)),
1222 int argc, char *argv[])
1223 {
1224 struct libmnt_fs *fs = mnt_new_fs();
1225 struct libmnt_table *tb = mnt_new_table();
1226 int rc;
1227
1228 if (argc < 3)
1229 return -1;
1230
1231 mnt_table_enable_comments(tb, TRUE);
1232 mnt_table_parse_fstab(tb, NULL);
1233
1234 mnt_fs_set_source(fs, argv[1]);
1235 mnt_fs_set_target(fs, argv[2]);
1236 mnt_fs_append_comment(fs, "# this is new filesystem\n");
1237
1238 mnt_table_add_fs(tb, fs);
1239 mnt_unref_fs(fs);
1240
1241 rc = mnt_table_replace_file(tb, mnt_get_fstab_path());
1242 mnt_unref_table(tb);
1243 return rc;
1244 }
1245
1246 int main(int argc, char *argv[])
1247 {
1248 struct libmnt_test tss[] = {
1249 { "--add", test_add, "<src> <target> <type> <options> add a line to mtab" },
1250 { "--remove", test_remove, "<target> MS_REMOUNT mtab change" },
1251 { "--move", test_move, "<old_target> <target> MS_MOVE mtab change" },
1252 { "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" },
1253 { "--replace",test_replace, "<src> <target> Add a line to LIBMOUNT_FSTAB and replace the original file" },
1254 { NULL }
1255 };
1256
1257 return mnt_run_test(tss, argc, argv);
1258 }
1259
1260 #endif /* TEST_PROGRAM */