libutil: Add openNewFileForWrite helper function, wrap callsites
This is purely a fix to use CreateFileW in mingw builds. Also adds some FIXMEs for suspicious symlink following on truncation that can probably be tightened down without any problems (other than nix-channel), but for now this is a no-op change other than consistently using O_CLOEXEC, which is harmless.
This commit is contained in:
@@ -133,11 +133,17 @@ class SampleStack : public EvalProfiler
|
||||
FrameInfo getPrimOpFrameInfo(const PrimOp & primOp, std::span<Value *> args, PosIdx pos);
|
||||
|
||||
public:
|
||||
SampleStack(EvalState & state, std::filesystem::path profileFile, std::chrono::nanoseconds period)
|
||||
SampleStack(EvalState & state, const std::filesystem::path & profileFile, std::chrono::nanoseconds period)
|
||||
: state(state)
|
||||
, sampleInterval(period)
|
||||
, profileFd([&]() {
|
||||
AutoCloseFD fd = toDescriptor(open(profileFile.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660));
|
||||
AutoCloseFD fd = openNewFileForWrite(
|
||||
profileFile,
|
||||
0660,
|
||||
{
|
||||
.truncateExisting = true,
|
||||
.followSymlinksOnTruncate = true, /* FIXME: Probably shouldn't follow symlinks. */
|
||||
});
|
||||
if (!fd)
|
||||
throw SysError("opening file %s", PathFmt(profileFile));
|
||||
return fd;
|
||||
|
||||
@@ -1035,14 +1035,13 @@ LogFile::LogFile(Store & store, const StorePath & drvPath, const LogFileSettings
|
||||
|
||||
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2), logSettings.compressLog ? ".bz2" : "");
|
||||
|
||||
fd = toDescriptor(open(
|
||||
logFileName.c_str(),
|
||||
O_CREAT | O_WRONLY | O_TRUNC
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
,
|
||||
0666));
|
||||
fd = openNewFileForWrite(
|
||||
logFileName,
|
||||
0666,
|
||||
{
|
||||
.truncateExisting = true,
|
||||
.followSymlinksOnTruncate = true, /* FIXME: Probably shouldn't follow symlinks. */
|
||||
});
|
||||
if (!fd)
|
||||
throw SysError("creating log file '%1%'", logFileName);
|
||||
|
||||
|
||||
@@ -198,6 +198,29 @@ Descriptor openDirectory(const std::filesystem::path & path);
|
||||
*/
|
||||
Descriptor openFileReadonly(const std::filesystem::path & path);
|
||||
|
||||
struct OpenNewFileForWriteParams
|
||||
{
|
||||
/**
|
||||
* Whether to truncate an existing file.
|
||||
*/
|
||||
bool truncateExisting:1 = false;
|
||||
/**
|
||||
* Whether to follow symlinks if @ref truncateExisting is true.
|
||||
*/
|
||||
bool followSymlinksOnTruncate:1 = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a `Descriptor` for write access or create it if it doesn't exist or truncate existing depending on @ref
|
||||
* truncateExisting.
|
||||
*
|
||||
* @param mode POSIX permission bits. Ignored on Windows.
|
||||
* @throws Nothing.
|
||||
*
|
||||
* @todo Reparse points on Windows.
|
||||
*/
|
||||
Descriptor openNewFileForWrite(const std::filesystem::path & path, mode_t mode, OpenNewFileForWriteParams params);
|
||||
|
||||
/**
|
||||
* Read the contents of a file into a string.
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,19 @@ Descriptor openFileReadonly(const std::filesystem::path & path)
|
||||
return open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
Descriptor openNewFileForWrite(const std::filesystem::path & path, mode_t mode, OpenNewFileForWriteParams params)
|
||||
{
|
||||
auto flags = O_WRONLY | O_CREAT | O_CLOEXEC;
|
||||
if (params.truncateExisting) {
|
||||
flags |= O_TRUNC;
|
||||
if (!params.followSymlinksOnTruncate)
|
||||
flags |= O_NOFOLLOW;
|
||||
} else {
|
||||
flags |= O_EXCL; /* O_CREAT | O_EXCL already ensures that symlinks are not followed. */
|
||||
}
|
||||
return open(path.c_str(), flags, mode);
|
||||
}
|
||||
|
||||
std::filesystem::path defaultTempDir()
|
||||
{
|
||||
return getEnvNonEmpty("TMPDIR").value_or("/tmp");
|
||||
|
||||
@@ -41,6 +41,19 @@ Descriptor openFileReadonly(const std::filesystem::path & path)
|
||||
/*hTemplateFile=*/nullptr);
|
||||
}
|
||||
|
||||
Descriptor
|
||||
openNewFileForWrite(const std::filesystem::path & path, [[maybe_unused]] mode_t mode, OpenNewFileForWriteParams params)
|
||||
{
|
||||
return CreateFileW(
|
||||
path.c_str(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
/*lpSecurityAttributes=*/nullptr,
|
||||
params.truncateExisting ? CREATE_ALWAYS : CREATE_NEW, /* TODO: Reparse points. */
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
/*hTemplateFile=*/nullptr);
|
||||
}
|
||||
|
||||
std::filesystem::path defaultTempDir()
|
||||
{
|
||||
wchar_t buf[MAX_PATH + 1];
|
||||
|
||||
@@ -44,7 +44,13 @@ static void readChannels()
|
||||
// Writes the list of channels.
|
||||
static void writeChannels()
|
||||
{
|
||||
auto channelsFD = AutoCloseFD{open(channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)};
|
||||
AutoCloseFD channelsFD = openNewFileForWrite(
|
||||
channelsList,
|
||||
0644,
|
||||
{
|
||||
.truncateExisting = true,
|
||||
.followSymlinksOnTruncate = true, /* Back-compat. */
|
||||
});
|
||||
if (!channelsFD)
|
||||
throw SysError("opening '%1%' for writing", channelsList.string());
|
||||
for (const auto & channel : channels)
|
||||
|
||||
@@ -112,7 +112,7 @@ std::tuple<StorePath, Hash> prefetchFile(
|
||||
if (executable)
|
||||
mode = 0700;
|
||||
|
||||
AutoCloseFD fd = toDescriptor(open(tmpFile.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, mode));
|
||||
AutoCloseFD fd = openNewFileForWrite(tmpFile, mode, {.truncateExisting = false});
|
||||
if (!fd)
|
||||
throw SysError("creating temporary file %s", PathFmt(tmpFile));
|
||||
|
||||
@@ -126,9 +126,9 @@ std::tuple<StorePath, Hash> prefetchFile(
|
||||
/* Optionally unpack the file. */
|
||||
if (unpack) {
|
||||
Activity act(*logger, lvlChatty, actUnknown, fmt("unpacking '%s'", url.to_string()));
|
||||
auto unpacked = (tmpDir.path() / "unpacked").string();
|
||||
auto unpacked = tmpDir.path() / "unpacked";
|
||||
createDirs(unpacked);
|
||||
unpackTarfile(tmpFile.string(), unpacked);
|
||||
unpackTarfile(tmpFile, unpacked);
|
||||
|
||||
auto entries = DirectoryIterator{unpacked};
|
||||
/* If the archive unpacks to a single file/directory, then use
|
||||
|
||||
Reference in New Issue
Block a user