commit b777da33c268ddb7169db87c4630712e936e23dc Author: Jacob Welsh AuthorDate: Thu Apr 18 20:49:44 2024 +0000 Commit: Jacob Welsh CommitDate: Thu Apr 18 20:49:44 2024 +0000 busybox/archival (tar etc): when restoring timestamps from an archive, preserve parent directory times by checking and restoring after each operation that would unintentionally change them. diff --git a/base/busybox/archival/libarchive/data_extract_all.c b/base/busybox/archival/libarchive/data_extract_all.c index 45776dc..69e69fb 100644 --- a/base/busybox/archival/libarchive/data_extract_all.c +++ b/base/busybox/archival/libarchive/data_extract_all.c @@ -11,6 +11,9 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) file_header_t *file_header = archive_handle->file_header; int dst_fd; int res; + int restore_parent_times = 0; + struct stat parent_stat; + char *slash = 0; #if ENABLE_FEATURE_TAR_SELINUX char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; @@ -23,12 +26,25 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) } #endif - if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { - char *slash = strrchr(file_header->name, '/'); + if (archive_handle->ah_flags & (ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_RESTORE_DATE)) { + slash = strrchr(file_header->name, '/'); if (slash) { + /* compound path: might need to create parent dir or later restore its times */ *slash = '\0'; - bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); + if (stat(file_header->name, &parent_stat) == -1) { + if (errno == ENOENT && (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS)) { + /* XXX doesn't preserve parent timestamps */ + bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); + } + } else if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { + restore_parent_times = 1; + } *slash = '/'; + } else if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { + /* simple filename: nothing to create but will still need to restore */ + if (stat(".", &parent_stat) == 0) { + restore_parent_times = 1; + } } } @@ -203,7 +219,19 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) } } - ret: ; + ret: + if (restore_parent_times) { + struct timespec t[2]; + t[0] = parent_stat.st_atim; + t[1] = parent_stat.st_mtim; + if (slash) { + *slash = '\0'; + utimensat(AT_FDCWD, file_header->name, t, 0); + *slash = '/'; + } else { + utimensat(AT_FDCWD, ".", t, 0); + } + } #if ENABLE_FEATURE_TAR_SELINUX if (sctx) { /* reset the context after creating an entry */