@@ -15,56 +15,83 @@ import (
1515 "google.golang.org/grpc/status"
1616)
1717
18- func loadC1z (filePath string , tmpDir string , opts ... DecoderOption ) (string , error ) {
19- var err error
20- workingDir , err := os .MkdirTemp (tmpDir , "c1z" )
18+ // Note(kans): decompressC1z is unfortunately called to load or create a c1z file so the error handling is rough.
19+ // It creates its own temporary directory so that it can also do its own cleanup.
20+ // It returns that directory for verification in tests.
21+ func decompressC1z (c1zPath string , workingDir string , opts ... DecoderOption ) (string , string , error ) {
22+ tmpDir , err := os .MkdirTemp (workingDir , "c1z" )
2123 if err != nil {
22- return "" , err
24+ return "" , tmpDir , err
2325 }
24- defer func () {
25- if err != nil {
26- if removeErr := os .RemoveAll (workingDir ); removeErr != nil {
27- err = errors .Join (err , removeErr )
26+
27+ var dbFile * os.File
28+ var c1zFile * os.File
29+ var decoder * decoder
30+ cleanupDir := func (e error ) error {
31+ if decoder != nil {
32+ err := decoder .Close ()
33+ if err != nil {
34+ e = errors .Join (e , err )
2835 }
2936 }
30- }()
31- dbFilePath := filepath .Join (workingDir , "db" )
32- dbFile , err := os .Create (dbFilePath )
37+ if c1zFile != nil {
38+ err := c1zFile .Close ()
39+ if err != nil {
40+ e = errors .Join (e , err )
41+ }
42+ }
43+ if dbFile != nil {
44+ err := dbFile .Close ()
45+ if err != nil {
46+ e = errors .Join (e , err )
47+ }
48+ }
49+ if e != nil {
50+ err := os .RemoveAll (tmpDir )
51+ if err != nil {
52+ e = errors .Join (e , err )
53+ }
54+ }
55+ return e
56+ }
57+
58+ dbFilePath := filepath .Join (tmpDir , "db" )
59+ dbFile , err = os .Create (dbFilePath )
3360 if err != nil {
34- return "" , err
61+ return "" , tmpDir , cleanupDir ( err )
3562 }
36- defer dbFile .Close ()
3763
38- if stat , err := os .Stat (filePath ); err == nil && stat .Size () != 0 {
39- c1zFile , err := os .Open (filePath )
40- if err != nil {
41- return "" , err
42- }
43- defer c1zFile .Close ()
64+ stat , err := os .Stat (c1zPath )
65+ if err != nil || stat .Size () == 0 {
66+ // TODO(kans): it would be nice to know more about the error....
67+ return dbFilePath , tmpDir , cleanupDir (nil )
68+ }
4469
45- r , err := NewDecoder (c1zFile , opts ... )
46- if err != nil {
47- return "" , err
48- }
49- _ , err = io .Copy (dbFile , r )
50- if err != nil {
51- return "" , err
52- }
53- err = r .Close ()
54- if err != nil {
55- return "" , err
56- }
70+ c1zFile , err = os .Open (c1zPath )
71+ if err != nil {
72+ return "" , tmpDir , cleanupDir (err )
73+ }
74+
75+ decoder , err = NewDecoder (c1zFile , opts ... )
76+ if err != nil {
77+ return "" , tmpDir , cleanupDir (err )
78+ }
79+
80+ _ , err = io .Copy (dbFile , decoder )
81+ if err != nil {
82+ return "" , tmpDir , cleanupDir (err )
5783 }
5884
5985 // CRITICAL: Sync the database file before returning to ensure all
6086 // decompressed data is on disk. On filesystems with aggressive caching
6187 // (like ZFS with large ARC), SQLite might otherwise open the file and
6288 // see incomplete data still in kernel buffers.
63- if err = dbFile .Sync (); err != nil {
64- return "" , fmt .Errorf ("failed to sync db file: %w" , err )
89+ err = dbFile .Sync ()
90+ if err != nil {
91+ return "" , tmpDir , cleanupDir (err )
6592 }
6693
67- return dbFilePath , nil
94+ return dbFilePath , tmpDir , cleanupDir ( nil )
6895}
6996
7097func saveC1z (dbFilePath string , outputFilePath string , encoderConcurrency int ) error {
0 commit comments