aboutsummaryrefslogtreecommitdiffstats
path: root/util-src/pposix.c
diff options
context:
space:
mode:
authorKim Alvefur <zash@zash.se>2017-03-01 01:33:00 +0100
committerKim Alvefur <zash@zash.se>2017-03-01 01:33:00 +0100
commite8b57d789b10a6aeb152483526b76bbda0b2b194 (patch)
tree9344d0c5bc095c10d86c6a7c18b657c7a4570105 /util-src/pposix.c
parent9c6c576f099d4f4f71edde0c47df60178b888afb (diff)
downloadprosody-e8b57d789b10a6aeb152483526b76bbda0b2b194.tar.gz
prosody-e8b57d789b10a6aeb152483526b76bbda0b2b194.zip
util.pposix: Add function for atomically appending data to a file
Diffstat (limited to 'util-src/pposix.c')
-rw-r--r--util-src/pposix.c65
1 files changed, 64 insertions, 1 deletions
diff --git a/util-src/pposix.c b/util-src/pposix.c
index 4b4552f5..a68aa89d 100644
--- a/util-src/pposix.c
+++ b/util-src/pposix.c
@@ -13,7 +13,7 @@
* POSIX support functions for Lua
*/
-#define MODULE_VERSION "0.3.6"
+#define MODULE_VERSION "0.3.7"
#if defined(__linux__)
@@ -788,6 +788,68 @@ int lc_fallocate(lua_State *L) {
}
}
+/*
+ * Append some data to a file handle
+ * Attempt to allocate space first
+ * Truncate to original size on failure
+ */
+int lc_atomic_append(lua_State *L) {
+ int err;
+ size_t len;
+
+ FILE *f = *(FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE);
+ const char *data = luaL_checklstring(L, 2, &len);
+
+ off_t offset = ftell(f);
+
+#if defined(__linux__)
+ /* Try to allocate space without changing the file size. */
+ if((err = fallocate(fileno(f), FALLOC_FL_KEEP_SIZE, offset, len))) {
+ if(errno != 0) {
+ /* Some old versions of Linux apparently use the return value instead of errno */
+ err = errno;
+ }
+ switch(err) {
+ case ENOSYS: /* Kernel doesn't implement fallocate */
+ case EOPNOTSUPP: /* Filesystem doesn't support it */
+ /* Ignore and proceed to try to write */
+ break;
+
+ case ENOSPC: /* No space left */
+ default: /* Other issues */
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(err));
+ lua_pushinteger(L, err);
+ return 3;
+ }
+ }
+#endif
+
+ if(fwrite(data, sizeof(char), len, f) == len) {
+ if(fflush(f) == 0) {
+ lua_pushboolean(L, 1); /* Great success! */
+ return 1;
+ } else {
+ err = errno;
+ }
+ } else {
+ err = ferror(f);
+ }
+
+ fseek(f, offset, SEEK_SET);
+
+ /* Cut partially written data */
+ if(ftruncate(fileno(f), offset)) {
+ /* The file is now most likely corrupted, throw hard error */
+ return luaL_error(L, "atomic_append() failed in ftruncate(): %s", strerror(errno));
+ }
+
+ lua_pushnil(L);
+ lua_pushstring(L, strerror(err));
+ lua_pushinteger(L, err);
+ return 3;
+}
+
/* Register functions */
int luaopen_util_pposix(lua_State *L) {
@@ -828,6 +890,7 @@ int luaopen_util_pposix(lua_State *L) {
#endif
{ "fallocate", lc_fallocate },
+ { "atomic_append", lc_atomic_append },
{ NULL, NULL }
};