@@ -253,9 +253,11 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
253253static int lfs_pred (lfs_t * lfs , const lfs_block_t dir [2 ], lfs_dir_t * pdir );
254254static int lfs_parent (lfs_t * lfs , const lfs_block_t dir [2 ],
255255 lfs_dir_t * parent , lfs_entry_t * entry );
256+ static int lfs_moved (lfs_t * lfs , const void * e );
256257static int lfs_relocate (lfs_t * lfs ,
257258 const lfs_block_t oldpair [2 ], const lfs_block_t newpair [2 ]);
258259int lfs_deorphan (lfs_t * lfs );
260+ int lfs_deduplicate (lfs_t * lfs );
259261
260262
261263/// Block allocator ///
@@ -722,8 +724,8 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
722724 return err ;
723725 }
724726
725- if ((entry -> d .type != LFS_TYPE_REG &&
726- entry -> d .type != LFS_TYPE_DIR ) ||
727+ if ((( 0x7f & entry -> d .type ) != LFS_TYPE_REG &&
728+ ( 0x7f & entry -> d .type ) != LFS_TYPE_DIR ) ||
727729 entry -> d .nlen != pathlen ) {
728730 continue ;
729731 }
@@ -741,6 +743,16 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
741743 }
742744 }
743745
746+ // check that entry has not been moved
747+ if (entry -> d .type & 0x80 ) {
748+ int moved = lfs_moved (lfs , & entry -> d .u );
749+ if (moved < 0 || moved ) {
750+ return (moved < 0 ) ? moved : LFS_ERR_NOENT ;
751+ }
752+
753+ entry -> d .type &= ~0x80 ;
754+ }
755+
744756 pathname += pathlen ;
745757 pathname += strspn (pathname , "/" );
746758 if (pathname [0 ] == '\0' ) {
@@ -764,6 +776,14 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
764776
765777/// Top level directory operations ///
766778int lfs_mkdir (lfs_t * lfs , const char * path ) {
779+ // make sure directories are clean
780+ if (!lfs -> deduplicated ) {
781+ int err = lfs_deduplicate (lfs );
782+ if (err ) {
783+ return err ;
784+ }
785+ }
786+
767787 // fetch parent directory
768788 lfs_dir_t cwd ;
769789 int err = lfs_dir_fetch (lfs , & cwd , lfs -> root );
@@ -880,10 +900,26 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
880900 return (err == LFS_ERR_NOENT ) ? 0 : err ;
881901 }
882902
883- if (entry .d .type == LFS_TYPE_REG ||
884- entry .d .type == LFS_TYPE_DIR ) {
885- break ;
903+ if ((0x7f & entry .d .type ) != LFS_TYPE_REG &&
904+ (0x7f & entry .d .type ) != LFS_TYPE_DIR ) {
905+ continue ;
906+ }
907+
908+ // check that entry has not been moved
909+ if (entry .d .type & 0x80 ) {
910+ int moved = lfs_moved (lfs , & entry .d .u );
911+ if (moved < 0 ) {
912+ return moved ;
913+ }
914+
915+ if (moved ) {
916+ continue ;
917+ }
918+
919+ entry .d .type &= ~0x80 ;
886920 }
921+
922+ break ;
887923 }
888924
889925 info -> type = entry .d .type ;
@@ -1113,6 +1149,14 @@ static int lfs_index_traverse(lfs_t *lfs,
11131149/// Top level file operations ///
11141150int lfs_file_open (lfs_t * lfs , lfs_file_t * file ,
11151151 const char * path , int flags ) {
1152+ // make sure directories are clean
1153+ if ((flags & 3 ) != LFS_O_RDONLY && !lfs -> deduplicated ) {
1154+ int err = lfs_deduplicate (lfs );
1155+ if (err ) {
1156+ return err ;
1157+ }
1158+ }
1159+
11161160 // allocate entry for file if it doesn't exist
11171161 lfs_dir_t cwd ;
11181162 int err = lfs_dir_fetch (lfs , & cwd , lfs -> root );
@@ -1598,6 +1642,14 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
15981642}
15991643
16001644int lfs_remove (lfs_t * lfs , const char * path ) {
1645+ // make sure directories are clean
1646+ if (!lfs -> deduplicated ) {
1647+ int err = lfs_deduplicate (lfs );
1648+ if (err ) {
1649+ return err ;
1650+ }
1651+ }
1652+
16011653 lfs_dir_t cwd ;
16021654 int err = lfs_dir_fetch (lfs , & cwd , lfs -> root );
16031655 if (err ) {
@@ -1654,6 +1706,14 @@ int lfs_remove(lfs_t *lfs, const char *path) {
16541706}
16551707
16561708int lfs_rename (lfs_t * lfs , const char * oldpath , const char * newpath ) {
1709+ // make sure directories are clean
1710+ if (!lfs -> deduplicated ) {
1711+ int err = lfs_deduplicate (lfs );
1712+ if (err ) {
1713+ return err ;
1714+ }
1715+ }
1716+
16571717 // find old entry
16581718 lfs_dir_t oldcwd ;
16591719 int err = lfs_dir_fetch (lfs , & oldcwd , lfs -> root );
@@ -1667,6 +1727,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
16671727 return err ;
16681728 }
16691729
1730+ // mark as moving
1731+ oldentry .d .type |= 0x80 ;
1732+ err = lfs_dir_update (lfs , & oldcwd , & oldentry , NULL );
1733+ if (err ) {
1734+ return err ;
1735+ }
1736+ oldentry .d .type &= ~0x80 ;
1737+
16701738 // allocate new entry
16711739 lfs_dir_t newcwd ;
16721740 err = lfs_dir_fetch (lfs , & newcwd , lfs -> root );
@@ -1716,35 +1784,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
17161784 }
17171785 }
17181786
1719- // fetch again in case newcwd == oldcwd
1720- err = lfs_dir_fetch (lfs , & oldcwd , oldcwd .pair );
1721- if (err ) {
1722- return err ;
1723- }
1724-
1725- err = lfs_dir_find (lfs , & oldcwd , & oldentry , & oldpath );
1726- if (err ) {
1727- return err ;
1728- }
1729-
1730- // remove from old location
1731- err = lfs_dir_remove (lfs , & oldcwd , & oldentry );
1732- if (err ) {
1733- return err ;
1734- }
1735-
1736- // shift over any files that are affected
1737- for (lfs_file_t * f = lfs -> files ; f ; f = f -> next ) {
1738- if (lfs_paircmp (f -> pair , oldcwd .pair ) == 0 ) {
1739- if (f -> poff == oldentry .off ) {
1740- f -> pair [0 ] = 0xffffffff ;
1741- f -> pair [1 ] = 0xffffffff ;
1742- } else if (f -> poff > oldentry .off ) {
1743- f -> poff -= lfs_entry_size (& oldentry );
1744- }
1745- }
1746- }
1747-
17481787 // if we were a directory, just run a deorphan step, this should
17491788 // collect us, although is expensive
17501789 if (prevexists && preventry .d .type == LFS_TYPE_DIR ) {
@@ -1754,6 +1793,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
17541793 }
17551794 }
17561795
1796+ // just deduplicate
1797+ err = lfs_deduplicate (lfs );
1798+ if (err ) {
1799+ return err ;
1800+ }
1801+
17571802 return 0 ;
17581803}
17591804
@@ -1802,6 +1847,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
18021847 lfs -> root [1 ] = 0xffffffff ;
18031848 lfs -> files = NULL ;
18041849 lfs -> deorphaned = false;
1850+ lfs -> deduplicated = false;
18051851
18061852 return 0 ;
18071853}
@@ -1979,7 +2025,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
19792025 }
19802026
19812027 dir .off += lfs_entry_size (& entry );
1982- if ((0xf & entry .d .type ) == (0xf & LFS_TYPE_REG )) {
2028+ if ((0x70 & entry .d .type ) == (0x70 & LFS_TYPE_REG )) {
19832029 int err = lfs_index_traverse (lfs , & lfs -> rcache , NULL ,
19842030 entry .d .u .file .head , entry .d .u .file .size , cb , data );
19852031 if (err ) {
@@ -2069,7 +2115,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
20692115 break ;
20702116 }
20712117
2072- if (((0xf & entry -> d .type ) == (0xf & LFS_TYPE_DIR )) &&
2118+ if (((0x70 & entry -> d .type ) == (0x70 & LFS_TYPE_DIR )) &&
20732119 lfs_paircmp (entry -> d .u .dir , dir ) == 0 ) {
20742120 return true;
20752121 }
@@ -2079,6 +2125,46 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
20792125 return false;
20802126}
20812127
2128+ static int lfs_moved (lfs_t * lfs , const void * e ) {
2129+ if (lfs_pairisnull (lfs -> root )) {
2130+ return 0 ;
2131+ }
2132+
2133+ // skip superblock
2134+ lfs_dir_t cwd ;
2135+ int err = lfs_dir_fetch (lfs , & cwd , (const lfs_block_t [2 ]){0 , 1 });
2136+ if (err ) {
2137+ return err ;
2138+ }
2139+
2140+ // iterate over all directory directory entries
2141+ lfs_entry_t entry ;
2142+ while (!lfs_pairisnull (cwd .d .tail )) {
2143+ int err = lfs_dir_fetch (lfs , & cwd , cwd .d .tail );
2144+ if (err ) {
2145+ return err ;
2146+ }
2147+
2148+ while (true) {
2149+ int err = lfs_dir_next (lfs , & cwd , & entry );
2150+ if (err && err != LFS_ERR_NOENT ) {
2151+ return err ;
2152+ }
2153+
2154+ if (err == LFS_ERR_NOENT ) {
2155+ break ;
2156+ }
2157+
2158+ if (!(0x80 & entry .d .type ) &&
2159+ memcmp (& entry .d .u , e , sizeof (entry .d .u )) == 0 ) {
2160+ return true;
2161+ }
2162+ }
2163+ }
2164+
2165+ return false;
2166+ }
2167+
20822168static int lfs_relocate (lfs_t * lfs ,
20832169 const lfs_block_t oldpair [2 ], const lfs_block_t newpair [2 ]) {
20842170 // find parent
@@ -2197,3 +2283,77 @@ int lfs_deorphan(lfs_t *lfs) {
21972283 return 0 ;
21982284}
21992285
2286+ int lfs_deduplicate (lfs_t * lfs ) {
2287+ lfs -> deduplicated = true;
2288+
2289+ if (lfs_pairisnull (lfs -> root )) {
2290+ return 0 ;
2291+ }
2292+
2293+ // skip superblock
2294+ lfs_dir_t cwd ;
2295+ int err = lfs_dir_fetch (lfs , & cwd , (const lfs_block_t [2 ]){0 , 1 });
2296+ if (err ) {
2297+ return err ;
2298+ }
2299+
2300+ // iterate over all directory directory entries
2301+ lfs_entry_t entry ;
2302+ while (!lfs_pairisnull (cwd .d .tail )) {
2303+ int err = lfs_dir_fetch (lfs , & cwd , cwd .d .tail );
2304+ if (err ) {
2305+ return err ;
2306+ }
2307+
2308+ while (true) {
2309+ int err = lfs_dir_next (lfs , & cwd , & entry );
2310+ if (err && err != LFS_ERR_NOENT ) {
2311+ return err ;
2312+ }
2313+
2314+ if (err == LFS_ERR_NOENT ) {
2315+ break ;
2316+ }
2317+
2318+ // found moved entry
2319+ if (entry .d .type & 0x80 ) {
2320+ int moved = lfs_moved (lfs , & entry .d .u );
2321+ if (moved < 0 ) {
2322+ return moved ;
2323+ }
2324+
2325+ if (moved ) {
2326+ LFS_DEBUG ("Found move %d %d" ,
2327+ entry .d .u .dir [0 ], entry .d .u .dir [1 ]);
2328+ int err = lfs_dir_remove (lfs , & cwd , & entry );
2329+ if (err ) {
2330+ return err ;
2331+ }
2332+
2333+ // shift over any files that are affected
2334+ for (lfs_file_t * f = lfs -> files ; f ; f = f -> next ) {
2335+ if (lfs_paircmp (f -> pair , cwd .pair ) == 0 ) {
2336+ if (f -> poff == entry .off ) {
2337+ f -> pair [0 ] = 0xffffffff ;
2338+ f -> pair [1 ] = 0xffffffff ;
2339+ } else if (f -> poff > entry .off ) {
2340+ f -> poff -= lfs_entry_size (& entry );
2341+ }
2342+ }
2343+ }
2344+ } else {
2345+ LFS_DEBUG ("Found partial move %d %d" ,
2346+ entry .d .u .dir [0 ], entry .d .u .dir [1 ]);
2347+ entry .d .type &= ~0x80 ;
2348+ int err = lfs_dir_update (lfs , & cwd , & entry , NULL );
2349+ if (err ) {
2350+ return err ;
2351+ }
2352+ }
2353+ }
2354+ }
2355+ }
2356+
2357+ return 0 ;
2358+ }
2359+
0 commit comments