1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package diskpacked
18
19 import (
20 "bytes"
21 "errors"
22 "fmt"
23 "io"
24 "os"
25 "strconv"
26
27 "perkeep.org/pkg/blob"
28 )
29
30 var errNoPunch = errors.New("punchHole not supported")
31
32
33 var punchHole func(file *os.File, offset int64, size int64) error
34
35 func (s *storage) delete(br blob.Ref) error {
36 meta, err := s.meta(br)
37 if err != nil {
38 return err
39 }
40 f, err := os.OpenFile(s.filename(meta.file), os.O_RDWR, 0666)
41 if err != nil {
42 return err
43 }
44 defer f.Close()
45
46
47 k := 1 + len(br.String()) + 1 + len(strconv.FormatUint(uint64(meta.size), 10)) + 1
48 off := meta.offset - int64(k)
49 b := make([]byte, k)
50 if k, err = f.ReadAt(b, off); err != nil {
51 return err
52 }
53 if b[0] != byte('[') || b[k-1] != byte(']') {
54 return fmt.Errorf("delete: cannot find header surroundings, found %q", b)
55 }
56 b = b[1 : k-1]
57 off++
58
59
60 dash := bytes.IndexByte(b, '-')
61 if dash < 0 {
62 return fmt.Errorf("delete: cannot find dash in ref %q", b)
63 }
64 space := bytes.IndexByte(b[dash+1:], ' ')
65 if space < 0 {
66 return fmt.Errorf("delete: cannot find space in header %q", b)
67 }
68 for i := 0; i < dash; i++ {
69 b[i] = 'x'
70 }
71 for i := dash + 1; i < dash+1+space; i++ {
72 b[i] = '0'
73 }
74
75
76 if _, err = f.WriteAt(b, off); err != nil {
77 return err
78 }
79
80
81 if punchHole != nil {
82 err = punchHole(f, meta.offset, int64(meta.size))
83 if err == nil {
84 return nil
85 }
86 if !errors.Is(err, errNoPunch) {
87 return err
88 }
89 }
90
91
92 n, err := f.Seek(meta.offset, io.SeekStart)
93 if err != nil {
94 return err
95 }
96 if n != meta.offset {
97 return fmt.Errorf("error seeking to %d: got %d", meta.offset, n)
98 }
99 _, err = io.CopyN(f, zeroReader{}, int64(meta.size))
100 return err
101 }
102
103 type zeroReader struct{}
104
105 func (z zeroReader) Read(p []byte) (n int, err error) {
106 for i := range p {
107 p[i] = 0
108 }
109 return len(p), nil
110 }