diff --git a/lfs.c b/lfs.c index cde549a..536bad6 100644 --- a/lfs.c +++ b/lfs.c @@ -2527,6 +2527,7 @@ static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file, #ifndef LFS_READONLY } else if (flags & LFS_O_TRUNC) { // truncate if requested + // always mark dirty in case we have custom attributes tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0); file->flags |= LFS_F_DIRTY; #endif @@ -2878,7 +2879,7 @@ static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) { } } - file->flags &= ~(LFS_F_DIRTY | LFS_F_WRITING | LFS_F_READING); + f->flags &= ~(LFS_F_DIRTY | LFS_F_WRITING | LFS_F_READING); } } diff --git a/lfs.h b/lfs.h index 8e3aa8b..4fa3c97 100644 --- a/lfs.h +++ b/lfs.h @@ -294,10 +294,10 @@ struct lfs_file_config { void *buffer; // Optional list of custom attributes related to the file. If the file - // is opened with read access, these attributes will be read from disk - // during the open call. If the file is opened with write access, the - // attributes will be written to disk every file sync or close. This - // write occurs atomically with update to the file's contents. + // is opened for reading, these attributes will be read from disk during + // open. If the file is open for writing, these attribute will be atomically + // written to disk when the file is written to disk. Note that these + // attributes are not written unless the file is modified. // // Custom attributes are uniquely identified by an 8-bit type and limited // to LFS_ATTR_MAX bytes. If the stored attribute is larger than the diff --git a/tests/test_interspersed.toml b/tests/test_interspersed.toml index 87a0578..2e042fb 100644 --- a/tests/test_interspersed.toml +++ b/tests/test_interspersed.toml @@ -1,7 +1,7 @@ [[case]] # interspersed file test define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +define.FILES = [4, 10, 26] code = ''' lfs_file_t files[FILES]; const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; @@ -55,7 +55,7 @@ code = ''' for (int j = 0; j < FILES; j++) { lfs_file_close(&lfs, &files[j]); } - + lfs_unmount(&lfs) => 0; ''' @@ -108,7 +108,7 @@ code = ''' assert(buffer[0] == '~'); } lfs_file_close(&lfs, &file); - + lfs_unmount(&lfs) => 0; ''' @@ -168,13 +168,13 @@ code = ''' } lfs_file_close(&lfs, &files[0]); lfs_file_close(&lfs, &files[1]); - + lfs_unmount(&lfs) => 0; ''' [[case]] # reentrant interspersed file test define.SIZE = [10, 100] -define.FILES = [4, 10, 26] +define.FILES = [4, 10, 26] reentrant = true code = ''' lfs_file_t files[FILES]; @@ -239,6 +239,268 @@ code = ''' for (int j = 0; j < FILES; j++) { lfs_file_close(&lfs, &files[j]); } - + + lfs_unmount(&lfs) => 0; +''' + +[[case]] # open same file reading from separate file handles +define.READERS = 3 +define.SIZE = [10, 100, 1000, 10000] +define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR'] +code = ''' + const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; + lfs_format(&lfs, &cfg) => 0; + lfs_mount(&lfs, &cfg) => 0; + lfs_file_open(&lfs, &file, "shared", LFS_O_CREAT | LFS_O_WRONLY) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1; + } + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + // open all files + lfs_mount(&lfs, &cfg) => 0; + lfs_file_t readers[READERS]; + for (int i = 0; i < READERS; i++) { + lfs_file_open(&lfs, &readers[i], "shared", RDMODE) => 0; + } + + // perform operations while all readers are open + for (int i = 0; i < READERS; i++) { + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == alphas[j % 26]); + } + } + + for (int i = 0; i < READERS; i++) { + lfs_file_close(&lfs, &readers[i]) => 0; + } + lfs_unmount(&lfs) => 0; +''' + +[[case]] # open same file reading and writing from separate file handles +define.READERS = 3 +define.SIZE = [10, 100, 1000, 10000] +define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR'] +define.WRMODE = ['LFS_O_WRONLY', 'LFS_O_RDWR'] +code = ''' + const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; + const char nums[] = "0123456789"; + lfs_format(&lfs, &cfg) => 0; + lfs_mount(&lfs, &cfg) => 0; + lfs_file_open(&lfs, &file, "shared", LFS_O_CREAT | LFS_O_WRONLY) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1; + } + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + // open all files + lfs_mount(&lfs, &cfg) => 0; + lfs_file_t writer; + lfs_file_t readers[READERS]; + lfs_file_open(&lfs, &writer, "shared", WRMODE) => 0; + for (int i = 0; i < READERS; i++) { + lfs_file_open(&lfs, &readers[i], "shared", RDMODE) => 0; + } + + // perform operations while all readers are open + for (int j = 0; j < SIZE; j++) { + lfs_file_write(&lfs, &writer, &nums[j % 10], 1) => 1; + } + + for (int i = 0; i < READERS; i++) { + for (int j = 0; j < SIZE/2; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == alphas[j % 26]); + } + } + + // sync, now write should reflect in all open files + lfs_file_sync(&lfs, &writer) => 0; + + for (int i = 0; i < READERS; i++) { + for (int j = SIZE/2; j < SIZE; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } + + // double check our writer reflects its own changes + if (WRMODE == LFS_O_RDWR) { + lfs_file_rewind(&lfs, &writer) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &writer, buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } + + for (int i = 0; i < READERS; i++) { + lfs_file_close(&lfs, &readers[i]) => 0; + } + lfs_unmount(&lfs) => 0; +''' + +[[case]] # check that attributes are updated in open files +define.READERS = 3 +define.SIZE = 10 +define.RDMODE = ['LFS_O_RDONLY', 'LFS_O_RDWR'] +define.WRMODE = ['LFS_O_WRONLY', 'LFS_O_RDWR'] +code = ''' + const char alphas[] = "abcdefghijklmnopqrstuvwxyz"; + const char nums[] = "0123456789"; + lfs_format(&lfs, &cfg) => 0; + lfs_mount(&lfs, &cfg) => 0; + const struct lfs_file_config filecfg = { + .attr_count = 3, + .attrs = (struct lfs_attr[]){ + {'A', "a", 1}, + {'B', "bb", 2}, + {'C', "ccc", 3}, + }, + }; + lfs_file_opencfg(&lfs, &file, "shared", + LFS_O_CREAT | LFS_O_WRONLY, &filecfg) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_write(&lfs, &file, &alphas[j % 26], 1) => 1; + } + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; + + // open all files + lfs_mount(&lfs, &cfg) => 0; + lfs_file_t writer; + const struct lfs_file_config writercfg = { + .attr_count = 3, + .attrs = (struct lfs_attr[]){ + {'A', &(uint8_t[1]){0}, 1}, + {'B', &(uint8_t[2]){0}, 2}, + {'C', &(uint8_t[3]){0}, 3}}}; + lfs_file_t readers[READERS]; + const struct lfs_file_config readercfgs[READERS] = { + { .attr_count = 3, + .attrs = (struct lfs_attr[]){ + {'A', &(uint8_t[1]){0}, 1}, + {'B', &(uint8_t[2]){0}, 2}, + {'C', &(uint8_t[3]){0}, 3}}}, + { .attr_count = 3, + .attrs = (struct lfs_attr[]){ + {'A', &(uint8_t[1]){0}, 1}, + {'B', &(uint8_t[2]){0}, 2}, + {'C', &(uint8_t[3]){0}, 3}}}, + { .attr_count = 3, + .attrs = (struct lfs_attr[]){ + {'A', &(uint8_t[1]){0}, 1}, + {'B', &(uint8_t[2]){0}, 2}, + {'C', &(uint8_t[3]){0}, 3}}}}; + lfs_file_opencfg(&lfs, &writer, "shared", + WRMODE, &writercfg) => 0; + for (int i = 0; i < READERS; i++) { + lfs_file_opencfg(&lfs, &readers[i], "shared", + RDMODE, &readercfgs[i]) => 0; + } + + // perform operations while all readers are open + writercfg.attrs[0].size = 1; + memcpy(writercfg.attrs[0].buffer, "0", 1); + writercfg.attrs[1].size = 2; + memcpy(writercfg.attrs[1].buffer, "11", 2); + writercfg.attrs[2].size = 3; + memcpy(writercfg.attrs[2].buffer, "222", 3); + for (int j = 0; j < SIZE; j++) { + lfs_file_write(&lfs, &writer, &nums[j % 10], 1) => 1; + } + + for (int i = 0; i < READERS; i++) { + assert(readercfgs[i].attrs[0].size == 1); + assert(memcmp(readercfgs[i].attrs[0].buffer, "a", 1) == 0); + assert(readercfgs[i].attrs[1].size == 2); + assert(memcmp(readercfgs[i].attrs[1].buffer, "bb", 2) == 0); + assert(readercfgs[i].attrs[2].size == 3); + assert(memcmp(readercfgs[i].attrs[2].buffer, "ccc", 3) == 0); + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == alphas[j % 26]); + } + } + + // sync, now write should reflect in all open files + lfs_file_sync(&lfs, &writer) => 0; + + for (int i = 0; i < READERS; i++) { + assert(readercfgs[i].attrs[0].size == 1); + assert(memcmp(readercfgs[i].attrs[0].buffer, "0", 1) == 0); + assert(readercfgs[i].attrs[1].size == 2); + assert(memcmp(readercfgs[i].attrs[1].buffer, "11", 2) == 0); + assert(readercfgs[i].attrs[2].size == 3); + assert(memcmp(readercfgs[i].attrs[2].buffer, "222", 3) == 0); + lfs_file_rewind(&lfs, &readers[i]) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } + + // double check our writer reflects its own changes + if (WRMODE == LFS_O_RDWR) { + assert(writercfg.attrs[0].size == 1); + assert(memcmp(writercfg.attrs[0].buffer, "0", 1) == 0); + assert(writercfg.attrs[1].size == 2); + assert(memcmp(writercfg.attrs[1].buffer, "11", 2) == 0); + assert(writercfg.attrs[2].size == 3); + assert(memcmp(writercfg.attrs[2].buffer, "222", 3) == 0); + lfs_file_rewind(&lfs, &writer) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &writer, buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } + + // now try explicit lfs_setattr calls, this should still update open files + lfs_setattr(&lfs, "shared", 'A', "A", 1) => 0; + lfs_setattr(&lfs, "shared", 'B', "BB", 2) => 0; + lfs_setattr(&lfs, "shared", 'C', "CCC", 3) => 0; + + for (int i = 0; i < READERS; i++) { + assert(readercfgs[i].attrs[0].size == 1); + assert(memcmp(readercfgs[i].attrs[0].buffer, "A", 1) == 0); + assert(readercfgs[i].attrs[1].size == 2); + assert(memcmp(readercfgs[i].attrs[1].buffer, "BB", 2) == 0); + assert(readercfgs[i].attrs[2].size == 3); + assert(memcmp(readercfgs[i].attrs[2].buffer, "CCC", 3) == 0); + lfs_file_rewind(&lfs, &readers[i]) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &readers[i], buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } + + if (WRMODE == LFS_O_RDWR) { + assert(writercfg.attrs[0].size == 1); + assert(memcmp(writercfg.attrs[0].buffer, "A", 1) == 0); + assert(writercfg.attrs[1].size == 2); + assert(memcmp(writercfg.attrs[1].buffer, "BB", 2) == 0); + assert(writercfg.attrs[2].size == 3); + assert(memcmp(writercfg.attrs[2].buffer, "CCC", 3) == 0); + lfs_file_rewind(&lfs, &writer) => 0; + for (int j = 0; j < SIZE; j++) { + lfs_file_read(&lfs, &writer, buffer, 1) => 1; + assert(buffer[0] == nums[j % 10]); + } + } else if (WRMODE == LFS_O_WRONLY) { + // this should NOT update wronly attributes, these may be + // stored in read-only memory + assert(writercfg.attrs[0].size == 1); + assert(memcmp(writercfg.attrs[0].buffer, "0", 1) == 0); + assert(writercfg.attrs[1].size == 2); + assert(memcmp(writercfg.attrs[1].buffer, "11", 2) == 0); + assert(writercfg.attrs[2].size == 3); + assert(memcmp(writercfg.attrs[2].buffer, "222", 3) == 0); + } + + for (int i = 0; i < READERS; i++) { + lfs_file_close(&lfs, &readers[i]) => 0; + } lfs_unmount(&lfs) => 0; '''