Home Download Docs Code Community
     1	/*
     2	Copyright 2013 The Perkeep Authors
     3	
     4	Licensed under the Apache License, Version 2.0 (the "License");
     5	you may not use this file except in compliance with the License.
     6	You may obtain a copy of the License at
     7	
     8	     http://www.apache.org/licenses/LICENSE-2.0
     9	
    10	Unless required by applicable law or agreed to in writing, software
    11	distributed under the License is distributed on an "AS IS" BASIS,
    12	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13	See the License for the specific language governing permissions and
    14	limitations under the License.
    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	// punchHole, if non-nil, punches a hole in f from offset to offset+size.
    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		// walk back, find the header, and overwrite the hash with xxxx-000000...
    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] // "sha1-xxxxxxxxxxxxxxxxxx nnnn" - everything between []
    57		off++
    58	
    59		// Replace b with "xxxx-000000000"
    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		// write back
    76		if _, err = f.WriteAt(b, off); err != nil {
    77			return err
    78		}
    79	
    80		// punch hole, if possible
    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		// fill with zero
    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	}
Website layout inspired by memcached.
Content by the authors.