Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/op/storage.go
1560 views
1
package op
2
3
import (
4
"context"
5
"fmt"
6
"runtime"
7
"sort"
8
"strings"
9
"time"
10
11
"github.com/alist-org/alist/v3/internal/db"
12
"github.com/alist-org/alist/v3/internal/driver"
13
"github.com/alist-org/alist/v3/internal/errs"
14
"github.com/alist-org/alist/v3/internal/model"
15
"github.com/alist-org/alist/v3/pkg/generic_sync"
16
"github.com/alist-org/alist/v3/pkg/utils"
17
mapset "github.com/deckarep/golang-set/v2"
18
"github.com/pkg/errors"
19
log "github.com/sirupsen/logrus"
20
)
21
22
// Although the driver type is stored,
23
// there is a storage in each driver,
24
// so it should actually be a storage, just wrapped by the driver
25
var storagesMap generic_sync.MapOf[string, driver.Driver]
26
27
func GetAllStorages() []driver.Driver {
28
return storagesMap.Values()
29
}
30
31
func HasStorage(mountPath string) bool {
32
return storagesMap.Has(utils.FixAndCleanPath(mountPath))
33
}
34
35
func GetStorageByMountPath(mountPath string) (driver.Driver, error) {
36
mountPath = utils.FixAndCleanPath(mountPath)
37
storageDriver, ok := storagesMap.Load(mountPath)
38
if !ok {
39
return nil, errors.Errorf("no mount path for an storage is: %s", mountPath)
40
}
41
return storageDriver, nil
42
}
43
44
// CreateStorage Save the storage to database so storage can get an id
45
// then instantiate corresponding driver and save it in memory
46
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
47
storage.Modified = time.Now()
48
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
49
50
//if storage.MountPath == "/" {
51
// return 0, errors.New("Mount path cannot be '/'")
52
//}
53
54
var err error
55
// check driver first
56
driverName := storage.Driver
57
driverNew, err := GetDriver(driverName)
58
if err != nil {
59
return 0, errors.WithMessage(err, "failed get driver new")
60
}
61
storageDriver := driverNew()
62
// insert storage to database
63
err = db.CreateStorage(&storage)
64
if err != nil {
65
return storage.ID, errors.WithMessage(err, "failed create storage in database")
66
}
67
// already has an id
68
err = initStorage(ctx, storage, storageDriver)
69
go callStorageHooks("add", storageDriver)
70
if err != nil {
71
return storage.ID, errors.Wrap(err, "failed init storage but storage is already created")
72
}
73
log.Debugf("storage %+v is created", storageDriver)
74
return storage.ID, nil
75
}
76
77
// LoadStorage load exist storage in db to memory
78
func LoadStorage(ctx context.Context, storage model.Storage) error {
79
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
80
// check driver first
81
driverName := storage.Driver
82
driverNew, err := GetDriver(driverName)
83
if err != nil {
84
return errors.WithMessage(err, "failed get driver new")
85
}
86
storageDriver := driverNew()
87
88
err = initStorage(ctx, storage, storageDriver)
89
go callStorageHooks("add", storageDriver)
90
log.Debugf("storage %+v is created", storageDriver)
91
return err
92
}
93
94
func getCurrentGoroutineStack() string {
95
buf := make([]byte, 1<<16)
96
n := runtime.Stack(buf, false)
97
return string(buf[:n])
98
}
99
100
// initStorage initialize the driver and store to storagesMap
101
func initStorage(ctx context.Context, storage model.Storage, storageDriver driver.Driver) (err error) {
102
storageDriver.SetStorage(storage)
103
driverStorage := storageDriver.GetStorage()
104
defer func() {
105
if err := recover(); err != nil {
106
errInfo := fmt.Sprintf("[panic] err: %v\nstack: %s\n", err, getCurrentGoroutineStack())
107
log.Errorf("panic init storage: %s", errInfo)
108
driverStorage.SetStatus(errInfo)
109
MustSaveDriverStorage(storageDriver)
110
storagesMap.Store(driverStorage.MountPath, storageDriver)
111
}
112
}()
113
// Unmarshal Addition
114
err = utils.Json.UnmarshalFromString(driverStorage.Addition, storageDriver.GetAddition())
115
if err == nil {
116
if ref, ok := storageDriver.(driver.Reference); ok {
117
if strings.HasPrefix(driverStorage.Remark, "ref:/") {
118
refMountPath := driverStorage.Remark
119
i := strings.Index(refMountPath, "\n")
120
if i > 0 {
121
refMountPath = refMountPath[4:i]
122
} else {
123
refMountPath = refMountPath[4:]
124
}
125
var refStorage driver.Driver
126
refStorage, err = GetStorageByMountPath(refMountPath)
127
if err != nil {
128
err = fmt.Errorf("ref: %w", err)
129
} else {
130
err = ref.InitReference(refStorage)
131
if err != nil && errs.IsNotSupportError(err) {
132
err = fmt.Errorf("ref: storage is not %s", storageDriver.Config().Name)
133
}
134
}
135
}
136
}
137
}
138
if err == nil {
139
err = storageDriver.Init(ctx)
140
}
141
storagesMap.Store(driverStorage.MountPath, storageDriver)
142
if err != nil {
143
driverStorage.SetStatus(err.Error())
144
err = errors.Wrap(err, "failed init storage")
145
} else {
146
driverStorage.SetStatus(WORK)
147
}
148
MustSaveDriverStorage(storageDriver)
149
return err
150
}
151
152
func EnableStorage(ctx context.Context, id uint) error {
153
storage, err := db.GetStorageById(id)
154
if err != nil {
155
return errors.WithMessage(err, "failed get storage")
156
}
157
if !storage.Disabled {
158
return errors.Errorf("this storage have enabled")
159
}
160
storage.Disabled = false
161
err = db.UpdateStorage(storage)
162
if err != nil {
163
return errors.WithMessage(err, "failed update storage in db")
164
}
165
err = LoadStorage(ctx, *storage)
166
if err != nil {
167
return errors.WithMessage(err, "failed load storage")
168
}
169
return nil
170
}
171
172
func DisableStorage(ctx context.Context, id uint) error {
173
storage, err := db.GetStorageById(id)
174
if err != nil {
175
return errors.WithMessage(err, "failed get storage")
176
}
177
if storage.Disabled {
178
return errors.Errorf("this storage have disabled")
179
}
180
storageDriver, err := GetStorageByMountPath(storage.MountPath)
181
if err != nil {
182
return errors.WithMessage(err, "failed get storage driver")
183
}
184
// drop the storage in the driver
185
if err := storageDriver.Drop(ctx); err != nil {
186
return errors.Wrap(err, "failed drop storage")
187
}
188
// delete the storage in the memory
189
storage.Disabled = true
190
storage.SetStatus(DISABLED)
191
err = db.UpdateStorage(storage)
192
if err != nil {
193
return errors.WithMessage(err, "failed update storage in db")
194
}
195
storagesMap.Delete(storage.MountPath)
196
go callStorageHooks("del", storageDriver)
197
return nil
198
}
199
200
// UpdateStorage update storage
201
// get old storage first
202
// drop the storage then reinitialize
203
func UpdateStorage(ctx context.Context, storage model.Storage) error {
204
oldStorage, err := db.GetStorageById(storage.ID)
205
if err != nil {
206
return errors.WithMessage(err, "failed get old storage")
207
}
208
if oldStorage.Driver != storage.Driver {
209
return errors.Errorf("driver cannot be changed")
210
}
211
storage.Modified = time.Now()
212
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
213
//if storage.MountPath == "/" {
214
// return errors.New("Mount path cannot be '/'")
215
//}
216
err = db.UpdateStorage(&storage)
217
if err != nil {
218
return errors.WithMessage(err, "failed update storage in database")
219
}
220
if storage.Disabled {
221
return nil
222
}
223
storageDriver, err := GetStorageByMountPath(oldStorage.MountPath)
224
if oldStorage.MountPath != storage.MountPath {
225
// mount path renamed, need to drop the storage
226
storagesMap.Delete(oldStorage.MountPath)
227
modifiedRoleIDs, err := db.UpdateRolePermissionsPathPrefix(oldStorage.MountPath, storage.MountPath)
228
if err != nil {
229
return errors.WithMessage(err, "failed to update role permissions")
230
}
231
for _, id := range modifiedRoleIDs {
232
roleCache.Del(fmt.Sprint(id))
233
}
234
235
//modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldStorage.MountPath, storage.MountPath)
236
//if err != nil {
237
// return errors.WithMessage(err, "failed to update user base path")
238
//}
239
for _, id := range modifiedRoleIDs {
240
roleCache.Del(fmt.Sprint(id))
241
242
users, err := db.GetUsersByRole(int(id))
243
if err != nil {
244
return errors.WithMessage(err, "failed to get users by role")
245
}
246
for _, user := range users {
247
userCache.Del(user.Username)
248
}
249
}
250
}
251
if err != nil {
252
return errors.WithMessage(err, "failed get storage driver")
253
}
254
err = storageDriver.Drop(ctx)
255
if err != nil {
256
return errors.Wrapf(err, "failed drop storage")
257
}
258
259
err = initStorage(ctx, storage, storageDriver)
260
go callStorageHooks("update", storageDriver)
261
log.Debugf("storage %+v is update", storageDriver)
262
return err
263
}
264
265
func DeleteStorageById(ctx context.Context, id uint) error {
266
storage, err := db.GetStorageById(id)
267
if err != nil {
268
return errors.WithMessage(err, "failed get storage")
269
}
270
if !storage.Disabled {
271
storageDriver, err := GetStorageByMountPath(storage.MountPath)
272
if err != nil {
273
return errors.WithMessage(err, "failed get storage driver")
274
}
275
// drop the storage in the driver
276
if err := storageDriver.Drop(ctx); err != nil {
277
return errors.Wrapf(err, "failed drop storage")
278
}
279
// delete the storage in the memory
280
storagesMap.Delete(storage.MountPath)
281
go callStorageHooks("del", storageDriver)
282
}
283
// delete the storage in the database
284
if err := db.DeleteStorageById(id); err != nil {
285
return errors.WithMessage(err, "failed delete storage in database")
286
}
287
return nil
288
}
289
290
// MustSaveDriverStorage call from specific driver
291
func MustSaveDriverStorage(driver driver.Driver) {
292
err := saveDriverStorage(driver)
293
if err != nil {
294
log.Errorf("failed save driver storage: %s", err)
295
}
296
}
297
298
func saveDriverStorage(driver driver.Driver) error {
299
storage := driver.GetStorage()
300
addition := driver.GetAddition()
301
str, err := utils.Json.MarshalToString(addition)
302
if err != nil {
303
return errors.Wrap(err, "error while marshal addition")
304
}
305
storage.Addition = str
306
err = db.UpdateStorage(storage)
307
if err != nil {
308
return errors.WithMessage(err, "failed update storage in database")
309
}
310
return nil
311
}
312
313
// getStoragesByPath get storage by longest match path, contains balance storage.
314
// for example, there is /a/b,/a/c,/a/d/e,/a/d/e.balance
315
// getStoragesByPath(/a/d/e/f) => /a/d/e,/a/d/e.balance
316
func getStoragesByPath(path string) []driver.Driver {
317
storages := make([]driver.Driver, 0)
318
curSlashCount := 0
319
storagesMap.Range(func(mountPath string, value driver.Driver) bool {
320
mountPath = utils.GetActualMountPath(mountPath)
321
// is this path
322
if utils.IsSubPath(mountPath, path) {
323
slashCount := strings.Count(utils.PathAddSeparatorSuffix(mountPath), "/")
324
// not the longest match
325
if slashCount > curSlashCount {
326
storages = storages[:0]
327
curSlashCount = slashCount
328
}
329
if slashCount == curSlashCount {
330
storages = append(storages, value)
331
}
332
}
333
return true
334
})
335
// make sure the order is the same for same input
336
sort.Slice(storages, func(i, j int) bool {
337
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath
338
})
339
return storages
340
}
341
342
// GetStorageVirtualFilesByPath Obtain the virtual file generated by the storage according to the path
343
// for example, there are: /a/b,/a/c,/a/d/e,/a/b.balance1,/av
344
// GetStorageVirtualFilesByPath(/a) => b,c,d
345
func GetStorageVirtualFilesByPath(prefix string) []model.Obj {
346
files := make([]model.Obj, 0)
347
storages := storagesMap.Values()
348
sort.Slice(storages, func(i, j int) bool {
349
if storages[i].GetStorage().Order == storages[j].GetStorage().Order {
350
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath
351
}
352
return storages[i].GetStorage().Order < storages[j].GetStorage().Order
353
})
354
355
prefix = utils.FixAndCleanPath(prefix)
356
set := mapset.NewSet[string]()
357
for _, v := range storages {
358
mountPath := utils.GetActualMountPath(v.GetStorage().MountPath)
359
// Exclude prefix itself and non prefix
360
if len(prefix) >= len(mountPath) || !utils.IsSubPath(prefix, mountPath) {
361
continue
362
}
363
name := strings.SplitN(strings.TrimPrefix(mountPath[len(prefix):], "/"), "/", 2)[0]
364
if set.Add(name) {
365
files = append(files, &model.Object{
366
Name: name,
367
Size: 0,
368
Modified: v.GetStorage().Modified,
369
IsFolder: true,
370
})
371
}
372
}
373
return files
374
}
375
376
var balanceMap generic_sync.MapOf[string, int]
377
378
// GetBalancedStorage get storage by path
379
func GetBalancedStorage(path string) driver.Driver {
380
path = utils.FixAndCleanPath(path)
381
storages := getStoragesByPath(path)
382
storageNum := len(storages)
383
switch storageNum {
384
case 0:
385
return nil
386
case 1:
387
return storages[0]
388
default:
389
virtualPath := utils.GetActualMountPath(storages[0].GetStorage().MountPath)
390
i, _ := balanceMap.LoadOrStore(virtualPath, 0)
391
i = (i + 1) % storageNum
392
balanceMap.Store(virtualPath, i)
393
return storages[i]
394
}
395
}
396
397