Further rework path handling

This commit is contained in:
Simon Quigley 2025-02-07 23:37:20 -06:00
parent a6c3fb165b
commit d74edb6a5a

View File

@ -449,7 +449,8 @@ void create_tarball(const std::string &tarball_path,
throw std::runtime_error(err); throw std::runtime_error(err);
} }
// For duplicate checking only for directories. // Track directories already added.
// For directories that are not symlinks, use their canonical absolute path as a key.
std::unordered_set<std::string> added_directories; std::unordered_set<std::string> added_directories;
for (auto it = fs::recursive_directory_iterator(directory, fs::directory_options::skip_permission_denied); for (auto it = fs::recursive_directory_iterator(directory, fs::directory_options::skip_permission_denied);
@ -458,13 +459,14 @@ void create_tarball(const std::string &tarball_path,
std::error_code ec; std::error_code ec;
fs::path rel_path = fs::relative(path, directory, ec); fs::path rel_path = fs::relative(path, directory, ec);
if (ec) { if (ec) {
if (log) log->append("Failed to compute relative path for: " + path.string() + " Error: " + ec.message()); if (log)
log->append("Failed to compute relative path for: " + path.string() + " Error: " + ec.message());
continue; continue;
} }
fs::path norm_rel = rel_path.lexically_normal(); fs::path norm_rel = rel_path.lexically_normal();
std::string rel_str = norm_rel.string(); std::string rel_str = norm_rel.string();
// Check exclusions regardless of type. // Apply exclusions (if any substring appears in the relative path, skip it)
bool is_excluded = std::any_of(exclusions.begin(), exclusions.end(), bool is_excluded = std::any_of(exclusions.begin(), exclusions.end(),
[&rel_str](const std::string &excl) { [&rel_str](const std::string &excl) {
return rel_str.find(excl) != std::string::npos; return rel_str.find(excl) != std::string::npos;
@ -472,18 +474,26 @@ void create_tarball(const std::string &tarball_path,
if (is_excluded) if (is_excluded)
continue; continue;
// For directories, check if we've already added this path.
fs::file_status fstatus = it->symlink_status(ec); fs::file_status fstatus = it->symlink_status(ec);
if (ec) { if (ec) {
if (log) log->append("Failed to get file status for: " + path.string() + " Error: " + ec.message()); if (log)
log->append("Failed to get file status for: " + path.string() + " Error: " + ec.message());
continue; continue;
} }
if (fs::is_directory(fstatus)) {
if (added_directories.find(rel_str) != added_directories.end()) // For directories (that are not symlinks), compute a canonical key.
continue; // Skip duplicate directory entry. if (fs::is_directory(fstatus) && !fs::is_symlink(fstatus)) {
added_directories.insert(rel_str); fs::path canon = fs::canonical(path, ec);
if (ec) {
// If canonical fails, fall back to the relative path.
canon = rel_path;
} }
// (For regular files or symlinks, always add them.) std::string canon_str = canon.string();
if (added_directories.find(canon_str) != added_directories.end())
continue;
added_directories.insert(canon_str);
}
// For files and symlinks, we always add them.
struct archive_entry *entry = archive_entry_new(); struct archive_entry *entry = archive_entry_new();
if (!entry) { if (!entry) {
@ -491,8 +501,7 @@ void create_tarball(const std::string &tarball_path,
log->append("Failed to create archive entry for: " + path.string()); log->append("Failed to create archive entry for: " + path.string());
continue; continue;
} }
// For directories, ensure the entry pathname ends with '/'
// For directories, ensure the entry name ends with '/'
std::string entry_path = rel_str; std::string entry_path = rel_str;
if (fs::is_directory(fstatus)) { if (fs::is_directory(fstatus)) {
if (!entry_path.empty() && entry_path.back() != '/') if (!entry_path.empty() && entry_path.back() != '/')