22use derive_setters:: Setters ;
33use log:: { info, warn} ;
44
5- use std:: collections:: { BTreeMap , BTreeSet } ;
5+ use std:: {
6+ collections:: { BTreeMap , BTreeSet } ,
7+ path:: PathBuf ,
8+ } ;
69
710use crate :: {
8- backend:: {
9- decrypt:: { DecryptFullBackend , DecryptWriteBackend } ,
10- node:: NodeType ,
11- } ,
12- blob:: {
13- BlobId , BlobType ,
14- packer:: Packer ,
15- tree:: { Tree , TreeId } ,
11+ backend:: { decrypt:: DecryptWriteBackend , node:: NodeType } ,
12+ blob:: tree:: {
13+ Tree , TreeId ,
14+ modify:: { ModifierAction , ModifierChange , NodeAction , TreeAction , TreeModifier , Visitor } ,
1615 } ,
1716 error:: { ErrorKind , RusticError , RusticResult } ,
18- index:: { ReadGlobalIndex , ReadIndex , indexer :: Indexer } ,
17+ index:: ReadGlobalIndex ,
1918 progress:: ProgressBars ,
20- repofile:: { SnapshotFile , StringList , snapshotfile:: SnapshotId } ,
19+ repofile:: { Node , SnapshotFile , StringList , snapshotfile:: SnapshotId } ,
2120 repository:: { IndexedFull , IndexedTree , Repository } ,
2221} ;
2322
@@ -60,19 +59,103 @@ impl Default for RepairSnapshotsOptions {
6059 }
6160}
6261
63- // TODO: add documentation
64- # [ derive ( Clone , Copy ) ]
65- pub ( crate ) enum Changed {
66- This ,
67- SubTree ,
68- None ,
62+ pub ( crate ) struct RepairState < ' a , I : ReadGlobalIndex > {
63+ opts : & ' a RepairSnapshotsOptions ,
64+ index : & ' a I ,
65+ changed : BTreeMap < TreeId , TreeId > ,
66+ unchanged : BTreeSet < TreeId > ,
67+ delete : Vec < SnapshotId > ,
6968}
7069
71- #[ derive( Default ) ]
72- pub ( crate ) struct RepairState {
73- replaced : BTreeMap < TreeId , ( Changed , TreeId ) > ,
74- seen : BTreeSet < TreeId > ,
75- delete : Vec < SnapshotId > ,
70+ impl < ' a , I : ReadGlobalIndex > RepairState < ' a , I > {
71+ fn new ( opts : & ' a RepairSnapshotsOptions , index : & ' a I ) -> Self {
72+ Self {
73+ opts,
74+ index,
75+ changed : BTreeMap :: new ( ) ,
76+ unchanged : BTreeSet :: new ( ) ,
77+ delete : Vec :: new ( ) ,
78+ }
79+ }
80+ }
81+
82+ impl < I : ReadGlobalIndex > Visitor for RepairState < ' _ , I > {
83+ fn pre_process ( & self , _path : & PathBuf , id : TreeId ) -> ModifierAction {
84+ if self . unchanged . contains ( & id) {
85+ ModifierAction :: Change ( ModifierChange :: Unchanged )
86+ } else if let Some ( r) = self . changed . get ( & id) {
87+ ModifierAction :: Change ( ModifierChange :: Changed ( * r) )
88+ } else {
89+ ModifierAction :: Process ( id)
90+ }
91+ }
92+ fn pre_process_tree ( & mut self , tree : RusticResult < Tree > ) -> RusticResult < TreeAction > {
93+ Ok ( tree. map_or_else (
94+ |err| {
95+ warn ! ( "{}" , err. display_log( ) ) ; // TODO: id in error message
96+ TreeAction :: ProcessChangedTree ( Tree :: new ( ) )
97+ } ,
98+ TreeAction :: ProcessUnchangedTree ,
99+ ) )
100+ }
101+
102+ fn process_node ( & mut self , _path : & PathBuf , mut node : Node , _id : TreeId ) -> NodeAction {
103+ match node. node_type {
104+ NodeType :: File => {
105+ let mut file_changed = false ;
106+ let mut new_content = Vec :: new ( ) ;
107+ let mut new_size = 0 ;
108+ for blob in node. content . take ( ) . unwrap ( ) {
109+ self . index . get_data ( & blob) . map_or_else (
110+ || {
111+ file_changed = true ;
112+ } ,
113+ |ie| {
114+ new_content. push ( blob) ;
115+ new_size += u64:: from ( ie. data_length ( ) ) ;
116+ } ,
117+ ) ;
118+ }
119+ if file_changed {
120+ warn ! ( "file {}: contents are missing" , node. name) ;
121+ node. name += & self . opts . suffix ;
122+ } else if new_size != node. meta . size {
123+ info ! ( "file {}: corrected file size" , node. name) ;
124+ }
125+ node. content = Some ( new_content) ;
126+ node. meta . size = new_size;
127+ if file_changed {
128+ NodeAction :: ChangedNode ( node)
129+ } else {
130+ NodeAction :: UnchangedNode ( node)
131+ }
132+ }
133+ NodeType :: Dir => {
134+ if let Some ( subtree) = node. subtree {
135+ NodeAction :: VisitTree ( subtree, node)
136+ } else {
137+ NodeAction :: CreateTree ( node)
138+ }
139+ }
140+ _ => NodeAction :: UnchangedNode ( node) , // Other types: no check needed
141+ }
142+ }
143+ fn post_process (
144+ & mut self ,
145+ _path : PathBuf ,
146+ id : TreeId ,
147+ changed : bool ,
148+ new_id : Option < TreeId > ,
149+ _tree : & Tree ,
150+ ) {
151+ if changed {
152+ if let Some ( new_id) = new_id {
153+ _ = self . changed . insert ( id, new_id) ;
154+ }
155+ } else {
156+ _ = self . unchanged . insert ( id) ;
157+ }
158+ }
76159}
77160
78161/// Runs the `repair snapshots` command
@@ -104,37 +187,21 @@ pub(crate) fn repair_snapshots<P: ProgressBars, S: IndexedFull>(
104187 ) ) ;
105188 }
106189
107- let mut state = RepairState :: default ( ) ;
108-
109- let indexer = Indexer :: new ( be. clone ( ) ) . into_shared ( ) ;
110- let mut packer = Packer :: new (
111- be. clone ( ) ,
112- BlobType :: Tree ,
113- indexer. clone ( ) ,
114- config_file,
115- repo. index ( ) . total_size ( BlobType :: Tree ) ,
116- ) ?;
190+ let mut state = RepairState :: new ( opts, repo. index ( ) ) ;
191+ let modifier = TreeModifier :: new ( be, repo. index ( ) , config_file, dry_run) ?;
117192
118193 for mut snap in snapshots {
119194 let snap_id = snap. id ;
120195 info ! ( "processing snapshot {snap_id}" ) ;
121- match repair_tree (
122- repo. dbe ( ) ,
123- opts,
124- repo. index ( ) ,
125- & mut packer,
126- Some ( snap. tree ) ,
127- & mut state,
128- dry_run,
129- ) ? {
130- ( Changed :: None , _) => {
196+ match modifier. modify_tree ( PathBuf :: new ( ) , snap. tree , & mut state) ? {
197+ ModifierChange :: Unchanged => {
131198 info ! ( "snapshot {snap_id} is ok." ) ;
132199 }
133- ( Changed :: This , _ ) => {
200+ ModifierChange :: Removed => {
134201 warn ! ( "snapshot {snap_id}: root tree is damaged -> marking for deletion!" ) ;
135202 state. delete . push ( snap_id) ;
136203 }
137- ( Changed :: SubTree , id) => {
204+ ModifierChange :: Changed ( id) => {
138205 // change snapshot tree
139206 if snap. original . is_none ( ) {
140207 snap. original = Some ( snap. id ) ;
@@ -152,11 +219,6 @@ pub(crate) fn repair_snapshots<P: ProgressBars, S: IndexedFull>(
152219 }
153220 }
154221
155- if !dry_run {
156- _ = packer. finalize ( ) ?;
157- indexer. write ( ) . unwrap ( ) . finalize ( ) ?;
158- }
159-
160222 if opts. delete {
161223 if dry_run {
162224 info ! ( "would have removed {} snapshots." , state. delete. len( ) ) ;
@@ -171,130 +233,3 @@ pub(crate) fn repair_snapshots<P: ProgressBars, S: IndexedFull>(
171233
172234 Ok ( ( ) )
173235}
174-
175- /// Repairs a tree
176- ///
177- /// # Type Parameters
178- ///
179- /// * `BE` - The type of the backend.
180- ///
181- /// # Arguments
182- ///
183- /// * `be` - The backend to use
184- /// * `opts` - The repair options to use
185- /// * `packer` - The packer to use
186- /// * `id` - The id of the tree to repair
187- /// * `replaced` - A map of already replaced trees
188- /// * `seen` - A set of already seen trees
189- /// * `dry_run` - Whether to actually modify the repository or just print what would be done
190- ///
191- /// # Returns
192- ///
193- /// A tuple containing the change status and the id of the repaired tree
194- pub ( crate ) fn repair_tree < BE : DecryptWriteBackend > (
195- be : & impl DecryptFullBackend ,
196- opts : & RepairSnapshotsOptions ,
197- index : & impl ReadGlobalIndex ,
198- packer : & mut Packer < BE > ,
199- id : Option < TreeId > ,
200- state : & mut RepairState ,
201- dry_run : bool ,
202- ) -> RusticResult < ( Changed , TreeId ) > {
203- let ( tree, changed) = match id {
204- None => ( Tree :: new ( ) , Changed :: This ) ,
205- Some ( id) => {
206- if state. seen . contains ( & id) {
207- return Ok ( ( Changed :: None , id) ) ;
208- }
209- if let Some ( r) = state. replaced . get ( & id) {
210- return Ok ( * r) ;
211- }
212-
213- let ( tree, mut changed) = Tree :: from_backend ( be, index, id) . map_or_else (
214- |err| {
215- warn ! ( "tree {id} could not be loaded: {}" , err. display_log( ) ) ;
216- ( Tree :: new ( ) , Changed :: This )
217- } ,
218- |tree| ( tree, Changed :: None ) ,
219- ) ;
220-
221- let mut new_tree = Tree :: new ( ) ;
222-
223- for mut node in tree {
224- match node. node_type {
225- NodeType :: File => {
226- let mut file_changed = false ;
227- let mut new_content = Vec :: new ( ) ;
228- let mut new_size = 0 ;
229- for blob in node. content . take ( ) . unwrap ( ) {
230- index. get_data ( & blob) . map_or_else (
231- || {
232- file_changed = true ;
233- } ,
234- |ie| {
235- new_content. push ( blob) ;
236- new_size += u64:: from ( ie. data_length ( ) ) ;
237- } ,
238- ) ;
239- }
240- if file_changed {
241- warn ! ( "file {}: contents are missing" , node. name) ;
242- node. name += & opts. suffix ;
243- changed = Changed :: SubTree ;
244- } else if new_size != node. meta . size {
245- info ! ( "file {}: corrected file size" , node. name) ;
246- changed = Changed :: SubTree ;
247- }
248- node. content = Some ( new_content) ;
249- node. meta . size = new_size;
250- }
251- NodeType :: Dir => {
252- let ( c, tree_id) =
253- repair_tree ( be, opts, index, packer, node. subtree , state, dry_run) ?;
254- match c {
255- Changed :: None => { }
256- Changed :: This => {
257- warn ! ( "dir {}: tree is missing" , node. name) ;
258- node. subtree = Some ( tree_id) ;
259- node. name += & opts. suffix ;
260- changed = Changed :: SubTree ;
261- }
262- Changed :: SubTree => {
263- node. subtree = Some ( tree_id) ;
264- changed = Changed :: SubTree ;
265- }
266- }
267- }
268- _ => { } // Other types: no check needed
269- }
270- new_tree. add ( node) ;
271- }
272- if matches ! ( changed, Changed :: None ) {
273- _ = state. seen . insert ( id) ;
274- }
275- ( new_tree, changed)
276- }
277- } ;
278-
279- match ( id, changed) {
280- ( None , Changed :: None ) => panic ! ( "this should not happen!" ) ,
281- ( Some ( id) , Changed :: None ) => Ok ( ( Changed :: None , id) ) ,
282- ( _, c) => {
283- // the tree has been changed => save it
284- let ( chunk, new_id) = tree. serialize ( ) . map_err ( |err| {
285- RusticError :: with_source ( ErrorKind :: Internal , "Failed to serialize tree." , err)
286- . ask_report ( )
287- } ) ?;
288-
289- if !index. has_tree ( & new_id) && !dry_run {
290- packer. add ( chunk. into ( ) , BlobId :: from ( * new_id) ) ?;
291- }
292-
293- if let Some ( id) = id {
294- _ = state. replaced . insert ( id, ( c, new_id) ) ;
295- }
296-
297- Ok ( ( c, new_id) )
298- }
299- }
300- }
0 commit comments