diff options
-rw-r--r-- | third_party/nix/src/libutil/util.cc | 58 |
1 files changed, 45 insertions, 13 deletions
diff --git a/third_party/nix/src/libutil/util.cc b/third_party/nix/src/libutil/util.cc index 0ad2c51148c9..b77fb8ac0ab4 100644 --- a/third_party/nix/src/libutil/util.cc +++ b/third_party/nix/src/libutil/util.cc @@ -252,17 +252,12 @@ bool isLink(const Path& path) { return S_ISLNK(st.st_mode); } -DirEntries readDirectory(const Path& path) { +DirEntries readDirectory(DIR* dir, const Path& path) { DirEntries entries; entries.reserve(64); - AutoCloseDir dir(opendir(path.c_str())); - if (!dir) { - throw SysError(format("opening directory '%1%'") % path); - } - struct dirent* dirent; - while (errno = 0, dirent = readdir(dir.get())) { /* sic */ + while (errno = 0, dirent = readdir(dir)) { /* sic */ checkInterrupt(); string name = dirent->d_name; if (name == "." || name == "..") { @@ -283,6 +278,15 @@ DirEntries readDirectory(const Path& path) { return entries; } +DirEntries readDirectory(const Path& path) { + AutoCloseDir dir(opendir(path.c_str())); + if (!dir) { + throw SysError(format("opening directory '%1%'") % path); + } + + return readDirectory(dir.get(), path); +} + unsigned char getFileType(const Path& path) { struct stat st = lstat(path); if (S_ISDIR(st.st_mode)) { @@ -380,11 +384,14 @@ void writeLine(int fd, string s) { writeFull(fd, s); } -static void _deletePath(const Path& path, unsigned long long& bytesFreed) { +static void _deletePath(int parentfd, const Path& path, + unsigned long long& bytesFreed) { checkInterrupt(); + string name(baseNameOf(path)); + struct stat st; - if (lstat(path.c_str(), &st) == -1) { + if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) { return; } @@ -399,17 +406,26 @@ static void _deletePath(const Path& path, unsigned long long& bytesFreed) { /* Make the directory accessible. */ const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR; if ((st.st_mode & PERM_MASK) != PERM_MASK) { - if (chmod(path.c_str(), st.st_mode | PERM_MASK) == -1) { + if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) { throw SysError(format("chmod '%1%'") % path); } } - for (auto& i : readDirectory(path)) { - _deletePath(path + "/" + i.name, bytesFreed); + int fd = openat(parentfd, path.c_str(), O_RDONLY); + if (!fd) { + throw SysError(format("opening directory '%1%'") % path); + } + AutoCloseDir dir(fdopendir(fd)); + if (!dir) { + throw SysError(format("opening directory '%1%'") % path); + } + for (auto& i : readDirectory(dir.get(), path)) { + _deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed); } } - if (remove(path.c_str()) == -1) { + int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0; + if (unlinkat(parentfd, name.c_str(), flags) == -1) { if (errno == ENOENT) { return; } @@ -417,6 +433,22 @@ static void _deletePath(const Path& path, unsigned long long& bytesFreed) { } } +static void _deletePath(const Path& path, unsigned long long& bytesFreed) { + Path dir = dirOf(path); + if (dir == "") dir = "/"; + + AutoCloseFD dirfd(open(dir.c_str(), O_RDONLY)); + if (!dirfd) { + // This really shouldn't fail silently, but it's left this way + // for backwards compatibility. + if (errno == ENOENT) return; + + throw SysError(format("opening directory '%1%'") % path); + } + + _deletePath(dirfd.get(), path, bytesFreed); +} + void deletePath(const Path& path) { unsigned long long dummy; deletePath(path, dummy); |