diff --git a/file.c b/file.c index 6840cfc..72de612 100644 --- a/file.c +++ b/file.c @@ -816,15 +816,15 @@ static void uci_file_commit(struct uci_context *ctx, struct uci_package **packag done: free(name); free(path); - uci_close_stream(f1); if (do_rename) { path = realpath(p->path, NULL); if (!path || stat(path, &statbuf) || chmod(filename, statbuf.st_mode) || rename(filename, path)) { unlink(filename); - UCI_THROW(ctx, UCI_ERR_IO); + ctx->err = UCI_ERR_IO; } free(path); } + uci_close_stream(f1); if (ctx->err) UCI_THROW(ctx, ctx->err); } diff --git a/util.c b/util.c index 61e42cd..2149894 100644 --- a/util.c +++ b/util.c @@ -218,9 +218,32 @@ __private FILE *uci_open_stream(struct uci_context *ctx, const char *filename, c if (fd < 0) goto error; - ret = flock(fd, (write ? LOCK_EX : LOCK_SH)); - if ((ret < 0) && (errno != ENOSYS)) - goto error_close; + if (write) { + ret = flock(fd, LOCK_EX); + if ((ret < 0) && (errno != ENOSYS)) + goto error_close; + } else { + while (1) { + ret = flock(fd, LOCK_SH | LOCK_NB); + if (ret == 0 || errno == ENOSYS) + break; + + if (errno == EINTR) + continue; + + if (errno == EWOULDBLOCK || errno == EAGAIN) { + /* Reopen to follow a potentially replaced inode while writer holds lock */ + close(fd); + usleep(1000); + fd = open(filename, flags, mode); + if (fd < 0) + goto error; + continue; + } + + goto error_close; + } + } ret = lseek(fd, 0, pos);