@@ -2610,7 +2610,7 @@ func recordDeletionStats(
26102610 }
26112611}
26122612
2613- func (m * dbMeta ) doEmptyDir (ctx Context , parent Ino , entries []* Entry , length * int64 , space * int64 , inodes * int64 , userGroupQuotas * []UserGroupQuotaDelta , skipCheckTrash ... bool ) syscall.Errno {
2613+ func (m * dbMeta ) doBatchUnlink (ctx Context , parent Ino , entries []Entry , length * int64 , space * int64 , inodes * int64 , userGroupQuotas * []UserGroupQuotaDelta , skipCheckTrash ... bool ) syscall.Errno {
26142614 if len (entries ) == 0 {
26152615 return 0
26162616 }
@@ -2630,13 +2630,11 @@ func (m *dbMeta) doEmptyDir(ctx Context, parent Ino, entries []*Entry, length *i
26302630 trashName string
26312631 lastLink bool
26322632 }
2633-
26342633 var entryInfos []entryInfo
26352634 var totalLength , totalSpace , totalInodes int64
26362635 if userGroupQuotas != nil {
26372636 * userGroupQuotas = make ([]UserGroupQuotaDelta , 0 , len (entries ))
26382637 }
2639-
26402638 err := m .txn (func (s * xorm.Session ) error {
26412639 pn := node {Inode : parent }
26422640 ok , err := s .Get (& pn )
@@ -2656,89 +2654,133 @@ func (m *dbMeta) doEmptyDir(ctx Context, parent Ino, entries []*Entry, length *i
26562654 entryInfos = make ([]entryInfo , 0 , len (entries ))
26572655 now := time .Now ().UnixNano ()
26582656
2659- for _ , entry := range entries {
2657+ inodes := make ([]Ino , 0 , len (entries ))
2658+ inodeToEntry := make (map [Ino ][]int ) // inode -> indices in entries
2659+ for i , entry := range entries {
26602660 e := edge {Parent : parent , Name : entry .Name , Inode : entry .Inode }
26612661 if entry .Attr != nil {
26622662 e .Type = entry .Attr .Typ
26632663 }
2664-
26652664 info := entryInfo {e : e , trash : trash }
2666- n := node {Inode : e .Inode }
2667- ok , err := s .ForUpdate ().Get (& n )
2668- if err != nil {
2669- return err
2665+ entryInfos = append (entryInfos , info )
2666+ if _ , exists := inodeToEntry [entry .Inode ]; ! exists {
2667+ inodes = append (inodes , entry .Inode )
26702668 }
2671- if ! ok {
2672- continue
2669+ inodeToEntry [entry .Inode ] = append (inodeToEntry [entry .Inode ], i )
2670+ }
2671+
2672+ if len (inodes ) > 0 {
2673+ var nodes []node
2674+ if err := s .ForUpdate ().In ("inode" , inodes ).Find (& nodes ); err != nil {
2675+ return err
26732676 }
2674- if ctx .Uid () != 0 && pn .Mode & 01000 != 0 && ctx .Uid () != pn .Uid && ctx .Uid () != n .Uid {
2675- return syscall .EACCES
2677+ nodeMap := make (map [Ino ]* node , len (nodes ))
2678+ for i := range nodes {
2679+ nodeMap [nodes [i ].Inode ] = & nodes [i ]
26762680 }
26772681
2678- if (n .Flags & FlagAppend ) != 0 || (n .Flags & FlagImmutable ) != 0 {
2679- return syscall .EPERM
2680- }
2681- if (n .Flags & FlagSkipTrash ) != 0 {
2682- info .trash = 0
2682+ for i := range entryInfos {
2683+ info := & entryInfos [i ]
2684+ n , ok := nodeMap [info .e .Inode ]
2685+ if ! ok {
2686+ entryInfos [i ].e .Inode = 0
2687+ continue
2688+ }
2689+ if ctx .Uid () != 0 && pn .Mode & 01000 != 0 && ctx .Uid () != pn .Uid && ctx .Uid () != n .Uid {
2690+ return syscall .EACCES
2691+ }
2692+ if (n .Flags & FlagAppend ) != 0 || (n .Flags & FlagImmutable ) != 0 {
2693+ return syscall .EPERM
2694+ }
2695+ if (n .Flags & FlagSkipTrash ) != 0 {
2696+ info .trash = 0
2697+ }
2698+ info .n = * n
26832699 }
26842700
2685- if info .trash > 0 {
2686- info .trashName = m .trashEntry (parent , e .Inode , string (e .Name ))
2687- if n .Nlink > 1 {
2688- if o , err := s .Get (& edge {Parent : info .trash , Name : []byte (info .trashName ), Inode : e .Inode , Type : e .Type }); err == nil && o {
2689- info .trash = 0
2690- }
2701+ filteredInfos := entryInfos [:0 ]
2702+ for i := range entryInfos {
2703+ if entryInfos [i ].e .Inode != 0 {
2704+ filteredInfos = append (filteredInfos , entryInfos [i ])
26912705 }
26922706 }
2707+ entryInfos = filteredInfos
2708+ }
26932709
2694- n .setCtime (now )
2695- if info .trash != 0 && n .Parent > 0 {
2696- n .Parent = info .trash
2710+ for i := range entryInfos {
2711+ info := & entryInfos [i ]
2712+ if info .trash > 0 && info .n .Nlink > 1 {
2713+ info .trashName = m .trashEntry (parent , info .e .Inode , string (info .e .Name ))
2714+ te := edge {
2715+ Parent : info .trash ,
2716+ Name : []byte (info .trashName ),
2717+ Inode : info .e .Inode ,
2718+ Type : info .e .Type ,
2719+ }
2720+ if ok , err := s .Get (& te ); err == nil && ok {
2721+ info .trash = 0
2722+ }
26972723 }
2724+ }
26982725
2699- info .n = n
2700- entryInfos = append (entryInfos , info )
2726+ for i := range entryInfos {
2727+ info := & entryInfos [i ]
2728+ info .n .setCtime (now )
2729+ if info .trash != 0 && info .n .Parent > 0 {
2730+ info .n .Parent = info .trash
2731+ }
27012732 }
27022733
2703- seen := make (map [Ino ]int )
2734+ seen := make (map [Ino ]uint32 )
27042735 for i := range entryInfos {
27052736 info := & entryInfos [i ]
27062737 if info .e .Type == TypeDirectory {
27072738 continue
27082739 }
2709- original := int64 (info .n .Nlink )
2710- processed := seen [info .e .Inode ]
2711- finalNlink := original - int64 (processed + 1 )
2740+ processed := seen [info .e .Inode ] + 1
2741+ finalNlink := int64 (info .n .Nlink ) - int64 (processed )
27122742 if finalNlink < 0 {
27132743 finalNlink = 0
27142744 }
27152745 // If trash is enabled and this would be the last link, keep one link by moving it into trash.
2716- if info .trash > 0 && finalNlink == 0 && info . e . Type != TypeDirectory {
2746+ if info .trash > 0 && finalNlink == 0 {
27172747 finalNlink = 1
27182748 }
27192749 info .lastLink = (info .trash == 0 && finalNlink == 0 )
27202750 if info .lastLink && info .e .Type == TypeFile && m .sid > 0 {
27212751 info .opened = m .of .IsOpen (info .e .Inode )
27222752 }
27232753 info .n .Nlink = uint32 (finalNlink )
2724- seen [info .e .Inode ] = processed + 1
2754+ seen [info .e .Inode ] = processed
27252755 }
27262756
27272757 trashInserted := make (map [Ino ]bool )
2758+ nowUnix := time .Now ().Unix ()
2759+
27282760 for _ , info := range entryInfos {
27292761 if info .e .Type == TypeDirectory {
27302762 continue
27312763 }
2732- if _ , err := s .Delete (& edge {Parent : parent , Name : info .e .Name }); err != nil {
2764+ e := edge {Parent : parent , Name : info .e .Name }
2765+ if _ , err := s .Delete (& e ); err != nil {
27332766 return err
27342767 }
27352768
27362769 if info .n .Nlink > 0 {
2737- if _ , err := s .Cols ("nlink" , "ctime" , "ctimensec" , "parent" ).Update (& info .n , & node {Inode : info .e .Inode }); err != nil {
2770+ if _ , err := s .Cols ("nlink" , "ctime" , "ctimensec" , "parent" ).Update (& info .n , & node {Inode : info .n .Inode }); err != nil {
27382771 return err
27392772 }
27402773 if info .trash > 0 && ! trashInserted [info .e .Inode ] {
2741- if err := mustInsert (s , & edge {Parent : info .trash , Name : []byte (info .trashName ), Inode : info .e .Inode , Type : info .e .Type }); err != nil {
2774+ if info .trashName == "" {
2775+ info .trashName = m .trashEntry (parent , info .e .Inode , string (info .e .Name ))
2776+ }
2777+ te := edge {
2778+ Parent : info .trash ,
2779+ Name : []byte (info .trashName ),
2780+ Inode : info .e .Inode ,
2781+ Type : info .e .Type ,
2782+ }
2783+ if err := mustInsert (s , & te ); err != nil {
27422784 return err
27432785 }
27442786 trashInserted [info .e .Inode ] = true
@@ -2750,15 +2792,15 @@ func (m *dbMeta) doEmptyDir(ctx Context, parent Ino, entries []*Entry, length *i
27502792 case TypeFile :
27512793 entrySpace := align4K (info .n .Length )
27522794 if info .opened {
2753- if err = mustInsert (s , sustained {Sid : m .sid , Inode : info .e .Inode }); err != nil {
2795+ if err : = mustInsert (s , & sustained {Sid : m .sid , Inode : info .e .Inode }); err != nil {
27542796 return err
27552797 }
2756- if _ , err := s .Cols ("nlink" , "ctime" , "ctimensec" ).Update (& info .n , & node {Inode : info .e .Inode }); err != nil {
2798+ if _ , err := s .Cols ("nlink" , "ctime" , "ctimensec" ).Update (& info .n , & node {Inode : info .n .Inode }); err != nil {
27572799 return err
27582800 }
27592801 recordDeletionStats (& info .n , entrySpace , 0 , & totalLength , & totalSpace , & totalInodes , userGroupQuotas , trash )
27602802 } else {
2761- if err = mustInsert (s , delfile {info .e .Inode , info .n .Length , time . Now (). Unix () }); err != nil {
2803+ if err : = mustInsert (s , & delfile {info .e .Inode , info .n .Length , nowUnix }); err != nil {
27622804 return err
27632805 }
27642806 if _ , err := s .Delete (& node {Inode : info .e .Inode }); err != nil {
@@ -2770,11 +2812,7 @@ func (m *dbMeta) doEmptyDir(ctx Context, parent Ino, entries []*Entry, length *i
27702812 if _ , err := s .Delete (& symlink {Inode : info .e .Inode }); err != nil {
27712813 return err
27722814 }
2773- if _ , err := s .Delete (& node {Inode : info .e .Inode }); err != nil {
2774- return err
2775- }
2776- entrySpace := align4K (0 )
2777- recordDeletionStats (& info .n , entrySpace , - entrySpace , & totalLength , & totalSpace , & totalInodes , userGroupQuotas , trash )
2815+ fallthrough
27782816 default :
27792817 if _ , err := s .Delete (& node {Inode : info .e .Inode }); err != nil {
27802818 return err
@@ -2788,7 +2826,12 @@ func (m *dbMeta) doEmptyDir(ctx Context, parent Ino, entries []*Entry, length *i
27882826 return err
27892827 }
27902828 }
2791- m .of .InvalidateChunk (info .e .Inode , invalidateAttrOnly )
2829+ }
2830+
2831+ for _ , info := range entryInfos {
2832+ if info .e .Type != TypeDirectory {
2833+ m .of .InvalidateChunk (info .e .Inode , invalidateAttrOnly )
2834+ }
27922835 }
27932836
27942837 return nil
0 commit comments