Joedb 10.2.1
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Posix_File.cpp
Go to the documentation of this file.
3
4#include <sys/file.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <sys/time.h>
8#include <errno.h>
9#include <string.h>
10
11#ifdef JOEDB_HAS_BROKEN_POSIX_LOCKING
12#define JOEDB_SETLK F_SETLK
13#define JOEDB_SETLKW F_SETLKW
14//#warning is C++23
15//#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.
16#else
17#define JOEDB_SETLK F_OFD_SETLK
18#define JOEDB_SETLKW F_OFD_SETLKW
19#endif
20
21namespace joedb
22{
23#ifndef _FILE_OFFSET_BITS
24 static_assert
25 (
26 sizeof(off_t) == sizeof(int64_t),
27 "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."
28 );
29#endif
30
31 /////////////////////////////////////////////////////////////////////////////
33 /////////////////////////////////////////////////////////////////////////////
34 (
35 const char *action,
36 const char *file_name
37 )
38 {
39 throw Exception
40 (
41 std::string(action) + ' ' + file_name + ": " + strerror(errno)
42 );
43 }
44
45 /////////////////////////////////////////////////////////////////////////////
46 // NOLINTNEXTLINE(readability-make-member-function-const)
47 int Posix_FD::lock(int command, short type, int64_t start, int64_t size)
48 /////////////////////////////////////////////////////////////////////////////
49 {
50 struct flock lock;
51 lock.l_type = type;
52 lock.l_whence = SEEK_SET;
53 lock.l_start = off_t(start);
54 lock.l_len = off_t(size);
55 lock.l_pid = 0;
56
57 return fcntl(fd, command, &lock);
58 }
59
60 /////////////////////////////////////////////////////////////////////////////
61 bool Posix_FD::try_exclusive_lock(int64_t start, int64_t size)
62 /////////////////////////////////////////////////////////////////////////////
63 {
64 return lock(JOEDB_SETLK, F_WRLCK, start, size) == 0;
65 }
66
67 /////////////////////////////////////////////////////////////////////////////
68 void Posix_FD::shared_lock(int64_t start, int64_t size)
69 /////////////////////////////////////////////////////////////////////////////
70 {
71 if (lock(JOEDB_SETLKW, F_RDLCK, start, size) < 0)
72 throw_last_error("read-locking", "file");
73 }
74
75 /////////////////////////////////////////////////////////////////////////////
76 void Posix_FD::exclusive_lock(int64_t start, int64_t size)
77 /////////////////////////////////////////////////////////////////////////////
78 {
79 if (lock(JOEDB_SETLKW, F_WRLCK, start, size) < 0)
80 throw_last_error("write-locking", "file");
81 }
82
83 /////////////////////////////////////////////////////////////////////////////
84 void Posix_FD::unlock(int64_t start, int64_t size) noexcept
85 /////////////////////////////////////////////////////////////////////////////
86 {
87 lock(JOEDB_SETLK, F_UNLCK, start, size);
88 }
89
90 /////////////////////////////////////////////////////////////////////////////
91 size_t Posix_FD::pread(char *buffer, size_t size, int64_t offset) const
92 /////////////////////////////////////////////////////////////////////////////
93 {
94 const ssize_t result = ::pread(fd, buffer, size, offset);
95
96 if (result < 0)
97 throw_last_error("reading", "file");
98
99 return size_t(result);
100 }
101
102 /////////////////////////////////////////////////////////////////////////////
103 void Posix_FD::pwrite(const char *buffer, size_t size, int64_t offset)
104 /////////////////////////////////////////////////////////////////////////////
105 {
106 size_t written = 0;
107
108 while (written < size)
109 {
110 const ssize_t result = ::pwrite
111 (
112 fd,
113 buffer + written,
114 size - written,
115 offset + written
116 );
117
118 if (result < 0)
119 throw_last_error("writing", "file");
120 else
121 written += size_t(result);
122 }
123 }
124
125 /////////////////////////////////////////////////////////////////////////////
127 /////////////////////////////////////////////////////////////////////////////
128 {
129#ifdef __APPLE__
130 if (fcntl(fd, F_FULLFSYNC) == -1)
131#else
132 if (fsync(fd) == -1)
133#endif
134 {
135 throw_last_error("syncing", "file");
136 }
137 }
138
139#if _POSIX_SYNCHRONIZED_IO > 0
140 /////////////////////////////////////////////////////////////////////////////
141 void Posix_FD::datasync()
142 /////////////////////////////////////////////////////////////////////////////
143 {
144 if (fdatasync(fd) == -1)
145 {
146 throw_last_error("syncing", "file");
147 }
148 }
149#endif
150
151 /////////////////////////////////////////////////////////////////////////////
152 Posix_FD::Posix_FD(const char *file_name, const Open_Mode mode):
153 /////////////////////////////////////////////////////////////////////////////
154 Abstract_File(mode)
155 {
156 if (mode == Open_Mode::read_existing)
157 fd = open(file_name, O_RDONLY);
158 else if (mode == Open_Mode::write_existing)
159 fd = open(file_name, O_RDWR);
160 else if (mode == Open_Mode::create_new)
161 fd = open(file_name, O_RDWR | O_CREAT | O_EXCL, 00644);
162 else if
163 (
165 mode == Open_Mode::shared_write ||
167 )
168 {
169 fd = open(file_name, O_RDWR | O_CREAT | O_EXCL, 00644);
170 if (fd < 0)
171 fd = open(file_name, O_RDWR);
172 }
173 else if (mode == Open_Mode::truncate)
174 fd = open(file_name, O_RDWR | O_CREAT | O_TRUNC, 00644);
175
176 if (fd < 0)
177 throw_last_error("opening", file_name);
178 }
179
180 /////////////////////////////////////////////////////////////////////////////
181 Posix_File::Posix_File(const char *file_name, const Open_Mode mode):
182 /////////////////////////////////////////////////////////////////////////////
183 Posix_FD(file_name, mode)
184 {
186 {
187 if (mode == Open_Mode::write_lock)
189 else if (!try_exclusive_lock(last_position, 1))
190 throw_last_error("locking", file_name);
191 }
192 }
193
194 /////////////////////////////////////////////////////////////////////////////
195 int64_t Posix_FD::get_size() const
196 /////////////////////////////////////////////////////////////////////////////
197 {
198 struct stat s;
199
200 if (fstat(fd, &s) < 0)
201 throw_last_error("getting size of", "file");
202
203 return int64_t(s.st_size);
204 }
205
206 /////////////////////////////////////////////////////////////////////////////
208 /////////////////////////////////////////////////////////////////////////////
209 {
210 close(fd);
211 }
212}
#define JOEDB_SETLKW
#define JOEDB_SETLK
static constexpr int64_t last_position
virtual void datasync()
Write data durably (no file-size change)
~Posix_FD() override
size_t pread(char *buffer, size_t size, int64_t offset) const override
Read a range of bytes.
static void throw_last_error(const char *action, const char *file_name)
void pwrite(const char *buffer, size_t size, int64_t offset) override
Write a range of bytes. Extend file size if necessary.
bool try_exclusive_lock(int64_t start, int64_t size)
int64_t get_size() const override
Get the size of the file, or -1 if it is unknown.
void sync() override
Write data durably (including file-size change)
void unlock(int64_t start, int64_t size) noexcept override
Remove a lock. The range should match the range of a corresponding lock.
void shared_lock(int64_t start, int64_t size) override
Lock a range of bytes for reading (prevents writes, not reads)
void exclusive_lock(int64_t start, int64_t size) override
Lock a range of bytes for writing (prevents both writes and reads)
Posix_FD(int fd, Open_Mode mode)
Definition Posix_File.h:30
Posix_File(int fd, Open_Mode mode)
Definition Posix_File.h:68
Open_Mode
Definition Open_Mode.h:8
@ truncate
create new file, or truncate existing file, and locks the file
@ create_new
fails if already exists, locks the file for writing
@ write_existing
fails if does not exist or locked, locks the file for writing
@ shared_write
like write_existing_or_create_new, but does not lock the file, and does not fail if locked
@ write_existing_or_create_new
either write_existing or create_new depending on whether the file exists. Racy in Posix,...
@ write_lock
like write_existing_or_create_new, but waits instead of failing if already locked
@ read_existing
fails if does not exist