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. 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>
@@ -26,8 +31,9 @@
26
31
  #include <sys/resource.h>
27
32
  #include <unistd.h>
28
33
  #else
29
- #include <io.h>
30
- #include <shlobj.h>
34
+ #include <io.h> /* For _get_osfhandle, _chsize */
35
+ #include <shlobj.h> /* For SHGetSpecialFolderPathW */
36
+ #include <windows.h>
31
37
  #endif // WIN32
32
38
 
33
39
  #ifdef __APPLE__
@@ -102,28 +108,28 @@ std::streampos GetFileSize(const char* path, std::streamsize max)
102
108
  bool FileCommit(FILE* file)
103
109
  {
104
110
  if (fflush(file) != 0) { // harmless if redundantly called
105
- LogPrintf("fflush failed: %s\n", SysErrorString(errno));
111
+ LogError("fflush failed: %s", SysErrorString(errno));
106
112
  return false;
107
113
  }
108
114
  #ifdef WIN32
109
115
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
110
116
  if (FlushFileBuffers(hFile) == 0) {
111
- LogPrintf("FlushFileBuffers failed: %s\n", Win32ErrorString(GetLastError()));
117
+ LogError("FlushFileBuffers failed: %s", Win32ErrorString(GetLastError()));
112
118
  return false;
113
119
  }
114
120
  #elif defined(__APPLE__) && defined(F_FULLFSYNC)
115
121
  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));
122
+ LogError("fcntl F_FULLFSYNC failed: %s", SysErrorString(errno));
117
123
  return false;
118
124
  }
119
125
  #elif HAVE_FDATASYNC
120
126
  if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
121
- LogPrintf("fdatasync failed: %s\n", SysErrorString(errno));
127
+ LogError("fdatasync failed: %s", SysErrorString(errno));
122
128
  return false;
123
129
  }
124
130
  #else
125
131
  if (fsync(fileno(file)) != 0 && errno != EINVAL) {
126
- LogPrintf("fsync failed: %s\n", SysErrorString(errno));
132
+ LogError("fsync failed: %s", SysErrorString(errno));
127
133
  return false;
128
134
  }
129
135
  #endif
@@ -156,19 +162,38 @@ bool TruncateFile(FILE* file, unsigned int length)
156
162
  */
157
163
  int RaiseFileDescriptorLimit(int nMinFD)
158
164
  {
165
+ Assert(nMinFD >= 0);
159
166
  #if defined(WIN32)
160
167
  return 2048;
161
168
  #else
162
169
  struct rlimit limitFD;
163
170
  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)
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
+ ) {
167
180
  limitFD.rlim_cur = limitFD.rlim_max;
181
+ }
182
+ if (current_limit != limitFD.rlim_cur) {
168
183
  setrlimit(RLIMIT_NOFILE, &limitFD);
169
184
  getrlimit(RLIMIT_NOFILE, &limitFD);
185
+ }
170
186
  }
171
- return limitFD.rlim_cur;
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);
172
197
  }
173
198
  return nMinFD; // getrlimit failed, assume it's fine
174
199
  #endif
@@ -185,11 +210,14 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
185
210
  HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
186
211
  LARGE_INTEGER nFileSize;
187
212
  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__)
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
193
221
  // OSX specific version
194
222
  // NOTE: Contrary to other OS versions, the OSX version assumes that
195
223
  // NOTE: offset is the size of the file.
@@ -207,25 +235,82 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
207
235
  #else
208
236
  #if defined(HAVE_POSIX_FALLOCATE)
209
237
  // Version using posix_fallocate
210
- off_t nEndPos = (off_t)offset + length;
211
- if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
238
+ if (0 == posix_fallocate(fileno(file), offset, length)) return;
212
239
  #endif
213
240
  // Fallback version
214
241
  // TODO: just write one byte per block
215
- static const char buf[65536] = {};
242
+ uint8_t buf[65536];
216
243
  if (fseek(file, offset, SEEK_SET)) {
217
244
  return;
218
245
  }
246
+ clearerr(file);
219
247
  while (length > 0) {
220
248
  unsigned int now = 65536;
221
249
  if (length < now)
222
250
  now = length;
223
- fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
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;
224
265
  length -= now;
225
266
  }
226
267
  #endif
227
268
  }
228
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
+
229
314
  #ifdef WIN32
230
315
  fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
231
316
  {
@@ -235,7 +320,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
235
320
  return fs::path(pszPath);
236
321
  }
237
322
 
238
- LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
323
+ LogError("SHGetSpecialFolderPathW() failed, could not obtain requested path.");
239
324
  return fs::path("");
240
325
  }
241
326
  #endif
@@ -269,25 +354,57 @@ std::string PermsToSymbolicString(fs::perms p)
269
354
  {
270
355
  std::string perm_str(9, '-');
271
356
 
272
- auto set_perm = [&](size_t pos, fs::perms required_perm, char letter) {
357
+ auto set_perm = [&](size_t pos, fs::perms required_perm, char letter, char else_letter = '\0') {
273
358
  if ((p & required_perm) != fs::perms::none) {
274
359
  perm_str[pos] = letter;
360
+ } else if (else_letter) {
361
+ perm_str[pos] = else_letter;
275
362
  }
276
363
  };
277
364
 
278
365
  set_perm(0, fs::perms::owner_read, 'r');
279
366
  set_perm(1, fs::perms::owner_write, 'w');
280
- set_perm(2, fs::perms::owner_exec, 'x');
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
+
281
373
  set_perm(3, fs::perms::group_read, 'r');
282
374
  set_perm(4, fs::perms::group_write, 'w');
283
- set_perm(5, fs::perms::group_exec, 'x');
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
+
284
381
  set_perm(6, fs::perms::others_read, 'r');
285
382
  set_perm(7, fs::perms::others_write, 'w');
286
- set_perm(8, fs::perms::others_exec, 'x');
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
+ }
287
388
 
288
389
  return perm_str;
289
390
  }
290
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
+
291
408
  std::optional<fs::perms> InterpretPermString(const std::string& s)
292
409
  {
293
410
  if (s == "owner") {
@@ -299,6 +416,8 @@ std::optional<fs::perms> InterpretPermString(const std::string& s)
299
416
  return fs::perms::owner_read | fs::perms::owner_write |
300
417
  fs::perms::group_read |
301
418
  fs::perms::others_read;
419
+ } else if (auto octal_perms = ConvertPermsToOctal(s)) {
420
+ return static_cast<fs::perms>(*octal_perms);
302
421
  } else {
303
422
  return std::nullopt;
304
423
  }
@@ -315,3 +434,31 @@ FSType GetFilesystemType(const fs::path& path)
315
434
  return FSType::OTHER;
316
435
  }
317
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
+ }