@@ -9,6 +9,7 @@ use crate::{
99} ;
1010
1111/// Virtual filesystem layer over easy-fs
12+ #[ derive( Clone ) ]
1213pub struct Inode {
1314 inode_id : u32 ,
1415
@@ -55,7 +56,7 @@ impl Inode {
5556 /// Find inode under a disk inode by name
5657 fn find_inode_id ( & self , name : & str , disk_inode : & DiskInode ) -> Option < u32 > {
5758 assert ! ( disk_inode. is_dir( ) ) ;
58- // data of `disk_node ` should be array of `Dirent`s
59+ // data of `disk_inode ` should be array of `Dirent`s
5960 let file_count = ( disk_inode. size as usize ) / DIRENT_SZ ;
6061 let mut dirent = DirEntry :: new_empty ( ) ;
6162 for i in 0 ..file_count {
@@ -243,9 +244,7 @@ impl Inode {
243244 self . create_inode ( name, DiskInodeType :: Directory )
244245 }
245246
246- /// Clear the data in current inode
247- pub fn clear ( & self ) {
248- let mut fs = self . fs . lock ( ) ;
247+ fn clear_locked ( & self , fs : & mut EasyFileSystem ) {
249248 self . modify_disk_inode ( |disk_inode| {
250249 assert ! ( disk_inode. is_file( ) ) ;
251250 let size = disk_inode. size ;
@@ -261,6 +260,12 @@ impl Inode {
261260 block_cache_sync_all ( ) ;
262261 }
263262
263+ /// Clear the data in current inode
264+ pub fn clear ( & self ) {
265+ let mut fs = self . fs . lock ( ) ;
266+ self . clear_locked ( & mut fs) ;
267+ }
268+
264269 /// Read data from current inode
265270 pub fn read_at ( & self , offset : usize , buf : & mut [ u8 ] ) -> usize {
266271 self . read_disk_inode ( |disk_inode| disk_inode. read_at ( offset, buf, & self . block_device ) )
@@ -296,4 +301,98 @@ impl Inode {
296301 pub fn is_file ( & self ) -> bool {
297302 self . read_disk_inode ( |disk_inode| disk_inode. is_file ( ) )
298303 }
304+
305+ /// Get link number
306+ pub fn nlink ( & self ) -> u32 {
307+ self . read_disk_inode ( |disk_inode| disk_inode. nlink )
308+ }
309+
310+ /// Create hard link `name` from `src`
311+ pub fn link ( & self , name : & str , src : & Inode ) -> Option < Arc < Inode > > {
312+ let mut fs = self . fs . lock ( ) ;
313+
314+ let op = |root_inode : & DiskInode | {
315+ assert ! ( root_inode. is_dir( ) ) ;
316+ self . find_inode_id ( name, root_inode)
317+ } ;
318+ // exist already
319+ if self . read_disk_inode ( op) . is_some ( ) {
320+ return None ;
321+ }
322+
323+ // add dirent under self
324+ self . modify_disk_inode ( |disk_inode| {
325+ let file_count = ( disk_inode. size as usize ) / DIRENT_SZ ;
326+ let new_size = ( file_count + 1 ) * DIRENT_SZ ;
327+ self . increase_size ( new_size as u32 , disk_inode, & mut fs) ;
328+ let dirent = DirEntry :: new ( name, src. inode_id ) ;
329+ disk_inode. write_at (
330+ file_count * DIRENT_SZ ,
331+ dirent. as_bytes ( ) ,
332+ & self . block_device ,
333+ ) ;
334+ } ) ;
335+ // inc src nlink
336+ src. modify_disk_inode ( |disk_inode| disk_inode. nlink += 1 ) ;
337+ Some ( Arc :: new ( Self :: clone ( src) ) )
338+ }
339+
340+ /// Remove hard link (return if removed successfully)
341+ pub fn unlink ( & self , name : & str ) -> bool {
342+ let mut fs = self . fs . lock ( ) ;
343+ // self is dir && "name" exists
344+ let target = self . modify_disk_inode ( |disk_inode| {
345+ if !disk_inode. is_dir ( ) {
346+ return None ;
347+ }
348+ let file_count = ( disk_inode. size as usize ) / DIRENT_SZ ;
349+ let mut dirent = DirEntry :: new_empty ( ) ;
350+ let mut swap = DirEntry :: new_empty ( ) ;
351+ match ( 0 ..file_count) . position ( |i| {
352+ assert_eq ! (
353+ disk_inode. read_at( i * DIRENT_SZ , dirent. as_bytes_mut( ) , & self . block_device) ,
354+ DIRENT_SZ
355+ ) ;
356+ dirent. name ( ) == name
357+ } ) {
358+ Some ( i) => {
359+ // target
360+ let target_inode_id = dirent. inode_number ( ) ;
361+ let ( target_block_id, target_block_offset) =
362+ fs. get_disk_inode_pos ( target_inode_id) ;
363+ let target = Self :: new (
364+ target_inode_id,
365+ target_block_id,
366+ target_block_offset,
367+ self . fs . clone ( ) ,
368+ self . block_device . clone ( ) ,
369+ ) ;
370+ // we don't actually delete i-th, but swap last to i-th, and decrease disk_inode.size only
371+ // in real world we should decrease data of this dir's actual space (at proper time?)
372+ disk_inode. read_at (
373+ ( file_count - 1 ) * DIRENT_SZ ,
374+ swap. as_bytes_mut ( ) ,
375+ & self . block_device ,
376+ ) ;
377+ disk_inode. write_at ( i * DIRENT_SZ , swap. as_bytes ( ) , & self . block_device ) ;
378+ disk_inode. size -= DIRENT_SZ as u32 ;
379+ Some ( target)
380+ }
381+ _ => None , // no such file
382+ }
383+ } ) ;
384+ // clear target's data if link decrease to 0
385+ if let Some ( target) = target {
386+ if target. modify_disk_inode ( |disk_inode| {
387+ disk_inode. nlink -= 1 ;
388+ disk_inode. nlink
389+ } ) == 0
390+ {
391+ target. clear_locked ( & mut fs) ;
392+ }
393+ true
394+ } else {
395+ false
396+ }
397+ }
299398}
0 commit comments