-
Notifications
You must be signed in to change notification settings - Fork 27
/
storage_filesystem.go
157 lines (133 loc) · 4.97 KB
/
storage_filesystem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
* knoxite
* Copyright (c) 2016-2017, Christian Muehlhaeuser <muesli@gmail.com>
*
* For license see LICENSE
*/
package knoxite
import (
"path/filepath"
"strconv"
)
const (
// RepoFilename is the default filename for the repository data.
RepoFilename = "repository.knoxite"
// ChunkIndexFilename is the default filename for the chunk-index.
ChunkIndexFilename = "index"
chunksDirname = "chunks"
snapshotsDirname = "snapshots"
)
// BackendFilesystem is used to store and access data on a filesytem based backend.
type BackendFilesystem interface {
// Stat stats a file on disk
Stat(path string) (uint64, error)
// CreatePath creates a dir including all its parents dirs, when required
CreatePath(path string) error
// ReadFile reads a file from disk
ReadFile(path string) ([]byte, error)
// WriteFile writes a file to disk
WriteFile(path string, data []byte) (uint64, error)
// DeleteFile deletes a file from disk
DeleteFile(path string) error
}
// StorageFilesystem is bridging a BackendFilesystem to a Backend interface.
type StorageFilesystem struct {
Path string
chunkPath string
snapshotPath string
chunkIndexPath string
repositoryPath string
storage *BackendFilesystem
}
// NewStorageFilesystem returns a StorageFilesystem object.
func NewStorageFilesystem(path string, storage BackendFilesystem) (StorageFilesystem, error) {
s := StorageFilesystem{
Path: path,
chunkPath: filepath.Join(path, chunksDirname),
snapshotPath: filepath.Join(path, snapshotsDirname),
chunkIndexPath: filepath.Join(path, chunksDirname, ChunkIndexFilename),
repositoryPath: filepath.Join(path, RepoFilename),
storage: &storage,
}
return s, nil
}
// LoadChunk loads a Chunk from disk.
func (backend StorageFilesystem) LoadChunk(shasum string, part, totalParts uint) ([]byte, error) {
path := filepath.Join(backend.chunkPath, SubDirForChunk(shasum))
fileName := filepath.Join(path, shasum+"."+strconv.FormatUint(uint64(part), 10)+"_"+strconv.FormatUint(uint64(totalParts), 10))
return (*backend.storage).ReadFile(fileName)
}
// StoreChunk stores a single Chunk on disk.
func (backend StorageFilesystem) StoreChunk(shasum string, part, totalParts uint, data []byte) (size uint64, err error) {
path := filepath.Join(backend.chunkPath, SubDirForChunk(shasum))
fileName := filepath.Join(path, shasum+"."+strconv.FormatUint(uint64(part), 10)+"_"+strconv.FormatUint(uint64(totalParts), 10))
n, err := (*backend.storage).Stat(fileName)
if err == nil && n == uint64(len(data)) {
return 0, nil
}
err = (*backend.storage).CreatePath(path)
if err != nil {
return 0, err
}
return (*backend.storage).WriteFile(fileName, data)
}
// DeleteChunk deletes a single Chunk.
func (backend StorageFilesystem) DeleteChunk(shasum string, part, totalParts uint) error {
path := filepath.Join(backend.chunkPath, SubDirForChunk(shasum))
fileName := filepath.Join(path, shasum+"."+strconv.FormatUint(uint64(part), 10)+"_"+strconv.FormatUint(uint64(totalParts), 10))
return (*backend.storage).DeleteFile(fileName)
}
// LoadSnapshot loads a snapshot.
func (backend StorageFilesystem) LoadSnapshot(id string) ([]byte, error) {
return (*backend.storage).ReadFile(filepath.Join(backend.snapshotPath, id))
}
// SaveSnapshot stores a snapshot.
func (backend StorageFilesystem) SaveSnapshot(id string, b []byte) error {
_, err := (*backend.storage).WriteFile(filepath.Join(backend.snapshotPath, id), b)
return err
}
// LoadChunkIndex reads the chunk-index.
func (backend StorageFilesystem) LoadChunkIndex() ([]byte, error) {
return (*backend.storage).ReadFile(backend.chunkIndexPath)
}
// SaveChunkIndex stores the chunk-index.
func (backend StorageFilesystem) SaveChunkIndex(b []byte) error {
_, err := (*backend.storage).WriteFile(backend.chunkIndexPath, b)
return err
}
// InitRepository creates a new repository.
func (backend StorageFilesystem) InitRepository() error {
if _, err := (*backend.storage).Stat(backend.repositoryPath); err == nil {
// Repo seems to already exist
return ErrRepositoryExists
}
paths := []string{backend.chunkPath, backend.snapshotPath}
for _, path := range paths {
if _, err := (*backend.storage).Stat(path); err == nil {
return ErrRepositoryExists
/*
if !stat.IsDir() {
return &os.PathError{Op: "create", Path: path, Err: errors.New("repository path contains an invalid file")}
}
*/
}
err := (*backend.storage).CreatePath(path)
if err != nil {
return err
}
}
return nil
}
// LoadRepository reads the metadata for a repository.
func (backend StorageFilesystem) LoadRepository() ([]byte, error) {
return (*backend.storage).ReadFile(backend.repositoryPath)
}
// SaveRepository stores the metadata for a repository.
func (backend StorageFilesystem) SaveRepository(b []byte) error {
_, err := (*backend.storage).WriteFile(backend.repositoryPath, b)
return err
}
// SubDirForChunk files a chunk into a subdir, based on the chunks name.
func SubDirForChunk(id string) string {
return filepath.Join(id[0:2], id[2:4])
}