Skip to content
Bitcoin Roots

v29.4-roots.4 · Commit 2: [PATCH 02/37] 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. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/util/fs_helpers.cpp CHANGED
@@ -8,15 +8,20 @@
8
  #include <bitcoin-build-config.h> // IWYU pragma: keep
9
 
10
  #include <logging.h>
 
11
  #include <sync.h>
 
 
12
  #include <util/fs.h>
13
  #include <util/syserror.h>
14
 
15
  #include <cerrno>
16
  #include <fstream>
 
17
  #include <map>
18
  #include <memory>
19
  #include <optional>
 
20
  #include <string>
21
  #include <system_error>
22
  #include <utility>
@@ -39,6 +44,7 @@
39
  #else
40
  #include <io.h> /* For _get_osfhandle, _chsize */
41
  #include <shlobj.h> /* For SHGetSpecialFolderPathW */
 
42
  #endif // WIN32
43
 
44
  /** Mutex to protect dir_locks. */
@@ -108,28 +114,28 @@ std::streampos GetFileSize(const char* path, std::streamsize max)
108
  bool FileCommit(FILE* file)
109
  {
110
  if (fflush(file) != 0) { // harmless if redundantly called
111
- LogPrintf("fflush failed: %s\n", SysErrorString(errno));
112
  return false;
113
  }
114
  #ifdef WIN32
115
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
116
  if (FlushFileBuffers(hFile) == 0) {
117
- LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
118
  return false;
119
  }
120
  #elif defined(__APPLE__) && defined(F_FULLFSYNC)
121
  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));
123
  return false;
124
  }
125
  #elif HAVE_FDATASYNC
126
  if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
127
- LogPrintf("fdatasync failed: %s\n", SysErrorString(errno));
128
  return false;
129
  }
130
  #else
131
  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
132
- LogPrintf("fsync failed: %s\n", SysErrorString(errno));
133
  return false;
134
  }
135
  #endif
@@ -162,19 +168,38 @@ bool TruncateFile(FILE* file, unsigned int length)
162
  */
163
  int RaiseFileDescriptorLimit(int nMinFD)
164
  {
 
165
  #if defined(WIN32)
166
  return 2048;
167
  #else
168
  struct rlimit limitFD;
169
  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)
 
 
 
 
 
 
173
  limitFD.rlim_cur = limitFD.rlim_max;
 
 
174
  setrlimit(RLIMIT_NOFILE, &limitFD);
175
  getrlimit(RLIMIT_NOFILE, &limitFD);
 
176
  }
177
- return limitFD.rlim_cur;
 
 
 
 
 
 
 
 
 
178
  }
179
  return nMinFD; // getrlimit failed, assume it's fine
180
  #endif
@@ -191,11 +216,14 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
191
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
192
  LARGE_INTEGER nFileSize;
193
  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__)
 
 
 
199
  // OSX specific version
200
  // NOTE: Contrary to other OS versions, the OSX version assumes that
201
  // NOTE: offset is the size of the file.
@@ -213,25 +241,81 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
213
  #else
214
  #if defined(HAVE_POSIX_FALLOCATE)
215
  // Version using posix_fallocate
216
- off_t nEndPos = (off_t)offset + length;
217
- if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
218
  #endif
219
  // Fallback version
220
  // TODO: just write one byte per block
221
- static const char buf[65536] = {};
222
  if (fseek(file, offset, SEEK_SET)) {
223
  return;
224
  }
 
225
  while (length > 0) {
226
  unsigned int now = 65536;
227
  if (length < now)
228
  now = length;
 
 
 
 
 
 
 
 
 
 
 
 
229
  fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
230
  length -= now;
231
  }
232
  #endif
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  #ifdef WIN32
236
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
237
  {
@@ -241,7 +325,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
241
  return fs::path(pszPath);
242
  }
243
 
244
- LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
245
  return fs::path("");
246
  }
247
  #endif
@@ -275,25 +359,57 @@ std::string PermsToSymbolicString(fs::perms p)
275
  {
276
  std::string perm_str(9, '-');
277
 
278
- auto set_perm = [&](size_t pos, fs::perms required_perm, char letter) {
279
  if ((p & required_perm) != fs::perms::none) {
280
  perm_str[pos] = letter;
 
 
281
  }
282
  };
283
 
284
  set_perm(0, fs::perms::owner_read, 'r');
285
  set_perm(1, fs::perms::owner_write, 'w');
286
- set_perm(2, fs::perms::owner_exec, 'x');
 
 
 
 
 
287
  set_perm(3, fs::perms::group_read, 'r');
288
  set_perm(4, fs::perms::group_write, 'w');
289
- set_perm(5, fs::perms::group_exec, 'x');
 
 
 
 
 
290
  set_perm(6, fs::perms::others_read, 'r');
291
  set_perm(7, fs::perms::others_write, 'w');
292
- set_perm(8, fs::perms::others_exec, 'x');
 
 
 
 
293
 
294
  return perm_str;
295
  }
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  std::optional<fs::perms> InterpretPermString(const std::string& s)
298
  {
299
  if (s == "owner") {
@@ -305,7 +421,38 @@ std::optional<fs::perms> InterpretPermString(const std::string& s)
305
  return fs::perms::owner_read | fs::perms::owner_write |
306
  fs::perms::group_read |
307
  fs::perms::others_read;
 
 
308
  } else {
309
  return std::nullopt;
310
  }
311
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  #include <bitcoin-build-config.h> // IWYU pragma: keep
9
 
10
  #include <logging.h>
11
+ #include <random.h>
12
  #include <sync.h>
13
+ #include <tinyformat.h>
14
+ #include <util/check.h>
15
  #include <util/fs.h>
16
  #include <util/syserror.h>
17
 
18
  #include <cerrno>
19
  #include <fstream>
20
+ #include <limits>
21
  #include <map>
22
  #include <memory>
23
  #include <optional>
24
+ #include <stdexcept>
25
  #include <string>
26
  #include <system_error>
27
  #include <utility>
 
44
  #else
45
  #include <io.h> /* For _get_osfhandle, _chsize */
46
  #include <shlobj.h> /* For SHGetSpecialFolderPathW */
47
+ #include <windows.h>
48
  #endif // WIN32
49
 
50
  /** Mutex to protect dir_locks. */
 
114
  bool FileCommit(FILE* file)
115
  {
116
  if (fflush(file) != 0) { // harmless if redundantly called
117
+ LogError("fflush failed: %s", SysErrorString(errno));
118
  return false;
119
  }
120
  #ifdef WIN32
121
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
122
  if (FlushFileBuffers(hFile) == 0) {
123
+ LogError("FlushFileBuffers failed: %s", Win32ErrorString(GetLastError()));
124
  return false;
125
  }
126
  #elif defined(__APPLE__) && defined(F_FULLFSYNC)
127
  if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
128
+ LogError("fcntl F_FULLFSYNC failed: %s", SysErrorString(errno));
129
  return false;
130
  }
131
  #elif HAVE_FDATASYNC
132
  if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
133
+ LogError("fdatasync failed: %s", SysErrorString(errno));
134
  return false;
135
  }
136
  #else
137
  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
138
+ LogError("fsync failed: %s", SysErrorString(errno));
139
  return false;
140
  }
141
  #endif
 
168
  */
169
  int RaiseFileDescriptorLimit(int nMinFD)
170
  {
171
+ Assert(nMinFD >= 0);
172
  #if defined(WIN32)
173
  return 2048;
174
  #else
175
  struct rlimit limitFD;
176
  if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
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
+ ) {
186
  limitFD.rlim_cur = limitFD.rlim_max;
187
+ }
188
+ if (current_limit != limitFD.rlim_cur) {
189
  setrlimit(RLIMIT_NOFILE, &limitFD);
190
  getrlimit(RLIMIT_NOFILE, &limitFD);
191
+ }
192
  }
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);
203
  }
204
  return nMinFD; // getrlimit failed, assume it's fine
205
  #endif
 
216
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
217
  LARGE_INTEGER nFileSize;
218
  int64_t nEndPos = (int64_t)offset + length;
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
227
  // OSX specific version
228
  // NOTE: Contrary to other OS versions, the OSX version assumes that
229
  // NOTE: offset is the size of the file.
 
241
  #else
242
  #if defined(HAVE_POSIX_FALLOCATE)
243
  // Version using posix_fallocate
244
+ if (0 == posix_fallocate(fileno(file), offset, length)) return;
 
245
  #endif
246
  // Fallback version
247
  // TODO: just write one byte per block
248
+ uint8_t buf[65536];
249
  if (fseek(file, offset, SEEK_SET)) {
250
  return;
251
  }
252
+ clearerr(file);
253
  while (length > 0) {
254
  unsigned int now = 65536;
255
  if (length < now)
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
+ }
269
  fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
270
  length -= now;
271
  }
272
  #endif
273
  }
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
+
319
  #ifdef WIN32
320
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
321
  {
 
325
  return fs::path(pszPath);
326
  }
327
 
328
+ LogError("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
329
  return fs::path("");
330
  }
331
  #endif
 
359
  {
360
  std::string perm_str(9, '-');
361
 
362
+ auto set_perm = [&](size_t pos, fs::perms required_perm, char letter, char else_letter = '\0') {
363
  if ((p & required_perm) != fs::perms::none) {
364
  perm_str[pos] = letter;
365
+ } else if (else_letter) {
366
+ perm_str[pos] = else_letter;
367
  }
368
  };
369
 
370
  set_perm(0, fs::perms::owner_read, 'r');
371
  set_perm(1, fs::perms::owner_write, 'w');
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
+
378
  set_perm(3, fs::perms::group_read, 'r');
379
  set_perm(4, fs::perms::group_write, 'w');
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
+
386
  set_perm(6, fs::perms::others_read, 'r');
387
  set_perm(7, fs::perms::others_write, 'w');
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
+ }
393
 
394
  return perm_str;
395
  }
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
+
413
  std::optional<fs::perms> InterpretPermString(const std::string& s)
414
  {
415
  if (s == "owner") {
 
421
  return fs::perms::owner_read | fs::perms::owner_write |
422
  fs::perms::group_read |
423
  fs::perms::others_read;
424
+ } else if (auto octal_perms = ConvertPermsToOctal(s)) {
425
+ return static_cast<fs::perms>(*octal_perms);
426
  } else {
427
  return std::nullopt;
428
  }
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
+ }