fs/io/FileOutputStream: add constructor with directory fd
This commit is contained in:
parent
dee8872395
commit
cf23fd8774
@ -31,8 +31,27 @@
|
|||||||
#include "system/Error.hxx"
|
#include "system/Error.hxx"
|
||||||
#include "util/StringFormat.hxx"
|
#include "util/StringFormat.hxx"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
FileOutputStream::FileOutputStream(FileDescriptor _directory_fd,
|
||||||
|
Path _path, Mode _mode)
|
||||||
|
:path(_path),
|
||||||
|
directory_fd(_directory_fd),
|
||||||
|
mode(_mode)
|
||||||
|
{
|
||||||
|
Open();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
|
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
|
||||||
:path(_path), mode(_mode)
|
:path(_path),
|
||||||
|
#ifdef __linux__
|
||||||
|
directory_fd(AT_FDCWD),
|
||||||
|
#endif
|
||||||
|
mode(_mode)
|
||||||
{
|
{
|
||||||
Open();
|
Open();
|
||||||
}
|
}
|
||||||
@ -155,8 +174,12 @@ FileOutputStream::Cancel() noexcept
|
|||||||
* Open a file using Linux's O_TMPFILE for writing the given file.
|
* Open a file using Linux's O_TMPFILE for writing the given file.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
OpenTempFile(FileDescriptor &fd, Path path)
|
OpenTempFile(FileDescriptor directory_fd,
|
||||||
|
FileDescriptor &fd, Path path)
|
||||||
{
|
{
|
||||||
|
if (directory_fd != FileDescriptor(AT_FDCWD))
|
||||||
|
return fd.Open(directory_fd, ".", O_TMPFILE|O_WRONLY, 0666);
|
||||||
|
|
||||||
const auto directory = path.GetDirectoryName();
|
const auto directory = path.GetDirectoryName();
|
||||||
if (directory.IsNull())
|
if (directory.IsNull())
|
||||||
return false;
|
return false;
|
||||||
@ -171,11 +194,15 @@ FileOutputStream::OpenCreate(bool visible)
|
|||||||
{
|
{
|
||||||
#ifdef HAVE_O_TMPFILE
|
#ifdef HAVE_O_TMPFILE
|
||||||
/* try Linux's O_TMPFILE first */
|
/* try Linux's O_TMPFILE first */
|
||||||
is_tmpfile = !visible && OpenTempFile(fd, GetPath());
|
is_tmpfile = !visible && OpenTempFile(directory_fd, fd, GetPath());
|
||||||
if (!is_tmpfile) {
|
if (!is_tmpfile) {
|
||||||
#endif
|
#endif
|
||||||
/* fall back to plain POSIX */
|
/* fall back to plain POSIX */
|
||||||
if (!fd.Open(GetPath().c_str(),
|
if (!fd.Open(
|
||||||
|
#ifdef __linux__
|
||||||
|
directory_fd,
|
||||||
|
#endif
|
||||||
|
GetPath().c_str(),
|
||||||
O_WRONLY|O_CREAT|O_TRUNC,
|
O_WRONLY|O_CREAT|O_TRUNC,
|
||||||
0666))
|
0666))
|
||||||
throw FormatErrno("Failed to create %s",
|
throw FormatErrno("Failed to create %s",
|
||||||
@ -194,7 +221,11 @@ FileOutputStream::OpenAppend(bool create)
|
|||||||
if (create)
|
if (create)
|
||||||
flags |= O_CREAT;
|
flags |= O_CREAT;
|
||||||
|
|
||||||
if (!fd.Open(path.c_str(), flags))
|
if (!fd.Open(
|
||||||
|
#ifdef __linux__
|
||||||
|
directory_fd,
|
||||||
|
#endif
|
||||||
|
path.c_str(), flags))
|
||||||
throw FormatErrno("Failed to append to %s",
|
throw FormatErrno("Failed to append to %s",
|
||||||
path.c_str());
|
path.c_str());
|
||||||
}
|
}
|
||||||
@ -225,12 +256,12 @@ FileOutputStream::Commit()
|
|||||||
|
|
||||||
#ifdef HAVE_O_TMPFILE
|
#ifdef HAVE_O_TMPFILE
|
||||||
if (is_tmpfile) {
|
if (is_tmpfile) {
|
||||||
unlink(GetPath().c_str());
|
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
|
||||||
|
|
||||||
/* hard-link the temporary file to the final path */
|
/* hard-link the temporary file to the final path */
|
||||||
if (linkat(AT_FDCWD,
|
if (linkat(AT_FDCWD,
|
||||||
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
|
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
|
||||||
AT_FDCWD, path.c_str(),
|
directory_fd.Get(), path.c_str(),
|
||||||
AT_SYMLINK_FOLLOW) < 0)
|
AT_SYMLINK_FOLLOW) < 0)
|
||||||
throw FormatErrno("Failed to commit %s",
|
throw FormatErrno("Failed to commit %s",
|
||||||
path.c_str());
|
path.c_str());
|
||||||
@ -259,7 +290,11 @@ FileOutputStream::Cancel() noexcept
|
|||||||
#ifdef HAVE_O_TMPFILE
|
#ifdef HAVE_O_TMPFILE
|
||||||
if (!is_tmpfile)
|
if (!is_tmpfile)
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
unlinkat(directory_fd.Get(), GetPath().c_str(), 0);
|
||||||
|
#else
|
||||||
unlink(GetPath().c_str());
|
unlink(GetPath().c_str());
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Mode::CREATE_VISIBLE:
|
case Mode::CREATE_VISIBLE:
|
||||||
|
@ -59,6 +59,10 @@ class Path;
|
|||||||
class FileOutputStream final : public OutputStream {
|
class FileOutputStream final : public OutputStream {
|
||||||
const AllocatedPath path;
|
const AllocatedPath path;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
const FileDescriptor directory_fd;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||||
#else
|
#else
|
||||||
@ -108,6 +112,11 @@ private:
|
|||||||
public:
|
public:
|
||||||
explicit FileOutputStream(Path _path, Mode _mode=Mode::CREATE);
|
explicit FileOutputStream(Path _path, Mode _mode=Mode::CREATE);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
FileOutputStream(FileDescriptor _directory_fd, Path _path,
|
||||||
|
Mode _mode=Mode::CREATE);
|
||||||
|
#endif
|
||||||
|
|
||||||
~FileOutputStream() noexcept {
|
~FileOutputStream() noexcept {
|
||||||
if (IsDefined())
|
if (IsDefined())
|
||||||
Cancel();
|
Cancel();
|
||||||
|
Loading…
Reference in New Issue
Block a user