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
52 changes: 52 additions & 0 deletions src/create_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,73 @@
#include <cerrno>
#include <cstdio>
#include <filesystem>
#include <string>
#include <system_error>
Comment thread
bugdea1er marked this conversation as resolved.

#if __has_include(<unistd.h>)
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

namespace tmp::detail {

namespace fs = std::filesystem;

namespace {

#if __has_include(<unistd.h>)
/// Creates and opens a binary temporary file as if by POSIX `mkstemp`
/// @returns A file descriptor associated with the temporary file
/// @throws fs::filesystem_error if cannot create a temporary file
int create_file_descriptor() {
fs::path temp_dir = fs::temp_directory_path();
int descriptor = int{};

#ifdef __linux__
descriptor = open(temp_dir.c_str(), O_RDWR | O_TMPFILE, S_IRUSR | S_IWUSR);
if (descriptor >= 0) {
return descriptor;
}
#endif

std::string path = temp_dir / "XXXXXX";

descriptor = mkstemp(path.data());
if (descriptor == -1) {
std::error_code ec = std::error_code(errno, std::system_category());
throw fs::filesystem_error("Cannot create a temporary file", ec);
}

unlink(path.data());

return descriptor;
}
#endif
} // namespace

/// Creates and opens a binary temporary file as if by POSIX `tmpfile`
/// @returns A pointer to the file stream associated with the temporary file
/// @throws fs::filesystem_error if cannot create a temporary file
std::FILE* create_file() {
#if __has_include(<unistd.h>)
int descriptor = create_file_descriptor();

// TODO: let `filebuf` use the file descriptor without `FILE` wrapping
std::FILE* file = fdopen(descriptor, "wb+");
if (file == nullptr) {
std::error_code ec = std::error_code(errno, std::system_category());
close(descriptor);

throw fs::filesystem_error("Cannot create a temporary file", ec);
}
#else
std::FILE* file = std::tmpfile();
if (file == nullptr) {
std::error_code ec = std::error_code(errno, std::generic_category());
throw fs::filesystem_error("Cannot create a temporary file", ec);
}
#endif

return file;
}
Expand Down
1 change: 0 additions & 1 deletion tests/directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include <filesystem>
#include <fstream>
#include <ios>
#include <stdexcept>
#include <string>
#include <string_view>
Expand Down
32 changes: 31 additions & 1 deletion tests/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <istream>
#include <iterator>
#include <ostream>
#include <system_error>
#include <type_traits>
#include <utility>

Expand All @@ -20,11 +19,14 @@
#include <Windows.h>
#else
#include <fcntl.h>
#include <sys/stat.h>
#endif

namespace tmp {
namespace {

namespace fs = std::filesystem;

/// Test fixture for `basic_file` tests
template<class charT> class file : public testing::Test {
public:
Expand Down Expand Up @@ -85,6 +87,34 @@ TYPED_TEST(file, create) {
basic_file<TypeParam> tmpfile = basic_file<TypeParam>();
EXPECT_TRUE(TestFixture::is_open(tmpfile.rdbuf()));
EXPECT_TRUE(TestFixture::is_open(tmpfile.native_handle()));

fs::path temp_dir = fs::temp_directory_path();
auto handle = tmpfile.native_handle();

#ifdef _WIN32
BY_HANDLE_FILE_INFORMATION file_info;
ASSERT_TRUE(GetFileInformationByHandle(handle, &file_info));

HANDLE dir_handle =
CreateFileW(temp_dir.c_str(), FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
ASSERT_NE(dir_handle, INVALID_HANDLE_VALUE);

BY_HANDLE_FILE_INFORMATION dir_info;
ASSERT_TRUE(GetFileInformationByHandle(dir_handle, &dir_info));
CloseHandle(dir_handle);

EXPECT_EQ(file_info.dwVolumeSerialNumber, dir_info.dwVolumeSerialNumber);
#else
struct stat file_stat {};
ASSERT_EQ(fstat(handle, &file_stat), 0);

struct stat dir_stat {};
ASSERT_EQ(stat(temp_dir.c_str(), &dir_stat), 0);

EXPECT_EQ(file_stat.st_dev, dir_stat.st_dev);
#endif
}

/// Tests multiple file creation
Expand Down
Loading