Skip to content

Commit

Permalink
Create file with O_EXCL flag set.
Browse files Browse the repository at this point in the history
When O_EXCL is used WITH O_CREAT open will fail if file exists. If flag
`FILE_FLAGS_NULL_IF_EXISTS` is used opening will return nullptr instead
of throwing error.
  • Loading branch information
mkaruza committed Jul 23, 2024
1 parent 40e2fcd commit 1d6e11c
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
8 changes: 8 additions & 0 deletions src/common/file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ constexpr FileOpenFlags FileFlags::FILE_FLAGS_WRITE;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_DIRECT_IO;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_FILE_CREATE;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_FILE_CREATE_NEW;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_EXCLUSIVE_CREATE;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_NULL_IF_EXISTS;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_APPEND;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_PRIVATE;
constexpr FileOpenFlags FileFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS;
Expand All @@ -66,6 +68,8 @@ void FileOpenFlags::Verify() {
(flags & FileOpenFlags::FILE_FLAGS_FILE_CREATE) || (flags & FileOpenFlags::FILE_FLAGS_FILE_CREATE_NEW);
bool is_private = (flags & FileOpenFlags::FILE_FLAGS_PRIVATE);
bool null_if_not_exists = flags & FileOpenFlags::FILE_FLAGS_NULL_IF_NOT_EXISTS;
bool exclusive_create = flags & FileOpenFlags::FILE_FLAGS_EXCLUSIVE_CREATE;
bool null_if_exists = flags & FileOpenFlags::FILE_FLAGS_NULL_IF_EXISTS;

// require either READ or WRITE (or both)
D_ASSERT(is_read || is_write);
Expand All @@ -80,6 +84,10 @@ void FileOpenFlags::Verify() {
D_ASSERT(!is_private || is_create);
// FILE_FLAGS_NULL_IF_NOT_EXISTS cannot be combined with CREATE/CREATE_NEW
D_ASSERT(!(null_if_not_exists && is_create));
// FILE_FLAGS_EXCLUSIVE_CREATE only can be combined with CREATE/CREATE_NEW
D_ASSERT(!exclusive_create || is_create);
// FILE_FLAGS_NULL_IF_EXISTS only can be set with EXCLUSIVE_CREATE
D_ASSERT(!null_if_exists || exclusive_create);
#endif
}

Expand Down
7 changes: 7 additions & 0 deletions src/common/local_file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,20 @@ unique_ptr<FileHandle> LocalFileSystem::OpenFile(const string &path_p, FileOpenF
filesec = 0666;
}

if (flags.ExclusiveCreate()) {
open_flags |= O_EXCL;
}

// Open the file
int fd = open(path.c_str(), open_flags, filesec);

if (fd == -1) {
if (flags.ReturnNullIfNotExists() && errno == ENOENT) {
return nullptr;
}
if (flags.ReturnNullIfExists() && errno == EEXIST) {
return nullptr;
}
throw IOException("Cannot open file \"%s\": %s", {{"errno", std::to_string(errno)}}, path, strerror(errno));
}
// #if defined(__DARWIN__) || defined(__APPLE__)
Expand Down
22 changes: 18 additions & 4 deletions src/include/duckdb/common/file_open_flags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ class FileOpenFlags {
static constexpr idx_t FILE_FLAGS_DIRECT_IO = idx_t(1 << 2);
static constexpr idx_t FILE_FLAGS_FILE_CREATE = idx_t(1 << 3);
static constexpr idx_t FILE_FLAGS_FILE_CREATE_NEW = idx_t(1 << 4);
static constexpr idx_t FILE_FLAGS_APPEND = idx_t(1 << 5);
static constexpr idx_t FILE_FLAGS_PRIVATE = idx_t(1 << 6);
static constexpr idx_t FILE_FLAGS_NULL_IF_NOT_EXISTS = idx_t(1 << 7);
static constexpr idx_t FILE_FLAGS_PARALLEL_ACCESS = idx_t(1 << 8);
static constexpr idx_t FILE_FLAGS_EXCLUSIVE_CREATE = idx_t(1 << 5);
static constexpr idx_t FILE_FLAGS_NULL_IF_EXISTS = idx_t(1 << 6);
static constexpr idx_t FILE_FLAGS_APPEND = idx_t(1 << 7);
static constexpr idx_t FILE_FLAGS_PRIVATE = idx_t(1 << 8);
static constexpr idx_t FILE_FLAGS_NULL_IF_NOT_EXISTS = idx_t(1 << 9);
static constexpr idx_t FILE_FLAGS_PARALLEL_ACCESS = idx_t(1 << 10);

public:
FileOpenFlags() = default;
Expand Down Expand Up @@ -99,6 +101,12 @@ class FileOpenFlags {
inline bool RequireParallelAccess() const {
return flags & FILE_FLAGS_PARALLEL_ACCESS;
}
inline bool ExclusiveCreate() const {
return flags & FILE_FLAGS_EXCLUSIVE_CREATE;
}
inline bool ReturnNullIfExists() const {
return flags & FILE_FLAGS_NULL_IF_EXISTS;
}

private:
idx_t flags = 0;
Expand All @@ -119,6 +127,12 @@ class FileFlags {
//! Always create a new file. If a file exists, the file is truncated. Cannot be used together with CREATE.
static constexpr FileOpenFlags FILE_FLAGS_FILE_CREATE_NEW =
FileOpenFlags(FileOpenFlags::FILE_FLAGS_FILE_CREATE_NEW);
//! Ensure that this call creates the file, throw is file exists
static constexpr FileOpenFlags FILE_FLAGS_EXCLUSIVE_CREATE =
FileOpenFlags(FileOpenFlags::FILE_FLAGS_EXCLUSIVE_CREATE);
//! Return NULL if the file exist instead of throwing an error
static constexpr FileOpenFlags FILE_FLAGS_NULL_IF_EXISTS =
FileOpenFlags(FileOpenFlags::FILE_FLAGS_NULL_IF_EXISTS);
//! Open file in append mode
static constexpr FileOpenFlags FILE_FLAGS_APPEND = FileOpenFlags(FileOpenFlags::FILE_FLAGS_APPEND);
//! Open file with restrictive permissions (600 on linux/mac) can only be used when creating, throws if file exists
Expand Down

0 comments on commit 1d6e11c

Please sign in to comment.