@@ -253,9 +253,11 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
253
253
static int lfs_pred (lfs_t * lfs , const lfs_block_t dir [2 ], lfs_dir_t * pdir );
254
254
static int lfs_parent (lfs_t * lfs , const lfs_block_t dir [2 ],
255
255
lfs_dir_t * parent , lfs_entry_t * entry );
256
+ static int lfs_moved (lfs_t * lfs , const void * e );
256
257
static int lfs_relocate (lfs_t * lfs ,
257
258
const lfs_block_t oldpair [2 ], const lfs_block_t newpair [2 ]);
258
259
int lfs_deorphan (lfs_t * lfs );
260
+ int lfs_deduplicate (lfs_t * lfs );
259
261
260
262
261
263
/// Block allocator ///
@@ -722,8 +724,8 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
722
724
return err ;
723
725
}
724
726
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 ) ||
727
729
entry -> d .nlen != pathlen ) {
728
730
continue ;
729
731
}
@@ -741,6 +743,16 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
741
743
}
742
744
}
743
745
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
+
744
756
pathname += pathlen ;
745
757
pathname += strspn (pathname , "/" );
746
758
if (pathname [0 ] == '\0' ) {
@@ -764,6 +776,14 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
764
776
765
777
/// Top level directory operations ///
766
778
int 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
+
767
787
// fetch parent directory
768
788
lfs_dir_t cwd ;
769
789
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) {
880
900
return (err == LFS_ERR_NOENT ) ? 0 : err ;
881
901
}
882
902
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 ;
886
920
}
921
+
922
+ break ;
887
923
}
888
924
889
925
info -> type = entry .d .type ;
@@ -1113,6 +1149,14 @@ static int lfs_index_traverse(lfs_t *lfs,
1113
1149
/// Top level file operations ///
1114
1150
int lfs_file_open (lfs_t * lfs , lfs_file_t * file ,
1115
1151
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
+
1116
1160
// allocate entry for file if it doesn't exist
1117
1161
lfs_dir_t cwd ;
1118
1162
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) {
1598
1642
}
1599
1643
1600
1644
int 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
+
1601
1653
lfs_dir_t cwd ;
1602
1654
int err = lfs_dir_fetch (lfs , & cwd , lfs -> root );
1603
1655
if (err ) {
@@ -1654,6 +1706,14 @@ int lfs_remove(lfs_t *lfs, const char *path) {
1654
1706
}
1655
1707
1656
1708
int 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
+
1657
1717
// find old entry
1658
1718
lfs_dir_t oldcwd ;
1659
1719
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) {
1667
1727
return err ;
1668
1728
}
1669
1729
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
+
1670
1738
// allocate new entry
1671
1739
lfs_dir_t newcwd ;
1672
1740
err = lfs_dir_fetch (lfs , & newcwd , lfs -> root );
@@ -1716,35 +1784,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
1716
1784
}
1717
1785
}
1718
1786
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
-
1748
1787
// if we were a directory, just run a deorphan step, this should
1749
1788
// collect us, although is expensive
1750
1789
if (prevexists && preventry .d .type == LFS_TYPE_DIR ) {
@@ -1754,6 +1793,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
1754
1793
}
1755
1794
}
1756
1795
1796
+ // just deduplicate
1797
+ err = lfs_deduplicate (lfs );
1798
+ if (err ) {
1799
+ return err ;
1800
+ }
1801
+
1757
1802
return 0 ;
1758
1803
}
1759
1804
@@ -1802,6 +1847,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
1802
1847
lfs -> root [1 ] = 0xffffffff ;
1803
1848
lfs -> files = NULL ;
1804
1849
lfs -> deorphaned = false;
1850
+ lfs -> deduplicated = false;
1805
1851
1806
1852
return 0 ;
1807
1853
}
@@ -1979,7 +2025,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
1979
2025
}
1980
2026
1981
2027
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 )) {
1983
2029
int err = lfs_index_traverse (lfs , & lfs -> rcache , NULL ,
1984
2030
entry .d .u .file .head , entry .d .u .file .size , cb , data );
1985
2031
if (err ) {
@@ -2069,7 +2115,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
2069
2115
break ;
2070
2116
}
2071
2117
2072
- if (((0xf & entry -> d .type ) == (0xf & LFS_TYPE_DIR )) &&
2118
+ if (((0x70 & entry -> d .type ) == (0x70 & LFS_TYPE_DIR )) &&
2073
2119
lfs_paircmp (entry -> d .u .dir , dir ) == 0 ) {
2074
2120
return true;
2075
2121
}
@@ -2079,6 +2125,46 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
2079
2125
return false;
2080
2126
}
2081
2127
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
+
2082
2168
static int lfs_relocate (lfs_t * lfs ,
2083
2169
const lfs_block_t oldpair [2 ], const lfs_block_t newpair [2 ]) {
2084
2170
// find parent
@@ -2197,3 +2283,77 @@ int lfs_deorphan(lfs_t *lfs) {
2197
2283
return 0 ;
2198
2284
}
2199
2285
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