-
Notifications
You must be signed in to change notification settings - Fork 0
Pack #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pack #10
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||
| // Package archive provides a read-only cache, backed by a zip archive. | ||||||||
| package archive | ||||||||
|
|
||||||||
| import ( | ||||||||
| "archive/zip" | ||||||||
| "errors" | ||||||||
| "io" | ||||||||
| "log/slog" | ||||||||
| "os" | ||||||||
|
|
||||||||
| "github.com/AlekSi/hardcache/internal/go/cache" | ||||||||
| "github.com/AlekSi/lazyerrors" | ||||||||
| ) | ||||||||
|
|
||||||||
| // Cache represents a read-only [cache.Cache], backed by a zip archive. | ||||||||
| type Cache struct { | ||||||||
| zr *zip.Reader | ||||||||
| c io.Closer | ||||||||
| d | ||||||||
|
Check failure on line 19 in internal/caches/archive/archive.go
|
||||||||
| l *slog.Logger | ||||||||
| } | ||||||||
|
|
||||||||
| // New creates a new [Cache]. | ||||||||
| func New(r io.ReaderAt, size int64, l *slog.Logger) (*Cache, error) { | ||||||||
| zr, err := zip.NewReader(r, size) | ||||||||
| if err != nil { | ||||||||
| return nil, lazyerrors.Error(err) | ||||||||
| } | ||||||||
|
|
||||||||
| os.CreateTemp() | ||||||||
|
Check failure on line 30 in internal/caches/archive/archive.go
|
||||||||
|
||||||||
| os.CreateTemp() |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation. The exported function Open lacks a documentation comment, which is required for all exported identifiers in Go.
| // Open opens a zip archive file and returns a new [Cache]. |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Function naming inconsistency. The function Open should be named consistently with Go conventions, such as OpenFile or NewFromFile, to clearly indicate it opens a cache from a file path. Additionally, it should have a documentation comment explaining its purpose.
| func Open(file string, l *slog.Logger) (*Cache, error) { | |
| // OpenFile opens a read-only Cache backed by a zip archive at the given file path. | |
| func OpenFile(file string, l *slog.Logger) (*Cache, error) { |
Check failure on line 53 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on windows-2025
c.dc undefined (type *Cache has no field or method dc)
Check failure on line 53 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on macos-26
c.dc undefined (type *Cache has no field or method dc)
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined field access. The field c.dc is accessed but never initialized in the New or Open functions, which will cause a nil pointer dereference at runtime.
Check failure on line 75 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on windows-2025
c.dc undefined (type *Cache has no field or method dc)
Check failure on line 75 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on macos-26
c.dc undefined (type *Cache has no field or method dc)
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined field access. The field c.dc is accessed but never initialized in the New or Open functions, which will cause a nil pointer dereference at runtime.
Check failure on line 80 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on windows-2025
not enough return values
Check failure on line 80 in internal/caches/archive/archive.go
GitHub Actions / Test 1.25.x on macos-26
not enough return values
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty return value. The function should return a string representing the fuzz directory, but returns an empty value instead.
| return | |
| return "" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,167 @@ | ||||||||||||||||||||||||||||||
| // Package pack provides functionality for packing and unpacking directories. | ||||||||||||||||||||||||||||||
| package pack | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||
| "archive/zip" | ||||||||||||||||||||||||||||||
| "compress/flate" | ||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||
| "io" | ||||||||||||||||||||||||||||||
| "io/fs" | ||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| "github.com/AlekSi/lazyerrors" | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Pack compresses the contents of the specified directory | ||||||||||||||||||||||||||||||
| // and writes it to the provided writer in ZIP format with the given comment. | ||||||||||||||||||||||||||||||
| func Pack(dir, comment string, w io.Writer) (resErr error) { | ||||||||||||||||||||||||||||||
| zw := zip.NewWriter(w) | ||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||
| if e := zw.Close(); resErr == nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(e) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| zw.RegisterCompressor(zip.Deflate, func(w io.Writer) (io.WriteCloser, error) { | ||||||||||||||||||||||||||||||
| return flate.NewWriter(w, flate.BestCompression) | ||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| root, err := os.OpenRoot(dir) | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||
| if e := root.Close(); resErr == nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(e) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if err = zw.AddFS(root.FS()); err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if err = zw.SetComment(comment); err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // putFile writes the contents of src file to the dst path. | ||||||||||||||||||||||||||||||
| // If dst already exists, it will be touched, but not overwritten. | ||||||||||||||||||||||||||||||
| func putFile(dst string, src fs.File) (resErr error) { | ||||||||||||||||||||||||||||||
| srcFI, err := src.Stat() | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| dstFI, err := os.Stat(dst) | ||||||||||||||||||||||||||||||
| if err == nil { | ||||||||||||||||||||||||||||||
| if err = os.Chtimes(dst, dstFI.ModTime(), srcFI.ModTime()); err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if !errors.Is(err, fs.ErrNotExist) { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| f, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, srcFI.Mode()) | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| if errors.Is(err, fs.ErrExist) { | ||||||||||||||||||||||||||||||
| // another process created the file in the meantime | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||
| if e := f.Close(); resErr == nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(e) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if _, err = io.Copy(f, src); err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Unpack extracts the contents of a ZIP archive from the provided reader | ||||||||||||||||||||||||||||||
| // and writes it to the specified directory. | ||||||||||||||||||||||||||||||
| // It also returns the archive comment. | ||||||||||||||||||||||||||||||
| func Unpack(r io.ReaderAt, size int64, dir string) (comment string, resErr error) { | ||||||||||||||||||||||||||||||
| zr, err := zip.NewReader(r, size) | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| comment = zr.Comment | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| root, err := os.OpenRoot(dir) | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||
| if e := root.Close(); resErr == nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(e) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| for _, f := range zr.File { | ||||||||||||||||||||||||||||||
| fp := f.Name | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if f.FileInfo().IsDir() { | ||||||||||||||||||||||||||||||
| err = root.MkdirAll(fp, f.Mode()) | ||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||
| resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // err = root.MkdirAll(fp[:len(fp)-len(f.FileInfo().Name())], 0o755) | ||||||||||||||||||||||||||||||
| // if err != nil { | ||||||||||||||||||||||||||||||
| // resErr = lazyerrors.Error(err) | ||||||||||||||||||||||||||||||
| // return | ||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+138
to
+143
|
||||||||||||||||||||||||||||||
| // err = root.MkdirAll(fp[:len(fp)-len(f.FileInfo().Name())], 0o755) | |
| // if err != nil { | |
| // resErr = lazyerrors.Error(err) | |
| // return | |
| // } |
Copilot
AI
Dec 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource leak on error path. If f.Open() succeeds but an error is returned, the dst file handle is closed but its error is ignored. Consider checking the close error or using a deferred close with proper error handling.
| dst.Close() | |
| if err != nil { | |
| resErr = lazyerrors.Error(err) | |
| return | |
| } | |
| closeErr := dst.Close() | |
| if err != nil { | |
| resErr = lazyerrors.Error(err) | |
| return | |
| } | |
| if closeErr != nil && resErr == nil { | |
| resErr = lazyerrors.Error(closeErr) | |
| return | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete field declaration. The field
dhas no type specified, which will cause a compilation error.