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