Joedb 10.5.0
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Posix_File.cpp
Go to the documentation of this file.
4
5#include <sys/file.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <sys/time.h>
9#include <errno.h>
10#include <string.h>
11#include <filesystem>
12
13#ifdef JOEDB_HAS_BROKEN_POSIX_LOCKING
14#define JOEDB_SETLK F_SETLK
15#define JOEDB_SETLKW F_SETLKW
16//#warning is C++23
17//#warning Joedb is using old-style POSIX locks. File locking is broken if you open the same file more than once within the same process. New OFD locks work better and should be enabled if your system supports them.
18#else
19#define JOEDB_SETLK F_OFD_SETLK
20#define JOEDB_SETLKW F_OFD_SETLKW
21#endif
22
23namespace joedb
24{
25#ifndef _FILE_OFFSET_BITS
26 static_assert
27 (
28 sizeof(off_t) == sizeof(int64_t),
29 "Define the _FILE_OFFSET_BITS macro to 32 or 64 to silence this error. 64 is recommended if possible. Joedb does not check for file-size overflow."
30 );
31#endif
32
33 /////////////////////////////////////////////////////////////////////////////
34 void Posix_File::throw_last_error(const char *action) const
35 /////////////////////////////////////////////////////////////////////////////
36 {
37 throw Exception
38 (
39 std::string(action) + ' ' + file_name + ": " + ::strerror(errno)
40 );
41 }
42
43 /////////////////////////////////////////////////////////////////////////////
44 // NOLINTNEXTLINE(readability-make-member-function-const)
45 int Posix_File::lock(int command, short type, int64_t start, int64_t size)
46 /////////////////////////////////////////////////////////////////////////////
47 {
48 struct flock lock;
49 lock.l_type = type;
50 lock.l_whence = SEEK_SET;
51 lock.l_start = off_t(start);
52 lock.l_len = off_t(size);
53 lock.l_pid = 0;
54
55 return ::fcntl(posix_fd.get_fd(), command, &lock);
56 }
57
58 /////////////////////////////////////////////////////////////////////////////
59 bool Posix_File::try_exclusive_lock(int64_t start, int64_t size)
60 /////////////////////////////////////////////////////////////////////////////
61 {
62 return lock(JOEDB_SETLK, F_WRLCK, start, size) == 0;
63 }
64
65 /////////////////////////////////////////////////////////////////////////////
66 void Posix_File::shared_lock(int64_t start, int64_t size)
67 /////////////////////////////////////////////////////////////////////////////
68 {
69 if (lock(JOEDB_SETLKW, F_RDLCK, start, size) < 0)
70 throw_last_error("read-locking");
71 }
72
73 /////////////////////////////////////////////////////////////////////////////
74 void Posix_File::exclusive_lock(int64_t start, int64_t size)
75 /////////////////////////////////////////////////////////////////////////////
76 {
77 if (lock(JOEDB_SETLKW, F_WRLCK, start, size) < 0)
78 throw_last_error("write-locking");
79 }
80
81 /////////////////////////////////////////////////////////////////////////////
82 void Posix_File::unlock(int64_t start, int64_t size) noexcept
83 /////////////////////////////////////////////////////////////////////////////
84 {
85 lock(JOEDB_SETLK, F_UNLCK, start, size);
86 }
87
88 /////////////////////////////////////////////////////////////////////////////
89 size_t Posix_File::pread(char *buffer, size_t size, int64_t offset) const
90 /////////////////////////////////////////////////////////////////////////////
91 {
92 const ssize_t result = ::pread(posix_fd.get_fd(), buffer, size, offset);
93
94 if (result < 0)
95 throw_last_error("reading");
96
97 return size_t(result);
98 }
99
100 /////////////////////////////////////////////////////////////////////////////
101 void Posix_File::pwrite(const char *buffer, size_t size, int64_t offset)
102 /////////////////////////////////////////////////////////////////////////////
103 {
104 size_t written = 0;
105
106 while (written < size)
107 {
108 const ssize_t result = ::pwrite
109 (
110 posix_fd.get_fd(),
111 buffer + written,
112 size - written,
113 offset + written
114 );
115
116 if (result < 0)
117 throw_last_error("writing");
118 else
119 written += size_t(result);
120 }
121 }
122
123 /////////////////////////////////////////////////////////////////////////////
125 /////////////////////////////////////////////////////////////////////////////
126 {
127#ifdef __APPLE__
128 return ::fcntl(fd, F_FULLFSYNC);
129#else
130 return ::fsync(fd);
131#endif
132 }
133
134 /////////////////////////////////////////////////////////////////////////////
136 /////////////////////////////////////////////////////////////////////////////
137 {
138 if (posix_fd.fullsync() == -1)
139 throw_last_error("syncing");
140
141 if (!created_durably && !file_name.empty())
142 {
143 auto directory = std::filesystem::path(file_name).parent_path();
144 if (directory.empty())
145 directory = ".";
146
147 Posix_FD dir_fd(::open(directory.c_str(), O_RDONLY | O_DIRECTORY));
148
149 if (dir_fd.get_fd() < 0)
150 {
151 // ignore errors (OK if the directory was moved, ...)
152 Global_Logger::warning("could not open parent directory for fsync: " + file_name);
153 }
154 else
155 {
156 if (dir_fd.fullsync() < 0)
157 {
158 // also ignore errors (do like sqlite)
159 Global_Logger::warning("fsync failed on parent directory: " + file_name);
160 }
161 }
162
163 created_durably = true;
164 }
165 }
166
167#if _POSIX_SYNCHRONIZED_IO > 0
168 /////////////////////////////////////////////////////////////////////////////
170 /////////////////////////////////////////////////////////////////////////////
171 {
172 if (::fdatasync(posix_fd.get_fd()) == -1)
173 {
174 throw_last_error("syncing");
175 }
176 }
177#endif
178
179 /////////////////////////////////////////////////////////////////////////////
180 Posix_FD::Posix_FD(const char *file_name, const Open_Mode mode)
181 /////////////////////////////////////////////////////////////////////////////
182 {
183 if (mode == Open_Mode::read_existing)
184 fd = ::open(file_name, O_RDONLY);
185 else if (mode == Open_Mode::write_existing)
186 fd = ::open(file_name, O_RDWR);
187 else if (mode == Open_Mode::create_new)
188 fd = ::open(file_name, O_RDWR | O_CREAT | O_EXCL, 00644);
189 else if
190 (
192 mode == Open_Mode::shared_write ||
194 )
195 {
196 fd = ::open(file_name, O_RDWR | O_CREAT, 00644);
197 }
198 else
199 fd = -1;
200 }
201
202 /////////////////////////////////////////////////////////////////////////////
203 Posix_File::Posix_File(const char *file_name, const Open_Mode mode):
204 /////////////////////////////////////////////////////////////////////////////
205 Abstract_File(mode),
206 posix_fd(file_name, mode),
207 file_name(file_name)
208 {
209 if (posix_fd.get_fd() < 0)
210 throw_last_error("opening");
211
212 if (mode == Open_Mode::write_lock)
214 else if (
216 mode == Open_Mode::create_new ||
218 {
219 if (!try_exclusive_lock(last_position, 1))
220 throw_last_error("locking");
221 }
222 }
223
224 /////////////////////////////////////////////////////////////////////////////
225 int64_t Posix_File::get_size() const
226 /////////////////////////////////////////////////////////////////////////////
227 {
228 struct stat s;
229
230 if (::fstat(posix_fd.get_fd(), &s) < 0)
231 throw_last_error("getting size of");
232
233 return int64_t(s.st_size);
234 }
235
236 /////////////////////////////////////////////////////////////////////////////
238 /////////////////////////////////////////////////////////////////////////////
239 {
240 ::close(fd);
241 }
242}
#define JOEDB_SETLKW
#define JOEDB_SETLK
static constexpr int64_t last_position
Abstract_File(Open_Mode mode)
virtual void datasync()
Write data durably (no file-size change).
static void warning(beman::cstring_view message) noexcept
Posix_FD(int fd)
Definition Posix_File.h:25
int get_fd() const
Definition Posix_File.h:23
size_t pread(char *buffer, size_t size, int64_t offset) const override
Read a range of bytes.
void unlock(int64_t start, int64_t size) noexcept override
Remove a lock. The range should match the range of a corresponding lock.
void pwrite(const char *buffer, size_t size, int64_t offset) override
Write a range of bytes. Extend file size if necessary.
void exclusive_lock(int64_t start, int64_t size) override
Lock a range of bytes for writing (prevents both writes and reads).
void shared_lock(int64_t start, int64_t size) override
Lock a range of bytes for reading (prevents writes, not reads).
void sync() override
Write data durably (including file-size change).
Posix_File(int fd, Open_Mode mode)
Definition Posix_File.h:54
int64_t get_size() const override
Get the size of the file, or -1 if it is unknown.
Open_Mode
Definition Open_Mode.h:8
@ create_new
fails if already exists, locks the file for writing
Definition Open_Mode.h:11
@ write_existing
fails if does not exist or locked, locks the file for writing
Definition Open_Mode.h:10
@ shared_write
like write_existing_or_create_new, but does not lock the file, and does not fail if locked
Definition Open_Mode.h:13
@ write_existing_or_create_new
either write_existing or create_new depending on whether the file exists
Definition Open_Mode.h:12
@ write_lock
like write_existing_or_create_new, but waits instead of failing if already locked
Definition Open_Mode.h:14
@ read_existing
fails if does not exist
Definition Open_Mode.h:9