@@ -254,6 +254,21 @@ class filesystem_extractor_ final : public filesystem_extractor::impl {
254254 extract (reader::filesystem_v2_lite const & fs, glob_matcher const * matcher,
255255 filesystem_extractor_options const & opts) override ;
256256
257+ filesystem_extractor::progress_info get_progress () const override {
258+ filesystem_extractor::progress_info pi;
259+
260+ {
261+ std::lock_guard lock (bytes_total_mx_);
262+ if (bytes_total_) {
263+ pi.total_bytes = *bytes_total_;
264+ }
265+ }
266+
267+ pi.extracted_bytes = bytes_written_.load ();
268+
269+ return pi;
270+ }
271+
257272 private:
258273 static la_ssize_t on_stream_write (struct archive * /* a*/ , void * client_data,
259274 void const * buffer, size_t length) {
@@ -372,6 +387,9 @@ class filesystem_extractor_ final : public filesystem_extractor::impl {
372387 std::array<int , 2 > pipefd_{-1 , -1 };
373388 std::unique_ptr<std::thread> iot_;
374389 sparse_file_mode sparse_mode_{sparse_file_mode::auto_detect};
390+ std::atomic<uint64_t > bytes_written_{0 };
391+ std::mutex mutable bytes_total_mx_;
392+ std::optional<uint64_t > bytes_total_{};
375393};
376394
377395template <typename LoggerPolicy>
@@ -420,18 +438,12 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
420438 });
421439 }
422440
423- vfs_stat vfs;
424- fs.statvfs (&vfs);
425-
426441 std::atomic<size_t > hard_error{0 };
427442 std::atomic<size_t > soft_error{0 };
428- std::atomic<uint64_t > bytes_written{0 };
429- uint64_t const bytes_total{vfs.blocks };
430443
431444 auto do_archive = [&](worker_ptr aptr, std::shared_ptr<::archive_entry> ae,
432445 reader::inode_view const & entry) {
433- bool added{false };
434-
446+ // hard links will have size 0
435447 if (auto const size = ::archive_entry_size (ae.get ());
436448 entry.is_regular_file () && size > 0 ) {
437449 reader::detail::file_reader fr (fs, entry);
@@ -449,8 +461,7 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
449461 aptr->add_job <struct archive *>(
450462 [this , &hard_error, &soft_error, &opts, extents = std::move (extents),
451463 ranges = fr.read_sequential (data_ranges, sem, opts.max_queued_bytes ),
452- ae = std::move (ae), size, &sparse_mode, &bytes_written,
453- bytes_total](struct archive * a) mutable {
464+ ae = std::move (ae), size, &sparse_mode](struct archive * a) mutable {
454465 try {
455466 assert (ae);
456467
@@ -521,9 +532,8 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
521532 extents.erase (extents.begin ());
522533 }
523534
524- if (opts.progress ) {
525- bytes_written += r.size ();
526- opts.progress (path, bytes_written, bytes_total);
535+ if (opts.enable_progress ) {
536+ bytes_written_ += r.size ();
527537 }
528538 }
529539
@@ -541,11 +551,7 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
541551 }
542552 }
543553 });
544-
545- added = true ;
546- }
547-
548- if (!added) {
554+ } else {
549555 aptr->add_job <struct archive *>(
550556 [this , ae = std::move (ae), &hard_error](struct archive * a) {
551557 try {
@@ -567,11 +573,22 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
567573 std::unordered_set<std::string> matched_dirs;
568574
569575 if (matcher) {
576+ std::unordered_set<uint32_t > seen_hardlinks;
577+ uint64_t data_size{0 };
578+
570579 // Collect all directories that contain matching files to make sure
571580 // we descend into them during the extraction walk below.
572581 fs.walk ([&](auto entry) {
573- if (!entry.inode ().is_directory ()) {
582+ auto inode = entry.inode ();
583+ if (!inode.is_directory ()) {
574584 if (matcher->match (entry.unix_path ())) {
585+ if (opts.enable_progress ) {
586+ auto stat = fs.getattr (inode);
587+ if (stat.nlink () == 1 ||
588+ seen_hardlinks.insert (inode.inode_num ()).second ) {
589+ data_size += stat.allocated_size ();
590+ }
591+ }
575592 while (auto parent = entry.parent ()) {
576593 if (!matched_dirs.insert (parent->unix_path ()).second ) {
577594 break ;
@@ -581,6 +598,16 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
581598 }
582599 }
583600 });
601+
602+ if (opts.enable_progress ) {
603+ std::lock_guard lock (bytes_total_mx_);
604+ bytes_total_.emplace (data_size);
605+ }
606+ } else if (opts.enable_progress ) {
607+ vfs_stat vfs;
608+ fs.statvfs (&vfs);
609+ std::lock_guard lock (bytes_total_mx_);
610+ bytes_total_.emplace (vfs.blocks );
584611 }
585612
586613 auto do_archive_entry = [&](auto const & entry) {
@@ -642,8 +669,8 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
642669 if (ec) {
643670 LOG_ERROR << " readlink() failed: " << ec.message ();
644671 }
645- if (opts.progress ) {
646- bytes_written += link.size ();
672+ if (opts.enable_progress ) {
673+ bytes_written_ += link.size ();
647674 }
648675#ifdef _WIN32
649676 std::filesystem::path linkpath (string_to_u8string (link));
@@ -720,7 +747,7 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
720747 return false ;
721748 }
722749
723- LOG_INFO << " extraction finished without errors" ;
750+ LOG_VERBOSE << " extraction finished without errors" ;
724751
725752 return true ;
726753}
0 commit comments