From 0fa9885cda0778467ca5737ac888ece5ef371b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Fri, 17 Jul 2015 14:38:36 +0200 Subject: vis: handle file truncation more gracefully If we use the file / virtual memory system as cache (using mmap(2)) and another process truncates the file we are editing, we have a problem. All we can do is catch the resulting SIGBUS, close the corresponding window and print a warning message. To test this use: $ dd if=/dev/zero of=TEST bs=8M count=1 $ vis TEST :! echo TRUNCATE > TEST --- editor.c | 2 ++ editor.h | 4 ++++ text.c | 8 ++++++++ text.h | 3 +++ vis.c | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/editor.c b/editor.c index ad9bd4e..8628458 100644 --- a/editor.c +++ b/editor.c @@ -358,6 +358,8 @@ void editor_window_close(Win *win) { ed->windows = win->next; if (ed->win == win) ed->win = win->next ? win->next : win->prev; + if (ed->prompt_window == win) + ed->prompt_window = NULL; window_free(win); if (ed->win) ed->ui->window_focus(ed->win->ui); diff --git a/editor.h b/editor.h index 76a3136..3e8e644 100644 --- a/editor.h +++ b/editor.h @@ -5,6 +5,7 @@ #include #include #include +#include typedef struct Editor Editor; typedef struct File File; @@ -190,6 +191,7 @@ enum Mark { struct File { Text *text; const char *name; + volatile sig_atomic_t truncated; bool is_stdin; struct stat stat; int refcount; @@ -244,6 +246,8 @@ struct Editor { Mode *mode_before_prompt; /* user mode which was active before entering prompt */ volatile bool running; /* exit main loop once this becomes false */ volatile sig_atomic_t cancel_filter; /* abort external command */ + volatile sig_atomic_t sigbus; + sigjmp_buf sigbus_jmpbuf; }; Editor *editor_new(Ui*); diff --git a/text.c b/text.c index be9a8f5..5f516bc 100644 --- a/text.c +++ b/text.c @@ -1185,6 +1185,14 @@ bool text_modified(Text *txt) { return txt->saved_action != txt->history; } +bool text_sigbus(Text *txt, const char *addr) { + for (Buffer *buf = txt->buffers; buf; buf = buf->next) { + if (buf->type == MMAP && buf->data <= addr && addr < buf->data + buf->size) + return true; + } + return false; +} + enum TextNewLine text_newline_type(Text *txt){ if (!txt->newlines) { txt->newlines = TEXT_NEWLINE_NL; /* default to UNIX style \n new lines */ diff --git a/text.h b/text.h index c322a10..89a5d9b 100644 --- a/text.h +++ b/text.h @@ -108,6 +108,9 @@ size_t text_history_get(Text*, size_t index); size_t text_size(Text*); /* query whether the text contains any unsaved modifications */ bool text_modified(Text*); +/* query whether `addr` is part of a memory mapped region associated with + * this text instance */ +bool text_sigbus(Text*, const char *addr); /* which type of new lines does the text use? */ enum TextNewLine { diff --git a/vis.c b/vis.c index 8e51bce..ffe45ce 100644 --- a/vis.c +++ b/vis.c @@ -2312,8 +2312,23 @@ static Key getkey(void) { return key; } +static void sigbus_handler(int sig, siginfo_t *siginfo, void *context) { + for (File *file = vis->files; file; file = file->next) { + if (text_sigbus(file->text, siginfo->si_addr)) + file->truncated = true; + } + vis->sigbus = true; + siglongjmp(vis->sigbus_jmpbuf, 1); +} + static void mainloop() { struct timespec idle = { .tv_nsec = 0 }, *timeout = NULL; + struct sigaction sa_sigbus; + memset(&sa_sigbus, 0, sizeof sa_sigbus); + sa_sigbus.sa_flags = SA_SIGINFO; + sa_sigbus.sa_sigaction = sigbus_handler; + if (sigaction(SIGBUS, &sa_sigbus, NULL)) + die("sigaction: %s", strerror(errno)); sigset_t emptyset, blockset; sigemptyset(&emptyset); sigemptyset(&blockset); @@ -2323,11 +2338,31 @@ static void mainloop() { editor_draw(vis); vis->running = true; + sigsetjmp(vis->sigbus_jmpbuf, 1); + while (vis->running) { fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); + if (vis->sigbus) { + char *name = NULL; + for (Win *next, *win = vis->windows; win; win = next) { + next = win->next; + if (win->file->truncated) { + free(name); + name = strdup(win->file->name); + editor_window_close(win); + } + } + if (!vis->windows) + die("WARNING: file `%s' truncated!\n", name ? name : "-"); + else + editor_info_show(vis, "WARNING: file `%s' truncated!\n", name ? name : "-"); + vis->sigbus = false; + free(name); + } + editor_update(vis); idle.tv_sec = vis->mode->idle_timeout; int r = pselect(1, &fds, NULL, NULL, timeout, &emptyset); -- cgit v1.2.3