From 3720a61daf2e03e42e34902c2636ce3e3d6b8485 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 9 Oct 2016 23:04:16 +0200 Subject: ifupdown: rewrite state file atomically By user's request. Decided to not use fcntl(F_SETLKW) in lieu of problems with locking on networked filesystems. The existence of /var/run/ifstate.new is treated as a write lock. rename() provides atomicity. function old new delta ifupdown_main 1019 1122 +103 Signed-off-by: Denys Vlasenko --- networking/ifupdown.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/networking/ifupdown.c b/networking/ifupdown.c index b0bc0d70f..1d0fc53cf 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c @@ -56,6 +56,7 @@ #endif #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS +#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH #define debug_noise(args...) /*fprintf(stderr, args)*/ @@ -1200,7 +1201,7 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface) static llist_t *read_iface_state(void) { llist_t *state_list = NULL; - FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH); + FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH); if (state_fp) { char *start, *end_ptr; @@ -1215,6 +1216,38 @@ static llist_t *read_iface_state(void) return state_list; } +/* read the previous state from the state file */ +static FILE *open_new_state_file(void) +{ + int fd, flags, cnt; + + cnt = 0; + flags = (O_WRONLY | O_CREAT | O_EXCL); + for (;;) { + fd = open(IFSTATE_FILE_PATH".new", flags, 0666); + if (fd >= 0) + break; + if (errno != EEXIST + || flags == (O_WRONLY | O_CREAT | O_TRUNC) + ) { + bb_perror_msg_and_die("can't open '%s'", + IFSTATE_FILE_PATH".new"); + } + /* Someone else created the .new file */ + if (cnt > 30 * 1000) { + /* Waited for 30*30/2 = 450 milliseconds, still EEXIST. + * Assuming a stale file, rewriting it. + */ + flags = (O_WRONLY | O_CREAT | O_TRUNC); + continue; + } + usleep(cnt); + cnt += 1000; + } + + return xfdopen_for_write(fd); +} + int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ifupdown_main(int argc UNUSED_PARAM, char **argv) @@ -1348,7 +1381,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv) any_failures = 1; } else if (!NO_ACT) { /* update the state file */ - FILE *state_fp; + FILE *new_state_fp = open_new_state_file(); llist_t *state; llist_t *state_list = read_iface_state(); llist_t *iface_state = find_iface_state(state_list, iface); @@ -1368,15 +1401,15 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv) } /* Actually write the new state */ - state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH); state = state_list; while (state) { if (state->data) { - fprintf(state_fp, "%s\n", state->data); + fprintf(new_state_fp, "%s\n", state->data); } state = state->link; } - fclose(state_fp); + fclose(new_state_fp); + xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH); llist_free(state_list, free); } next: -- cgit v1.2.3