v29.4-roots.2 · Commit 2: [PATCH 02/21] runtime: preserve shared operator controls
src/util/fs_helpers.cpp
Before: src/util/fs_helpers.cpp · After: src/util/fs_helpers.cpp
File metadata
index 4d06afe144202245409a978d233c2959772a5bd5..1ae5d88fa874ce55dda1373683601dd5357bfe9c 100644
Download original patch · Release notes
Diff rows 1–312 of 312. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.
src/util/fs_helpers.cpp
CHANGED
|
@@ -8,15 +8,20 @@
|
|
|
8
8
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
|
9
9
|
|
|
10
10
|
#include <logging.h>
|
|
11
|
+
#include <random.h>
|
|
11
12
|
#include <sync.h>
|
|
13
|
+
#include <tinyformat.h>
|
|
14
|
+
#include <util/check.h>
|
|
12
15
|
#include <util/fs.h>
|
|
13
16
|
#include <util/syserror.h>
|
|
14
17
|
|
|
15
18
|
#include <cerrno>
|
|
16
19
|
#include <fstream>
|
|
20
|
+
#include <limits>
|
|
17
21
|
#include <map>
|
|
18
22
|
#include <memory>
|
|
19
23
|
#include <optional>
|
|
24
|
+
#include <stdexcept>
|
|
20
25
|
#include <string>
|
|
21
26
|
#include <system_error>
|
|
22
27
|
#include <utility>
|
|
@@ -39,6 +44,7 @@
|
|
|
39
44
|
#else
|
|
40
45
|
#include <io.h> /* For _get_osfhandle, _chsize */
|
|
41
46
|
#include <shlobj.h> /* For SHGetSpecialFolderPathW */
|
|
47
|
+
#include <windows.h>
|
|
42
48
|
#endif // WIN32
|
|
43
49
|
|
|
44
50
|
/** Mutex to protect dir_locks. */
|
|
@@ -108,28 +114,28 @@ std::streampos GetFileSize(const char* path, std::streamsize max)
|
|
|
108
114
|
bool FileCommit(FILE* file)
|
|
109
115
|
{
|
|
110
116
|
if (fflush(file) != 0) { // harmless if redundantly called
|
|
111
|
-
LogPrintf("fflush failed: %s\n", SysErrorString(errno));
|
|
117
|
+
LogError("fflush failed: %s", SysErrorString(errno));
|
|
112
118
|
return false;
|
|
113
119
|
}
|
|
114
120
|
#ifdef WIN32
|
|
115
121
|
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
|
116
122
|
if (FlushFileBuffers(hFile) == 0) {
|
|
117
|
-
LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
|
|
123
|
+
LogError("FlushFileBuffers failed: %s", Win32ErrorString(GetLastError()));
|
|
118
124
|
return false;
|
|
119
125
|
}
|
|
120
126
|
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
|
|
121
127
|
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
|
|
122
|
-
LogPrintf("fcntl F_FULLFSYNC failed: %s\n", SysErrorString(errno));
|
|
128
|
+
LogError("fcntl F_FULLFSYNC failed: %s", SysErrorString(errno));
|
|
123
129
|
return false;
|
|
124
130
|
}
|
|
125
131
|
#elif HAVE_FDATASYNC
|
|
126
132
|
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
|
|
127
|
-
LogPrintf("fdatasync failed: %s\n", SysErrorString(errno));
|
|
133
|
+
LogError("fdatasync failed: %s", SysErrorString(errno));
|
|
128
134
|
return false;
|
|
129
135
|
}
|
|
130
136
|
#else
|
|
131
137
|
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
|
|
132
|
-
LogPrintf("fsync failed: %s\n", SysErrorString(errno));
|
|
138
|
+
LogError("fsync failed: %s", SysErrorString(errno));
|
|
133
139
|
return false;
|
|
134
140
|
}
|
|
135
141
|
#endif
|
|
@@ -162,19 +168,38 @@ bool TruncateFile(FILE* file, unsigned int length)
|
|
|
162
168
|
*/
|
|
163
169
|
int RaiseFileDescriptorLimit(int nMinFD)
|
|
164
170
|
{
|
|
171
|
+
Assert(nMinFD >= 0);
|
|
165
172
|
#if defined(WIN32)
|
|
166
173
|
return 2048;
|
|
167
174
|
#else
|
|
168
175
|
struct rlimit limitFD;
|
|
169
176
|
if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
|
|
170
|
-
if (limitFD.rlim_cur < (rlim_t)nMinFD) {
|
|
171
|
-
limitFD.rlim_cur = nMinFD;
|
|
172
|
-
if (limitFD.rlim_cur > limitFD.rlim_max)
|
|
177
|
+
// If the current soft limit is already higher, don't raise it
|
|
178
|
+
if (limitFD.rlim_cur != RLIM_INFINITY && std::cmp_less(limitFD.rlim_cur, nMinFD)) {
|
|
179
|
+
const auto current_limit{limitFD.rlim_cur};
|
|
180
|
+
limitFD.rlim_cur = std::in_range<rlim_t>(nMinFD) ? static_cast<rlim_t>(nMinFD) : limitFD.rlim_max;
|
|
181
|
+
// Don't raise soft limit beyond hard limit
|
|
182
|
+
if (limitFD.rlim_max != RLIM_INFINITY && (
|
|
183
|
+
limitFD.rlim_cur > limitFD.rlim_max
|
|
184
|
+
)
|
|
185
|
+
) {
|
|
173
186
|
limitFD.rlim_cur = limitFD.rlim_max;
|
|
187
|
+
}
|
|
188
|
+
if (current_limit != limitFD.rlim_cur) {
|
|
174
189
|
setrlimit(RLIMIT_NOFILE, &limitFD);
|
|
175
190
|
getrlimit(RLIMIT_NOFILE, &limitFD);
|
|
191
|
+
}
|
|
176
192
|
}
|
|
177
|
-
return limitFD.rlim_cur;
|
|
193
|
+
// Check the (possibly raised) current soft limit against the special
|
|
194
|
+
// value of RLIM_INFINITY. Some platforms implement this as the maximum
|
|
195
|
+
// uint64, others as int64 (-1). Avoid casting even if the return type
|
|
196
|
+
// is changed to uint64_t. We also cap unlikely but possible values
|
|
197
|
+
// that would overflow int.
|
|
198
|
+
if (limitFD.rlim_cur == RLIM_INFINITY ||
|
|
199
|
+
std::cmp_greater_equal(limitFD.rlim_cur, std::numeric_limits<int>::max())) {
|
|
200
|
+
return std::numeric_limits<int>::max();
|
|
201
|
+
}
|
|
202
|
+
return static_cast<int>(limitFD.rlim_cur);
|
|
178
203
|
}
|
|
179
204
|
return nMinFD; // getrlimit failed, assume it's fine
|
|
180
205
|
#endif
|
|
@@ -191,11 +216,14 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
|
|
|
191
216
|
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
|
192
217
|
LARGE_INTEGER nFileSize;
|
|
193
218
|
int64_t nEndPos = (int64_t)offset + length;
|
|
194
|
-
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
|
|
195
|
-
nFileSize.u.HighPart = nEndPos >> 32;
|
|
196
|
-
SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
|
|
197
|
-
SetEndOfFile(hFile);
|
|
198
|
-
#elif defined(__APPLE__)
|
|
219
|
+
if (GetFileSizeEx(hFile, &nFileSize) && (int64_t{nFileSize.u.HighPart} << 32 | nFileSize.u.LowPart) <= nEndPos) {
|
|
220
|
+
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
|
|
221
|
+
nFileSize.u.HighPart = nEndPos >> 32;
|
|
222
|
+
if (SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN)) {
|
|
223
|
+
SetEndOfFile(hFile);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
#elif 0
|
|
199
227
|
// OSX specific version
|
|
200
228
|
// NOTE: Contrary to other OS versions, the OSX version assumes that
|
|
201
229
|
// NOTE: offset is the size of the file.
|
|
@@ -213,25 +241,81 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
|
|
|
213
241
|
#else
|
|
214
242
|
#if defined(HAVE_POSIX_FALLOCATE)
|
|
215
243
|
// Version using posix_fallocate
|
|
216
|
-
off_t nEndPos = (off_t)offset + length;
|
|
217
|
-
if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
|
|
244
|
+
if (0 == posix_fallocate(fileno(file), offset, length)) return;
|
|
218
245
|
#endif
|
|
219
246
|
// Fallback version
|
|
220
247
|
// TODO: just write one byte per block
|
|
221
|
-
static const char buf[65536] = {};
|
|
248
|
+
uint8_t buf[65536];
|
|
222
249
|
if (fseek(file, offset, SEEK_SET)) {
|
|
223
250
|
return;
|
|
224
251
|
}
|
|
252
|
+
clearerr(file);
|
|
225
253
|
while (length > 0) {
|
|
226
254
|
unsigned int now = 65536;
|
|
227
255
|
if (length < now)
|
|
228
256
|
now = length;
|
|
257
|
+
const size_t rlen = fread(buf, 1, now, file);
|
|
258
|
+
if (rlen < now) {
|
|
259
|
+
if (ferror(file)) {
|
|
260
|
+
// Don't clobber anything, just give up
|
|
261
|
+
clearerr(file);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
memset(&buf[rlen], 0, now - rlen);
|
|
265
|
+
if (0 != fseek(file, -rlen, SEEK_CUR)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
229
269
|
fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
|
|
230
270
|
length -= now;
|
|
231
271
|
}
|
|
232
272
|
#endif
|
|
233
273
|
}
|
|
234
274
|
|
|
275
|
+
FILE* AdviseSequential(FILE *file) {
|
|
276
|
+
#ifdef _POSIX_C_SOURCE
|
|
277
|
+
# if _POSIX_C_SOURCE >= 200112L
|
|
278
|
+
// Since this whole thing is advisory anyway, we can ignore any errors
|
|
279
|
+
// encountered up to and including the posix_fadvise call. However, we must
|
|
280
|
+
// rewind the file to the appropriate position if we've changed the seek
|
|
281
|
+
// offset.
|
|
282
|
+
if (file == nullptr) {
|
|
283
|
+
return nullptr;
|
|
284
|
+
}
|
|
285
|
+
const int fd = fileno(file);
|
|
286
|
+
if (fd == -1) {
|
|
287
|
+
return file;
|
|
288
|
+
}
|
|
289
|
+
const off_t start = lseek(fd, 0, SEEK_CUR);
|
|
290
|
+
if (start == -1) {
|
|
291
|
+
return file;
|
|
292
|
+
}
|
|
293
|
+
posix_fadvise(fd, start, 0, POSIX_FADV_WILLNEED);
|
|
294
|
+
posix_fadvise(fd, start, 0, POSIX_FADV_SEQUENTIAL);
|
|
295
|
+
# endif
|
|
296
|
+
#endif
|
|
297
|
+
return file;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
int CloseAndUncache(FILE *file) {
|
|
301
|
+
#ifdef _POSIX_C_SOURCE
|
|
302
|
+
# if _POSIX_C_SOURCE >= 200112L
|
|
303
|
+
// Ignore any errors up to and including the posix_fadvise call since it's
|
|
304
|
+
// advisory.
|
|
305
|
+
if (file != nullptr) {
|
|
306
|
+
const int fd = fileno(file);
|
|
307
|
+
if (fd != -1) {
|
|
308
|
+
const off_t end = lseek(fd, 0, SEEK_END);
|
|
309
|
+
if (end != (off_t)-1) {
|
|
310
|
+
posix_fadvise(fd, 0, end, POSIX_FADV_DONTNEED);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
# endif
|
|
315
|
+
#endif
|
|
316
|
+
return std::fclose(file);
|
|
317
|
+
}
|
|
318
|
+
|
|
235
319
|
#ifdef WIN32
|
|
236
320
|
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
|
237
321
|
{
|
|
@@ -241,7 +325,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
|
|
|
241
325
|
return fs::path(pszPath);
|
|
242
326
|
}
|
|
243
327
|
|
|
244
|
-
LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
|
|
328
|
+
LogError("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
|
|
245
329
|
return fs::path("");
|
|
246
330
|
}
|
|
247
331
|
#endif
|
|
@@ -275,25 +359,57 @@ std::string PermsToSymbolicString(fs::perms p)
|
|
|
275
359
|
{
|
|
276
360
|
std::string perm_str(9, '-');
|
|
277
361
|
|
|
278
|
-
auto set_perm = [&](size_t pos, fs::perms required_perm, char letter) {
|
|
362
|
+
auto set_perm = [&](size_t pos, fs::perms required_perm, char letter, char else_letter = '\0') {
|
|
279
363
|
if ((p & required_perm) != fs::perms::none) {
|
|
280
364
|
perm_str[pos] = letter;
|
|
365
|
+
} else if (else_letter) {
|
|
366
|
+
perm_str[pos] = else_letter;
|
|
281
367
|
}
|
|
282
368
|
};
|
|
283
369
|
|
|
284
370
|
set_perm(0, fs::perms::owner_read, 'r');
|
|
285
371
|
set_perm(1, fs::perms::owner_write, 'w');
|
|
286
|
-
set_perm(2, fs::perms::owner_exec, 'x');
|
|
372
|
+
if ((p & fs::perms::owner_exec) != fs::perms::none) {
|
|
373
|
+
set_perm(2, fs::perms::set_uid, 's', 'x');
|
|
374
|
+
} else {
|
|
375
|
+
set_perm(2, fs::perms::set_uid, 'S');
|
|
376
|
+
}
|
|
377
|
+
|
|
287
378
|
set_perm(3, fs::perms::group_read, 'r');
|
|
288
379
|
set_perm(4, fs::perms::group_write, 'w');
|
|
289
|
-
set_perm(5, fs::perms::group_exec, 'x');
|
|
380
|
+
if ((p & fs::perms::group_exec) != fs::perms::none) {
|
|
381
|
+
set_perm(5, fs::perms::set_gid, 's', 'x');
|
|
382
|
+
} else {
|
|
383
|
+
set_perm(5, fs::perms::set_gid, 'S');
|
|
384
|
+
}
|
|
385
|
+
|
|
290
386
|
set_perm(6, fs::perms::others_read, 'r');
|
|
291
387
|
set_perm(7, fs::perms::others_write, 'w');
|
|
292
|
-
set_perm(8, fs::perms::others_exec, 'x');
|
|
388
|
+
if ((p & fs::perms::others_exec) != fs::perms::none) {
|
|
389
|
+
set_perm(8, fs::perms::sticky_bit, 't', 'x');
|
|
390
|
+
} else {
|
|
391
|
+
set_perm(8, fs::perms::sticky_bit, 'T');
|
|
392
|
+
}
|
|
293
393
|
|
|
294
394
|
return perm_str;
|
|
295
395
|
}
|
|
296
396
|
|
|
397
|
+
static std::optional<unsigned> StringToOctal(const std::string& str)
|
|
398
|
+
{
|
|
399
|
+
unsigned ret = 0;
|
|
400
|
+
for (char c : str) {
|
|
401
|
+
if (c < '0' || c > '7') return std::nullopt;
|
|
402
|
+
ret = (ret << 3) | (c - '0');
|
|
403
|
+
}
|
|
404
|
+
return ret;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static auto ConvertPermsToOctal(const std::string& str) noexcept -> std::optional<unsigned>
|
|
408
|
+
{
|
|
409
|
+
if ((str.length() == 3) || (str.length() == 4)) return StringToOctal(str);
|
|
410
|
+
return std::nullopt;
|
|
411
|
+
}
|
|
412
|
+
|
|
297
413
|
std::optional<fs::perms> InterpretPermString(const std::string& s)
|
|
298
414
|
{
|
|
299
415
|
if (s == "owner") {
|
|
@@ -305,7 +421,38 @@ std::optional<fs::perms> InterpretPermString(const std::string& s)
|
|
|
305
421
|
return fs::perms::owner_read | fs::perms::owner_write |
|
|
306
422
|
fs::perms::group_read |
|
|
307
423
|
fs::perms::others_read;
|
|
424
|
+
} else if (auto octal_perms = ConvertPermsToOctal(s)) {
|
|
425
|
+
return static_cast<fs::perms>(*octal_perms);
|
|
308
426
|
} else {
|
|
309
427
|
return std::nullopt;
|
|
310
428
|
}
|
|
311
429
|
}
|
|
430
|
+
|
|
431
|
+
bool IsDirWritable(const fs::path& dir_path)
|
|
432
|
+
{
|
|
433
|
+
// Attempt to create a tmp file in the directory
|
|
434
|
+
if (!fs::is_directory(dir_path)) throw std::runtime_error(strprintf("Path %s is not a directory", fs::PathToString(dir_path)));
|
|
435
|
+
FastRandomContext rng;
|
|
436
|
+
const auto tmp = dir_path / fs::PathFromString(strprintf(".tmp_%d", rng.rand64()));
|
|
437
|
+
|
|
438
|
+
if (const auto created{fsbridge::fopen(tmp, "wbx")}) {
|
|
439
|
+
std::fclose(created);
|
|
440
|
+
std::error_code ec;
|
|
441
|
+
fs::remove(tmp, ec); // clean up, ignore errors
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
bool IsSymlink(const fs::path& path)
|
|
448
|
+
{
|
|
449
|
+
#ifdef WIN32
|
|
450
|
+
DWORD file_attrs = GetFileAttributesW(path.wstring().c_str());
|
|
451
|
+
if (file_attrs == INVALID_FILE_ATTRIBUTES) {
|
|
452
|
+
throw fs::filesystem_error("Unable to get file attributes", fs::PathToString(path), std::make_error_code(std::errc::invalid_argument));
|
|
453
|
+
}
|
|
454
|
+
return (file_attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
|
455
|
+
#else
|
|
456
|
+
return fs::is_symlink(path);
|
|
457
|
+
#endif
|
|
458
|
+
}
|