upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp61
1 files changed, 58 insertions, 3 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index d655ad5..45130ff 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -31,6 +31,9 @@
31#include <netinet/in.h> 31#include <netinet/in.h>
32#include <arpa/inet.h> 32#include <arpa/inet.h>
33#include <unistd.h> 33#include <unistd.h>
34#include <fcntl.h>
35#else
36#include <io.h>
34#endif 37#endif
35 38
36#include <atomic> 39#include <atomic>
@@ -457,9 +460,50 @@ struct VersionedBlob {
457 uint64 seq = 0; 460 uint64 seq = 0;
458}; 461};
459 462
460// Replace `path` with `data` atomically. Caller must hold the write mutex for 463// Push a file's contents out of the C library's buffer AND out of the kernel's
461// `path`: the temp name is derived from the target, so two threads in here at 464// page cache, so the bytes exist on the device before anything points at them.
462// once for one path is precisely the race this exists to prevent. 465static bool SyncFileHandle(FILE* f)
466{
467 if (fflush(f) != 0)
468 return false;
469#ifdef _WIN32
470 return _commit(_fileno(f)) == 0;
471#else
472 return fsync(fileno(f)) == 0;
473#endif
474}
475
476// Make the directory entry that rename() created durable too.
477//
478// A tmp+rename is only atomic with respect to a crash of the process. Against
479// a power cut or a hard VM reset the rename can reach the platter while the
480// data it points at has not, and the file that comes back is short or empty.
481// For pending_payouts.json that is not a corrupt file, it is a silent one:
482// LoadPendingPayouts catches the parse error, logs a single warning, and the
483// node comes up owing nobody anything.
484//
485// Best effort -- a filesystem that does not allow the directory to be opened
486// for this is not a reason to refuse to write the ledger.
487static void SyncDirectoryOf(const std::string& path)
488{
489#ifndef _WIN32
490 size_t slash = path.find_last_of('/');
491 std::string dir = (slash == std::string::npos) ? std::string(".")
492 : path.substr(0, slash);
493 if (dir.empty())
494 dir = "/";
495 int fd = open(dir.c_str(), O_RDONLY);
496 if (fd < 0)
497 return;
498 fsync(fd);
499 close(fd);
500#endif
501}
502
503// Replace `path` with `data` atomically and durably. Caller must hold the
504// write mutex for `path`: the temp name is derived from the target, so two
505// threads in here at once for one path is precisely the race this exists to
506// prevent.
463static bool WriteFileAtomic(const std::string& path, const std::string& data, 507static bool WriteFileAtomic(const std::string& path, const std::string& data,
464 const char* what) 508 const char* what)
465{ 509{
@@ -476,6 +520,16 @@ static bool WriteFileAtomic(const std::string& path, const std::string& data,
476 fclose(f); 520 fclose(f);
477 return false; 521 return false;
478 } 522 }
523 // Before the rename, not after: publishing a name that points at nothing
524 // is the failure this ordering exists to prevent. If the data cannot be
525 // made durable, do not rename -- the previous file is still intact and
526 // still correct, and the next write will try again.
527 if (!SyncFileHandle(f)) {
528 LogPrint("payout", "[payout] WARNING: could not flush %s to disk (%s)"
529 " -- keeping the previous file\n", tmp.c_str(), what);
530 fclose(f);
531 return false;
532 }
479 // fclose() is where buffered data actually reaches the OS, so a failure 533 // fclose() is where buffered data actually reaches the OS, so a failure
480 // here means the .tmp is short -- renaming it over the real file would 534 // here means the .tmp is short -- renaming it over the real file would
481 // destroy the good copy, which is exactly what these files exist to 535 // destroy the good copy, which is exactly what these files exist to
@@ -494,6 +548,7 @@ static bool WriteFileAtomic(const std::string& path, const std::string& data,
494 path.c_str(), what); 548 path.c_str(), what);
495 return false; 549 return false;
496 } 550 }
551 SyncDirectoryOf(path);
497 return true; 552 return true;
498} 553}
499 554