Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 2: [PATCH 02/68] feat(runtime): preserve Roots operator controls on Core 30.3

src/util/fs_helpers.cpp

Before: src/util/fs_helpers.cpp · After: src/util/fs_helpers.cpp

File metadata
index b5dc1cb33207149889e08eb9407627be13c82e10..cb8567323decf15152ba464b2f33987a0647a41b 100644

Download original patch · Release notes

Diff rows 1–319 of 319. 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>
@@ -26,8 +31,9 @@
26
  #include <sys/resource.h>
27
  #include <unistd.h>
28
  #else
29
- #include <io.h>
30
- #include <shlobj.h>
 
31
  #endif // WIN32
32
 
33
  #ifdef __APPLE__
@@ -102,28 +108,28 @@ std::streampos GetFileSize(const char* path, std::streamsize max)
102
  bool FileCommit(FILE* file)
103
  {
104
  if (fflush(file) != 0) { // harmless if redundantly called
105
- LogPrintf("fflush failed: %s\n", SysErrorString(errno));
106
  return false;
107
  }
108
  #ifdef WIN32
109
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
110
  if (FlushFileBuffers(hFile) == 0) {
111
- LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
112
  return false;
113
  }
114
  #elif defined(__APPLE__) && defined(F_FULLFSYNC)
115
  if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
116
- LogPrintf("fcntl F_FULLFSYNC failed: %s\n", SysErrorString(errno));
117
  return false;
118
  }
119
  #elif HAVE_FDATASYNC
120
  if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
121
- LogPrintf("fdatasync failed: %s\n", SysErrorString(errno));
122
  return false;
123
  }
124
  #else
125
  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
126
- LogPrintf("fsync failed: %s\n", SysErrorString(errno));
127
  return false;
128
  }
129
  #endif
@@ -156,19 +162,38 @@ bool TruncateFile(FILE* file, unsigned int length)
156
  */
157
  int RaiseFileDescriptorLimit(int nMinFD)
158
  {
 
159
  #if defined(WIN32)
160
  return 2048;
161
  #else
162
  struct rlimit limitFD;
163
  if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
164
- if (limitFD.rlim_cur < (rlim_t)nMinFD) {
165
- limitFD.rlim_cur = nMinFD;
166
- if (limitFD.rlim_cur > limitFD.rlim_max)
 
 
 
 
 
 
167
  limitFD.rlim_cur = limitFD.rlim_max;
 
 
168
  setrlimit(RLIMIT_NOFILE, &limitFD);
169
  getrlimit(RLIMIT_NOFILE, &limitFD);
 
170
  }
171
- return limitFD.rlim_cur;
 
 
 
 
 
 
 
 
 
172
  }
173
  return nMinFD; // getrlimit failed, assume it's fine
174
  #endif
@@ -185,11 +210,14 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
185
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
186
  LARGE_INTEGER nFileSize;
187
  int64_t nEndPos = (int64_t)offset + length;
188
- nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
189
- nFileSize.u.HighPart = nEndPos >> 32;
190
- SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
191
- SetEndOfFile(hFile);
192
- #elif defined(__APPLE__)
 
 
 
193
  // OSX specific version
194
  // NOTE: Contrary to other OS versions, the OSX version assumes that
195
  // NOTE: offset is the size of the file.
@@ -207,25 +235,82 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
207
  #else
208
  #if defined(HAVE_POSIX_FALLOCATE)
209
  // Version using posix_fallocate
210
- off_t nEndPos = (off_t)offset + length;
211
- if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
212
  #endif
213
  // Fallback version
214
  // TODO: just write one byte per block
215
- static const char buf[65536] = {};
216
  if (fseek(file, offset, SEEK_SET)) {
217
  return;
218
  }
 
219
  while (length > 0) {
220
  unsigned int now = 65536;
221
  if (length < now)
222
  now = length;
223
- fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  length -= now;
225
  }
226
  #endif
227
  }
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  #ifdef WIN32
230
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
231
  {
@@ -235,7 +320,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
235
  return fs::path(pszPath);
236
  }
237
 
238
- LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
239
  return fs::path("");
240
  }
241
  #endif
@@ -269,25 +354,57 @@ std::string PermsToSymbolicString(fs::perms p)
269
  {
270
  std::string perm_str(9, '-');
271
 
272
- auto set_perm = [&](size_t pos, fs::perms required_perm, char letter) {
273
  if ((p & required_perm) != fs::perms::none) {
274
  perm_str[pos] = letter;
 
 
275
  }
276
  };
277
 
278
  set_perm(0, fs::perms::owner_read, 'r');
279
  set_perm(1, fs::perms::owner_write, 'w');
280
- set_perm(2, fs::perms::owner_exec, 'x');
 
 
 
 
 
281
  set_perm(3, fs::perms::group_read, 'r');
282
  set_perm(4, fs::perms::group_write, 'w');
283
- set_perm(5, fs::perms::group_exec, 'x');
 
 
 
 
 
284
  set_perm(6, fs::perms::others_read, 'r');
285
  set_perm(7, fs::perms::others_write, 'w');
286
- set_perm(8, fs::perms::others_exec, 'x');
 
 
 
 
287
 
288
  return perm_str;
289
  }
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  std::optional<fs::perms> InterpretPermString(const std::string& s)
292
  {
293
  if (s == "owner") {
@@ -299,6 +416,8 @@ std::optional<fs::perms> InterpretPermString(const std::string& s)
299
  return fs::perms::owner_read | fs::perms::owner_write |
300
  fs::perms::group_read |
301
  fs::perms::others_read;
 
 
302
  } else {
303
  return std::nullopt;
304
  }
@@ -315,3 +434,31 @@ FSType GetFilesystemType(const fs::path& path)
315
  return FSType::OTHER;
316
  }
317
  #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
 
31
  #include <sys/resource.h>
32
  #include <unistd.h>
33
  #else
34
+ #include <io.h> /* For _get_osfhandle, _chsize */
35
+ #include <shlobj.h> /* For SHGetSpecialFolderPathW */
36
+ #include <windows.h>
37
  #endif // WIN32
38
 
39
  #ifdef __APPLE__
 
108
  bool FileCommit(FILE* file)
109
  {
110
  if (fflush(file) != 0) { // harmless if redundantly called
111
+ LogError("fflush failed: %s", SysErrorString(errno));
112
  return false;
113
  }
114
  #ifdef WIN32
115
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
116
  if (FlushFileBuffers(hFile) == 0) {
117
+ LogError("FlushFileBuffers failed: %s", 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
+ LogError("fcntl F_FULLFSYNC failed: %s", 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
+ LogError("fdatasync failed: %s", SysErrorString(errno));
128
  return false;
129
  }
130
  #else
131
  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
132
+ LogError("fsync failed: %s", SysErrorString(errno));
133
  return false;
134
  }
135
  #endif
 
162
  */
163
  int RaiseFileDescriptorLimit(int nMinFD)
164
  {
165
+ Assert(nMinFD >= 0);
166
  #if defined(WIN32)
167
  return 2048;
168
  #else
169
  struct rlimit limitFD;
170
  if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
171
+ // If the current soft limit is already higher, don't raise it
172
+ if (limitFD.rlim_cur != RLIM_INFINITY && std::cmp_less(limitFD.rlim_cur, nMinFD)) {
173
+ const auto current_limit{limitFD.rlim_cur};
174
+ limitFD.rlim_cur = std::in_range<rlim_t>(nMinFD) ? static_cast<rlim_t>(nMinFD) : limitFD.rlim_max;
175
+ // Don't raise soft limit beyond hard limit
176
+ if (limitFD.rlim_max != RLIM_INFINITY && (
177
+ limitFD.rlim_cur > limitFD.rlim_max
178
+ )
179
+ ) {
180
  limitFD.rlim_cur = limitFD.rlim_max;
181
+ }
182
+ if (current_limit != limitFD.rlim_cur) {
183
  setrlimit(RLIMIT_NOFILE, &limitFD);
184
  getrlimit(RLIMIT_NOFILE, &limitFD);
185
+ }
186
  }
187
+ // Check the (possibly raised) current soft limit against the special
188
+ // value of RLIM_INFINITY. Some platforms implement this as the maximum
189
+ // uint64, others as int64 (-1). Avoid casting even if the return type
190
+ // is changed to uint64_t. We also cap unlikely but possible values
191
+ // that would overflow int.
192
+ if (limitFD.rlim_cur == RLIM_INFINITY ||
193
+ std::cmp_greater_equal(limitFD.rlim_cur, std::numeric_limits<int>::max())) {
194
+ return std::numeric_limits<int>::max();
195
+ }
196
+ return static_cast<int>(limitFD.rlim_cur);
197
  }
198
  return nMinFD; // getrlimit failed, assume it's fine
199
  #endif
 
210
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
211
  LARGE_INTEGER nFileSize;
212
  int64_t nEndPos = (int64_t)offset + length;
213
+ if (GetFileSizeEx(hFile, &nFileSize) && (int64_t{nFileSize.u.HighPart} << 32 | nFileSize.u.LowPart) <= nEndPos) {
214
+ nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
215
+ nFileSize.u.HighPart = nEndPos >> 32;
216
+ if (SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN)) {
217
+ SetEndOfFile(hFile);
218
+ }
219
+ }
220
+ #elif 0
221
  // OSX specific version
222
  // NOTE: Contrary to other OS versions, the OSX version assumes that
223
  // NOTE: offset is the size of the file.
 
235
  #else
236
  #if defined(HAVE_POSIX_FALLOCATE)
237
  // Version using posix_fallocate
238
+ if (0 == posix_fallocate(fileno(file), offset, length)) return;
 
239
  #endif
240
  // Fallback version
241
  // TODO: just write one byte per block
242
+ uint8_t buf[65536];
243
  if (fseek(file, offset, SEEK_SET)) {
244
  return;
245
  }
246
+ clearerr(file);
247
  while (length > 0) {
248
  unsigned int now = 65536;
249
  if (length < now)
250
  now = length;
251
+ const size_t rlen = fread(buf, 1, now, file);
252
+ if (rlen < now) {
253
+ if (ferror(file)) {
254
+ // Don't clobber anything, just give up
255
+ clearerr(file);
256
+ return;
257
+ }
258
+ memset(&buf[rlen], 0, now - rlen);
259
+ }
260
+ // Rewind after every read so allocation preserves existing bytes.
261
+ if (fseek(file, -static_cast<long>(rlen), SEEK_CUR)) return;
262
+ if (fwrite(buf, 1, now, file) != now) return; // Advisory allocation may fail.
263
+ // An update stream needs a positioning operation between writing and reading.
264
+ if (fseek(file, 0, SEEK_CUR)) return;
265
  length -= now;
266
  }
267
  #endif
268
  }
269
 
270
+ FILE* AdviseSequential(FILE *file) {
271
+ #ifdef _POSIX_C_SOURCE
272
+ # if _POSIX_C_SOURCE >= 200112L
273
+ // Since this whole thing is advisory anyway, we can ignore any errors
274
+ // encountered up to and including the posix_fadvise call. However, we must
275
+ // rewind the file to the appropriate position if we've changed the seek
276
+ // offset.
277
+ if (file == nullptr) {
278
+ return nullptr;
279
+ }
280
+ const int fd = fileno(file);
281
+ if (fd == -1) {
282
+ return file;
283
+ }
284
+ const off_t start = lseek(fd, 0, SEEK_CUR);
285
+ if (start == -1) {
286
+ return file;
287
+ }
288
+ posix_fadvise(fd, start, 0, POSIX_FADV_WILLNEED);
289
+ posix_fadvise(fd, start, 0, POSIX_FADV_SEQUENTIAL);
290
+ # endif
291
+ #endif
292
+ return file;
293
+ }
294
+
295
+ int CloseAndUncache(FILE *file) {
296
+ #ifdef _POSIX_C_SOURCE
297
+ # if _POSIX_C_SOURCE >= 200112L
298
+ // Ignore any errors up to and including the posix_fadvise call since it's
299
+ // advisory.
300
+ if (file != nullptr) {
301
+ const int fd = fileno(file);
302
+ if (fd != -1) {
303
+ const off_t end = lseek(fd, 0, SEEK_END);
304
+ if (end != (off_t)-1) {
305
+ posix_fadvise(fd, 0, end, POSIX_FADV_DONTNEED);
306
+ }
307
+ }
308
+ }
309
+ # endif
310
+ #endif
311
+ return std::fclose(file);
312
+ }
313
+
314
  #ifdef WIN32
315
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
316
  {
 
320
  return fs::path(pszPath);
321
  }
322
 
323
+ LogError("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
324
  return fs::path("");
325
  }
326
  #endif
 
354
  {
355
  std::string perm_str(9, '-');
356
 
357
+ auto set_perm = [&](size_t pos, fs::perms required_perm, char letter, char else_letter = '\0') {
358
  if ((p & required_perm) != fs::perms::none) {
359
  perm_str[pos] = letter;
360
+ } else if (else_letter) {
361
+ perm_str[pos] = else_letter;
362
  }
363
  };
364
 
365
  set_perm(0, fs::perms::owner_read, 'r');
366
  set_perm(1, fs::perms::owner_write, 'w');
367
+ if ((p & fs::perms::owner_exec) != fs::perms::none) {
368
+ set_perm(2, fs::perms::set_uid, 's', 'x');
369
+ } else {
370
+ set_perm(2, fs::perms::set_uid, 'S');
371
+ }
372
+
373
  set_perm(3, fs::perms::group_read, 'r');
374
  set_perm(4, fs::perms::group_write, 'w');
375
+ if ((p & fs::perms::group_exec) != fs::perms::none) {
376
+ set_perm(5, fs::perms::set_gid, 's', 'x');
377
+ } else {
378
+ set_perm(5, fs::perms::set_gid, 'S');
379
+ }
380
+
381
  set_perm(6, fs::perms::others_read, 'r');
382
  set_perm(7, fs::perms::others_write, 'w');
383
+ if ((p & fs::perms::others_exec) != fs::perms::none) {
384
+ set_perm(8, fs::perms::sticky_bit, 't', 'x');
385
+ } else {
386
+ set_perm(8, fs::perms::sticky_bit, 'T');
387
+ }
388
 
389
  return perm_str;
390
  }
391
 
392
+ static std::optional<unsigned> StringToOctal(const std::string& str)
393
+ {
394
+ unsigned ret = 0;
395
+ for (char c : str) {
396
+ if (c < '0' || c > '7') return std::nullopt;
397
+ ret = (ret << 3) | (c - '0');
398
+ }
399
+ return ret;
400
+ }
401
+
402
+ static auto ConvertPermsToOctal(const std::string& str) noexcept -> std::optional<unsigned>
403
+ {
404
+ if ((str.length() == 3) || (str.length() == 4)) return StringToOctal(str);
405
+ return std::nullopt;
406
+ }
407
+
408
  std::optional<fs::perms> InterpretPermString(const std::string& s)
409
  {
410
  if (s == "owner") {
 
416
  return fs::perms::owner_read | fs::perms::owner_write |
417
  fs::perms::group_read |
418
  fs::perms::others_read;
419
+ } else if (auto octal_perms = ConvertPermsToOctal(s)) {
420
+ return static_cast<fs::perms>(*octal_perms);
421
  } else {
422
  return std::nullopt;
423
  }
 
434
  return FSType::OTHER;
435
  }
436
  #endif
437
+ bool IsDirWritable(const fs::path& dir_path)
438
+ {
439
+ // Attempt to create a tmp file in the directory
440
+ if (!fs::is_directory(dir_path)) throw std::runtime_error(strprintf("Path %s is not a directory", fs::PathToString(dir_path)));
441
+ FastRandomContext rng;
442
+ const auto tmp = dir_path / fs::PathFromString(strprintf(".tmp_%d", rng.rand64()));
443
+
444
+ if (const auto created{fsbridge::fopen(tmp, "wbx")}) {
445
+ std::fclose(created);
446
+ std::error_code ec;
447
+ fs::remove(tmp, ec); // clean up, ignore errors
448
+ return true;
449
+ }
450
+ return false;
451
+ }
452
+
453
+ bool IsSymlink(const fs::path& path)
454
+ {
455
+ #ifdef WIN32
456
+ DWORD file_attrs = GetFileAttributesW(path.wstring().c_str());
457
+ if (file_attrs == INVALID_FILE_ATTRIBUTES) {
458
+ throw fs::filesystem_error("Unable to get file attributes", fs::PathToString(path), std::make_error_code(std::errc::invalid_argument));
459
+ }
460
+ return (file_attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
461
+ #else
462
+ return fs::is_symlink(path);
463
+ #endif
464
+ }