@@ -125,6 +125,9 @@ struct ignentry {
125
125
126
126
static int linkchk (FTSENT * );
127
127
static int dirlinkchk (FTSENT * );
128
+ #ifdef __APPLE__
129
+ static int clonechk (FTSENT * );
130
+ #endif
128
131
static void usage (void );
129
132
static void prthumanval (int64_t );
130
133
static void ignoreadd (const char * );
@@ -152,14 +155,14 @@ main(int argc, char *argv[])
152
155
uint64_t threshold , threshold_sign ;
153
156
int ftsoptions ;
154
157
int depth ;
155
- int Hflag , Lflag , aflag , sflag , dflag , cflag ;
158
+ int Cflag , Hflag , Lflag , aflag , sflag , dflag , cflag ;
156
159
int lflag , ch , notused , rval ;
157
160
char * * save ;
158
161
static char dot [] = "." ;
159
162
160
163
setlocale (LC_ALL , "" );
161
164
162
- Hflag = Lflag = aflag = sflag = dflag = cflag = lflag = hflag = Aflag = 0 ;
165
+ Cflag = Hflag = Lflag = aflag = sflag = dflag = cflag = lflag = hflag = Aflag = 0 ;
163
166
164
167
save = argv ;
165
168
ftsoptions = FTS_PHYSICAL ;
@@ -175,12 +178,15 @@ main(int argc, char *argv[])
175
178
depth = INT_MAX ;
176
179
SLIST_INIT (& ignores );
177
180
178
- while ((ch = getopt_long (argc , argv , "+AB :HI:LPasd:cghklmnrt:x" ,
181
+ while ((ch = getopt_long (argc , argv , "+ACB :HI:LPasd:cghklmnrt:x" ,
179
182
long_options , NULL )) != -1 )
180
183
switch (ch ) {
181
184
case 'A' :
182
185
Aflag = 1 ;
183
186
break ;
187
+ case 'C' :
188
+ Cflag = 1 ;
189
+ break ;
184
190
case 'B' :
185
191
errno = 0 ;
186
192
cblocksize = atoi (optarg );
@@ -390,6 +396,11 @@ main(int argc, char *argv[])
390
396
linkchk (p ))
391
397
break ;
392
398
399
+ #ifdef __APPLE__
400
+ if (Cflag == 0 && clonechk (p ))
401
+ break ;
402
+ #endif
403
+
393
404
curblocks = Aflag ?
394
405
howmany (p -> fts_statp -> st_size , cblocksize ) :
395
406
howmany (p -> fts_statp -> st_blocks , cblocksize );
@@ -706,6 +717,112 @@ dirlinkchk(FTSENT *p)
706
717
return (0 );
707
718
}
708
719
720
+ #if __APPLE__
721
+ static int
722
+ clonechk (FTSENT * p )
723
+ {
724
+ struct links_entry {
725
+ struct links_entry * next ;
726
+ struct links_entry * previous ;
727
+ uint64_t cloneid ;
728
+ dev_t dev ;
729
+ };
730
+ static const size_t links_hash_initial_size = 8192 ;
731
+ static struct links_entry * * buckets ;
732
+ static size_t number_buckets ;
733
+ static unsigned long number_entries ;
734
+ static char stop_allocating ;
735
+ struct links_entry * le , * * new_buckets ;
736
+ struct stat * st ;
737
+ uint64_t cloneid ;
738
+ size_t i , new_size ;
739
+ int hash ;
740
+ struct attrbuf {
741
+ int size ;
742
+ uint64_t value ;
743
+ } __attribute((aligned (4 ), packed )) buf ;
744
+ struct attrlist attrList ;
745
+
746
+ memset (& attrList , 0 , sizeof (attrList ));
747
+ attrList .bitmapcount = ATTR_BIT_MAP_COUNT ;
748
+ attrList .forkattr = ATTR_CMNEXT_CLONEID ;
749
+ if (-1 == getattrlist (p -> fts_path , & attrList , & buf , sizeof (buf ), FSOPT_ATTR_CMN_EXTENDED ))
750
+ return 0 ;
751
+ cloneid = buf .value ;
752
+ st = p -> fts_statp ;
753
+
754
+ /* If necessary, initialize the hash table. */
755
+ if (buckets == NULL ) {
756
+ number_buckets = links_hash_initial_size ;
757
+ buckets = malloc (number_buckets * sizeof (buckets [0 ]));
758
+ if (buckets == NULL )
759
+ errx (1 , "No memory for clone detection" );
760
+ for (i = 0 ; i < number_buckets ; i ++ )
761
+ buckets [i ] = NULL ;
762
+ }
763
+
764
+ /* If the hash table is getting too full, enlarge it. */
765
+ if (number_entries > number_buckets * 10 && !stop_allocating ) {
766
+ new_size = number_buckets * 2 ;
767
+ new_buckets = calloc (new_size , sizeof (struct links_entry * ));
768
+
769
+ if (new_buckets == NULL ) {
770
+ stop_allocating = 1 ;
771
+ warnx ("No more memory for tracking clones" );
772
+ } else {
773
+ for (i = 0 ; i < number_buckets ; i ++ ) {
774
+ while (buckets [i ] != NULL ) {
775
+ /* Remove entry from old bucket. */
776
+ le = buckets [i ];
777
+ buckets [i ] = le -> next ;
778
+
779
+ /* Add entry to new bucket. */
780
+ hash = (le -> dev ^ le -> cloneid ) % new_size ;
781
+
782
+ if (new_buckets [hash ] != NULL )
783
+ new_buckets [hash ]-> previous =
784
+ le ;
785
+ le -> next = new_buckets [hash ];
786
+ le -> previous = NULL ;
787
+ new_buckets [hash ] = le ;
788
+ }
789
+ }
790
+ free (buckets );
791
+ buckets = new_buckets ;
792
+ number_buckets = new_size ;
793
+ }
794
+ }
795
+
796
+ /* Try to locate this entry in the hash table. */
797
+ hash = ( st -> st_dev ^ cloneid ) % number_buckets ;
798
+ for (le = buckets [hash ]; le != NULL ; le = le -> next ) {
799
+ if (le -> dev == st -> st_dev && le -> cloneid == cloneid ) {
800
+ return (1 );
801
+ }
802
+ }
803
+
804
+ if (stop_allocating )
805
+ return (0 );
806
+
807
+ /* Add this entry to the clone ids cache. */
808
+ le = malloc (sizeof (struct links_entry ));
809
+ if (le == NULL ) {
810
+ stop_allocating = 1 ;
811
+ warnx ("No more memory for tracking clones" );
812
+ return (0 );
813
+ }
814
+ le -> dev = st -> st_dev ;
815
+ le -> cloneid = cloneid ;
816
+ number_entries ++ ;
817
+ le -> next = buckets [hash ];
818
+ le -> previous = NULL ;
819
+ if (buckets [hash ] != NULL )
820
+ buckets [hash ]-> previous = le ;
821
+ buckets [hash ] = le ;
822
+ return (0 );
823
+ }
824
+ #endif
825
+
709
826
static void
710
827
prthumanval (int64_t bytes )
711
828
{
0 commit comments