-
Notifications
You must be signed in to change notification settings - Fork 29
Merge tag 'v2.11.3' #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b062c88
dbe96d0
74f32c8
2311cd7
fd5e7f6
488e84b
87d5f61
58eaa98
24d23dd
d3375f1
6cb4e86
30563cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -258,7 +258,7 @@ static int lfs_bd_prog(lfs_t *lfs, | |||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| // pcache must have been flushed, either by programming and | ||||||
| // pcache must have been flushed, either by programming an | ||||||
| // entire block or manually flushing the pcache | ||||||
| LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); | ||||||
|
|
||||||
|
|
@@ -286,7 +286,7 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { | |||||
|
|
||||||
| // some operations on paths | ||||||
| static inline lfs_size_t lfs_path_namelen(const char *path) { | ||||||
| return strcspn(path, "/"); | ||||||
| return (lfs_size_t)strcspn(path, "/"); | ||||||
| } | ||||||
|
|
||||||
| static inline bool lfs_path_islast(const char *path) { | ||||||
|
|
@@ -1291,6 +1291,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | |||||
|
|
||||||
| // found a match for our fetcher? | ||||||
| if ((fmask & tag) == (fmask & ftag)) { | ||||||
| LFS_ASSERT(cb != NULL); | ||||||
| int res = cb(data, tag, &(struct lfs_diskoff){ | ||||||
| dir->pair[0], off+sizeof(tag)}); | ||||||
| if (res < 0) { | ||||||
|
|
@@ -1501,7 +1502,7 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, | |||||
| if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { | ||||||
| name += strspn(name, "/"); | ||||||
| } | ||||||
| lfs_size_t namelen = strcspn(name, "/"); | ||||||
| lfs_size_t namelen = (lfs_size_t)strcspn(name, "/"); | ||||||
|
|
||||||
| // skip '.' | ||||||
| if (namelen == 1 && memcmp(name, ".", 1) == 0) { | ||||||
|
|
@@ -1520,7 +1521,7 @@ static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, | |||||
| int depth = 1; | ||||||
| while (true) { | ||||||
| suffix += strspn(suffix, "/"); | ||||||
| sufflen = strcspn(suffix, "/"); | ||||||
| sufflen = (lfs_size_t)strcspn(suffix, "/"); | ||||||
| if (sufflen == 0) { | ||||||
| break; | ||||||
| } | ||||||
|
|
@@ -1761,7 +1762,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | |||||
|
|
||||||
| commit->off = noff; | ||||||
| // perturb valid bit? | ||||||
| commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24); | ||||||
| commit->ptag = ntag ^ ((lfs_tag_t)(0x80 & ~eperturb) << 24); | ||||||
| // reset crc for next commit | ||||||
| commit->crc = 0xffffffff; | ||||||
|
|
||||||
|
|
@@ -3244,10 +3245,12 @@ static int lfs_file_open_(lfs_t *lfs, lfs_file_t *file, | |||||
| #endif | ||||||
|
|
||||||
| static int lfs_file_close_(lfs_t *lfs, lfs_file_t *file) { | ||||||
| #ifndef LFS_READONLY | ||||||
| int err = lfs_file_sync_(lfs, file); | ||||||
| #else | ||||||
| int err = 0; | ||||||
| #ifndef LFS_READONLY | ||||||
| // it's not safe to do anything if our file errored | ||||||
| if (!(file->flags & LFS_F_ERRED)) { | ||||||
| err = lfs_file_sync_(lfs, file); | ||||||
| } | ||||||
| #endif | ||||||
|
|
||||||
| // remove from list of mdirs | ||||||
|
|
@@ -3429,18 +3432,12 @@ static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | |||||
|
|
||||||
| #ifndef LFS_READONLY | ||||||
| static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file) { | ||||||
| if (file->flags & LFS_F_ERRED) { | ||||||
| // it's not safe to do anything if our file errored | ||||||
| return 0; | ||||||
| } | ||||||
|
|
||||||
| int err = lfs_file_flush(lfs, file); | ||||||
| if (err) { | ||||||
| file->flags |= LFS_F_ERRED; | ||||||
| return err; | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| if ((file->flags & LFS_F_DIRTY) && | ||||||
| !lfs_pair_isnull(file->m.pair)) { | ||||||
| // before we commit metadata, we need sync the disk to make sure | ||||||
|
|
@@ -3485,6 +3482,17 @@ static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file) { | |||||
| file->flags &= ~LFS_F_DIRTY; | ||||||
| } | ||||||
|
|
||||||
| // mark any other file handles as dirty + desync | ||||||
| for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { | ||||||
| if (file != f | ||||||
| && f->type == LFS_TYPE_REG | ||||||
| && lfs_pair_cmp(f->m.pair, file->m.pair) == 0 | ||||||
| && f->id == file->id) { | ||||||
| f->flags |= LFS_F_DUSTY; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY; | ||||||
| return 0; | ||||||
| } | ||||||
| #endif | ||||||
|
|
@@ -3692,7 +3700,7 @@ static lfs_ssize_t lfs_file_write_(lfs_t *lfs, lfs_file_t *file, | |||||
| return nsize; | ||||||
| } | ||||||
|
|
||||||
| file->flags &= ~LFS_F_ERRED; | ||||||
| file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not clear If a stale handle starts writing, it still depends on the old CTZ chain until Suggested fix- file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY;
+ file->flags &= ~LFS_F_ERRED;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| return nsize; | ||||||
| } | ||||||
| #endif | ||||||
|
|
@@ -4771,7 +4779,8 @@ int lfs_fs_traverse_(lfs_t *lfs, | |||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) { | ||||||
| if (((f->flags & LFS_F_DIRTY) || (f->flags & LFS_F_DUSTY)) | ||||||
| && !(f->flags & LFS_F_INLINE)) { | ||||||
| int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, | ||||||
| f->ctz.head, f->ctz.size, cb, data); | ||||||
| if (err) { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -250,6 +250,189 @@ code = ''' | |||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| ''' | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # multiple handle allocation test | ||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||
| # this tests that multiple open handles to the same file don't clobber | ||||||||||||||||||||||||||||
| # each other | ||||||||||||||||||||||||||||
| [cases.test_alloc_multihandle] | ||||||||||||||||||||||||||||
| defines.FILES = 2 | ||||||||||||||||||||||||||||
| defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' | ||||||||||||||||||||||||||||
| defines.GC = [false, true] | ||||||||||||||||||||||||||||
| defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2'] | ||||||||||||||||||||||||||||
| defines.INFER_BC = [false, true] | ||||||||||||||||||||||||||||
| defines.SYNC = [false, true] | ||||||||||||||||||||||||||||
| code = ''' | ||||||||||||||||||||||||||||
| const char *names[] = {"eggs", "spinach"}; | ||||||||||||||||||||||||||||
| lfs_file_t files[FILES]; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| lfs_t lfs; | ||||||||||||||||||||||||||||
| lfs_format(&lfs, cfg) => 0; | ||||||||||||||||||||||||||||
| struct lfs_config cfg_ = *cfg; | ||||||||||||||||||||||||||||
| if (INFER_BC) { | ||||||||||||||||||||||||||||
| cfg_.block_count = 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_mount(&lfs, &cfg_) => 0; | ||||||||||||||||||||||||||||
| lfs_mkdir(&lfs, "breakfast") => 0; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // write one file | ||||||||||||||||||||||||||||
| char path[1024]; | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &files[0], path, | ||||||||||||||||||||||||||||
| LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||||||||||||||||||||||||||
| if (GC) { | ||||||||||||||||||||||||||||
| lfs_fs_gc(&lfs) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| size_t size = strlen(names[0]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| lfs_file_write(&lfs, &files[0], names[0], size) => size; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| // sync? | ||||||||||||||||||||||||||||
| if (SYNC) { | ||||||||||||||||||||||||||||
| lfs_file_sync(&lfs, &files[0]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // write the other file | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &files[1], path, | ||||||||||||||||||||||||||||
| LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||||||||||||||||||||||||
| if (GC) { | ||||||||||||||||||||||||||||
| lfs_fs_gc(&lfs) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| size = strlen(names[1]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| lfs_file_write(&lfs, &files[1], names[1], size) => size; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| // sync? | ||||||||||||||||||||||||||||
| if (SYNC) { | ||||||||||||||||||||||||||||
| lfs_file_sync(&lfs, &files[1]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // try to read from both | ||||||||||||||||||||||||||||
| for (int n = 0; n < FILES; n++) { | ||||||||||||||||||||||||||||
| lfs_file_rewind(&lfs, &files[n]) => 0; | ||||||||||||||||||||||||||||
| size_t size = strlen(names[n]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| uint8_t buffer[1024]; | ||||||||||||||||||||||||||||
| lfs_file_read(&lfs, &files[n], buffer, size) => size; | ||||||||||||||||||||||||||||
| assert(memcmp(buffer, names[n], size) == 0); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| for (int n = 0; n < FILES; n++) { | ||||||||||||||||||||||||||||
| lfs_file_close(&lfs, &files[n]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_unmount(&lfs) => 0; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // check after remounting | ||||||||||||||||||||||||||||
| lfs_mount(&lfs, &cfg_) => 0; | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| // last one wins | ||||||||||||||||||||||||||||
| int n = FILES-1; | ||||||||||||||||||||||||||||
| char path[1024]; | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_t file; | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||||||||||||||||||||||||||
| size_t size = strlen(names[n]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| uint8_t buffer[1024]; | ||||||||||||||||||||||||||||
| lfs_file_read(&lfs, &file, buffer, size) => size; | ||||||||||||||||||||||||||||
| assert(memcmp(buffer, names[n], size) == 0); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_file_close(&lfs, &file) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_unmount(&lfs) => 0; | ||||||||||||||||||||||||||||
| ''' | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # multiple handle allocation reuse test | ||||||||||||||||||||||||||||
| [cases.test_alloc_multihandle_reuse] | ||||||||||||||||||||||||||||
| defines.FILES = 2 | ||||||||||||||||||||||||||||
| defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))' | ||||||||||||||||||||||||||||
| defines.CYCLES = [1, 10] | ||||||||||||||||||||||||||||
| defines.INFER_BC = [false, true] | ||||||||||||||||||||||||||||
| defines.SYNC = [false, true] | ||||||||||||||||||||||||||||
|
Comment on lines
+346
to
+351
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the missing Lines 371 and 388 branch on Suggested fix [cases.test_alloc_multihandle_reuse]
defines.FILES = 2
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
defines.CYCLES = [1, 10]
defines.INFER_BC = [false, true]
+defines.GC = [false, true]
defines.SYNC = [false, true]📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| code = ''' | ||||||||||||||||||||||||||||
| const char *names[] = {"eggs", "spinach"}; | ||||||||||||||||||||||||||||
| lfs_file_t files[FILES]; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| lfs_t lfs; | ||||||||||||||||||||||||||||
| lfs_format(&lfs, cfg) => 0; | ||||||||||||||||||||||||||||
| struct lfs_config cfg_ = *cfg; | ||||||||||||||||||||||||||||
| if (INFER_BC) { | ||||||||||||||||||||||||||||
| cfg_.block_count = 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| lfs_mount(&lfs, &cfg_) => 0; | ||||||||||||||||||||||||||||
| lfs_mkdir(&lfs, "breakfast") => 0; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // write one file | ||||||||||||||||||||||||||||
| char path[1024]; | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &files[0], path, | ||||||||||||||||||||||||||||
| LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; | ||||||||||||||||||||||||||||
| if (GC) { | ||||||||||||||||||||||||||||
| lfs_fs_gc(&lfs) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| size_t size = strlen(names[0]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| lfs_file_write(&lfs, &files[0], names[0], size) => size; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| // sync? | ||||||||||||||||||||||||||||
| if (SYNC) { | ||||||||||||||||||||||||||||
| lfs_file_sync(&lfs, &files[0]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (int c = 0; c < CYCLES; c++) { | ||||||||||||||||||||||||||||
| // write the other file | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &files[1], path, | ||||||||||||||||||||||||||||
| LFS_O_RDWR | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||||||||||||||||||||||||
| if (GC) { | ||||||||||||||||||||||||||||
| lfs_fs_gc(&lfs) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| size = strlen(names[1]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| lfs_file_write(&lfs, &files[1], names[1], size) => size; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| // sync? | ||||||||||||||||||||||||||||
| if (SYNC) { | ||||||||||||||||||||||||||||
| lfs_file_sync(&lfs, &files[1]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // try to read from both | ||||||||||||||||||||||||||||
| for (int n = 0; n < FILES; n++) { | ||||||||||||||||||||||||||||
| lfs_file_rewind(&lfs, &files[n]) => 0; | ||||||||||||||||||||||||||||
| size_t size = strlen(names[n]); | ||||||||||||||||||||||||||||
| for (lfs_size_t i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| uint8_t buffer[1024]; | ||||||||||||||||||||||||||||
| lfs_file_read(&lfs, &files[n], buffer, size) => size; | ||||||||||||||||||||||||||||
| assert(memcmp(buffer, names[n], size) == 0); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| lfs_file_close(&lfs, &files[1]) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_file_close(&lfs, &files[0]) => 0; | ||||||||||||||||||||||||||||
| lfs_unmount(&lfs) => 0; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // check after remounting | ||||||||||||||||||||||||||||
| lfs_mount(&lfs, &cfg_) => 0; | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| // last one wins | ||||||||||||||||||||||||||||
| int n = (SYNC) ? FILES-1 : 0; | ||||||||||||||||||||||||||||
| char path[1024]; | ||||||||||||||||||||||||||||
| sprintf(path, "breakfast/quiche"); | ||||||||||||||||||||||||||||
| lfs_file_t file; | ||||||||||||||||||||||||||||
| lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||||||||||||||||||||||||||
| size_t size = strlen(names[n]); | ||||||||||||||||||||||||||||
| for (int i = 0; i < SIZE; i += size) { | ||||||||||||||||||||||||||||
| uint8_t buffer[1024]; | ||||||||||||||||||||||||||||
| lfs_file_read(&lfs, &file, buffer, size) => size; | ||||||||||||||||||||||||||||
| assert(memcmp(buffer, names[n], size) == 0); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_file_close(&lfs, &file) => 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| lfs_unmount(&lfs) => 0; | ||||||||||||||||||||||||||||
| ''' | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # exhaustion test | ||||||||||||||||||||||||||||
| [cases.test_alloc_exhaustion] | ||||||||||||||||||||||||||||
| defines.INFER_BC = [false, true] | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only transfer
LFS_F_DUSTYafter this handle actually commits a new version.A stale handle can reach this block with
LFS_F_DUSTYset butLFS_F_DIRTYclear. In that case,lfs_file_sync_()currently clears its dusty bit and marks siblings dusty even though this handle still points at the orphaned CTZ chain. A later alloc scan/GC can then recycle blocks that this still-open handle may read from.Suggested fix
static int lfs_file_sync_(lfs_t *lfs, lfs_file_t *file) { int err = lfs_file_flush(lfs, file); + bool committed = false; if (err) { file->flags |= LFS_F_ERRED; return err; } if ((file->flags & LFS_F_DIRTY) && !lfs_pair_isnull(file->m.pair)) { @@ } file->flags &= ~LFS_F_DIRTY; + committed = true; } - // mark any other file handles as dirty + desync - for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { - if (file != f - && f->type == LFS_TYPE_REG - && lfs_pair_cmp(f->m.pair, file->m.pair) == 0 - && f->id == file->id) { - f->flags |= LFS_F_DUSTY; + if (committed) { + // mark any other file handles as desynced + for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { + if (file != f + && f->type == LFS_TYPE_REG + && lfs_pair_cmp(f->m.pair, file->m.pair) == 0 + && f->id == file->id) { + f->flags |= LFS_F_DUSTY; + } } + file->flags &= ~LFS_F_DUSTY; } - file->flags &= ~LFS_F_ERRED & ~LFS_F_DUSTY; + file->flags &= ~LFS_F_ERRED; return 0; }🤖 Prompt for AI Agents