Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/libutil-tests/file-system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -388,14 +388,13 @@ TEST(openFileEnsureBeneathNoSymlinks, works)
TEST(createAnonymousTempFile, works)
{
auto fd = createAnonymousTempFile();
auto fd_ = fromDescriptorReadOnly(fd.get());
writeFull(fd.get(), "test");
lseek(fd_, 0, SEEK_SET);
lseek(fd.get(), 0, SEEK_SET);
FdSource source{fd.get()};
EXPECT_EQ(source.drain(), "test");
lseek(fd_, 0, SEEK_END);
lseek(fd.get(), 0, SEEK_END);
writeFull(fd.get(), "test");
lseek(fd_, 0, SEEK_SET);
lseek(fd.get(), 0, SEEK_SET);
EXPECT_EQ(source.drain(), "testtest");
}

Expand All @@ -406,14 +405,20 @@ TEST(createAnonymousTempFile, works)
TEST(FdSource, restartWorks)
{
auto fd = createAnonymousTempFile();
auto fd_ = fromDescriptorReadOnly(fd.get());
writeFull(fd.get(), "hello world");
lseek(fd_, 0, SEEK_SET);
lseek(fd.get(), 0, SEEK_SET);
FdSource source{fd.get()};
EXPECT_EQ(source.drain(), "hello world");
source.restart();
EXPECT_EQ(source.drain(), "hello world");
EXPECT_EQ(source.drain(), "");
}

TEST(createTempDir, works)
{
auto tmpDir = createTempDir();
nix::AutoDelete delTmpDir(tmpDir, /*recursive=*/true);
ASSERT_TRUE(std::filesystem::is_directory(tmpDir));
}

} // namespace nix
160 changes: 0 additions & 160 deletions src/libutil/file-system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,6 @@ Path canonPath(PathView path, bool resolveSymlinks)
if (!isAbsolute(path))
throw Error("not an absolute path: '%1%'", path);

// For Windows
auto rootName = std::filesystem::path{path}.root_name();

/* This just exists because we cannot set the target of `remaining`
(the callback parameter) directly to a newly-constructed string,
since it is `std::string_view`. */
Expand Down Expand Up @@ -147,8 +144,6 @@ Path canonPath(PathView path, bool resolveSymlinks)
}
});

if (!rootName.empty())
ret = rootName.string() + std::move(ret);
return ret;
}

Expand Down Expand Up @@ -380,14 +375,6 @@ void syncParent(const Path & path)
fd.fsync();
}

#ifdef __FreeBSD__
# define MOUNTEDPATHS_PARAM , std::set<Path> & mountedPaths
# define MOUNTEDPATHS_ARG , mountedPaths
#else
# define MOUNTEDPATHS_PARAM
# define MOUNTEDPATHS_ARG
#endif

void recursiveSync(const Path & path)
{
/* If it's a file or symlink, just fsync and return. */
Expand Down Expand Up @@ -432,129 +419,6 @@ 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
checkInterrupt();

# ifdef __FreeBSD__
// In case of emergency (unmount fails for some reason) not recurse into mountpoints.
// This prevents us from tearing up the nullfs-mounted nix store.
if (mountedPaths.find(path) != mountedPaths.end()) {
return;
}
# endif

std::string name(path.filename());
assert(name != "." && name != ".." && !name.empty());

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);
}

if (!S_ISDIR(st.st_mode)) {
/* We are about to delete a file. Will it likely free space? */

switch (st.st_nlink) {
/* Yes: last link. */
case 1:
bytesFreed += st.st_size;
break;
/* Maybe: yes, if 'auto-optimise-store' or manual optimisation
was performed. Instead of checking for real let's assume
it's an optimised file and space will be freed.

In worst case we will double count on freed space for files
with exactly two hardlinks for unoptimised packages.
*/
case 2:
bytesFreed += st.st_size;
break;
/* No: 3+ links. */
default:
break;
}
}

if (S_ISDIR(st.st_mode)) {
/* 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);
}

int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (fd == -1)
throw SysError("opening directory %1%", path);
AutoCloseDir dir(fdopendir(fd));
if (!dir)
throw SysError("opening directory %1%", path);

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);
}
if (errno)
throw SysError("reading directory %1%", path);
}

int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
if (errno == ENOENT)
return;
try {
throw SysError("cannot unlink %1%", path);
} catch (...) {
if (!ex)
ex = std::current_exception();
else
ignoreExceptionExceptInterrupt();
}
}
#else
// TODO implement
throw UnimplementedError("_deletePath");
#endif
}

static void _deletePath(const std::filesystem::path & path, uint64_t & bytesFreed MOUNTEDPATHS_PARAM)
{
assert(path.is_absolute());
assert(path.parent_path() != path);

AutoCloseFD dirfd = toDescriptor(open(path.parent_path().string().c_str(), O_RDONLY));
if (!dirfd) {
if (errno == ENOENT)
return;
throw SysError("opening directory %s", path.parent_path());
}

std::exception_ptr ex;

_deletePath(dirfd.get(), path, bytesFreed, ex MOUNTEDPATHS_ARG);

if (ex)
std::rethrow_exception(ex);
}

void deletePath(const std::filesystem::path & path)
{
uint64_t dummy;
deletePath(path, dummy);
}

void createDir(const Path & path, mode_t mode)
{
if (mkdir(
Expand All @@ -577,25 +441,6 @@ void createDirs(const std::filesystem::path & path)
}
}

void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed)
{
// Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path);
#ifdef __FreeBSD__
std::set<Path> mountedPaths;
struct statfs * mntbuf;
int count;
if ((count = getmntinfo(&mntbuf, MNT_WAIT)) < 0) {
throw SysError("getmntinfo");
}

for (int i = 0; i < count; i++) {
mountedPaths.emplace(mntbuf[i].f_mntonname);
}
#endif
bytesFreed = 0;
_deletePath(path, bytesFreed MOUNTEDPATHS_ARG);
}

//////////////////////////////////////////////////////////////////////

AutoDelete::AutoDelete()
Expand Down Expand Up @@ -672,11 +517,6 @@ void AutoUnmount::cancel()

//////////////////////////////////////////////////////////////////////

std::filesystem::path defaultTempDir()
{
return getEnvNonEmpty("TMPDIR").value_or("/tmp");
}

std::filesystem::path createTempDir(const std::filesystem::path & tmpRoot, const std::string & prefix, mode_t mode)
{
while (1) {
Expand Down
10 changes: 10 additions & 0 deletions src/libutil/include/nix/util/file-descriptor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,14 @@ Descriptor openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPath & p

MakeError(EndOfFile, Error);

#ifdef _WIN32

/**
* Windows specific replacement for POSIX `lseek` that operates on a `HANDLE` and not
* a file descriptor.
*/
off_t lseek(Descriptor fd, off_t offset, int whence);

#endif

} // namespace nix
22 changes: 22 additions & 0 deletions src/libutil/include/nix/util/file-path-impl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ struct UnixPathTrait
{
return path.rfind('/', from);
}

static size_t rootNameLen(StringView)
{
return 0;
}
};

/**
Expand Down Expand Up @@ -83,6 +88,18 @@ struct WindowsPathTrait
size_t p2 = path.rfind(preferredSep, from);
return p1 == String::npos ? p2 : p2 == String::npos ? p1 : std::max(p1, p2);
}

static size_t rootNameLen(StringView path)
{
if (path.size() >= 2 && path[1] == ':') {
char driveLetter = path[0];
if ((driveLetter >= 'A' && driveLetter <= 'Z') || (driveLetter >= 'a' && driveLetter <= 'z'))
return 2;
}
/* TODO: This needs to also handle UNC paths.
* https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths */
return 0;
}
};

template<typename CharT>
Expand Down Expand Up @@ -116,6 +133,11 @@ typename PathDict::String canonPathInner(typename PathDict::StringView remaining
typename PathDict::String result;
result.reserve(256);

if (auto rootNameLength = PathDict::rootNameLen(remaining)) {
result += remaining.substr(0, rootNameLength); /* Copy drive letter verbatim. */
remaining.remove_prefix(rootNameLength);
}

while (true) {

/* Skip slashes. */
Expand Down
2 changes: 2 additions & 0 deletions src/libutil/include/nix/util/file-system.hh
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");

/**
* Return `TMPDIR`, or the default temporary directory if unset or empty.
* Uses GetTempPathW on windows which respects TMP, TEMP, USERPROFILE env variables.
* Does not resolve symlinks and the returned path might not be directory or exist at all.
*/
std::filesystem::path defaultTempDir();

Expand Down
3 changes: 2 additions & 1 deletion src/libutil/nar-accessor.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nix/util/nar-accessor.hh"
#include "nix/util/file-descriptor.hh"
#include "nix/util/archive.hh"

#include <map>
Expand Down Expand Up @@ -281,7 +282,7 @@ GetNarBytes seekableGetNarBytes(const Path & path)
GetNarBytes seekableGetNarBytes(Descriptor fd)
{
return [fd](uint64_t offset, uint64_t length) {
if (::lseek(fromDescriptorReadOnly(fd), offset, SEEK_SET) == -1)
if (lseek(fd, offset, SEEK_SET) == -1)
throw SysError("seeking in file");

std::string buf(length, 0);
Expand Down
6 changes: 3 additions & 3 deletions src/libutil/serialise.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nix/util/serialise.hh"
#include "nix/util/file-descriptor.hh"
#include "nix/util/compression.hh"
#include "nix/util/signals.hh"
#include "nix/util/util.hh"
Expand Down Expand Up @@ -162,11 +163,11 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
_good = false;
throw SysError("reading from file");
}
#endif
if (n == 0) {
_good = false;
throw EndOfFile(std::string(*endOfFileError));
}
#endif
read += n;
return n;
}
Expand Down Expand Up @@ -207,8 +208,7 @@ void FdSource::restart()
throw Error("can't seek to the start of a file");
buffer.reset();
read = bufPosIn = bufPosOut = 0;
int fd_ = fromDescriptorReadOnly(fd);
if (lseek(fd_, 0, SEEK_SET) == -1)
if (lseek(fd, 0, SEEK_SET) == -1)
throw SysError("seeking to the start of a file");
}

Expand Down
Loading
Loading