чување 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:
| M | draw.c | | | 21 | ++++++++++++++------- |
| M | draw.h | | | 8 | +++++--- |
| M | sled.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);