Yu Watanabe | db9ecf0 | 2020-11-09 13:23:58 +0900 | [diff] [blame] | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 2 | |
Thomas Hindoe Paaboel Andersen | 11c3a36 | 2015-11-30 21:43:37 +0100 | [diff] [blame] | 3 | #include <errno.h> |
| 4 | #include <fcntl.h> |
| 5 | #include <stdbool.h> |
| 6 | #include <stddef.h> |
Thomas Hindoe Paaboel Andersen | 11c3a36 | 2015-11-30 21:43:37 +0100 | [diff] [blame] | 7 | #include <unistd.h> |
| 8 | |
Zbigniew Jędrzejewski-Szmek | 265e9be | 2018-05-22 11:33:01 +0200 | [diff] [blame] | 9 | #include "alloc-util.h" |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 10 | #include "btrfs-util.h" |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 11 | #include "cgroup-util.h" |
Reverend Homer | 8fb3f00 | 2016-12-09 12:04:30 +0300 | [diff] [blame] | 12 | #include "dirent-util.h" |
Lennart Poettering | 3ffd4af | 2015-10-25 13:14:12 +0100 | [diff] [blame] | 13 | #include "fd-util.h" |
Thomas Hindoe Paaboel Andersen | 93cc777 | 2015-12-01 23:22:03 +0100 | [diff] [blame] | 14 | #include "log.h" |
| 15 | #include "macro.h" |
Zbigniew Jędrzejewski-Szmek | 049af8a | 2018-11-29 10:24:39 +0100 | [diff] [blame] | 16 | #include "mountpoint-util.h" |
Lennart Poettering | 07630ce | 2015-10-24 22:58:24 +0200 | [diff] [blame] | 17 | #include "path-util.h" |
Lennart Poettering | 3ffd4af | 2015-10-25 13:14:12 +0100 | [diff] [blame] | 18 | #include "rm-rf.h" |
Lennart Poettering | 8fcde01 | 2015-10-26 22:01:44 +0100 | [diff] [blame] | 19 | #include "stat-util.h" |
Lennart Poettering | 07630ce | 2015-10-24 22:58:24 +0200 | [diff] [blame] | 20 | #include "string-util.h" |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 21 | |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 22 | static bool is_physical_fs(const struct statfs *sfs) { |
| 23 | return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); |
| 24 | } |
| 25 | |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 26 | static int patch_dirfd_mode( |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 27 | int dfd, |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 28 | mode_t *ret_old_mode) { |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 29 | |
| 30 | struct stat st; |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 31 | |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 32 | assert(dfd >= 0); |
| 33 | assert(ret_old_mode); |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 34 | |
| 35 | if (fstat(dfd, &st) < 0) |
| 36 | return -errno; |
| 37 | if (!S_ISDIR(st.st_mode)) |
| 38 | return -ENOTDIR; |
Frantisek Sumsal | 1d6cc5d | 2020-10-04 11:29:23 +0200 | [diff] [blame] | 39 | if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */ |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 40 | return -EACCES; /* original error */ |
| 41 | if (st.st_uid != geteuid()) /* this only works if the UID matches ours */ |
| 42 | return -EACCES; |
| 43 | |
| 44 | if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0) |
| 45 | return -errno; |
| 46 | |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 47 | *ret_old_mode = st.st_mode; |
| 48 | return 0; |
| 49 | } |
| 50 | |
| 51 | int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags) { |
| 52 | |
| 53 | mode_t old_mode; |
| 54 | int r; |
| 55 | |
| 56 | /* Like unlinkat(), but tries harder: if we get EACCESS we'll try to set the r/w/x bits on the |
| 57 | * directory. This is useful if we run unprivileged and have some files where the w bit is |
| 58 | * missing. */ |
| 59 | |
| 60 | if (unlinkat(dfd, filename, unlink_flags) >= 0) |
| 61 | return 0; |
| 62 | if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD)) |
| 63 | return -errno; |
| 64 | |
| 65 | r = patch_dirfd_mode(dfd, &old_mode); |
| 66 | if (r < 0) |
| 67 | return r; |
| 68 | |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 69 | if (unlinkat(dfd, filename, unlink_flags) < 0) { |
| 70 | r = -errno; |
| 71 | /* Try to restore the original access mode if this didn't work */ |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 72 | (void) fchmod(dfd, old_mode); |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 73 | return r; |
| 74 | } |
| 75 | |
Allen Webb | 6b08fdf | 2021-06-28 11:07:59 -0500 | [diff] [blame^] | 76 | if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0) |
| 77 | return -errno; |
| 78 | |
| 79 | /* If this worked, we won't reset the old mode by default, since we'll need it for other entries too, |
| 80 | * and we should destroy the whole thing */ |
| 81 | return 0; |
| 82 | } |
| 83 | |
| 84 | int fstatat_harder(int dfd, |
| 85 | const char *filename, |
| 86 | struct stat *ret, |
| 87 | int fstatat_flags, |
| 88 | RemoveFlags remove_flags) { |
| 89 | |
| 90 | mode_t old_mode; |
| 91 | int r; |
| 92 | |
| 93 | /* Like unlink_harder() but does the same for fstatat() */ |
| 94 | |
| 95 | if (fstatat(dfd, filename, ret, fstatat_flags) >= 0) |
| 96 | return 0; |
| 97 | if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD)) |
| 98 | return -errno; |
| 99 | |
| 100 | r = patch_dirfd_mode(dfd, &old_mode); |
| 101 | if (r < 0) |
| 102 | return r; |
| 103 | |
| 104 | if (fstatat(dfd, filename, ret, fstatat_flags) < 0) { |
| 105 | r = -errno; |
| 106 | (void) fchmod(dfd, old_mode); |
| 107 | return r; |
| 108 | } |
| 109 | |
| 110 | if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0) |
| 111 | return -errno; |
| 112 | |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 113 | return 0; |
| 114 | } |
| 115 | |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 116 | int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { |
| 117 | _cleanup_closedir_ DIR *d = NULL; |
Reverend Homer | 8fb3f00 | 2016-12-09 12:04:30 +0300 | [diff] [blame] | 118 | struct dirent *de; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 119 | int ret = 0, r; |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 120 | struct statfs sfs; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 121 | |
| 122 | assert(fd >= 0); |
| 123 | |
Lennart Poettering | c0ba6b9 | 2019-04-29 15:45:19 +0200 | [diff] [blame] | 124 | /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed |
| 125 | * fd, in all cases, including on failure.. */ |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 126 | |
| 127 | if (!(flags & REMOVE_PHYSICAL)) { |
| 128 | |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 129 | r = fstatfs(fd, &sfs); |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 130 | if (r < 0) { |
| 131 | safe_close(fd); |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 132 | return -errno; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 133 | } |
| 134 | |
Evgeny Vereshchagin | f0bef27 | 2016-10-13 16:50:46 +0300 | [diff] [blame] | 135 | if (is_physical_fs(&sfs)) { |
Zbigniew Jędrzejewski-Szmek | 265e9be | 2018-05-22 11:33:01 +0200 | [diff] [blame] | 136 | /* We refuse to clean physical file systems with this call, |
| 137 | * unless explicitly requested. This is extra paranoia just |
| 138 | * to be sure we never ever remove non-state data. */ |
| 139 | _cleanup_free_ char *path = NULL; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 140 | |
Zbigniew Jędrzejewski-Szmek | 265e9be | 2018-05-22 11:33:01 +0200 | [diff] [blame] | 141 | (void) fd_get_path(fd, &path); |
| 142 | log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.", |
| 143 | strna(path)); |
| 144 | |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 145 | safe_close(fd); |
| 146 | return -EPERM; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | d = fdopendir(fd); |
| 151 | if (!d) { |
| 152 | safe_close(fd); |
| 153 | return errno == ENOENT ? 0 : -errno; |
| 154 | } |
| 155 | |
Reverend Homer | 8fb3f00 | 2016-12-09 12:04:30 +0300 | [diff] [blame] | 156 | FOREACH_DIRENT_ALL(de, d, return -errno) { |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 157 | bool is_dir; |
| 158 | struct stat st; |
| 159 | |
Lennart Poettering | 49bfc87 | 2017-02-02 00:06:18 +0100 | [diff] [blame] | 160 | if (dot_or_dot_dot(de->d_name)) |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 161 | continue; |
| 162 | |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 163 | if (de->d_type == DT_UNKNOWN || |
| 164 | (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 165 | if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { |
| 166 | if (ret == 0 && errno != ENOENT) |
| 167 | ret = -errno; |
| 168 | continue; |
| 169 | } |
| 170 | |
| 171 | is_dir = S_ISDIR(st.st_mode); |
| 172 | } else |
| 173 | is_dir = de->d_type == DT_DIR; |
| 174 | |
| 175 | if (is_dir) { |
Lennart Poettering | 50b6eec | 2019-04-29 16:21:01 +0200 | [diff] [blame] | 176 | _cleanup_close_ int subdir_fd = -1; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 177 | |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 178 | /* if root_dev is set, remove subdirectories only if device is same */ |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 179 | if (root_dev && st.st_dev != root_dev->st_dev) |
| 180 | continue; |
| 181 | |
| 182 | subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); |
| 183 | if (subdir_fd < 0) { |
| 184 | if (ret == 0 && errno != ENOENT) |
| 185 | ret = -errno; |
| 186 | continue; |
| 187 | } |
| 188 | |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 189 | /* Stop at mount points */ |
Martin Pitt | 5d40903 | 2015-05-27 09:56:03 +0200 | [diff] [blame] | 190 | r = fd_is_mount_point(fd, de->d_name, 0); |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 191 | if (r < 0) { |
| 192 | if (ret == 0 && r != -ENOENT) |
| 193 | ret = r; |
| 194 | |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 195 | continue; |
| 196 | } |
Lennart Poettering | 50b6eec | 2019-04-29 16:21:01 +0200 | [diff] [blame] | 197 | if (r > 0) |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 198 | continue; |
Lennart Poettering | f25afeb | 2015-04-04 14:42:39 +0200 | [diff] [blame] | 199 | |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 200 | if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 201 | |
| 202 | /* This could be a subvolume, try to remove it */ |
| 203 | |
Lennart Poettering | 5bcd08d | 2015-10-21 19:38:21 +0200 | [diff] [blame] | 204 | r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 205 | if (r < 0) { |
Yu Watanabe | 4c70109 | 2017-10-04 23:01:32 +0900 | [diff] [blame] | 206 | if (!IN_SET(r, -ENOTTY, -EINVAL)) { |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 207 | if (ret == 0) |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 208 | ret = r; |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 209 | |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 210 | continue; |
| 211 | } |
| 212 | |
Lennart Poettering | 50b6eec | 2019-04-29 16:21:01 +0200 | [diff] [blame] | 213 | /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ |
| 214 | } else |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 215 | /* It was a subvolume, continue. */ |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 216 | continue; |
Lennart Poettering | 9e9b663 | 2015-04-04 19:22:00 +0200 | [diff] [blame] | 217 | } |
| 218 | |
Lennart Poettering | 50b6eec | 2019-04-29 16:21:01 +0200 | [diff] [blame] | 219 | /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 220 | * system type again for each directory */ |
Lennart Poettering | 50b6eec | 2019-04-29 16:21:01 +0200 | [diff] [blame] | 221 | r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 222 | if (r < 0 && ret == 0) |
| 223 | ret = r; |
| 224 | |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 225 | r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags); |
| 226 | if (r < 0 && r != -ENOENT && ret == 0) |
| 227 | ret = r; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 228 | |
| 229 | } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { |
| 230 | |
Lennart Poettering | 2899fb0 | 2020-07-23 15:24:54 +0200 | [diff] [blame] | 231 | r = unlinkat_harder(fd, de->d_name, 0, flags); |
| 232 | if (r < 0 && r != -ENOENT && ret == 0) |
| 233 | ret = r; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 234 | } |
| 235 | } |
Reverend Homer | 8fb3f00 | 2016-12-09 12:04:30 +0300 | [diff] [blame] | 236 | return ret; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 237 | } |
| 238 | |
| 239 | int rm_rf(const char *path, RemoveFlags flags) { |
| 240 | int fd, r; |
| 241 | struct statfs s; |
| 242 | |
| 243 | assert(path); |
| 244 | |
Lennart Poettering | c2f64c0 | 2019-03-29 16:09:49 +0100 | [diff] [blame] | 245 | /* For now, don't support dropping subvols when also only dropping directories, since we can't do |
| 246 | * this race-freely. */ |
| 247 | if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) |
| 248 | return -EINVAL; |
| 249 | |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 250 | /* We refuse to clean the root file system with this call. This is extra paranoia to never cause a |
| 251 | * really seriously broken system. */ |
Zbigniew Jędrzejewski-Szmek | baaa35a | 2018-11-20 23:40:44 +0100 | [diff] [blame] | 252 | if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW)) |
| 253 | return log_error_errno(SYNTHETIC_ERRNO(EPERM), |
| 254 | "Attempted to remove entire root file system (\"%s\"), and we can't allow that.", |
| 255 | path); |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 256 | |
Zbigniew Jędrzejewski-Szmek | d94a24c | 2018-04-20 15:36:20 +0200 | [diff] [blame] | 257 | if (FLAGS_SET(flags, REMOVE_SUBVOLUME | REMOVE_ROOT | REMOVE_PHYSICAL)) { |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 258 | /* Try to remove as subvolume first */ |
Lennart Poettering | 5bcd08d | 2015-10-21 19:38:21 +0200 | [diff] [blame] | 259 | r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 260 | if (r >= 0) |
| 261 | return r; |
| 262 | |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 263 | if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT) |
| 264 | return 0; |
| 265 | |
Yu Watanabe | 4c70109 | 2017-10-04 23:01:32 +0900 | [diff] [blame] | 266 | if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR)) |
Lennart Poettering | d9e2daa | 2015-04-06 10:57:17 +0200 | [diff] [blame] | 267 | return r; |
| 268 | |
| 269 | /* Not btrfs or not a subvolume */ |
| 270 | } |
| 271 | |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 272 | fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); |
| 273 | if (fd < 0) { |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 274 | if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) |
| 275 | return 0; |
| 276 | |
Andreas Rammhold | ec2ce0c | 2017-09-29 09:58:22 +0200 | [diff] [blame] | 277 | if (!IN_SET(errno, ENOTDIR, ELOOP)) |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 278 | return -errno; |
| 279 | |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 280 | if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES)) |
| 281 | return 0; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 282 | |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 283 | if (FLAGS_SET(flags, REMOVE_ROOT)) { |
| 284 | |
| 285 | if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { |
| 286 | if (statfs(path, &s) < 0) |
| 287 | return -errno; |
| 288 | |
| 289 | if (is_physical_fs(&s)) |
| 290 | return log_error_errno(SYNTHETIC_ERRNO(EPERM), |
| 291 | "Attempted to remove files from a disk file system under \"%s\", refusing.", |
| 292 | path); |
| 293 | } |
| 294 | |
| 295 | if (unlink(path) < 0) { |
| 296 | if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) |
| 297 | return 0; |
| 298 | |
| 299 | return -errno; |
| 300 | } |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 301 | } |
| 302 | |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 303 | return 0; |
| 304 | } |
| 305 | |
| 306 | r = rm_rf_children(fd, flags, NULL); |
| 307 | |
Lennart Poettering | c0228b4 | 2019-03-29 16:13:03 +0100 | [diff] [blame] | 308 | if (FLAGS_SET(flags, REMOVE_ROOT) && |
| 309 | rmdir(path) < 0 && |
| 310 | r >= 0 && |
| 311 | (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT)) |
| 312 | r = -errno; |
Lennart Poettering | c687863 | 2015-04-04 11:52:57 +0200 | [diff] [blame] | 313 | |
| 314 | return r; |
| 315 | } |