]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/chown-recursive.c
2 This file is part of systemd.
4 Copyright 2017 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <sys/types.h>
24 #include "user-util.h"
27 #include "dirent-util.h"
28 #include "chown-recursive.h"
30 static int chown_one(int fd
, const char *name
, const struct stat
*st
, uid_t uid
, gid_t gid
) {
36 if ((!uid_is_valid(uid
) || st
->st_uid
== uid
) &&
37 (!gid_is_valid(gid
) || st
->st_gid
== gid
))
41 r
= fchownat(fd
, name
, uid
, gid
, AT_SYMLINK_NOFOLLOW
);
43 r
= fchown(fd
, uid
, gid
);
47 /* The linux kernel alters the mode in some cases of chown(). Let's undo this. */
49 if (!S_ISLNK(st
->st_mode
))
50 r
= fchmodat(fd
, name
, st
->st_mode
, 0);
51 else /* There's currently no AT_SYMLINK_NOFOLLOW for fchmodat() */
54 r
= fchmod(fd
, st
->st_mode
);
61 static int chown_recursive_internal(int fd
, const struct stat
*st
, uid_t uid
, gid_t gid
) {
68 if (S_ISDIR(st
->st_mode
)) {
69 _cleanup_closedir_
DIR *d
= NULL
;
79 FOREACH_DIRENT_ALL(de
, d
, r
= -errno
; goto finish
) {
82 if (dot_or_dot_dot(de
->d_name
))
85 if (fstatat(dirfd(d
), de
->d_name
, &fst
, AT_SYMLINK_NOFOLLOW
) < 0) {
90 if (S_ISDIR(fst
.st_mode
)) {
93 subdir_fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
99 r
= chown_recursive_internal(subdir_fd
, &fst
, uid
, gid
);
105 r
= chown_one(dirfd(d
), de
->d_name
, &fst
, uid
, gid
);
113 r
= chown_one(dirfd(d
), NULL
, st
, uid
, gid
);
115 r
= chown_one(fd
, NULL
, st
, uid
, gid
);
119 r
= r
> 0 || changed
;
126 int path_chown_recursive(const char *path
, uid_t uid
, gid_t gid
) {
127 _cleanup_close_
int fd
= -1;
131 fd
= open(path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
|O_NOATIME
);
135 if (!uid_is_valid(uid
) && !gid_is_valid(gid
))
136 return 0; /* nothing to do */
138 if (fstat(fd
, &st
) < 0)
141 /* Let's take a shortcut: if the top-level directory is properly owned, we don't descend into the whole tree,
142 * under the assumption that all is OK anyway. */
144 if ((!uid_is_valid(uid
) || st
.st_uid
== uid
) &&
145 (!gid_is_valid(gid
) || st
.st_gid
== gid
))
148 r
= chown_recursive_internal(fd
, &st
, uid
, gid
);
149 fd
= -1; /* we donated the fd to the call, regardless if it succeeded or failed */