sled

Једноставни уређивач текста
git clone https://git.sr.ht/~strahinja/sled
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 8cb65b2a4472ef67bd2f823f2c97c0a97773f4cc
родитељ 4f9a87acd2ff4e0b4f5917ce8e3d1e0fa112ced2
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Thu, 29 Feb 2024 21:20:12 +0100

(Hopefully) fix unveil(2) and dealing with symlinks

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
Mdraw.c | 21++++++++++++++-------
Mdraw.h | 8+++++---
Msled.c | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
измењених датотека: 3, додавања: 172(+), брисања: 85(-)

diff --git a/draw.c b/draw.c @@ -92,13 +92,16 @@ void init_drawstate(struct DrawState* state, char* error, char* action, char* filename, char* prompt, int skip_paste_and_mode_init) { - state->error = error; - state->action = action; - state->filename = filename; - state->input = NULL; - state->search = NULL; - state->chain_dialog = 0; - state->buffer = NULL; + state->error = error; + state->action = action; + state->filename = filename; + state->real_filename = NULL; + state->backup_filename = NULL; + state->rfn_len = 0; + state->input = NULL; + state->search = NULL; + state->chain_dialog = 0; + state->buffer = NULL; init_coord(&state->coord, 0, 0); state->display_column = 0; init_coord(&state->first_shown, 0, 0); @@ -123,11 +126,13 @@ init_drawstate(struct DrawState* state, char* error, char* action, state->input_update_callback = NULL; state->running = 1; state->dirty = 0; + state->new_file = 1; state->recalculate_size = 1; CLEAR(state->mode, M_HELP | M_INPUT | M_INPUT_NUM | M_PROMPT | M_SELECTION); init_coord(&state->sel_start, 0, 0); init_coord(&state->sel_end, 0, 0); + state->backup_possible = 1; } void @@ -148,6 +153,8 @@ free_drawstate(struct DrawState* state, int skip_paste_free) size_t row = 0; for (row = 0; row < state->buffer_size; row++) free_bufferline(state->buffer + row); + free(state->backup_filename); + free(state->real_filename); free(state->buffer); if (!skip_paste_free) { diff --git a/draw.h b/draw.h @@ -58,6 +58,9 @@ struct DrawState { char* prompt; char* action; char* filename; + char* real_filename; /* what symlink points to */ + char* backup_filename; + size_t rfn_len; uint32_t* input; uint32_t* search; int chain_dialog; /* show input dialogs in sequence? */ @@ -82,15 +85,14 @@ struct DrawState { void (*input_update_callback)(struct DrawState*); int running; int dirty; + int new_file; int recalculate_size; int mode; int maxx; int maxy; struct Coord sel_start; /* buffer coordinate */ struct Coord sel_end; /* buffer coordinate */ -#ifdef CREATE_BACKUPS - int backup_possible; /* is saving backup possible? */ -#endif + int backup_possible; /* is saving backup possible? */ }; struct Key { diff --git a/sled.c b/sled.c @@ -60,6 +60,7 @@ extern const char* program_name; int clear_selection(struct DrawState* state); int create_file(struct DrawState* dstate); +char* dirname(char* path); int do_search_replace(struct DrawState* state); int erase_selection(struct DrawState* state); void get_selection(int* ssr, int* ssc, int* ser, int* sec, @@ -125,6 +126,35 @@ create_file(struct DrawState* state) return LOAD_ERR_NONE; } +char* +dirname(char* path) +{ + static char* result = NULL; + static const char* dot = "."; + char* p = NULL; + char* slash = NULL; + + if (!path || !*path) + return NULL; + + free(result); + result = malloc(MAXPATH); + memset(result, 0, MAXPATH); + + strcpy(result, path); + p = result + strlen(result) - 1; + if (*p == '/') + *p = 0; + + slash = strrchr(result, '/'); + if (!slash) + return (char*)dot; + + *slash = 0; + + return result; +} + int do_search_replace(struct DrawState* state) { @@ -845,83 +875,72 @@ save_file(struct DrawState* state) FILE* output = NULL; char* buf = NULL; #ifdef CREATE_BACKUPS - char* backup_filename = NULL; - int new_file = 0; struct stat st; #endif - char* real_filename = NULL; - size_t rfn_len = 0; - if (!(buf = calloc(MAXBUFLINE, 1)) - || !(real_filename = calloc(MAXPATH, 1))) - return SAVE_ERR_CANT_ALLOC; -#ifdef CREATE_BACKUPS - if (!(backup_filename = calloc(MAXPATH, 1))) + if (!(buf = calloc(MAXBUFLINE, 1))) return SAVE_ERR_CANT_ALLOC; -#endif strcpy(state->action, actions[ACT_WRITING]); redraw_status(state); + errno = 0; #ifdef CREATE_BACKUPS - if (lstat(state->filename, &st) != -1) - { - if (S_ISLNK(st.st_mode)) - { - rfn_len = readlink(state->filename, real_filename, - MAXPATH); - if (rfn_len == -1) - return SAVE_ERR_CANT_READLINK; - real_filename[rfn_len] = 0; - if (stat(real_filename, &st) == -1) - return SAVE_ERR_CANT_STAT_FILE; - } - strcpy(backup_filename, state->filename); - strcat(backup_filename, backup_suffix); #ifdef BACKUP_FAIL_OK - if (state->backup_possible) - rename(rfn_len > 0 ? real_filename : state->filename, - backup_filename); -#else - if (rename(rfn_len > 0 ? real_filename : state->filename, - backup_filename) - == -1) + if (state->backup_possible) + { + if (unlink(state->backup_filename) < 0 && errno != ENOENT) return SAVE_ERR_CANT_MOVE_FILE; -#endif - } /* We don't error out here to account for new files */ - else - new_file = 1; -#endif + errno = 0; + rename(state->rfn_len > 0 ? state->real_filename + : state->filename, + state->backup_filename); + errno = 0; /* ignore errors */ + } +#else /* !BACKUP_FAIL_OK */ + if (unlink(state->backup_filename) < 0 && errno != ENOENT) + return SAVE_ERR_CANT_MOVE_FILE; + + if (rename(state->rfn_len > 0 ? state->real_filename : state->filename, + state->backup_filename) + == -1) + return SAVE_ERR_CANT_MOVE_FILE; +#endif /* BACKUP_FAIL_OK */ +#endif /* CREATE_BACKUPS */ - if (!(output = fopen(rfn_len > 0 ? real_filename : state->filename, - "wt"))) + if (!(output = fopen(state->rfn_len > 0 ? state->real_filename + : state->filename, + "w"))) return SAVE_ERR_CANT_OPEN_FILE; #ifdef CREATE_BACKUPS #ifdef BACKUP_FAIL_OK if (state->backup_possible) { - if (rfn_len > 0) + if (state->rfn_len > 0) { if (unlink(state->filename) == -1) return SAVE_ERR_CANT_UNLINK; - if (symlink(real_filename, state->filename) == -1) + if (symlink(state->real_filename, state->filename) == -1) return SAVE_ERR_CANT_SYMLINK; } - if (!new_file) - chmod(rfn_len > 0 ? real_filename : state->filename, + if (!state->new_file) + chmod(state->rfn_len > 0 ? state->real_filename + : state->filename, st.st_mode); } #else /* !BACKUP_FAIL_OK */ - if (rfn_len > 0) + if (state->rfn_len > 0) { if (unlink(state->filename) == -1) return SAVE_ERR_CANT_UNLINK; - if (symlink(real_filename, state->filename) == -1) + if (symlink(state->real_filename, state->filename) == -1) return SAVE_ERR_CANT_SYMLINK; } if (!new_file) - chmod(rfn_len > 0 ? real_filename : state->filename, st.st_mode); + chmod(state->rfn_len > 0 ? state->real_filename + : state->filename, + st.st_mode); #endif /* BACKUP_FAIL_OK */ #endif /* CREATE_BACKUPS */ @@ -936,11 +955,7 @@ save_file(struct DrawState* state) save_file_cleanup: fclose(output); -#ifdef CREATE_BACKUPS - free(backup_filename); -#endif free(buf); - free(real_filename); *state->action = 0; return SAVE_ERR_NONE; } @@ -2175,8 +2190,7 @@ main(int argc, char** argv) int result; int dir_access; int dir_access_errno; - char dirname[MAXPATH]; - char* slash = NULL; + char* dir = NULL; *error = 0; *action = 0; @@ -2184,7 +2198,7 @@ main(int argc, char** argv) *filename = 0; #ifdef __OpenBSD__ - if (pledge("stdio tty unveil rpath wpath cpath", NULL) < 0) + if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0) { perror("sled: pledge"); exit(1); @@ -2236,31 +2250,95 @@ main(int argc, char** argv) init_drawstate(&state, error, action, filename, prompt, 0); - strcpy(dirname, filename); - if ((slash = strrchr(dirname, '/'))) - *slash = 0; - else - { - *dirname = '.'; - *(dirname + 1) = 0; - } - #ifdef __OpenBSD__ - if (unveil(dirname, "rwc") < 0) + if (unveil(state.filename, "rwc") < 0) { perror("sled: unveil"); exit(1); } - if (unveil(NULL, NULL) < 0) +#endif + + dir = dirname(state.filename); +#ifdef __OpenBSD__ + if (unveil(dir, "rwc") < 0) { perror("sled: unveil"); exit(1); } #endif +#ifdef CREATE_BACKUPS + errno = 0; + if (lstat(state.filename, &st) != -1) + { + if (S_ISLNK(st.st_mode)) + { + if (!(state.real_filename = calloc(MAXPATH, 1))) + return print_error(1, + "Allocation failed (out of memory?)"); + + state.rfn_len = readlink(state.filename, + state.real_filename, MAXPATH); + if (state.rfn_len == -1) + return print_error(1, strerror(errno)); + state.real_filename[state.rfn_len] = 0; +#ifdef __OpenBSD__ + if (unveil(state.real_filename, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + if (stat(state.real_filename, &st) == -1) + return print_error(1, strerror(errno)); + } + if (!(state.backup_filename = calloc(MAXPATH, 1))) + return print_error(1, + "Allocation failed (out of memory?)"); + + /* Backup filename is the target file + suffix if the file to + * write is a symlink, otherwise file + suffix */ + strcpy(state.backup_filename, + state.rfn_len > 0 ? state.real_filename + : state.filename); + strcat(state.backup_filename, backup_suffix); +#ifdef __OpenBSD__ + if (unveil(state.backup_filename, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + } /* We don't error out here on ENOENT to account for new files */ + else if (errno == ENOENT) + state.new_file = 1; + else + return print_error(1, strerror(errno)); +#endif /* CREATE_BACKUPS */ + + if (state.rfn_len > 0) + { + dir = dirname(state.real_filename); +#ifdef __OpenBSD__ + if (unveil(dir, "rwc") < 0) + { + perror("sled: unveil"); + exit(1); + } +#endif + } - dir_access = access(dirname, W_OK); + dir_access = access(dir, W_OK | X_OK); dir_access_errno = errno; - if (stat(filename, &st) == -1) +#ifdef BACKUP_FAIL_OK + state.backup_possible = dir_access != -1; +#else /* !BACKUP_FAIL_OK */ + if (dir_access == -1) + { + errno = dir_access_errno; + goto file_error; + } +#endif /* BACKUP_FAIL_OK */ + if (stat(state.filename, &st) == -1) { if (errno != ENOENT || dir_access == -1) { @@ -2281,19 +2359,19 @@ main(int argc, char** argv) if (!S_ISREG(st.st_mode)) return print_error(EINVAL, "Is not a regular file"); -#ifdef CREATE_BACKUPS -#ifdef BACKUP_FAIL_OK - state.backup_possible = dir_access != -1; -#else /* !BACKUP_FAIL_OK */ - if (dir_access == -1) +#ifdef __OpenBSD__ + if (unveil(NULL, NULL) < 0) { - errno = dir_access_errno; - goto file_error; + perror("sled: unveil"); + exit(1); } -#endif /* BACKUP_FAIL_OK */ -#endif /* CREATE_BACKUPS */ +#endif + + if (access(state.filename, F_OK | R_OK | W_OK) == -1) + goto file_error; - if (access(filename, F_OK | R_OK | W_OK) == -1) + if (state.rfn_len > 0 + && access(state.real_filename, F_OK | R_OK | W_OK) == -1) goto file_error; result = load_file(&state, &lineno, &col);