summaryrefslogtreecommitdiffstats
path: root/osi
diff options
context:
space:
mode:
Diffstat (limited to 'osi')
-rw-r--r--osi/src/config.c91
-rw-r--r--osi/src/eager_reader.c4
-rw-r--r--osi/src/reactor.c2
-rw-r--r--osi/src/semaphore.c6
-rw-r--r--osi/src/socket.c10
-rw-r--r--osi/test/alarm_test.cpp2
-rw-r--r--osi/test/atomic_test.cpp6
-rw-r--r--osi/test/eager_reader_test.cpp4
-rw-r--r--osi/test/reactor_test.cpp4
9 files changed, 95 insertions, 34 deletions
diff --git a/osi/src/config.c b/osi/src/config.c
index b342a2e9b..63a43f810 100644
--- a/osi/src/config.c
+++ b/osi/src/config.c
@@ -21,11 +21,13 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/types.h>
#include "osi/include/allocator.h"
#include "osi/include/config.h"
@@ -282,16 +284,39 @@ bool config_save(const config_t *config, const char *filename) {
assert(filename != NULL);
assert(*filename != '\0');
- char *temp_filename = osi_calloc(strlen(filename) + 5);
- if (!temp_filename) {
- LOG_ERROR("%s unable to allocate memory for filename.", __func__);
- return false;
+ // Steps to ensure content of config file gets to disk:
+ //
+ // 1) Open and write to temp file (e.g. bt_config.conf.new).
+ // 2) Sync the temp file to disk with fsync().
+ // 3) Rename temp file to actual config file (e.g. bt_config.conf).
+ // This ensures atomic update.
+ // 4) Sync directory that has the conf file with fsync().
+ // This ensures directory entries are up-to-date.
+ int dir_fd = -1;
+ FILE *fp = NULL;
+
+ // Build temp config file based on config file (e.g. bt_config.conf.new).
+ static const char *temp_file_ext = ".new";
+ const int filename_len = strlen(filename);
+ const int temp_filename_len = filename_len + strlen(temp_file_ext) + 1;
+ char *temp_filename = osi_calloc(temp_filename_len);
+ snprintf(temp_filename, temp_filename_len, "%s%s", filename, temp_file_ext);
+
+ // Extract directory from file path (e.g. /data/misc/bluedroid).
+ char *temp_dirname = osi_strdup(filename);
+ const char *directoryname = dirname(temp_dirname);
+ if (!directoryname) {
+ LOG_ERROR("%s error extracting directory from '%s': %s", __func__, filename, strerror(errno));
+ goto error;
}
- strcpy(temp_filename, filename);
- strcat(temp_filename, ".new");
+ dir_fd = TEMP_FAILURE_RETRY(open(directoryname, O_RDONLY));
+ if (dir_fd < 0) {
+ LOG_ERROR("%s unable to open dir '%s': %s", __func__, directoryname, strerror(errno));
+ goto error;
+ }
- FILE *fp = fopen(temp_filename, "wt");
+ fp = fopen(temp_filename, "wt");
if (!fp) {
LOG_ERROR("%s unable to write file '%s': %s", __func__, temp_filename, strerror(errno));
goto error;
@@ -299,20 +324,38 @@ bool config_save(const config_t *config, const char *filename) {
for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) {
const section_t *section = (const section_t *)list_node(node);
- fprintf(fp, "[%s]\n", section->name);
+ if (fprintf(fp, "[%s]\n", section->name) < 0) {
+ LOG_ERROR("%s unable to write to file '%s': %s", __func__, temp_filename, strerror(errno));
+ goto error;
+ }
for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) {
const entry_t *entry = (const entry_t *)list_node(enode);
- fprintf(fp, "%s = %s\n", entry->key, entry->value);
+ if (fprintf(fp, "%s = %s\n", entry->key, entry->value) < 0) {
+ LOG_ERROR("%s unable to write to file '%s': %s", __func__, temp_filename, strerror(errno));
+ goto error;
+ }
}
// Only add a separating newline if there are more sections.
- if (list_next(node) != list_end(config->sections))
- fputc('\n', fp);
+ if (list_next(node) != list_end(config->sections)) {
+ if (fputc('\n', fp) == EOF) {
+ LOG_ERROR("%s unable to write to file '%s': %s", __func__, temp_filename, strerror(errno));
+ goto error;
+ }
+ }
}
- fflush(fp);
- fclose(fp);
+ // Sync written temp file out to disk. fsync() is blocking until data makes it to disk.
+ if (fsync(fileno(fp)) < 0) {
+ LOG_WARN("%s unable to fsync file '%s': %s", __func__, temp_filename, strerror(errno));
+ }
+
+ if (fclose(fp) == EOF) {
+ LOG_ERROR("%s unable to close file '%s': %s", __func__, temp_filename, strerror(errno));
+ goto error;
+ }
+ fp = NULL;
// Change the file's permissions to Read/Write by User and Group
if (chmod(temp_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) {
@@ -320,17 +363,35 @@ bool config_save(const config_t *config, const char *filename) {
goto error;
}
+ // Rename written temp file to the actual config file.
if (rename(temp_filename, filename) == -1) {
LOG_ERROR("%s unable to commit file '%s': %s", __func__, filename, strerror(errno));
goto error;
}
+ // This should ensure the directory is updated as well.
+ if (fsync(dir_fd) < 0) {
+ LOG_WARN("%s unable to fsync dir '%s': %s", __func__, directoryname, strerror(errno));
+ }
+
+ if (close(dir_fd) < 0) {
+ LOG_ERROR("%s unable to close dir '%s': %s", __func__, directoryname, strerror(errno));
+ goto error;
+ }
+
osi_free(temp_filename);
+ osi_free(temp_dirname);
return true;
-error:;
+error:
+ // This indicates there is a write issue. Unlink as partial data is not acceptable.
unlink(temp_filename);
+ if (fp)
+ fclose(fp);
+ if (dir_fd != -1)
+ close(dir_fd);
osi_free(temp_filename);
+ osi_free(temp_dirname);
return false;
}
diff --git a/osi/src/eager_reader.c b/osi/src/eager_reader.c
index 3ca8ad13c..e93947d51 100644
--- a/osi/src/eager_reader.c
+++ b/osi/src/eager_reader.c
@@ -228,7 +228,7 @@ static bool has_byte(const eager_reader_t *reader) {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
- select(reader->bytes_available_fd + 1, &read_fds, NULL, NULL, &timeout);
+ TEMP_FAILURE_RETRY(select(reader->bytes_available_fd + 1, &read_fds, NULL, NULL, &timeout));
return FD_ISSET(reader->bytes_available_fd, &read_fds);
}
@@ -244,7 +244,7 @@ static void inbound_data_waiting(void *context) {
buffer->length = 0;
buffer->offset = 0;
- int bytes_read = read(reader->inbound_fd, buffer->data, reader->buffer_size);
+ int bytes_read = TEMP_FAILURE_RETRY(read(reader->inbound_fd, buffer->data, reader->buffer_size));
if (bytes_read > 0) {
// Save the data for later
buffer->length = bytes_read;
diff --git a/osi/src/reactor.c b/osi/src/reactor.c
index c3d54c111..1650c1d7b 100644
--- a/osi/src/reactor.c
+++ b/osi/src/reactor.c
@@ -259,7 +259,7 @@ static reactor_status_t run_reactor(reactor_t *reactor, int iterations) {
int ret;
do {
- ret = epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1);
+ ret = TEMP_FAILURE_RETRY(epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1));
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
diff --git a/osi/src/semaphore.c b/osi/src/semaphore.c
index 5ee992649..76fba0d55 100644
--- a/osi/src/semaphore.c
+++ b/osi/src/semaphore.c
@@ -73,12 +73,12 @@ bool semaphore_try_wait(semaphore_t *semaphore) {
assert(semaphore != NULL);
assert(semaphore->fd != INVALID_FD);
- int flags = fcntl(semaphore->fd, F_GETFL);
+ int flags = TEMP_FAILURE_RETRY(fcntl(semaphore->fd, F_GETFL));
if (flags == -1) {
LOG_ERROR("%s unable to get flags for semaphore fd: %s", __func__, strerror(errno));
return false;
}
- if (fcntl(semaphore->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ if (TEMP_FAILURE_RETRY(fcntl(semaphore->fd, F_SETFL, flags | O_NONBLOCK)) == -1) {
LOG_ERROR("%s unable to set O_NONBLOCK for semaphore fd: %s", __func__, strerror(errno));
return false;
}
@@ -87,7 +87,7 @@ bool semaphore_try_wait(semaphore_t *semaphore) {
if (eventfd_read(semaphore->fd, &value) == -1)
return false;
- if (fcntl(semaphore->fd, F_SETFL, flags) == -1)
+ if (TEMP_FAILURE_RETRY(fcntl(semaphore->fd, F_SETFL, flags)) == -1)
LOG_ERROR("%s unable to resetore flags for semaphore fd: %s", __func__, strerror(errno));
return true;
}
diff --git a/osi/src/socket.c b/osi/src/socket.c
index 91f084e79..9905ae300 100644
--- a/osi/src/socket.c
+++ b/osi/src/socket.c
@@ -121,7 +121,7 @@ bool socket_listen(const socket_t *socket, port_t port) {
socket_t *socket_accept(const socket_t *socket) {
assert(socket != NULL);
- int fd = accept(socket->fd, NULL, NULL);
+ int fd = TEMP_FAILURE_RETRY(accept(socket->fd, NULL, NULL));
if (fd == INVALID_FD) {
LOG_ERROR("%s unable to accept socket: %s", __func__, strerror(errno));
return NULL;
@@ -142,14 +142,14 @@ ssize_t socket_read(const socket_t *socket, void *buf, size_t count) {
assert(socket != NULL);
assert(buf != NULL);
- return recv(socket->fd, buf, count, MSG_DONTWAIT);
+ return TEMP_FAILURE_RETRY(recv(socket->fd, buf, count, MSG_DONTWAIT));
}
ssize_t socket_write(const socket_t *socket, const void *buf, size_t count) {
assert(socket != NULL);
assert(buf != NULL);
- return send(socket->fd, buf, count, MSG_DONTWAIT);
+ return TEMP_FAILURE_RETRY(send(socket->fd, buf, count, MSG_DONTWAIT));
}
ssize_t socket_write_and_transfer_fd(const socket_t *socket, const void *buf, size_t count, int fd) {
@@ -179,7 +179,7 @@ ssize_t socket_write_and_transfer_fd(const socket_t *socket, const void *buf, si
header->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(header) = fd;
- ssize_t ret = sendmsg(socket->fd, &msg, MSG_DONTWAIT);
+ ssize_t ret = TEMP_FAILURE_RETRY(sendmsg(socket->fd, &msg, MSG_DONTWAIT));
close(fd);
return ret;
}
@@ -188,7 +188,7 @@ ssize_t socket_bytes_available(const socket_t *socket) {
assert(socket != NULL);
int size = 0;
- if (ioctl(socket->fd, FIONREAD, &size) == -1)
+ if (TEMP_FAILURE_RETRY(ioctl(socket->fd, FIONREAD, &size)) == -1)
return -1;
return size;
}
diff --git a/osi/test/alarm_test.cpp b/osi/test/alarm_test.cpp
index 287d40812..fec828f90 100644
--- a/osi/test/alarm_test.cpp
+++ b/osi/test/alarm_test.cpp
@@ -32,7 +32,7 @@ static int cb_counter;
static const uint64_t EPSILON_MS = 5;
static void msleep(uint64_t ms) {
- usleep(ms * 1000);
+ TEMP_FAILURE_RETRY(usleep(ms * 1000));
}
class AlarmTest : public AlarmTestHarness {
diff --git a/osi/test/atomic_test.cpp b/osi/test/atomic_test.cpp
index b0039ab3f..6cde546e5 100644
--- a/osi/test/atomic_test.cpp
+++ b/osi/test/atomic_test.cpp
@@ -17,7 +17,7 @@ struct atomic_test_s32_s {
void *atomic_thread(void *context) {
struct atomic_test_s32_s *at = (struct atomic_test_s32_s *)context;
for (int i = 0; i < at->max_val; i++) {
- usleep(1);
+ TEMP_FAILURE_RETRY(usleep(1));
atomic_inc_prefix_s32(&at->data[i]);
}
return NULL;
@@ -26,9 +26,9 @@ void *atomic_thread(void *context) {
void *atomic_thread_inc_dec(void *context) {
struct atomic_test_s32_s *at = (struct atomic_test_s32_s *)context;
for (int i = 0; i < at->max_val; i++) {
- usleep(1);
+ TEMP_FAILURE_RETRY(usleep(1));
atomic_inc_prefix_s32(&at->data[i]);
- usleep(1);
+ TEMP_FAILURE_RETRY(usleep(1));
atomic_dec_prefix_s32(&at->data[i]);
}
return NULL;
diff --git a/osi/test/eager_reader_test.cpp b/osi/test/eager_reader_test.cpp
index ad00e17b0..d979f42a6 100644
--- a/osi/test/eager_reader_test.cpp
+++ b/osi/test/eager_reader_test.cpp
@@ -126,7 +126,7 @@ TEST_F(EagerReaderTest, test_small_data) {
thread_t *read_thread = thread_new("read_thread");
eager_reader_register(reader, thread_get_reactor(read_thread), expect_data, (void *)small_data);
- write(pipefd[1], small_data, strlen(small_data));
+ TEMP_FAILURE_RETRY(write(pipefd[1], small_data, strlen(small_data)));
semaphore_wait(done);
eager_reader_free(reader);
@@ -139,7 +139,7 @@ TEST_F(EagerReaderTest, test_large_data_multibyte) {
thread_t *read_thread = thread_new("read_thread");
eager_reader_register(reader, thread_get_reactor(read_thread), expect_data_multibyte, (void *)large_data);
- write(pipefd[1], large_data, strlen(large_data));
+ TEMP_FAILURE_RETRY(write(pipefd[1], large_data, strlen(large_data)));
semaphore_wait(done);
eager_reader_free(reader);
diff --git a/osi/test/reactor_test.cpp b/osi/test/reactor_test.cpp
index 6e3a0092a..73a6ae07b 100644
--- a/osi/test/reactor_test.cpp
+++ b/osi/test/reactor_test.cpp
@@ -64,7 +64,7 @@ TEST_F(ReactorTest, reactor_start_wait_stop) {
reactor_t *reactor = reactor_new();
spawn_reactor_thread(reactor);
- usleep(50 * 1000);
+ TEMP_FAILURE_RETRY(usleep(50 * 1000));
EXPECT_TRUE(thread_running);
reactor_stop(reactor);
@@ -108,7 +108,7 @@ TEST_F(ReactorTest, reactor_unregister_from_separate_thread) {
reactor_object_t *object = reactor_register(reactor, fd, NULL, NULL, NULL);
spawn_reactor_thread(reactor);
- usleep(50 * 1000);
+ TEMP_FAILURE_RETRY(usleep(50 * 1000));
reactor_unregister(object);
reactor_stop(reactor);