aboutsummaryrefslogtreecommitdiffstats
path: root/src/fs/io/FileOutputStream.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/io/FileOutputStream.cxx')
-rw-r--r--src/fs/io/FileOutputStream.cxx81
1 files changed, 72 insertions, 9 deletions
diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx
index dc4456d1f..11b5b2351 100644
--- a/src/fs/io/FileOutputStream.cxx
+++ b/src/fs/io/FileOutputStream.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * Copyright (C) 2003-2015 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,9 +20,20 @@
#include "config.h"
#include "FileOutputStream.hxx"
#include "fs/FileSystem.hxx"
-#include "system/fd_util.h"
#include "util/Error.hxx"
+FileOutputStream *
+FileOutputStream::Create(Path path, Error &error)
+{
+ FileOutputStream *f = new FileOutputStream(path, error);
+ if (!f->IsDefined()) {
+ delete f;
+ f = nullptr;
+ }
+
+ return f;
+}
+
#ifdef WIN32
FileOutputStream::FileOutputStream(Path _path, Error &error)
@@ -80,14 +91,47 @@ FileOutputStream::Cancel()
#include <unistd.h>
#include <errno.h>
+#ifdef HAVE_LINKAT
+#ifndef O_TMPFILE
+/* supported since Linux 3.11 */
+#define __O_TMPFILE 020000000
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#include <stdio.h>
+#endif
+
+/**
+ * Open a file using Linux's O_TMPFILE for writing the given file.
+ */
+static int
+OpenTempFile(Path path)
+{
+ const auto directory = path.GetDirectoryName();
+ if (directory.IsNull())
+ return -1;
+
+ return OpenFile(directory, O_TMPFILE|O_WRONLY, 0666);
+}
+
+#endif /* HAVE_LINKAT */
+
FileOutputStream::FileOutputStream(Path _path, Error &error)
- :path(_path),
- fd(open_cloexec(path.c_str(),
- O_WRONLY|O_CREAT|O_TRUNC,
- 0666))
+ :path(_path)
{
- if (fd < 0)
- error.FormatErrno("Failed to create %s", path.c_str());
+#ifdef HAVE_LINKAT
+ /* try Linux's O_TMPFILE first */
+ fd = OpenTempFile(path);
+ is_tmpfile = fd >= 0;
+ if (!is_tmpfile) {
+#endif
+ /* fall back to plain POSIX */
+ fd = OpenFile(path,
+ O_WRONLY|O_CREAT|O_TRUNC,
+ 0666);
+ if (fd < 0)
+ error.FormatErrno("Failed to create %s", path.c_str());
+#ifdef HAVE_LINKAT
+ }
+#endif
}
bool
@@ -113,6 +157,22 @@ FileOutputStream::Commit(Error &error)
{
assert(IsDefined());
+#if HAVE_LINKAT
+ if (is_tmpfile) {
+ RemoveFile(path);
+
+ /* hard-link the temporary file to the final path */
+ char fd_path[64];
+ snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
+ if (linkat(AT_FDCWD, fd_path, AT_FDCWD, path.c_str(),
+ AT_SYMLINK_FOLLOW) < 0) {
+ error.FormatErrno("Failed to commit %s", path.c_str());
+ close(fd);
+ return false;
+ }
+ }
+#endif
+
bool success = close(fd) == 0;
fd = -1;
if (!success)
@@ -129,7 +189,10 @@ FileOutputStream::Cancel()
close(fd);
fd = -1;
- RemoveFile(path);
+#ifdef HAVE_LINKAT
+ if (!is_tmpfile)
+#endif
+ RemoveFile(path);
}
#endif