]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/chown-recursive.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2017 Lennart Poettering
10 #include "user-util.h"
13 #include "dirent-util.h"
14 #include "chown-recursive.h"
16 static int chown_one(int fd
, const char *name
, const struct stat
*st
, uid_t uid
, gid_t gid
) {
22 if ((!uid_is_valid(uid
) || st
->st_uid
== uid
) &&
23 (!gid_is_valid(gid
) || st
->st_gid
== gid
))
27 r
= fchownat(fd
, name
, uid
, gid
, AT_SYMLINK_NOFOLLOW
);
29 r
= fchown(fd
, uid
, gid
);
33 /* The linux kernel alters the mode in some cases of chown(). Let's undo this. */
35 if (!S_ISLNK(st
->st_mode
))
36 r
= fchmodat(fd
, name
, st
->st_mode
, 0);
37 else /* There's currently no AT_SYMLINK_NOFOLLOW for fchmodat() */
40 r
= fchmod(fd
, st
->st_mode
);
47 static int chown_recursive_internal(int fd
, const struct stat
*st
, uid_t uid
, gid_t gid
) {
54 if (S_ISDIR(st
->st_mode
)) {
55 _cleanup_closedir_
DIR *d
= NULL
;
65 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
; goto finish
) {
68 if (dot_or_dot_dot(de
->d_name
))
71 if (fstatat(dirfd(d
), de
->d_name
, &fst
, AT_SYMLINK_NOFOLLOW
) < 0) {
76 if (S_ISDIR(fst
.st_mode
)) {
79 subdir_fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
85 r
= chown_recursive_internal(subdir_fd
, &fst
, uid
, gid
);
91 r
= chown_one(dirfd(d
), de
->d_name
, &fst
, uid
, gid
);
99 r
= chown_one(dirfd(d
), NULL
, st
, uid
, gid
);
101 r
= chown_one(fd
, NULL
, st
, uid
, gid
);
105 r
= r
> 0 || changed
;
112 int path_chown_recursive(const char *path
, uid_t uid
, gid_t gid
) {
113 _cleanup_close_
int fd
= -1;
117 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
121 if (!uid_is_valid(uid
) && !gid_is_valid(gid
))
122 return 0; /* nothing to do */
124 if (fstat(fd
, &st
) < 0)
127 /* Let's take a shortcut: if the top-level directory is properly owned, we don't descend into the whole tree,
128 * under the assumption that all is OK anyway. */
130 if ((!uid_is_valid(uid
) || st
.st_uid
== uid
) &&
131 (!gid_is_valid(gid
) || st
.st_gid
== gid
))
134 r
= chown_recursive_internal(fd
, &st
, uid
, gid
);
135 fd
= -1; /* we donated the fd to the call, regardless if it succeeded or failed */