Update dir traversal code

This commit is contained in:
topjohnwu 2020-04-12 13:38:57 -07:00
parent 37d38b62b1
commit ab853e1fcf

View File

@ -50,7 +50,7 @@ if (entry->d_name == "."sv || entry->d_name == ".."sv) \
continue;\ continue;\
} }
static void post_order_walk(int dirfd, const function<int(int, dirent *)> &&fn) { static void post_order_walk(int dirfd, const function<void(int, dirent *)> &&fn) {
auto dir = xopen_dir(dirfd); auto dir = xopen_dir(dirfd);
if (!dir) return; if (!dir) return;
@ -62,8 +62,21 @@ static void post_order_walk(int dirfd, const function<int(int, dirent *)> &&fn)
} }
} }
static int remove_at(int dirfd, struct dirent *entry) { static void pre_order_walk(int dirfd, const function<bool(int, dirent *)> &&fn) {
return unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0); auto dir = xopen_dir(dirfd);
if (!dir) return;
for (dirent *entry; (entry = xreaddir(dir.get()));) {
SKIP_DOTS
if (!fn(dirfd, entry))
continue;
if (entry->d_type == DT_DIR)
pre_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), std::move(fn));
}
}
static void remove_at(int dirfd, struct dirent *entry) {
unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0);
} }
void rm_rf(const char *path) { void rm_rf(const char *path) {
@ -361,17 +374,17 @@ void backup_folder(const char *dir, vector<raw_file> &files) {
char path[4096]; char path[4096];
xrealpath(dir, path); xrealpath(dir, path);
int len = strlen(path); int len = strlen(path);
post_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> int { pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> bool {
int fd = xopenat(dfd, entry->d_name, O_RDONLY); int fd = xopenat(dfd, entry->d_name, O_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return false;
run_finally f([&]{ close(fd); }); run_finally f([&]{ close(fd); });
if (fd_path(fd, path, sizeof(path)) < 0) if (fd_path(fd, path, sizeof(path)) < 0)
return -1; return false;
raw_file file; raw_file file;
file.path = path + len + 1; file.path = path + len + 1;
if (fgetattr(fd, &file.attr) < 0) if (fgetattr(fd, &file.attr) < 0)
return -1; return false;
if (entry->d_type == DT_REG) { if (entry->d_type == DT_REG) {
fd_full_read(fd, file.buf, file.sz); fd_full_read(fd, file.buf, file.sz);
} else if (entry->d_type == DT_LNK) { } else if (entry->d_type == DT_LNK) {
@ -381,14 +394,14 @@ void backup_folder(const char *dir, vector<raw_file> &files) {
memcpy(file.buf, path, file.sz); memcpy(file.buf, path, file.sz);
} }
files.emplace_back(std::move(file)); files.emplace_back(std::move(file));
return 0; return true;
}); });
} }
void restore_folder(const char *dir, vector<raw_file> &files) { void restore_folder(const char *dir, vector<raw_file> &files) {
string base(dir); string base(dir);
// Reversed post-order means folders will always be first // Pre-order means folders will always be first
for (raw_file &file : reversed(files)) { for (raw_file &file : files) {
string path = base + "/" + file.path; string path = base + "/" + file.path;
if (S_ISDIR(file.attr.st.st_mode)) { if (S_ISDIR(file.attr.st.st_mode)) {
mkdirs(path.data(), 0); mkdirs(path.data(), 0);