-
Notifications
You must be signed in to change notification settings - Fork 26
This branch is for debugging purposes only, and will be deleted once we identity the issue #102
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
Changes from all commits
d71277b
83bd11d
39984c8
6ae47c4
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 |
|---|---|---|
|
|
@@ -68,7 +68,7 @@ func (f *FilesystemDataSource) SetCurrentFile(file string) error { | |
| err := f.currentFile.Close() | ||
| f.currentFile = nil | ||
| if err != nil { | ||
| return nil | ||
| return err | ||
| } | ||
| } | ||
| currentFile, err := os.Open(filepath.Join(f.basePath, file)) | ||
|
|
@@ -87,8 +87,134 @@ func (f *FilesystemDataSource) Seek(offset int64, whence int) (int64, error) { | |
| return f.currentFile.Seek(offset, whence) | ||
| } | ||
|
|
||
| // validateRequiredFiles scans the delta to collect all required source files and validates they exist. | ||
| func validateRequiredFiles(delta io.ReadSeeker, dataSource *FilesystemDataSource) error { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: ENTRY - basePath=%q\n", dataSource.basePath) | ||
|
|
||
| // Read and validate header | ||
| buf := make([]byte, len(protocol.DeltaHeader)) | ||
| _, err := io.ReadFull(delta, buf) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: failed to read header: %v\n", err) | ||
| return err | ||
| } | ||
| if !bytes.Equal(buf, protocol.DeltaHeader[:]) { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: invalid delta format\n") | ||
| return fmt.Errorf("invalid delta format") | ||
| } | ||
|
|
||
| decoder, err := zstd.NewReader(delta) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: failed to create zstd decoder: %v\n", err) | ||
| return err | ||
| } | ||
|
|
||
| r := bufio.NewReader(decoder) | ||
| requiredFiles := make(map[string]bool) | ||
|
|
||
| // Scan delta to collect all DeltaOpOpen operations | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: scanning delta for DeltaOpOpen operations\n") | ||
| opCount := 0 | ||
| for { | ||
| op, err := r.ReadByte() | ||
| if err != nil { | ||
| if err == io.EOF { | ||
| break | ||
| } | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: error reading op byte: %v\n", err) | ||
| return err | ||
| } | ||
|
|
||
| size, err := binary.ReadUvarint(r) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: error reading size: %v\n", err) | ||
| return err | ||
| } | ||
|
|
||
| switch op { | ||
| case protocol.DeltaOpOpen: | ||
| opCount++ | ||
| nameBytes := make([]byte, size) | ||
|
Comment on lines
+135
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The case protocol.DeltaOpOpen:
opCount++
if size > maxFilenameSize {
return fmt.Errorf("filename size %d exceeds maximum allowed %d", size, maxFilenameSize)
}
nameBytes := make([]byte, size) |
||
| _, err = io.ReadFull(r, nameBytes) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: error reading filename: %v\n", err) | ||
| return err | ||
| } | ||
| filename := string(nameBytes) | ||
| requiredFiles[filename] = true | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: DeltaOpOpen #%d: %q\n", opCount, filename) | ||
| case protocol.DeltaOpData, protocol.DeltaOpAddData: | ||
| // Skip the data bytes | ||
| _, err = io.CopyN(io.Discard, r, int64(size)) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: error skipping data: %v\n", err) | ||
| return err | ||
| } | ||
| case protocol.DeltaOpCopy, protocol.DeltaOpSeek: | ||
| // These operations don't have additional data to skip | ||
| } | ||
| } | ||
|
|
||
| // Close decoder before seeking to ensure no buffered data interferes | ||
| decoder.Close() | ||
|
|
||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: scan complete - found %d required files\n", len(requiredFiles)) | ||
|
|
||
| // Validate all required files exist | ||
| for file := range requiredFiles { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: checking file=%q\n", file) | ||
|
|
||
| cleanFile := protocol.CleanPath(file) | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: after CleanPath=%q\n", cleanFile) | ||
|
|
||
| if len(cleanFile) == 0 { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: SKIPPED (cleanFile is empty)\n") | ||
| continue // Skip invalid paths; Apply() will catch these | ||
| } | ||
|
|
||
| // Convert to native path separators for the current OS | ||
| nativePath := filepath.FromSlash(cleanFile) | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: after FromSlash=%q\n", nativePath) | ||
|
|
||
| filePath := filepath.Join(dataSource.basePath, nativePath) | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: full path=%q\n", filePath) | ||
|
|
||
| fileInfo, err := os.Stat(filePath) | ||
| if err != nil { | ||
| if os.IsNotExist(err) { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: FILE DOES NOT EXIST - returning error\n") | ||
| return fmt.Errorf("required source file does not exist: %s", cleanFile) | ||
| } | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: os.Stat error: %v\n", err) | ||
| return fmt.Errorf("error accessing source file %s: %w", cleanFile, err) | ||
| } | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: EXISTS - size=%d\n", fileInfo.Size()) | ||
| } | ||
|
|
||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: all files validated successfully\n") | ||
|
|
||
| // Reset delta reader to the beginning for actual patching | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: seeking back to start\n") | ||
| _, err = delta.Seek(0, io.SeekStart) | ||
| if err != nil { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: seek failed: %v\n", err) | ||
| } else { | ||
| fmt.Fprintf(os.Stderr, "[DEBUG] validateRequiredFiles: EXIT - success\n") | ||
| } | ||
| return err | ||
| } | ||
|
|
||
| // Apply applies a binary patch from a delta reader to produce output using the data source. | ||
| func Apply(delta io.Reader, dataSource DataSource, dst io.Writer) error { | ||
| // Validate required files if we have a seekable delta and FilesystemDataSource | ||
| if seeker, ok := delta.(io.ReadSeeker); ok { | ||
| if fsDataSource, ok := dataSource.(*FilesystemDataSource); ok { | ||
| if err := validateRequiredFiles(seeker, fsDataSource); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+210
to
+216
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling |
||
|
|
||
| buf := make([]byte, len(protocol.DeltaHeader)) | ||
| _, err := io.ReadFull(delta, buf) | ||
| if err != nil { | ||
|
|
@@ -167,7 +293,7 @@ func Apply(delta io.Reader, dataSource DataSource, dst io.Writer) error { | |
| return err | ||
| } | ||
|
|
||
| for i := uint64(0); i < size; i++ { | ||
| for i := range size { | ||
| addBytes[i] += addBytes2[i] | ||
| } | ||
| if _, err := dst.Write(addBytes); err != nil { | ||
|
|
||
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.
The
zstd.Readeris not closed if an error occurs during the delta scanning loop, leading to a resource leak. Deferringdecoder.Close()immediately after its successful creation ensures that resources are always released, even on error paths. Note that the manual call todecoder.Close()later in the function can then be removed.