Initial DDC release

This commit is contained in:
SirBlob committed 2026-10-25 20:24:50 -04:00
commit 434f6441da
57 files changed
+4606

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
package extract
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
type entryWriter struct {
ctx context.Context
root string
limits Limits
progress Progress
written int64
entries int
}
func (writer *entryWriter) writeDirectory(name string) error {
target, err := writer.prepare(name)
if err != nil || target == "" {
return err
}
return os.MkdirAll(target, 0o755)
}
func (writer *entryWriter) writeFile(name string, content io.Reader) error {
target, err := writer.prepare(name)
if err != nil || target == "" {
return err
}
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return err
}
file, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
if err != nil {
return err
}
remaining := writer.limits.MaxBytes - writer.written
source := &progressReader{ctx: writer.ctx, reader: io.LimitReader(content, remaining+1), progress: writer.progress}
copied, copyErr := io.Copy(file, source)
closeErr := file.Close()
writer.written += copied
if copyErr != nil {
return copyErr
}
if writer.written > writer.limits.MaxBytes {
return ErrTooLarge
}
return closeErr
}
func (writer *entryWriter) prepare(name string) (string, error) {
writer.entries++
if writer.entries > writer.limits.MaxEntries {
return "", ErrTooManyEntries
}
if err := writer.ctx.Err(); err != nil {
return "", err
}
return writer.resolve(name)
}
func (writer *entryWriter) resolve(name string) (string, error) {
normalized := strings.ReplaceAll(name, "\\", "/")
if strings.HasPrefix(normalized, "/") {
return "", fmt.Errorf("%w: %s", ErrUnsafeEntry, name)
}
cleaned := filepath.Clean(filepath.FromSlash(normalized))
if cleaned == "." {
return "", nil
}
if !filepath.IsLocal(cleaned) {
return "", fmt.Errorf("%w: %s", ErrUnsafeEntry, name)
}
target := filepath.Join(writer.root, cleaned)
relative, err := filepath.Rel(writer.root, target)
if err != nil || !filepath.IsLocal(relative) {
return "", fmt.Errorf("%w: %s", ErrUnsafeEntry, name)
}
return target, nil
}
type progressReader struct {
ctx context.Context
reader io.Reader
progress Progress
}
func (reader *progressReader) Read(buffer []byte) (int, error) {
if err := reader.ctx.Err(); err != nil {
return 0, err
}
count, err := reader.reader.Read(buffer)
if count > 0 {
reader.progress.Add(int64(count))
}
return count, err
}