1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "dirent-util.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "string-util.h"
32 #include "user-util.h"
35 int unlink_noerrno(const char *path) {
46 /// UNNEEDED by elogind
48 int rmdir_parents(const char *path, const char *stop) {
57 /* Skip trailing slashes */
58 while (l > 0 && path[l-1] == '/')
64 /* Skip last component */
65 while (l > 0 && path[l-1] != '/')
68 /* Skip trailing slashes */
69 while (l > 0 && path[l-1] == '/')
79 if (path_startswith(stop, t)) {
96 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
100 ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
104 /* renameat2() exists since Linux 3.15, btrfs added support for it later.
105 * If it is not implemented, fallback to another method. */
106 if (!IN_SET(errno, EINVAL, ENOSYS))
109 /* The link()/unlink() fallback does not work on directories. But
110 * renameat() without RENAME_NOREPLACE gives the same semantics on
111 * directories, except when newpath is an *empty* directory. This is
113 ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
114 if (ret >= 0 && S_ISDIR(buf.st_mode)) {
115 ret = renameat(olddirfd, oldpath, newdirfd, newpath);
116 return ret >= 0 ? 0 : -errno;
119 /* If it is not a directory, use the link()/unlink() fallback. */
120 ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
124 ret = unlinkat(olddirfd, oldpath, 0);
126 /* backup errno before the following unlinkat() alters it */
128 (void) unlinkat(newdirfd, newpath, 0);
137 int readlinkat_malloc(int fd, const char *p, char **ret) {
152 n = readlinkat(fd, p, c, l-1);
159 if ((size_t) n < l-1) {
170 int readlink_malloc(const char *p, char **ret) {
171 return readlinkat_malloc(AT_FDCWD, p, ret);
174 /// UNNEEDED by elogind
176 int readlink_value(const char *p, char **ret) {
177 _cleanup_free_ char *link = NULL;
181 r = readlink_malloc(p, &link);
185 value = basename(link);
189 value = strdup(value);
199 int readlink_and_make_absolute(const char *p, char **r) {
200 _cleanup_free_ char *target = NULL;
207 j = readlink_malloc(p, &target);
211 k = file_in_same_dir(p, target);
219 /// UNNEEDED by elogind
221 int readlink_and_canonicalize(const char *p, char **r) {
228 j = readlink_and_make_absolute(p, &t);
232 s = canonicalize_file_name(t);
239 path_kill_slashes(*r);
244 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
245 _cleanup_free_ char *target = NULL, *t = NULL;
249 full = prefix_roota(root, path);
250 r = readlink_malloc(full, &target);
254 t = file_in_same_dir(path, target);
265 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
268 /* Under the assumption that we are running privileged we
269 * first change the access mode and only then hand out
270 * ownership to avoid a window where access is too open. */
272 if (mode != MODE_INVALID)
273 if (chmod(path, mode) < 0)
276 if (uid != UID_INVALID || gid != GID_INVALID)
277 if (chown(path, uid, gid) < 0)
283 int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
286 /* Under the assumption that we are running privileged we
287 * first change the access mode and only then hand out
288 * ownership to avoid a window where access is too open. */
290 if (mode != MODE_INVALID)
291 if (fchmod(fd, mode) < 0)
294 if (uid != UID_INVALID || gid != GID_INVALID)
295 if (fchown(fd, uid, gid) < 0)
301 int fchmod_umask(int fd, mode_t m) {
306 r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
312 int fd_warn_permissions(const char *path, int fd) {
315 if (fstat(fd, &st) < 0)
318 if (st.st_mode & 0111)
319 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
321 if (st.st_mode & 0002)
322 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
324 if (getpid() == 1 && (st.st_mode & 0044) != 0044)
325 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
330 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
331 _cleanup_close_ int fd;
337 mkdir_parents(path, 0755);
339 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
343 if (mode != MODE_INVALID) {
344 r = fchmod(fd, mode);
349 if (uid != UID_INVALID || gid != GID_INVALID) {
350 r = fchown(fd, uid, gid);
355 if (stamp != USEC_INFINITY) {
356 struct timespec ts[2];
358 timespec_store(&ts[0], stamp);
360 r = futimens(fd, ts);
362 r = futimens(fd, NULL);
369 int touch(const char *path) {
370 return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
373 int symlink_idempotent(const char *from, const char *to) {
374 _cleanup_free_ char *p = NULL;
380 if (symlink(from, to) < 0) {
384 r = readlink_malloc(to, &p);
395 int symlink_atomic(const char *from, const char *to) {
396 _cleanup_free_ char *t = NULL;
402 r = tempfn_random(to, NULL, &t);
406 if (symlink(from, t) < 0)
409 if (rename(t, to) < 0) {
417 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
418 _cleanup_free_ char *t = NULL;
423 r = tempfn_random(path, NULL, &t);
427 if (mknod(t, mode, dev) < 0)
430 if (rename(t, path) < 0) {
438 int mkfifo_atomic(const char *path, mode_t mode) {
439 _cleanup_free_ char *t = NULL;
444 r = tempfn_random(path, NULL, &t);
448 if (mkfifo(t, mode) < 0)
451 if (rename(t, path) < 0) {
459 int get_files_in_directory(const char *path, char ***list) {
460 _cleanup_closedir_ DIR *d = NULL;
461 size_t bufsize = 0, n = 0;
462 _cleanup_strv_free_ char **l = NULL;
466 /* Returns all files in a directory in *list, and the number
467 * of files as return value. If list is NULL returns only the
479 if (!de && errno != 0)
484 dirent_ensure_type(d, de);
486 if (!dirent_is_file(de))
490 /* one extra slot is needed for the terminating NULL */
491 if (!GREEDY_REALLOC(l, bufsize, n + 2))
494 l[n] = strdup(de->d_name);
505 l = NULL; /* avoid freeing */