@@ -83,7 +83,13 @@ processed before processing the current one
8383#include "utils.h"
8484
8585/* define so that descend and ascend always have valid functions to call */
86- static int noop (void * user_struct ) {
86+ static int noop_descend (void * user_struct , int * keep_going ) {
87+ (void ) user_struct ;
88+ (void ) keep_going ;
89+ return 0 ;
90+ }
91+
92+ static int noop_ascend (void * user_struct ) {
8793 (void ) user_struct ;
8894 return 0 ;
8995}
@@ -99,8 +105,10 @@ void bottomup_destroy(void *p) {
99105
100106struct UserArgs {
101107 size_t user_struct_size ;
102- BU_f descend ;
103- BU_f ascend ;
108+ size_t min_level ;
109+ size_t max_level ;
110+ BU_descend_f descend ;
111+ BU_ascend_f ascend ;
104112 int track_non_dirs ;
105113 int generate_alt_name ;
106114 trie_t * skip ;
@@ -210,7 +218,10 @@ static int ascend_to_top(QPTPool_t *ctx, const size_t id, void *data, void *args
210218 bu -> tid .up = id ;
211219
212220 /* call user ascend function */
213- const int asc_rc = ua -> ascend (bu );
221+ int asc_rc = 0 ;
222+ if ((ua -> min_level <= bu -> level ) && (bu -> level <= ua -> max_level )) {
223+ asc_rc = ua -> ascend (bu );
224+ }
214225
215226 /* clean up 'struct BottomUp's here, when they are */
216227 /* children instead of when they are the parent */
@@ -348,12 +359,19 @@ static int descend_to_bottom(QPTPool_t *ctx, const size_t id, void *data, void *
348359
349360 /* this will probably be a bottleneck since subdirectories won't */
350361 /* be queued/processed while the descend function runs */
351- const int desc_rc = ua -> descend (bu );
362+ int desc_rc = 0 ;
363+ int keep_descending = 1 ;
364+ if ((ua -> min_level <= bu -> level ) && (bu -> level <= ua -> max_level )) {
365+ desc_rc = ua -> descend (bu , & keep_descending );
366+ }
352367
353- bu -> refs . remaining = bu -> subdir_count ;
368+ /* if the descent function succeeded */
354369 if (desc_rc == 0 ) {
355370 /* if there are subdirectories, this directory cannot go back up just yet */
356- if (bu -> subdir_count ) {
371+ if (keep_descending && (next_level <= ua -> max_level ) && bu -> subdir_count ) {
372+ /* decrement each time child triggers parent for processing */
373+ bu -> refs .remaining = bu -> subdir_count ;
374+
357375 /* have to lock to prevent subdirs from getting popped */
358376 /* off before all of them have been enqueued */
359377 pthread_mutex_lock (& bu -> refs .mutex );
@@ -367,8 +385,10 @@ static int descend_to_bottom(QPTPool_t *ctx, const size_t id, void *data, void *
367385 }
368386 pthread_mutex_unlock (& bu -> refs .mutex );
369387 }
370- /* start working upwards */
371388 else {
389+ bu -> refs .remaining = 0 ;
390+
391+ /* start working upwards */
372392 QPTPool_enqueue (ctx , id , ascend_to_top , bu );
373393 }
374394 }
@@ -385,7 +405,8 @@ static int descend_to_bottom(QPTPool_t *ctx, const size_t id, void *data, void *
385405
386406QPTPool_t * parallel_bottomup_init (const size_t thread_count ,
387407 const size_t user_struct_size ,
388- BU_f descend , BU_f ascend ,
408+ const size_t min_level , const size_t max_level ,
409+ BU_descend_f descend , BU_ascend_f ascend ,
389410 const int track_non_dirs ,
390411 const int generate_alt_name ) {
391412 if (user_struct_size < sizeof (struct BottomUp )) {
@@ -395,8 +416,10 @@ QPTPool_t *parallel_bottomup_init(const size_t thread_count,
395416
396417 struct UserArgs * ua = calloc (1 , sizeof (* ua ));
397418 ua -> user_struct_size = user_struct_size ;
398- ua -> descend = descend ?descend :noop ;
399- ua -> ascend = ascend ?ascend :noop ;
419+ ua -> min_level = min_level ;
420+ ua -> max_level = max_level ;
421+ ua -> descend = descend ?descend :noop_descend ;
422+ ua -> ascend = ascend ?ascend :noop_ascend ;
400423 ua -> track_non_dirs = track_non_dirs ;
401424 ua -> generate_alt_name = generate_alt_name ;
402425
@@ -434,9 +457,7 @@ int parallel_bottomup_enqueue(QPTPool_t *pool,
434457 new_pathname (root , path , len , NULL , 0 );
435458
436459 if (ua -> generate_alt_name ) {
437- int ret = new_alt_pathname (root , root -> name , root -> name_len , NULL , 0 );
438-
439- if (ret ) {
460+ if (new_alt_pathname (root , root -> name , root -> name_len , NULL , 0 )) {
440461 fprintf (stderr , "%s could not fit into ALT_NAME buffer\n" , root -> name );
441462 bottomup_destroy (root );
442463 return -1 ;
@@ -451,6 +472,52 @@ int parallel_bottomup_enqueue(QPTPool_t *pool,
451472 return 0 ;
452473}
453474
475+ static int parallel_bottomup_enqueue_subdirs (QPTPool_t * pool ,
476+ const char * path , const size_t len ,
477+ const size_t min_level , const refstr_t * subtree_list ,
478+ void * extra_args ) {
479+ struct UserArgs * ua = NULL ;
480+ QPTPool_get_args (pool , (void * * ) & ua );
481+
482+ FILE * file = fopen (subtree_list -> data , "r" );
483+ if (!file ) {
484+ const int err = errno ;
485+ fprintf (stderr , "could not open directory list file \"%s\": %s (%d)\n" ,
486+ subtree_list -> data , strerror (err ), err );
487+ return -1 ;
488+ }
489+
490+ char * line = NULL ;
491+ size_t n = 0 ;
492+ ssize_t got = 0 ;
493+ while ((got = getline (& line , & n , file )) != -1 ) {
494+ /* remove trailing CRLF */
495+ const size_t line_len = trailing_non_match_index (line , got , "\r\n" , 2 );
496+
497+ struct BottomUp * root = (struct BottomUp * ) calloc (ua -> user_struct_size , 1 );
498+ new_pathname (root , path , len , line , line_len );
499+
500+ if (ua -> generate_alt_name ) {
501+ if (new_alt_pathname (root , root -> name , root -> name_len , NULL , 0 )) {
502+ fprintf (stderr , "%s could not fit into ALT_NAME buffer\n" , root -> name );
503+ bottomup_destroy (root );
504+ return -1 ;
505+ }
506+ }
507+
508+ root -> parent = NULL ;
509+ root -> extra_args = extra_args ;
510+ root -> level = min_level ;
511+
512+ QPTPool_enqueue (pool , 0 , descend_to_bottom , root );
513+ }
514+
515+ free (line );
516+ fclose (file );
517+
518+ return 0 ;
519+ }
520+
454521int parallel_bottomup_fini (QPTPool_t * pool ) {
455522 if (!pool ) {
456523 return -1 ;
@@ -481,13 +548,24 @@ int parallel_bottomup_fini(QPTPool_t *pool) {
481548}
482549
483550int parallel_bottomup (char * * root_names , const size_t root_count ,
551+ const size_t min_level , const size_t max_level ,
552+ const refstr_t * subtree_list ,
484553 const size_t thread_count ,
485554 const size_t user_struct_size ,
486- BU_f descend , BU_f ascend ,
555+ BU_descend_f descend , BU_ascend_f ascend ,
487556 const int track_non_dirs ,
488557 const int generate_alt_name ,
489558 void * extra_args ) {
490- QPTPool_t * pool = parallel_bottomup_init (thread_count , user_struct_size , descend , ascend ,
559+ if (min_level && subtree_list -> data && subtree_list -> len ) {
560+ if (root_count > 1 ) {
561+ fprintf (stderr , "Error: Only one root may be provided when a -y and -D are both provided\n" );
562+ return -1 ;
563+ }
564+ }
565+
566+ QPTPool_t * pool = parallel_bottomup_init (thread_count , user_struct_size ,
567+ min_level , max_level ,
568+ descend , ascend ,
491569 track_non_dirs , generate_alt_name );
492570 if (!pool ) {
493571 return -1 ;
@@ -498,8 +576,14 @@ int parallel_bottomup(char **root_names, const size_t root_count,
498576
499577 /* enqueue all root directories */
500578 size_t good_roots = 0 ;
501- for (size_t i = 0 ; i < root_count ; i ++ ) {
502- good_roots += !parallel_bottomup_enqueue (pool , root_names [i ], strlen (root_names [i ]), extra_args );
579+ if (min_level && subtree_list -> data && subtree_list -> len ) {
580+ good_roots += !parallel_bottomup_enqueue_subdirs (pool , root_names [0 ], strlen (root_names [0 ]),
581+ min_level , subtree_list , extra_args );
582+ }
583+ else {
584+ for (size_t i = 0 ; i < root_count ; i ++ ) {
585+ good_roots += !parallel_bottomup_enqueue (pool , root_names [i ], strlen (root_names [i ]), extra_args );
586+ }
503587 }
504588
505589 return - (parallel_bottomup_fini (pool ) || (root_count != good_roots ));
0 commit comments