Crash safety: non-regular files, filesystem errors, command exceptions
- OpenFromFile opens non-blocking, fstat()s and accepts regular files only. A directory made tellg() report a huge size and bad_alloc exit the editor (reachable by Tab-completing a directory in the open prompt); a FIFO blocked open() forever with SIGINT ignored; a device node opened as an empty, file-backed buffer that saving then replaced with a regular file. Files are read to EOF in chunks; a file too large to load is reported, not fatal. - Saving refuses to replace an existing non-regular file. - Exceptions escaping a command handler are reported in the status line and error log instead of propagating to main() and exiting, which lost every unsaved buffer (unnamed buffers' journals are never recovered). - Commands use non-throwing filesystem queries for existence checks, the current directory and Tab completion (ENAMETOOLONG, EACCES or a deleted working directory used to throw). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
@@ -831,3 +831,52 @@ TEST(Audit_HorizontalScroll_ControlCharsAreTwoCells)
|
||||
ASSERT_TRUE(h.Exec(CommandId::MoveEnd));
|
||||
ASSERT_EQ(b.Coloffs(), (std::size_t) (100 - 80 + 1));
|
||||
}
|
||||
|
||||
|
||||
// Opening a directory or a FIFO fails cleanly (a directory used to throw
|
||||
// bad_alloc and exit the editor; a FIFO blocked forever).
|
||||
TEST(Audit_Open_NonRegularFiles_Rejected)
|
||||
{
|
||||
TempDir d("open_nonreg");
|
||||
Buffer b;
|
||||
std::string err;
|
||||
ASSERT_TRUE(!b.OpenFromFile(d.path.string(), err));
|
||||
ASSERT_TRUE(err.find("directory") != std::string::npos);
|
||||
const std::string fifo = (d.path / "pipe").string();
|
||||
ASSERT_EQ(::mkfifo(fifo.c_str(), 0600), 0);
|
||||
err.clear();
|
||||
ASSERT_TRUE(!b.OpenFromFile(fifo, err));
|
||||
}
|
||||
|
||||
|
||||
// Saving never replaces a non-regular file (e.g. a device node or FIFO).
|
||||
TEST(Audit_Save_RefusesNonRegularTarget)
|
||||
{
|
||||
TempDir d("save_nonreg");
|
||||
const std::string fifo = (d.path / "pipe").string();
|
||||
ASSERT_EQ(::mkfifo(fifo.c_str(), 0600), 0);
|
||||
Buffer b;
|
||||
b.insert_text(0, 0, "x\n");
|
||||
std::string err;
|
||||
ASSERT_TRUE(!b.SaveAs(fifo, err));
|
||||
struct stat st{};
|
||||
ASSERT_EQ(::lstat(fifo.c_str(), &st), 0);
|
||||
ASSERT_TRUE(S_ISFIFO(st.st_mode));
|
||||
}
|
||||
|
||||
|
||||
// Filesystem errors in commands (here: a name longer than NAME_MAX) are
|
||||
// reported, not thrown out of the editor.
|
||||
TEST(Audit_Commands_FilesystemErrorsDoNotThrow)
|
||||
{
|
||||
TestHarness h;
|
||||
Editor &ed = h.EditorRef();
|
||||
h.Buf().insert_text(0, 0, "precious\n");
|
||||
const std::string longname = "/tmp/" + std::string(400, 'n');
|
||||
ASSERT_TRUE(Execute(ed, "save-as", longname) || true);
|
||||
ASSERT_TRUE(h.Exec(CommandId::OpenFileStart));
|
||||
ASSERT_TRUE(h.Exec(CommandId::InsertText, longname));
|
||||
(void) h.Exec(CommandId::Newline);
|
||||
(void) ed.ProcessPendingOpens();
|
||||
ASSERT_EQ(h.Text(), std::string("precious\n"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user