diff --git a/weed/filer/zip_extract.go b/weed/filer/zip_extract.go index e88879c5b1..cd3c7bb0de 100644 --- a/weed/filer/zip_extract.go +++ b/weed/filer/zip_extract.go @@ -20,17 +20,17 @@ const ( // SpoolZipReader buffers r into a seekable source for zip.NewReader: memory for // small archives, a temp file otherwise. The returned cleanup releases it. -func SpoolZipReader(r io.Reader, size int64) (ra io.ReaderAt, n int64, cleanup func(), err error) { +func SpoolZipReader(r io.Reader) (ra io.ReaderAt, n int64, cleanup func(), err error) { nop := func() {} - if size >= 0 && size <= zipSpoolMemoryLimit { - var buf []byte - if buf, err = io.ReadAll(r); err != nil { - return nil, 0, nop, err - } - return bytes.NewReader(buf), int64(len(buf)), nop, nil + head, err := io.ReadAll(io.LimitReader(r, zipSpoolMemoryLimit+1)) + if err != nil { + return nil, 0, nop, err } - var f *os.File - if f, err = os.CreateTemp("", "seaweedfs-extract-*.zip"); err != nil { + if int64(len(head)) <= zipSpoolMemoryLimit { + return bytes.NewReader(head), int64(len(head)), nop, nil + } + f, err := os.CreateTemp("", "seaweedfs-extract-*.zip") + if err != nil { return nil, 0, nop, err } cleanup = func() { @@ -38,7 +38,16 @@ func SpoolZipReader(r io.Reader, size int64) (ra io.ReaderAt, n int64, cleanup f f.Close() os.Remove(name) } - if n, err = io.Copy(f, r); err != nil { + if _, err = f.Write(head); err == nil { + var rest int64 + if rest, err = io.Copy(f, io.LimitReader(r, ZipExtractMaxBytes+1)); err == nil { + n = int64(len(head)) + rest + } + } + if err == nil && n > ZipExtractMaxBytes { + err = fmt.Errorf("zip archive exceeds %d bytes", ZipExtractMaxBytes) + } + if err != nil { cleanup() return nil, 0, nop, err } diff --git a/weed/filer/zip_extract_test.go b/weed/filer/zip_extract_test.go index dfd6e26e03..c1cde117e7 100644 --- a/weed/filer/zip_extract_test.go +++ b/weed/filer/zip_extract_test.go @@ -47,7 +47,7 @@ func TestSanitizeZipMemberName(t *testing.T) { func TestSpoolZipReaderMemory(t *testing.T) { data := []byte(strings.Repeat("x", 1024)) - ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(data), int64(len(data))) + ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(data)) if err != nil { t.Fatalf("spool: %v", err) } @@ -66,7 +66,7 @@ func TestSpoolZipReaderMemory(t *testing.T) { func TestSpoolZipReaderTempFile(t *testing.T) { data := bytes.Repeat([]byte("y"), zipSpoolMemoryLimit+1) - ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(data), int64(len(data))) + ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(data)) if err != nil { t.Fatalf("spool: %v", err) } @@ -113,7 +113,7 @@ func TestSpoolZipReaderRoundTrip(t *testing.T) { w.Write([]byte("hello")) zw.Close() - ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) + ra, n, cleanup, err := SpoolZipReader(bytes.NewReader(buf.Bytes())) if err != nil { t.Fatalf("spool: %v", err) } diff --git a/weed/server/filer_server_handlers_extract.go b/weed/server/filer_server_handlers_extract.go index 5868e41ee6..19c1194a0f 100644 --- a/weed/server/filer_server_handlers_extract.go +++ b/weed/server/filer_server_handlers_extract.go @@ -58,7 +58,7 @@ func (fs *FilerServer) extractZipArchive(ctx context.Context, w http.ResponseWri } } - ra, size, cleanup, err := filer.SpoolZipReader(reader, contentLength) + ra, size, cleanup, err := filer.SpoolZipReader(reader) if err != nil { writeJsonError(w, r, http.StatusInternalServerError, fmt.Errorf("read input: %w", err)) return