1
1
use crate :: {
2
2
cache:: Cache ,
3
3
fungi:: {
4
- meta:: { FileType , Inode , Walk , WalkVisitor } ,
4
+ meta:: { FileType , Inode , Mode , Walk , WalkVisitor } ,
5
5
Reader , Result , Writer ,
6
6
} ,
7
7
store:: { get_router, BlockStore , Router , Store , Stores } ,
8
8
} ;
9
9
use anyhow:: Context ;
10
10
use hex:: ToHex ;
11
- use std:: path:: Path ;
11
+ use std:: collections:: { HashMap , HashSet } ;
12
+ use std:: path:: { Path , PathBuf } ;
12
13
use tokio:: io:: AsyncReadExt ;
13
14
15
+ const ROOT_PATH : & str = "/" ;
16
+
14
17
pub async fn merge < S : Store > (
15
18
writer : Writer ,
16
19
store : S ,
@@ -38,12 +41,33 @@ pub async fn merge<S: Store>(
38
41
}
39
42
40
43
let store = store. into ( ) ;
44
+
45
+ let mut path_to_inode_map = HashMap :: new ( ) ;
46
+ let root_path = PathBuf :: from ( ROOT_PATH ) ;
47
+
48
+ let root_inode = Inode {
49
+ name : ROOT_PATH . into ( ) ,
50
+ mode : Mode :: new ( FileType :: Dir , 0o755 ) ,
51
+ ..Default :: default ( )
52
+ } ;
53
+ let root_ino = writer. inode ( root_inode) . await ?;
54
+ path_to_inode_map. insert ( root_path, root_ino) ;
55
+
41
56
for target_flist in target_flists {
42
57
let reader = Reader :: new ( & target_flist) . await ?;
43
58
let router = get_router ( & reader) . await ?;
44
- let cache = Cache :: new ( cache. clone ( ) , router) ;
59
+ let cache_instance = Cache :: new ( cache. clone ( ) , router) ;
60
+
61
+ let mut visited = HashSet :: new ( ) ;
62
+ let mut visitor = MergeVisitor {
63
+ writer : writer. clone ( ) ,
64
+ reader : reader. clone ( ) ,
65
+ store : & store,
66
+ cache : cache_instance,
67
+ path_to_inode : & mut path_to_inode_map,
68
+ visited : & mut visited,
69
+ } ;
45
70
46
- let mut visitor = MergeVisitor :: new ( writer. clone ( ) , reader. clone ( ) , & store, cache) ;
47
71
reader. walk ( & mut visitor) . await ?;
48
72
}
49
73
@@ -58,24 +82,80 @@ where
58
82
reader : Reader ,
59
83
store : & ' a BlockStore < S > ,
60
84
cache : Cache < Router < Stores > > ,
85
+ path_to_inode : & ' a mut HashMap < PathBuf , u64 > ,
86
+ visited : & ' a mut HashSet < u64 > ,
61
87
}
62
88
63
89
impl < ' a , S > MergeVisitor < ' a , S >
64
90
where
65
91
S : Store ,
66
92
{
67
- pub fn new (
68
- writer : Writer ,
69
- reader : Reader ,
70
- store : & ' a BlockStore < S > ,
71
- cache : Cache < Router < Stores > > ,
72
- ) -> Self {
73
- Self {
74
- writer,
75
- reader,
76
- store,
77
- cache,
93
+ async fn ensure_parent_directory ( & mut self , path : & Path ) -> Result < u64 > {
94
+ if path. to_str ( ) == Some ( ROOT_PATH ) {
95
+ return Ok ( * self . path_to_inode . get ( path) . unwrap_or ( & 1 ) ) ;
78
96
}
97
+
98
+ if let Some ( ino) = self . path_to_inode . get ( path) {
99
+ return Ok ( * ino) ;
100
+ }
101
+
102
+ let parent_path = path. parent ( ) . unwrap_or ( Path :: new ( ROOT_PATH ) ) ;
103
+ let parent_ino = Box :: pin ( self . ensure_parent_directory ( parent_path) ) . await ?;
104
+
105
+ let dir_name = path
106
+ . file_name ( )
107
+ . map ( |name| name. to_string_lossy ( ) . to_string ( ) )
108
+ . unwrap_or_default ( ) ;
109
+
110
+ let dir_inode = Inode {
111
+ parent : parent_ino,
112
+ name : dir_name,
113
+ mode : Mode :: new ( FileType :: Dir , 0o755 ) ,
114
+ ..Default :: default ( )
115
+ } ;
116
+
117
+ let new_ino = self . writer . inode ( dir_inode) . await ?;
118
+ self . path_to_inode . insert ( path. to_path_buf ( ) , new_ino) ;
119
+
120
+ Ok ( new_ino)
121
+ }
122
+
123
+ async fn copy_blocks ( & mut self , source_ino : u64 , dest_ino : u64 ) -> Result < ( ) > {
124
+ let blocks = self . reader . blocks ( source_ino) . await ?;
125
+
126
+ for block in & blocks {
127
+ self . writer . block ( dest_ino, & block. id , & block. key ) . await ?;
128
+ }
129
+
130
+ let mut blocks_to_store = Vec :: new ( ) ;
131
+ for block in blocks {
132
+ if self . store . get ( & block) . await . is_err ( ) {
133
+ blocks_to_store. push ( block) ;
134
+ }
135
+ }
136
+
137
+ for block in blocks_to_store {
138
+ let ( _, mut file) = self . cache . get ( & block) . await ?;
139
+ let mut data = Vec :: new ( ) ;
140
+ if let Err ( e) = file. read_to_end ( & mut data) . await {
141
+ log:: error!(
142
+ "failed to read block {}: {}" ,
143
+ block. id. as_slice( ) . encode_hex:: <String >( ) ,
144
+ e
145
+ ) ;
146
+ return Err ( e. into ( ) ) ;
147
+ }
148
+ if let Err ( e) = self . store . set ( & data) . await {
149
+ log:: error!(
150
+ "failed to set block {}: {}" ,
151
+ block. id. as_slice( ) . encode_hex:: <String >( ) ,
152
+ e
153
+ ) ;
154
+ return Err ( e. into ( ) ) ;
155
+ }
156
+ }
157
+
158
+ Ok ( ( ) )
79
159
}
80
160
}
81
161
@@ -84,46 +164,66 @@ impl<'a, S> WalkVisitor for MergeVisitor<'a, S>
84
164
where
85
165
S : Store ,
86
166
{
87
- async fn visit ( & mut self , _path : & Path , node : & Inode ) -> Result < Walk > {
167
+ async fn visit ( & mut self , path : & Path , node : & Inode ) -> Result < Walk > {
168
+ if !self . visited . insert ( node. ino ) {
169
+ return Ok ( Walk :: Continue ) ;
170
+ }
171
+
88
172
match node. mode . file_type ( ) {
89
173
FileType :: Dir => {
90
- self . writer . inode ( node. clone ( ) ) . await ?;
91
- return Ok ( Walk :: Continue ) ;
174
+ if path. to_str ( ) == Some ( ROOT_PATH ) {
175
+ return Ok ( Walk :: Continue ) ;
176
+ }
177
+
178
+ let dir_name = path
179
+ . file_name ( )
180
+ . map ( |name| name. to_string_lossy ( ) . to_string ( ) )
181
+ . unwrap_or_default ( ) ;
182
+
183
+ let parent_path = path. parent ( ) . unwrap_or ( Path :: new ( ROOT_PATH ) ) ;
184
+ let parent_ino = self . ensure_parent_directory ( parent_path) . await ?;
185
+
186
+ let dir_node = Inode {
187
+ parent : parent_ino,
188
+ name : dir_name,
189
+ mode : node. mode . clone ( ) ,
190
+ uid : node. uid ,
191
+ gid : node. gid ,
192
+ rdev : node. rdev ,
193
+ ctime : node. ctime ,
194
+ mtime : node. mtime ,
195
+ data : node. data . clone ( ) ,
196
+ ..Default :: default ( )
197
+ } ;
198
+
199
+ let dir_ino = self . writer . inode ( dir_node) . await ?;
200
+ self . path_to_inode . insert ( path. to_path_buf ( ) , dir_ino) ;
92
201
}
93
202
FileType :: Regular => {
94
- let ino = self . writer . inode ( node. clone ( ) ) . await ?;
95
- let blocks = self . reader . blocks ( node. ino ) . await ?;
203
+ let file_name = path
204
+ . file_name ( )
205
+ . map ( |name| name. to_string_lossy ( ) . to_string ( ) )
206
+ . unwrap_or_default ( ) ;
96
207
97
- for block in & blocks {
98
- self . writer . block ( ino, & block. id , & block. key ) . await ?;
99
- }
208
+ let parent_path = path. parent ( ) . unwrap_or ( Path :: new ( ROOT_PATH ) ) ;
209
+ let parent_ino = self . ensure_parent_directory ( parent_path) . await ?;
100
210
101
- let mut blocks_to_store = Vec :: new ( ) ;
102
- for block in blocks {
103
- if self . store . get ( & block) . await . is_err ( ) {
104
- blocks_to_store. push ( block) ;
105
- }
106
- }
107
- for block in blocks_to_store {
108
- let ( _, mut file) = self . cache . get ( & block) . await ?;
109
- let mut data = Vec :: new ( ) ;
110
- if let Err ( e) = file. read_to_end ( & mut data) . await {
111
- log:: error!(
112
- "failed to read block {}: {}" ,
113
- block. id. as_slice( ) . encode_hex:: <String >( ) ,
114
- e
115
- ) ;
116
- return Err ( e. into ( ) ) ;
117
- }
118
- if let Err ( e) = self . store . set ( & data) . await {
119
- log:: error!(
120
- "failed to set block {}: {}" ,
121
- block. id. as_slice( ) . encode_hex:: <String >( ) ,
122
- e
123
- ) ;
124
- return Err ( e. into ( ) ) ;
125
- }
126
- }
211
+ let file_node = Inode {
212
+ parent : parent_ino,
213
+ name : file_name,
214
+ size : node. size ,
215
+ uid : node. uid ,
216
+ gid : node. gid ,
217
+ mode : node. mode . clone ( ) ,
218
+ rdev : node. rdev ,
219
+ ctime : node. ctime ,
220
+ mtime : node. mtime ,
221
+ data : node. data . clone ( ) ,
222
+ ..Default :: default ( )
223
+ } ;
224
+
225
+ let ino = self . writer . inode ( file_node) . await ?;
226
+ self . copy_blocks ( node. ino , ino) . await ?;
127
227
}
128
228
_ => {
129
229
log:: warn!( "Unknown file type for node: {:?}" , node) ;
0 commit comments