libutil: Add unix::readLinkAt function
This will be used in a following commit.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "nix/util/file-descriptor.hh"
|
||||
#include "nix/util/file-system.hh"
|
||||
@@ -194,4 +195,57 @@ TEST(fchmodatTryNoFollow, fallbackWithoutProc)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* readLinkAt
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(readLinkAt, works)
|
||||
{
|
||||
std::filesystem::path tmpDir = nix::createTempDir();
|
||||
nix::AutoDelete delTmpDir(tmpDir, /*recursive=*/true);
|
||||
|
||||
std::string mediumTarget(PATH_MAX / 2, 'x');
|
||||
std::string longTarget(PATH_MAX - 1, 'y');
|
||||
|
||||
{
|
||||
RestoreSink sink(/*startFsync=*/false);
|
||||
sink.dstPath = tmpDir;
|
||||
sink.dirFd = openDirectory(tmpDir);
|
||||
sink.createSymlink(CanonPath("link"), "target");
|
||||
sink.createSymlink(CanonPath("relative"), "../relative/path");
|
||||
sink.createSymlink(CanonPath("absolute"), "/absolute/path");
|
||||
sink.createSymlink(CanonPath("medium"), mediumTarget);
|
||||
sink.createSymlink(CanonPath("long"), longTarget);
|
||||
sink.createDirectory(CanonPath("a"));
|
||||
sink.createDirectory(CanonPath("a/b"));
|
||||
sink.createSymlink(CanonPath("a/b/link"), "nested_target");
|
||||
sink.createRegularFile(CanonPath("regular"), [](CreateRegularFileSink &) {});
|
||||
sink.createDirectory(CanonPath("dir"));
|
||||
}
|
||||
|
||||
AutoCloseFD dirFd = openDirectory(tmpDir);
|
||||
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("link")), "target");
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("relative")), "../relative/path");
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("absolute")), "/absolute/path");
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("medium")), mediumTarget);
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("long")), longTarget);
|
||||
EXPECT_EQ(readLinkAt(dirFd.get(), CanonPath("a/b/link")), "nested_target");
|
||||
|
||||
AutoCloseFD subDirFd = openDirectory(tmpDir / "a");
|
||||
EXPECT_EQ(readLinkAt(subDirFd.get(), CanonPath("b/link")), "nested_target");
|
||||
|
||||
EXPECT_THAT(
|
||||
[&] { readLinkAt(dirFd.get(), CanonPath("regular")); },
|
||||
Throws<SysError>(::testing::Field(&SysError::errNo, EINVAL)));
|
||||
|
||||
EXPECT_THAT(
|
||||
[&] { readLinkAt(dirFd.get(), CanonPath("dir")); },
|
||||
Throws<SysError>(::testing::Field(&SysError::errNo, EINVAL)));
|
||||
|
||||
EXPECT_THAT(
|
||||
[&] { readLinkAt(dirFd.get(), CanonPath("nonexistent")); },
|
||||
Throws<SysError>(::testing::Field(&SysError::errNo, ENOENT)));
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -268,6 +268,11 @@ Descriptor openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPath & p
|
||||
*/
|
||||
void fchmodatTryNoFollow(Descriptor dirFd, const CanonPath & path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Read a symlink relative to a directory file descriptor.
|
||||
*/
|
||||
std::string readLinkAt(Descriptor dirFd, const CanonPath & path);
|
||||
|
||||
} // namespace unix
|
||||
#endif
|
||||
|
||||
|
||||
@@ -411,4 +411,20 @@ Descriptor unix::openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPa
|
||||
return openFileEnsureBeneathNoSymlinksIterative(dirFd, path, flags, mode);
|
||||
}
|
||||
|
||||
std::string unix::readLinkAt(Descriptor dirFd, const CanonPath & path)
|
||||
{
|
||||
assert(!path.isRoot());
|
||||
assert(!path.rel().starts_with('/')); /* Just in case the invariant is somehow broken. */
|
||||
std::vector<char> buf;
|
||||
for (ssize_t bufSize = PATH_MAX / 4; true; bufSize += bufSize / 2) {
|
||||
checkInterrupt();
|
||||
buf.resize(bufSize);
|
||||
ssize_t rlSize = ::readlinkat(dirFd, path.rel_c_str(), buf.data(), bufSize);
|
||||
if (rlSize == -1)
|
||||
throw SysError("reading symbolic link '%1%'", path);
|
||||
else if (rlSize < bufSize)
|
||||
return {buf.data(), static_cast<std::size_t>(rlSize)};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
Reference in New Issue
Block a user