diff --git a/src/core/fileobj.c b/src/core/fileobj.c index a9c7146..e5b8ce8 100644 --- a/src/core/fileobj.c +++ b/src/core/fileobj.c @@ -405,7 +405,10 @@ fileobj_getsize(fileobj_t *fobj) int fileobj_setsize(fileobj_t *fobj, size_t len) { - if (fileobj_dataload(fobj) == -1) { + /* + * Only load the data if truncating to non-zero. + */ + if (len && fileobj_dataload(fobj) == -1) { app_elog(LOG_DEBUG, "%s: fileobj_dataload() failed", __func__); errno = EIO; return -1; @@ -415,7 +418,7 @@ fileobj_setsize(fileobj_t *fobj, size_t len) * Note: if new length is zero, then sbuffer_move() will free the * old buffer and will return NULL. */ - if (len && sbuffer_move(&fobj->sbuf, len, 0) == NULL) { + if (sbuffer_move(&fobj->sbuf, len, 0) == NULL && len) { app_elog(LOG_DEBUG, "%s: sbuffer_move() failed", __func__); return -1; } diff --git a/src/core/storage.c b/src/core/storage.c index c2ffb84..30fac86 100644 --- a/src/core/storage.c +++ b/src/core/storage.c @@ -208,6 +208,7 @@ storage_map_obj(rvault_t *vault, int fd, size_t file_len) return NULL; } if ((hdr = safe_mmap(file_len, fd, 0)) == NULL) { + app_elog(LOG_DEBUG, "%s: mmap() failed", __func__); return NULL; } aetag_len = crypto_get_aetaglen(vault->crypto); @@ -325,8 +326,10 @@ storage_read_data(rvault_t *vault, int fd, size_t file_len, sbuffer_t *sbuf) if (FILEOBJ_EDATA_LEN(hdr) == 0) { /* * Note: it is currently an error to have no encrypted data. - * Empty file is represented as an empty file. + * Empty file is represented as a fully empty file without + * the header. */ + app_elog(LOG_DEBUG, "%s: edata length is zero", __func__); goto out; } memset(&tmpsbuf, 0, sizeof(sbuffer_t)); diff --git a/src/fuse/rvaultfs.c b/src/fuse/rvaultfs.c index cef4587..44986d4 100644 --- a/src/fuse/rvaultfs.c +++ b/src/fuse/rvaultfs.c @@ -100,7 +100,13 @@ rvaultfs_truncate(const char *path, off_t size) if (size < 0) { return -EINVAL; } - if ((fobj = fileobj_open(vault, path, O_WRONLY, FOBJ_OMASK)) == NULL) { + + /* + * Note: use O_RDWR instead of O_WRONLY, since the data might have + * to be loaded before truncation (e.g. in order decompress and + * determine the correct offset). + */ + if ((fobj = fileobj_open(vault, path, O_RDWR, FOBJ_OMASK)) == NULL) { return -errno; } if (fileobj_setsize(fobj, (size_t)size) == -1) { diff --git a/src/tests/t_fileobj.c b/src/tests/t_fileobj.c index 8469812..80aced1 100644 --- a/src/tests/t_fileobj.c +++ b/src/tests/t_fileobj.c @@ -23,6 +23,23 @@ #define TEST_BLOCK_COUNT 128 #define TEST_BLOCK_SIZE (32U * 1024) // 32 KB +static void +test_file_basic(rvault_t *vault) +{ + fileobj_t *fobj; + ssize_t nbytes; + + fobj = fileobj_open(vault, "/basic", O_CREAT | O_RDWR, FOBJ_OMASK); + assert(fobj != NULL); + fileobj_close(fobj); + + fobj = fileobj_open(vault, "/basic", O_RDONLY, FOBJ_OMASK); + assert(fobj != NULL); + nbytes = fileobj_getsize(fobj); + assert(nbytes == 0); + fileobj_close(fobj); +} + static void test_file_expand(rvault_t *vault) { @@ -118,7 +135,7 @@ test_file_onebyte(rvault_t *vault) } static void -test_file_zero(rvault_t *vault) +test_file_setsize(rvault_t *vault) { static const unsigned zeros[16]; unsigned buf[16]; @@ -150,6 +167,23 @@ test_file_zero(rvault_t *vault) nbytes = fileobj_pread(fobj, buf, sizeof(buf), 0); assert(nbytes == 0); fileobj_close(fobj); + + /* + * Write some data, shrink size, expand, check. + */ + fobj = fileobj_open(vault, "/empty", O_CREAT | O_RDWR, FOBJ_OMASK); + assert(fobj != NULL); + + nbytes = fileobj_pwrite(fobj, (const void *)"ab", 2, 0); + assert(nbytes == 2); + + fileobj_setsize(fobj, 1); + fileobj_setsize(fobj, 3); + + nbytes = fileobj_pread(fobj, buf, sizeof(buf), 0); + assert(nbytes == 3); + assert(memcmp(buf, "a\0\0", 3) == 0); + fileobj_close(fobj); } static void @@ -157,9 +191,10 @@ run_tests(const char *cipher) { char *base_path = NULL; rvault_t *vault = mock_get_vault(cipher, &base_path); + test_file_basic(vault); test_file_expand(vault); test_file_onebyte(vault); - test_file_zero(vault); + test_file_setsize(vault); mock_cleanup_vault(vault, base_path); }