static int swap_list_get(MountPoint **head) { _cleanup_fclose_ FILE *proc_swaps = NULL; unsigned int i; int r; assert(head); proc_swaps = fopen("/proc/swaps", "re"); if (!proc_swaps) return (errno == ENOENT) ? 0 : -errno; (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); for (i = 2;; i++) { MountPoint *swap; char *dev = NULL, *d; int k; k = fscanf(proc_swaps, "%ms " /* device/file */ "%*s " /* type of swap */ "%*s " /* swap size */ "%*s " /* used */ "%*s\n", /* priority */ &dev); if (k != 1) { if (k == EOF) break; log_warning("Failed to parse /proc/swaps:%u.", i); free(dev); continue; } if (endswith(dev, " (deleted)")) { free(dev); continue; } r = cunescape(dev, UNESCAPE_RELAX, &d); free(dev); if (r < 0) return r; swap = new0(MountPoint, 1); if (!swap) { free(d); return -ENOMEM; } swap->path = d; LIST_PREPEND(mount_point, *head, swap); } return 0; }
static void test_cunescape(void) { _cleanup_free_ char *unescaped; unescaped = cunescape("abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\003\\177\\234\\313"); assert_se(streq(unescaped, "abc\\\"\b\f\n\r\t\v\003\177\234\313")); }
int umount_recursive(const char *prefix, int flags) { int n = 0, r; bool again; /* Try to umount everything recursively below a * directory. Also, take care of stacked mounts, and keep * unmounting them until they are gone. */ do { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; again = false; r = 0; proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); if (!proc_self_mountinfo) return -errno; (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER); for (;;) { _cleanup_free_ char *path = NULL, *p = NULL; int k; k = fscanf(proc_self_mountinfo, "%*s " /* (1) mount id */ "%*s " /* (2) parent id */ "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ "%*s" /* (6) mount options */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%*s " /* (9) file system type */ "%*s" /* (10) mount source */ "%*s" /* (11) mount options 2 */ "%*[^\n]", /* some rubbish at the end */ &path); if (k != 1) { if (k == EOF) break; continue; } k = cunescape(path, UNESCAPE_RELAX, &p); if (k < 0) return k; if (!path_startswith(p, prefix)) continue; if (umount2(p, flags) < 0) { r = log_debug_errno(errno, "Failed to umount %s: %m", p); continue; } log_debug("Successfully unmounted %s", p); again = true; n++; break; } } while (again); return r < 0 ? r : n; }
/* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */ int bind_remount_recursive_with_mountinfo( const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; int r; assert(proc_self_mountinfo); /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We * do not have any effect on future submounts that might get propagated, they migt be writable. This includes * future submounts that have been triggered via autofs. * * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the * remount operation. Note that we'll ignore the blacklist for the top-level path. */ cleaned = strdup(prefix); if (!cleaned) return -ENOMEM; path_simplify(cleaned, false); done = set_new(&path_hash_ops); if (!done) return -ENOMEM; for (;;) { _cleanup_set_free_free_ Set *todo = NULL; bool top_autofs = false; char *x; unsigned long orig_flags; todo = set_new(&path_hash_ops); if (!todo) return -ENOMEM; rewind(proc_self_mountinfo); for (;;) { _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; int k; k = fscanf(proc_self_mountinfo, "%*s " /* (1) mount id */ "%*s " /* (2) parent id */ "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ "%*s" /* (6) mount options (superblock) */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%ms " /* (9) file system type */ "%*s" /* (10) mount source */ "%*s" /* (11) mount options (bind mount) */ "%*[^\n]", /* some rubbish at the end */ &path, &type); if (k != 2) { if (k == EOF) break; continue; } r = cunescape(path, UNESCAPE_RELAX, &p); if (r < 0) return r; if (!path_startswith(p, cleaned)) continue; /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall * operate on. */ if (!path_equal(cleaned, p)) { bool blacklisted = false; char **i; STRV_FOREACH(i, blacklist) { if (path_equal(*i, cleaned)) continue; if (!path_startswith(*i, cleaned)) continue; if (path_startswith(p, *i)) { blacklisted = true; log_debug("Not remounting %s blacklisted by %s, called for %s", p, *i, cleaned); break; } } if (blacklisted) continue; } /* Let's ignore autofs mounts. If they aren't * triggered yet, we want to avoid triggering * them, as we don't make any guarantees for * future submounts anyway. If they are * already triggered, then we will find * another entry for this. */ if (streq(type, "autofs")) { top_autofs = top_autofs || path_equal(cleaned, p); continue; } if (!set_contains(done, p)) { r = set_consume(todo, p); p = NULL; if (r == -EEXIST) continue; if (r < 0) return r; } } /* If we have no submounts to process anymore and if * the root is either already done, or an autofs, we * are done */ if (set_isempty(todo) && (top_autofs || set_contains(done, cleaned))) return 0; if (!set_contains(done, cleaned) && !set_contains(todo, cleaned)) { /* The prefix directory itself is not yet a mount, make it one. */ if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0) return -errno; orig_flags = 0; (void) get_mount_flags(cleaned, &orig_flags); orig_flags &= ~MS_RDONLY; if (mount(NULL, cleaned, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Made top-level directory %s a mount point.", prefix); r = set_put_strdup(done, cleaned); if (r < 0) return r; } while ((x = set_steal_first(todo))) { r = set_consume(done, x); if (IN_SET(r, 0, -EEXIST)) continue; if (r < 0) return r; /* Deal with mount points that are obstructed by a later mount */ r = path_is_mount_point(x, NULL, 0); if (IN_SET(r, 0, -ENOENT)) continue; if (IN_SET(r, -EACCES, -EPERM)) { /* Even if root user invoke this, submounts under private FUSE or NFS mount points * may not be acceessed. E.g., * * $ bindfs --no-allow-other ~/mnt/mnt ~/mnt/mnt * $ bindfs --no-allow-other ~/mnt ~/mnt * * Then, root user cannot access the mount point ~/mnt/mnt. * In such cases, the submounts are ignored, as we have no way to manage them. */ log_debug_errno(r, "Failed to determine '%s' is mount point or not, ignoring: %m", x); continue; } if (r < 0) return r; /* Try to reuse the original flag set */ orig_flags = 0; (void) get_mount_flags(x, &orig_flags); orig_flags &= ~MS_RDONLY; if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Remounted %s read-only.", x); } }
static int mount_points_list_get(MountPoint **head) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; unsigned int i; int r; assert(head); proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); if (!proc_self_mountinfo) return -errno; for (i = 1;; i++) { _cleanup_free_ char *path = NULL; char *p = NULL; MountPoint *m; int k; k = fscanf(proc_self_mountinfo, "%*s " /* (1) mount id */ "%*s " /* (2) parent id */ "%*s " /* (3) major:minor */ "%*s " /* (4) root */ "%ms " /* (5) mount point */ "%*s" /* (6) mount options */ "%*[^-]" /* (7) optional fields */ "- " /* (8) separator */ "%*s " /* (9) file system type */ "%*s" /* (10) mount source */ "%*s" /* (11) mount options 2 */ "%*[^\n]", /* some rubbish at the end */ &path); if (k != 1) { if (k == EOF) break; log_warning("Failed to parse /proc/self/mountinfo:%u.", i); continue; } r = cunescape(path, UNESCAPE_RELAX, &p); if (r < 0) return r; /* Ignore mount points we can't unmount because they * are API or because we are keeping them open (like * /dev/console). Also, ignore all mounts below API * file systems, since they are likely virtual too, * and hence not worth spending time on. Also, in * unprivileged containers we might lack the rights to * unmount these things, hence don't bother. */ if (mount_point_is_api(p) || mount_point_ignore(p) || path_startswith(p, "/dev") || path_startswith(p, "/sys") || path_startswith(p, "/proc")) { free(p); continue; } m = new0(MountPoint, 1); if (!m) { free(p); return -ENOMEM; } m->path = p; LIST_PREPEND(mount_point, *head, m); } return 0; }
static int write_one_file(Item *i, const char *path) { int r, e, fd, flags; struct stat st; mode_t u; flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND : i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0; u = umask(0); label_context_set(path, S_IFREG); fd = open(path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode); e = errno; label_context_clear(); umask(u); errno = e; if (fd < 0) { if (i->type == WRITE_FILE && errno == ENOENT) return 0; log_error("Failed to create file %s: %m", path); return -errno; } if (i->argument) { ssize_t n; size_t l; _cleanup_free_ char *unescaped; unescaped = cunescape(i->argument); if (unescaped == NULL) { close_nointr_nofail(fd); return log_oom(); } l = strlen(unescaped); n = write(fd, unescaped, l); if (n < 0 || (size_t) n < l) { log_error("Failed to write file %s: %s", path, n < 0 ? strerror(-n) : "Short write"); close_nointr_nofail(fd); return n < 0 ? n : -EIO; } } close_nointr_nofail(fd); if (stat(path, &st) < 0) { log_error("stat(%s) failed: %m", path); return -errno; } if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", path); return -EEXIST; } r = item_set_perms(i, path); if (r < 0) return r; return 0; }
static void test_cunescape(void) { _cleanup_free_ char *unescaped; assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0); assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00")); unescaped = mfree(unescaped); /* incomplete sequences */ assert_se(cunescape("\\x0", 0, &unescaped) < 0); assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "\\x0")); unescaped = mfree(unescaped); assert_se(cunescape("\\x", 0, &unescaped) < 0); assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "\\x")); unescaped = mfree(unescaped); assert_se(cunescape("\\", 0, &unescaped) < 0); assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "\\")); unescaped = mfree(unescaped); assert_se(cunescape("\\11", 0, &unescaped) < 0); assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "\\11")); unescaped = mfree(unescaped); assert_se(cunescape("\\1", 0, &unescaped) < 0); assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "\\1")); unescaped = mfree(unescaped); assert_se(cunescape("\\u0000", 0, &unescaped) < 0); assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "ßßΠA")); unescaped = mfree(unescaped); assert_se(cunescape("\\073", 0, &unescaped) >= 0); assert_se(streq_ptr(unescaped, ";")); unescaped = mfree(unescaped); assert_se(cunescape("A=A\\\\x0aB", 0, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "A=A\\x0aB")); unescaped = mfree(unescaped); assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0); assert_se(streq_ptr(unescaped, "A=A\\x0aB")); }