int path_is_os_tree(const char *path) { int r; assert(path); /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from * the case where just the os-release file is missing. */ if (laccess(path, F_OK) < 0) return -errno; /* We use /usr/lib/os-release as flag file if something is an OS */ r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); if (r == -ENOENT) { /* Also check for the old location in /etc, just in case. */ r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); if (r == -ENOENT) return 0; /* We got nothing */ } if (r < 0) return r; return 1; }
static int equivalent(const char *a, const char *b) { _cleanup_free_ char *x = NULL, *y = NULL; int r; r = chase_symlinks(a, NULL, 0, &x); if (r < 0) return r; r = chase_symlinks(b, NULL, 0, &y); if (r < 0) return r; return path_equal(x, y); }
static int determine_image(const char *image, bool permit_non_existing, char **ret) { int r; /* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the * usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side * (among other things, to make the path independent of the client's working directory) before passing it * over. */ if (image_name_is_valid(image)) { char *c; if (!arg_quiet && laccess(image, F_OK) >= 0) log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. " "Prefix argument with './' to force reference to file in current working directory.", image); c = strdup(image); if (!c) return log_oom(); *ret = c; return 0; } if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Operations on images by path not supported when connecting to remote systems."); r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret); if (r < 0) return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image); return 0; }
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) { _cleanup_free_ char *p = NULL; int r; /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */ assert(ret); if (major(devno) == 0 && minor(devno) == 0) { char *s; /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in * /dev/block/ and /dev/char/, hence we handle them specially here. */ if (S_ISCHR(mode)) s = strdup("/run/systemd/inaccessible/chr"); else if (S_ISBLK(mode)) s = strdup("/run/systemd/inaccessible/blk"); else return -ENODEV; if (!s) return -ENOMEM; *ret = s; return 0; } r = device_path_make_major_minor(mode, devno, &p); if (r < 0) return r; return chase_symlinks(p, NULL, 0, ret); }
static int unit_file_find_dir( const char *original_root, const char *path, char ***dirs) { _cleanup_free_ char *chased = NULL; int r; assert(path); r = chase_symlinks(path, original_root, 0, &chased); if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir. */ return 0; if (r == -ENAMETOOLONG) { /* Also, ignore -ENAMETOOLONG but log about it. After all, users are not even able to create the * drop-in dir in such case. This mostly happens for device units with an overly long /sys path. */ log_debug_errno(r, "Path '%s' too long, couldn't canonicalize, ignoring.", path); return 0; } if (r < 0) return log_warning_errno(r, "Failed to canonicalize path '%s': %m", path); r = strv_push(dirs, chased); if (r < 0) return log_oom(); chased = NULL; return 0; }
static const char * init_file_tmp_name (void) { static char *file = 0; if (!file) { const char *name = init_file_name(); const char *suffix = ".tmp"; char *n2 = chase_symlinks (name); if (n2) name = n2; if (!name || !*name) file = ""; else { file = (char *) malloc(strlen(name) + strlen(suffix) + 2); strcpy(file, name); strcat(file, suffix); } if (n2) free (n2); } if (file && *file) return file; else return 0; }
STRV_FOREACH_PAIR(link, p, links) { _cleanup_free_ char *target, *f, *l; assert_se(f = strjoin(original_dir, *p)); assert_se(l = strjoin(copy_dir, *link)); assert_se(chase_symlinks(l, NULL, 0, &target) == 1); assert_se(path_equal(f, target)); }
static int make_volatile(const char *path) { _cleanup_free_ char *old_usr = NULL; int r; assert(path); r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr); if (r < 0) return log_error_errno(r, "/usr not available in old root: %m"); r = mkdir_p("/run/systemd/volatile-sysroot", 0700); if (r < 0) return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m"); r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755"); if (r < 0) goto finish_rmdir; if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) { r = log_error_errno(errno, "Failed to create /usr directory: %m"); goto finish_umount; } r = mount_verbose(LOG_ERR, old_usr, "/run/systemd/volatile-sysroot/usr", NULL, MS_BIND|MS_REC, NULL); if (r < 0) goto finish_umount; r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", true, NULL); if (r < 0) { log_error_errno(r, "Failed to remount /usr read-only: %m"); goto finish_umount; } r = umount_recursive(path, 0); if (r < 0) { log_error_errno(r, "Failed to unmount %s: %m", path); goto finish_umount; } if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC, ignoring: %m", path); r = mount_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL); finish_umount: (void) umount_recursive("/run/systemd/volatile-sysroot", 0); finish_rmdir: (void) rmdir("/run/systemd/volatile-sysroot"); return r; }
static int directory_image_get_os_release(Image *image, char ***ret, sd_bus_error *error) { _cleanup_free_ char *path = NULL; int r; assert(image); assert(ret); r = chase_symlinks("/etc/os-release", image->path, CHASE_PREFIX_ROOT, &path); if (r == -ENOENT) r = chase_symlinks("/usr/lib/os-release", image->path, CHASE_PREFIX_ROOT, &path); if (r == -ENOENT) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Image does not contain OS release information"); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to resolve %s: %m", image->path); r = load_env_file_pairs(NULL, path, NULL, ret); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to open %s: %m", path); return 0; }
int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink, mkdir_func_t _mkdir) { struct stat st; int r; assert(_mkdir != mkdir); if (_mkdir(path, mode) >= 0) { r = chmod_and_chown(path, mode, uid, gid); if (r < 0) return r; } if (lstat(path, &st) < 0) return -errno; if (follow_symlink && S_ISLNK(st.st_mode)) { _cleanup_free_ char *p = NULL; r = chase_symlinks(path, NULL, CHASE_NONEXISTENT, &p); if (r < 0) return r; if (r == 0) return mkdir_safe_internal(p, mode, uid, gid, false, _mkdir); if (lstat(p, &st) < 0) return -errno; } if ((st.st_mode & 0007) > (mode & 0007) || (st.st_mode & 0070) > (mode & 0070) || (st.st_mode & 0700) > (mode & 0700) || (uid != UID_INVALID && st.st_uid != uid) || (gid != GID_INVALID && st.st_gid != gid) || !S_ISDIR(st.st_mode)) return -EEXIST; return 0; }
static void test_chase_symlinks(void) { _cleanup_free_ char *result = NULL; char temp[] = "/tmp/test-chase.XXXXXX"; const char *top, *p, *q; int r; assert_se(mkdtemp(temp)); top = strjoina(temp, "/top"); assert_se(mkdir(top, 0700) >= 0); p = strjoina(top, "/dot"); assert_se(symlink(".", p) >= 0); p = strjoina(top, "/dotdot"); assert_se(symlink("..", p) >= 0); p = strjoina(top, "/dotdota"); assert_se(symlink("../a", p) >= 0); p = strjoina(temp, "/a"); assert_se(symlink("b", p) >= 0); p = strjoina(temp, "/b"); assert_se(symlink("/usr", p) >= 0); p = strjoina(temp, "/start"); assert_se(symlink("top/dot/dotdota", p) >= 0); /* Paths that use symlinks underneath the "root" */ r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/usr")); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r == -ENOENT); q = strjoina(temp, "/usr"); r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, q)); assert_se(mkdir(q, 0700) >= 0); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, q)); p = strjoina(temp, "/slash"); assert_se(symlink("/", p) >= 0); result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/")); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, temp)); /* Paths that would "escape" outside of the "root" */ p = strjoina(temp, "/6dots"); assert_se(symlink("../../..", p) >= 0); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, temp)); p = strjoina(temp, "/6dotsusr"); assert_se(symlink("../../../usr", p) >= 0); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, q)); p = strjoina(temp, "/top/8dotsusr"); assert_se(symlink("../../../../usr", p) >= 0); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0 && path_equal(result, q)); /* Paths that contain repeated slashes */ p = strjoina(temp, "/slashslash"); assert_se(symlink("///usr///", p) >= 0); result = mfree(result); r = chase_symlinks(p, NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/usr")); result = mfree(result); r = chase_symlinks(p, temp, 0, &result); assert_se(r > 0); assert_se(path_equal(result, q)); /* Paths using . */ result = mfree(result); r = chase_symlinks("/etc/./.././", NULL, 0, &result); assert_se(r > 0); assert_se(path_equal(result, "/")); result = mfree(result); r = chase_symlinks("/etc/./.././", "/etc", 0, &result); assert_se(r > 0 && path_equal(result, "/etc")); result = mfree(result); r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result); assert_se(r == -ENOTDIR); /* Path that loops back to self */ result = mfree(result); p = strjoina(temp, "/recursive-symlink"); assert_se(symlink("recursive-symlink", p) >= 0); r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ELOOP); /* Path which doesn't exist */ p = strjoina(temp, "/idontexist"); r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, p)); result = mfree(result); p = strjoina(temp, "/idontexist/meneither"); r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == 0); assert_se(path_equal(result, p)); result = mfree(result); /* Path which doesn't exist, but contains weird stuff */ p = strjoina(temp, "/idontexist/.."); r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == -ENOENT); assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); }