package store import ( "crypto/sha1" "encoding/hex" "errors" "fmt" "os" "path/filepath" ) type Store struct { Path string } func (s *Store) Dir(path, sum string) string { return filepath.Join(s.Path, path, sum[0:1], sum[1:2]) } func (s *Store) MkDirs(dirs []string) error { for _, dir := range dirs { if err := os.MkdirAll(filepath.Join(s.Path, dir), 0755); err != nil { return err } } return nil } func (s *Store) FileExists(filename string) bool { fs, err := os.Stat(filename) if errors.Is(err, os.ErrNotExist) { return false } return !fs.IsDir() } func (s *Store) SaveTemp(path string, sumb string, b []byte) error { sum := sha1.New() sum.Write(b) sumString := hex.EncodeToString(sum.Sum(nil)) if sumb != sumString { return errors.New("wrong checksum") } dir := s.Dir(path, sumString) filename := filepath.Join(dir, sumString) if s.FileExists(filename) { return nil } if err := os.MkdirAll(dir, 0755); err != nil { return err } fs, err := os.Create(filename) if err != nil { return err } defer fs.Close() _, err = fs.Write(b) return err } func (s *Store) CombineTemp(path string, sumb []string) (string, uint64, error) { tmpdir := filepath.Join("tmp", path) sum := sha1.New() size := uint64(0) for _, sb := range sumb { dir := s.Dir(tmpdir, sb) filename := filepath.Join(dir, sb) if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { return "", 0, fmt.Errorf("%s: chunk %s doesn't exists", path, sb) } b, err := os.ReadFile(filename) if err != nil { return "", 0, err } sum.Write(b) size += uint64(len(b)) } return hex.EncodeToString(sum.Sum(nil)), size, nil } func (s *Store) CommitTemp(path string, sumb []string) error { tmpdir := filepath.Join("tmp", path) originaldir := s.Dir("original", path) originalname := filepath.Join(originaldir, path) if s.FileExists(originalname) { return fmt.Errorf("original file already exists") } os.MkdirAll(originaldir, 0755) fs, err := os.Create(originalname) if err != nil { return err } defer fs.Close() for _, sb := range sumb { dir := s.Dir(tmpdir, sb) filename := filepath.Join(dir, sb) if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { return fmt.Errorf("%s: chunk %s doesn't exists", path, sb) } b, err := os.ReadFile(filename) if err != nil { return err } if _, err := fs.Write(b); err != nil { return err } } return nil }