1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 02:21:16 +02:00

libutil: ensure that _deletePath does NOT use absolute paths with dirfds

When calling `_deletePath` with a parent file descriptor, `openat` is
made effective by using relative paths to the directory file descriptor.

To avoid the problem, the signature is changed to resist misuse with an
assert in the prologue of the function.

Change-Id: I6b3fc766bad2afe54dc27d47d1df3873e188de96
Signed-off-by: Raito Bezarius <raito@lix.systems>
This commit is contained in:
Raito Bezarius 2025-03-26 12:42:55 +01:00 committed by Jörg Thalheim
parent 4e59d3fdb2
commit 5ec047f348

View file

@ -444,6 +444,8 @@ void recursiveSync(const Path & path)
static void _deletePath(Descriptor parentfd, const std::filesystem::path & path, uint64_t & bytesFreed, std::exception_ptr & ex MOUNTEDPATHS_PARAM)
{
#ifndef _WIN32
/* This ensures that `name` is an immediate child of `parentfd`. */
assert(!path.empty() && path.string().find('/') == std::string::npos && "`name` is an immediate child to `parentfd`");
checkInterrupt();
#ifdef __FreeBSD__
@ -454,13 +456,13 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
}
#endif
std::string name(baseNameOf(path.native()));
std::string name(path.native());
struct stat st;
if (fstatat(parentfd, name.c_str(), &st,
AT_SYMLINK_NOFOLLOW) == -1) {
if (errno == ENOENT) return;
throw SysError("getting status of %1%", path);
throw SysError("getting status of '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
}
if (!S_ISDIR(st.st_mode)) {
@ -491,23 +493,24 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
/* Make the directory accessible. */
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1)
throw SysError("chmod %1%", path);
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) {
throw SysError("chmod '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
}
}
int fd = openat(parentfd, path.c_str(), O_RDONLY);
int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (fd == -1)
throw SysError("opening directory %1%", path);
throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
AutoCloseDir dir(fdopendir(fd));
if (!dir)
throw SysError("opening directory %1%", path);
throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
checkInterrupt();
std::string childName = dirent->d_name;
if (childName == "." || childName == "..") continue;
_deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed, ex MOUNTEDPATHS_ARG);
_deletePath(dirfd(dir.get()), childName, bytesFreed, ex MOUNTEDPATHS_ARG);
}
if (errno) throw SysError("reading directory %1%", path);
}
@ -516,7 +519,7 @@ static void _deletePath(Descriptor parentfd, const std::filesystem::path & path,
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
if (errno == ENOENT) return;
try {
throw SysError("cannot unlink %1%", path);
throw SysError("cannot unlink '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
} catch (...) {
if (!ex)
ex = std::current_exception();
@ -544,7 +547,7 @@ static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFree
std::exception_ptr ex;
_deletePath(dirfd.get(), path, bytesFreed, ex MOUNTEDPATHS_ARG);
_deletePath(dirfd.get(), path.filename(), bytesFreed, ex MOUNTEDPATHS_ARG);
if (ex)
std::rethrow_exception(ex);