44//
55
66use std:: borrow:: Borrow ;
7- use std:: cmp:: { Eq , PartialEq } ;
8- use std:: collections:: { HashMap , VecDeque } ;
9- use std:: collections:: hash_map :: Entry :: * ;
7+ use std:: cmp:: { Eq , Ordering , PartialEq } ;
8+ use std:: collections:: { BTreeMap , VecDeque } ;
9+ use std:: collections:: btree_map :: Entry :: * ;
1010use std:: hash:: { Hash , Hasher } ;
11+ use std:: ops:: Bound :: { Excluded , Included , Unbounded } ;
1112use std:: path:: { Path , PathBuf } ;
1213use std:: sync:: Arc ;
1314
@@ -27,7 +28,7 @@ struct InodeTableEntry {
2728pub struct InodeTable {
2829 table : Vec < InodeTableEntry > ,
2930 free_list : VecDeque < usize > ,
30- by_path : HashMap < Arc < PathBuf > , usize > ,
31+ by_path : BTreeMap < Arc < PathBuf > , usize > ,
3132}
3233
3334impl InodeTable {
@@ -42,7 +43,7 @@ impl InodeTable {
4243 let mut inode_table = InodeTable {
4344 table : Vec :: new ( ) ,
4445 free_list : VecDeque :: new ( ) ,
45- by_path : HashMap :: new ( )
46+ by_path : BTreeMap :: new ( )
4647 } ;
4748 let root = Arc :: new ( PathBuf :: from ( "/" ) ) ;
4849 inode_table. table . push ( InodeTableEntry {
@@ -180,9 +181,38 @@ impl InodeTable {
180181 /// Change an inode's path to a different one, without changing the inode number.
181182 /// Lookup counts remain unchanged, even if this is replacing another file.
182183 pub fn rename ( & mut self , oldpath : & Path , newpath : Arc < PathBuf > ) {
183- let idx = self . by_path . remove ( Pathish :: new ( oldpath) ) . unwrap ( ) ;
184- self . table [ idx] . path = Some ( newpath. clone ( ) ) ;
185- self . by_path . insert ( newpath, idx) ; // this can replace a path with a new inode
184+ // none means it's a single path
185+ let mut range = None ;
186+ for ( candidate, _) in self . by_path . range :: < Pathish , _ > ( ( Excluded ( Pathish :: new ( oldpath) ) , Unbounded ) ) {
187+ if candidate. starts_with ( oldpath) {
188+ if let Some ( ( start, _end) ) = range {
189+ range = Some ( ( start, Included ( candidate. clone ( ) ) ) ) ;
190+ } else {
191+ range = Some ( ( Included ( Arc :: new ( oldpath. to_owned ( ) ) ) , Included ( Arc :: clone ( candidate) ) ) ) ;
192+ }
193+ } else {
194+ break ;
195+ }
196+ }
197+
198+ if let Some ( range) = range {
199+ let mut new_entries = vec ! [ ] ;
200+ for ( path, idx) in self . by_path . extract_if ( range, |_, _| true ) {
201+ let suffix = path. strip_prefix ( oldpath) . unwrap ( ) ;
202+ let new_entry_path = if suffix. as_os_str ( ) . is_empty ( ) {
203+ Arc :: clone ( & newpath)
204+ } else {
205+ Arc :: new ( newpath. as_path ( ) . join ( suffix) )
206+ } ;
207+ self . table [ idx] . path = Some ( Arc :: clone ( & new_entry_path) ) ;
208+ new_entries. push ( ( new_entry_path, idx) ) ;
209+ }
210+ self . by_path . extend ( new_entries) ;
211+ } else {
212+ let idx = self . by_path . remove ( Pathish :: new ( oldpath) ) . unwrap ( ) ;
213+ self . table [ idx] . path = Some ( newpath. clone ( ) ) ;
214+ self . by_path . insert ( newpath, idx) ; // this can replace a path with a new inode
215+ }
186216 }
187217
188218 /// Remove the path->inode mapping for a given path, but keep the inode around.
@@ -245,6 +275,18 @@ impl Hash for Pathish {
245275impl Eq for Pathish {
246276}
247277
278+ impl PartialOrd < Self > for Pathish {
279+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
280+ Some ( self . cmp ( other) )
281+ }
282+ }
283+
284+ impl Ord for Pathish {
285+ fn cmp ( & self , other : & Self ) -> Ordering {
286+ self . inner . cmp ( & other. inner )
287+ }
288+ }
289+
248290impl PartialEq for Pathish {
249291 fn eq ( & self , other : & Pathish ) -> bool {
250292 self . inner . eq ( & other. inner )
@@ -341,3 +383,31 @@ fn test_unlink() {
341383 assert_eq ! ( 0 , table. forget( inode, 1 ) ) ;
342384 assert ! ( table. get_path( inode) . is_none( ) ) ;
343385}
386+
387+ #[ test]
388+ fn test_rename_directory ( ) {
389+ let mut table = InodeTable :: new ( ) ;
390+ let a = table. add ( Arc :: new ( PathBuf :: from ( "/a_file" ) ) ) ;
391+ let d = table. add ( Arc :: new ( PathBuf :: from ( "/directory" ) ) ) ;
392+ let d_f1 = table. add ( Arc :: new ( PathBuf :: from ( "/directory/file1" ) ) ) ;
393+ let d_f2 = table. add ( Arc :: new ( PathBuf :: from ( "/directory/file2" ) ) ) ;
394+ let z = table. add ( Arc :: new ( PathBuf :: from ( "/z_file" ) ) ) ;
395+
396+ table. rename ( Path :: new ( "/a_file" ) , Arc :: new ( PathBuf :: from ( "/a_file_renamed" ) ) ) ;
397+ assert_eq ! ( table. get_inode( Path :: new( "/a_file" ) ) , None ) ;
398+ assert_eq ! ( table. get_inode( Path :: new( "/a_file_renamed" ) ) , Some ( a. 0 ) ) ;
399+
400+ table. rename ( Path :: new ( "/directory" ) , Arc :: new ( PathBuf :: from ( "/new_directory" ) ) ) ;
401+
402+ assert_eq ! ( table. get_inode( Path :: new( "/a_file_renamed" ) ) , Some ( a. 0 ) ) ;
403+ assert_eq ! ( table. get_inode( Path :: new( "/z_file" ) ) , Some ( z. 0 ) ) ;
404+
405+ assert_eq ! ( table. get_inode( Path :: new( "/new_directory" ) ) , Some ( d. 0 ) ) ;
406+ assert_eq ! ( table. get_inode( Path :: new( "/new_directory/file1" ) ) , Some ( d_f1. 0 ) ) ;
407+ assert_eq ! ( table. get_inode( Path :: new( "/new_directory/file2" ) ) , Some ( d_f2. 0 ) ) ;
408+
409+ assert_eq ! ( table. get_inode( Path :: new( "/directory" ) ) , None ) ;
410+ assert_eq ! ( table. get_inode( Path :: new( "/directory/file1" ) ) , None ) ;
411+ assert_eq ! ( table. get_inode( Path :: new( "/directory/file2" ) ) , None ) ;
412+
413+ }
0 commit comments