WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit ef0e382

Browse files
committed
Store to zip modification time of directories. Restore modification times on unzip. Tests.
1 parent a19269d commit ef0e382

File tree

2 files changed

+291
-404
lines changed

2 files changed

+291
-404
lines changed

src/utils/utils.go

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -501,35 +501,85 @@ func ZipDirectory(destination string, source string) (err error) {
501501
// Get base name for zip structure
502502
baseName := strings.TrimSuffix(filepath.Base(destination), ".zip")
503503

504+
// First pass: add the root directory with its modification time
505+
rootInfo, err := os.Stat(source)
506+
if err == nil && rootInfo.IsDir() {
507+
header, err := zip.FileInfoHeader(rootInfo)
508+
if err != nil {
509+
log.Error(err)
510+
} else {
511+
header.Name = baseName + "/" // Trailing slash indicates directory
512+
header.Method = zip.Store
513+
header.Modified = rootInfo.ModTime()
514+
515+
_, err = writer.CreateHeader(header)
516+
if err != nil {
517+
log.Error(err)
518+
} else {
519+
fmt.Fprintf(os.Stderr, "\r\033[2K")
520+
fmt.Fprintf(os.Stderr, "\rAdding %s", baseName+"/")
521+
}
522+
}
523+
}
524+
525+
// Second pass: add all other directories and files
504526
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
505527
if err != nil {
506528
log.Error(err)
507529
return nil
508530
}
509-
if info.Mode().IsRegular() {
510-
f1, err := os.Open(path)
531+
532+
// Skip root directory (we already added it)
533+
if path == source {
534+
return nil
535+
}
536+
537+
// Calculate relative path from source directory
538+
relPath, err := filepath.Rel(source, path)
539+
if err != nil {
540+
log.Error(err)
541+
return nil
542+
}
543+
544+
// Create zip path with base name structure
545+
zipPath := filepath.Join(baseName, relPath)
546+
zipPath = filepath.ToSlash(zipPath)
547+
548+
if info.IsDir() {
549+
// Add directory entry to zip with original modification time
550+
header, err := zip.FileInfoHeader(info)
511551
if err != nil {
512552
log.Error(err)
513553
return nil
514554
}
555+
header.Name = zipPath + "/" // Trailing slash indicates directory
556+
header.Method = zip.Store
557+
// Preserve the original modification time
558+
header.Modified = info.ModTime()
515559

516-
// Calculate relative path from source directory
517-
relPath, err := filepath.Rel(source, path)
560+
_, err = writer.CreateHeader(header)
518561
if err != nil {
519562
log.Error(err)
520-
f1.Close()
521563
return nil
522564
}
523565

524-
// Create zip path with base name structure
525-
zipPath := filepath.Join(baseName, relPath)
526-
zipPath = filepath.ToSlash(zipPath)
566+
fmt.Fprintf(os.Stderr, "\r\033[2K")
567+
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath+"/")
568+
return nil
569+
}
570+
571+
if info.Mode().IsRegular() {
572+
f1, err := os.Open(path)
573+
if err != nil {
574+
log.Error(err)
575+
return nil
576+
}
577+
defer f1.Close()
527578

528579
// Create file header with modified time
529580
header, err := zip.FileInfoHeader(info)
530581
if err != nil {
531582
log.Error(err)
532-
f1.Close()
533583
return nil
534584
}
535585
header.Name = zipPath
@@ -538,23 +588,20 @@ func ZipDirectory(destination string, source string) (err error) {
538588
w1, err := writer.CreateHeader(header)
539589
if err != nil {
540590
log.Error(err)
541-
f1.Close()
542591
return nil
543592
}
544593

545594
if _, err := io.Copy(w1, f1); err != nil {
546595
log.Error(err)
547-
f1.Close()
548596
return nil
549597
}
550598

551-
f1.Close()
552-
553599
fmt.Fprintf(os.Stderr, "\r\033[2K")
554600
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
555601
}
556602
return nil
557603
})
604+
558605
if err != nil {
559606
log.Error(err)
560607
return fmt.Errorf("error during directory walk: %w", err)
@@ -571,17 +618,32 @@ func UnzipDirectory(destination string, source string) error {
571618
}
572619
defer archive.Close()
573620

621+
// Store modification times for all files and directories
622+
modTimes := make(map[string]time.Time)
623+
624+
// First pass: extract all files and directories, store modification times
574625
for _, f := range archive.File {
575626
filePath := filepath.Join(destination, f.Name)
576627
fmt.Fprintf(os.Stderr, "\r\033[2K")
577628
fmt.Fprintf(os.Stderr, "\rUnzipping file %s", filePath)
629+
578630
// Issue #593 conceal path traversal vulnerability
579631
// make sure the filepath does not have ".."
580632
filePath = filepath.Clean(filePath)
581633
if strings.Contains(filePath, "..") {
582634
log.Errorf("Invalid file path %s\n", filePath)
583-
continue // Skip file but continue extraction
635+
continue
636+
}
637+
638+
// Store modification time for this entry (BOTH files and directories)
639+
modifiedTime := f.Modified
640+
if modifiedTime.IsZero() {
641+
modifiedTime = f.FileHeader.Modified
584642
}
643+
if !modifiedTime.IsZero() {
644+
modTimes[filePath] = modifiedTime
645+
}
646+
585647
if f.FileInfo().IsDir() {
586648
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
587649
log.Error(err)
@@ -623,15 +685,18 @@ func UnzipDirectory(destination string, source string) error {
623685

624686
dstFile.Close()
625687
fileInArchive.Close()
688+
}
626689

627-
// Set modified time from zip file header
628-
modifiedTime := f.Modified
629-
if modifiedTime.IsZero() {
630-
modifiedTime = f.FileHeader.Modified
631-
}
632-
if !modifiedTime.IsZero() {
633-
if err := os.Chtimes(filePath, modifiedTime, modifiedTime); err != nil {
634-
log.Error(err)
690+
// Second pass: restore modification times for ALL files and directories
691+
for path, modTime := range modTimes {
692+
if err := os.Chtimes(path, modTime, modTime); err != nil {
693+
log.Errorf("Failed to set modification time for %s: %v", path, err)
694+
} else {
695+
fi, err := os.Lstat(path)
696+
if err != nil ||
697+
!modTime.UTC().Equal(fi.ModTime().UTC()) {
698+
log.Errorf("Failed to set modification time for %s: %v", path, err)
699+
fmt.Fprintf(os.Stderr, "Failed to set modification time %s %v: %v\n", path, modTime, err)
635700
}
636701
}
637702
}

0 commit comments

Comments
 (0)