fs.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. package op
  2. import (
  3. "context"
  4. "os"
  5. stdpath "path"
  6. "time"
  7. "github.com/IceWhaleTech/CasaOS/internal/driver"
  8. "github.com/IceWhaleTech/CasaOS/model"
  9. "github.com/IceWhaleTech/CasaOS/pkg/generic_sync"
  10. "github.com/IceWhaleTech/CasaOS/pkg/singleflight"
  11. "github.com/IceWhaleTech/CasaOS/pkg/utils"
  12. "github.com/Xhofe/go-cache"
  13. "github.com/pkg/errors"
  14. pkgerr "github.com/pkg/errors"
  15. log "github.com/sirupsen/logrus"
  16. )
  17. // In order to facilitate adding some other things before and after file op
  18. var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
  19. var listG singleflight.Group[[]model.Obj]
  20. func updateCacheObj(storage driver.Driver, path string, oldObj model.Obj, newObj model.Obj) {
  21. key := Key(storage, path)
  22. objs, ok := listCache.Get(key)
  23. if ok {
  24. for i, obj := range objs {
  25. if obj.GetName() == oldObj.GetName() {
  26. objs[i] = newObj
  27. break
  28. }
  29. }
  30. listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
  31. }
  32. }
  33. func delCacheObj(storage driver.Driver, path string, obj model.Obj) {
  34. key := Key(storage, path)
  35. objs, ok := listCache.Get(key)
  36. if ok {
  37. for i, oldObj := range objs {
  38. if oldObj.GetName() == obj.GetName() {
  39. objs = append(objs[:i], objs[i+1:]...)
  40. break
  41. }
  42. }
  43. listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
  44. }
  45. }
  46. var addSortDebounceMap generic_sync.MapOf[string, func(func())]
  47. func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
  48. key := Key(storage, path)
  49. objs, ok := listCache.Get(key)
  50. if ok {
  51. for i, obj := range objs {
  52. if obj.GetName() == newObj.GetName() {
  53. objs[i] = newObj
  54. return
  55. }
  56. }
  57. // Simple separation of files and folders
  58. if len(objs) > 0 && objs[len(objs)-1].IsDir() == newObj.IsDir() {
  59. objs = append(objs, newObj)
  60. } else {
  61. objs = append([]model.Obj{newObj}, objs...)
  62. }
  63. if storage.Config().LocalSort {
  64. debounce, _ := addSortDebounceMap.LoadOrStore(key, utils.NewDebounce(time.Minute))
  65. log.Debug("addCacheObj: wait start sort")
  66. debounce(func() {
  67. log.Debug("addCacheObj: start sort")
  68. model.SortFiles(objs, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
  69. addSortDebounceMap.Delete(key)
  70. })
  71. }
  72. listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
  73. }
  74. }
  75. func ClearCache(storage driver.Driver, path string) {
  76. listCache.Del(Key(storage, path))
  77. }
  78. func Key(storage driver.Driver, path string) string {
  79. return stdpath.Join(storage.GetStorage().MountPath, utils.FixAndCleanPath(path))
  80. }
  81. // List files in storage, not contains virtual file
  82. func List(ctx context.Context, storage driver.Driver, path string, args model.ListArgs, refresh ...bool) ([]model.Obj, error) {
  83. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  84. return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  85. }
  86. path = utils.FixAndCleanPath(path)
  87. log.Debugf("op.List %s", path)
  88. key := Key(storage, path)
  89. if !utils.IsBool(refresh...) {
  90. if files, ok := listCache.Get(key); ok {
  91. log.Debugf("use cache when list %s", path)
  92. return files, nil
  93. }
  94. }
  95. dir, err := GetUnwrap(ctx, storage, path)
  96. if err != nil {
  97. return nil, errors.WithMessage(err, "failed get dir")
  98. }
  99. log.Debugf("list dir: %+v", dir)
  100. if !dir.IsDir() {
  101. return nil, errors.WithStack(errors.New("not a folder"))
  102. }
  103. objs, err, _ := listG.Do(key, func() ([]model.Obj, error) {
  104. files, err := storage.List(ctx, dir, args)
  105. if err != nil {
  106. return nil, errors.Wrapf(err, "failed to list objs")
  107. }
  108. // set path
  109. for _, f := range files {
  110. if s, ok := f.(model.SetPath); ok && f.GetPath() == "" && dir.GetPath() != "" {
  111. s.SetPath(stdpath.Join(dir.GetPath(), f.GetName()))
  112. }
  113. }
  114. // warp obj name
  115. model.WrapObjsName(files)
  116. // call hooks
  117. go func(reqPath string, files []model.Obj) {
  118. for _, hook := range ObjsUpdateHooks {
  119. hook(args.ReqPath, files)
  120. }
  121. }(args.ReqPath, files)
  122. // sort objs
  123. if storage.Config().LocalSort {
  124. model.SortFiles(files, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
  125. }
  126. model.ExtractFolder(files, storage.GetStorage().ExtractFolder)
  127. if !storage.Config().NoCache {
  128. if len(files) > 0 {
  129. log.Debugf("set cache: %s => %+v", key, files)
  130. listCache.Set(key, files, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
  131. } else {
  132. log.Debugf("del cache: %s", key)
  133. listCache.Del(key)
  134. }
  135. }
  136. return files, nil
  137. })
  138. return objs, err
  139. }
  140. // Get object from list of files
  141. func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
  142. path = utils.FixAndCleanPath(path)
  143. log.Debugf("op.Get %s", path)
  144. // is root folder
  145. if utils.PathEqual(path, "/") {
  146. var rootObj model.Obj
  147. switch r := storage.GetAddition().(type) {
  148. case driver.IRootId:
  149. rootObj = &model.Object{
  150. ID: r.GetRootId(),
  151. Name: RootName,
  152. Size: 0,
  153. Modified: storage.GetStorage().Modified,
  154. IsFolder: true,
  155. Path: path,
  156. }
  157. case driver.IRootPath:
  158. rootObj = &model.Object{
  159. Path: r.GetRootPath(),
  160. Name: RootName,
  161. Size: 0,
  162. Modified: storage.GetStorage().Modified,
  163. IsFolder: true,
  164. }
  165. default:
  166. if storage, ok := storage.(driver.Getter); ok {
  167. obj, err := storage.GetRoot(ctx)
  168. if err != nil {
  169. return nil, errors.WithMessage(err, "failed get root obj")
  170. }
  171. rootObj = obj
  172. }
  173. }
  174. if rootObj == nil {
  175. return nil, errors.Errorf("please implement IRootPath or IRootId or Getter method")
  176. }
  177. return &model.ObjWrapName{
  178. Name: RootName,
  179. Obj: rootObj,
  180. }, nil
  181. }
  182. // not root folder
  183. dir, name := stdpath.Split(path)
  184. files, err := List(ctx, storage, dir, model.ListArgs{})
  185. if err != nil {
  186. return nil, errors.WithMessage(err, "failed get parent list")
  187. }
  188. for _, f := range files {
  189. // TODO maybe copy obj here
  190. if f.GetName() == name {
  191. return f, nil
  192. }
  193. }
  194. log.Debugf("cant find obj with name: %s", name)
  195. return nil, errors.WithStack(errors.New("object not found"))
  196. }
  197. func GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
  198. obj, err := Get(ctx, storage, path)
  199. if err != nil {
  200. return nil, err
  201. }
  202. return model.UnwrapObjs(obj), err
  203. }
  204. var linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
  205. var linkG singleflight.Group[*model.Link]
  206. // Link get link, if is an url. should have an expiry time
  207. func Link(ctx context.Context, storage driver.Driver, path string, args model.LinkArgs) (*model.Link, model.Obj, error) {
  208. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  209. return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  210. }
  211. file, err := GetUnwrap(ctx, storage, path)
  212. if err != nil {
  213. return nil, nil, errors.WithMessage(err, "failed to get file")
  214. }
  215. if file.IsDir() {
  216. return nil, nil, errors.WithStack(errors.New("not a file"))
  217. }
  218. key := Key(storage, path) + ":" + args.IP
  219. if link, ok := linkCache.Get(key); ok {
  220. return link, file, nil
  221. }
  222. fn := func() (*model.Link, error) {
  223. link, err := storage.Link(ctx, file, args)
  224. if err != nil {
  225. return nil, errors.Wrapf(err, "failed get link")
  226. }
  227. if link.Expiration != nil {
  228. linkCache.Set(key, link, cache.WithEx[*model.Link](*link.Expiration))
  229. }
  230. return link, nil
  231. }
  232. link, err, _ := linkG.Do(key, fn)
  233. return link, file, err
  234. }
  235. // Other api
  236. func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (interface{}, error) {
  237. obj, err := GetUnwrap(ctx, storage, args.Path)
  238. if err != nil {
  239. return nil, errors.WithMessagef(err, "failed to get obj")
  240. }
  241. if o, ok := storage.(driver.Other); ok {
  242. return o.Other(ctx, model.OtherArgs{
  243. Obj: obj,
  244. Method: args.Method,
  245. Data: args.Data,
  246. })
  247. } else {
  248. return nil, errors.New("not implement")
  249. }
  250. }
  251. var mkdirG singleflight.Group[interface{}]
  252. func MakeDir(ctx context.Context, storage driver.Driver, path string, lazyCache ...bool) error {
  253. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  254. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  255. }
  256. path = utils.FixAndCleanPath(path)
  257. key := Key(storage, path)
  258. _, err, _ := mkdirG.Do(key, func() (interface{}, error) {
  259. // check if dir exists
  260. f, err := GetUnwrap(ctx, storage, path)
  261. if err != nil {
  262. if errors.Is(pkgerr.Cause(err), errors.New("object not found")) {
  263. parentPath, dirName := stdpath.Split(path)
  264. err = MakeDir(ctx, storage, parentPath)
  265. if err != nil {
  266. return nil, errors.WithMessagef(err, "failed to make parent dir [%s]", parentPath)
  267. }
  268. parentDir, err := GetUnwrap(ctx, storage, parentPath)
  269. // this should not happen
  270. if err != nil {
  271. return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath)
  272. }
  273. switch s := storage.(type) {
  274. case driver.MkdirResult:
  275. var newObj model.Obj
  276. newObj, err = s.MakeDir(ctx, parentDir, dirName)
  277. if err == nil {
  278. if newObj != nil {
  279. addCacheObj(storage, parentPath, model.WrapObjName(newObj))
  280. } else if !utils.IsBool(lazyCache...) {
  281. ClearCache(storage, parentPath)
  282. }
  283. }
  284. case driver.Mkdir:
  285. err = s.MakeDir(ctx, parentDir, dirName)
  286. if err == nil && !utils.IsBool(lazyCache...) {
  287. ClearCache(storage, parentPath)
  288. }
  289. default:
  290. return nil, errors.New("not implement")
  291. }
  292. return nil, errors.WithStack(err)
  293. }
  294. return nil, errors.WithMessage(err, "failed to check if dir exists")
  295. }
  296. // dir exists
  297. if f.IsDir() {
  298. return nil, nil
  299. }
  300. // dir to make is a file
  301. return nil, errors.New("file exists")
  302. })
  303. return err
  304. }
  305. func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
  306. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  307. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  308. }
  309. srcPath = utils.FixAndCleanPath(srcPath)
  310. dstDirPath = utils.FixAndCleanPath(dstDirPath)
  311. srcRawObj, err := Get(ctx, storage, srcPath)
  312. if err != nil {
  313. return errors.WithMessage(err, "failed to get src object")
  314. }
  315. srcObj := model.UnwrapObjs(srcRawObj)
  316. dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
  317. if err != nil {
  318. return errors.WithMessage(err, "failed to get dst dir")
  319. }
  320. srcDirPath := stdpath.Dir(srcPath)
  321. switch s := storage.(type) {
  322. case driver.MoveResult:
  323. var newObj model.Obj
  324. newObj, err = s.Move(ctx, srcObj, dstDir)
  325. if err == nil {
  326. delCacheObj(storage, srcDirPath, srcRawObj)
  327. if newObj != nil {
  328. addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
  329. } else if !utils.IsBool(lazyCache...) {
  330. ClearCache(storage, dstDirPath)
  331. }
  332. }
  333. case driver.Move:
  334. err = s.Move(ctx, srcObj, dstDir)
  335. if err == nil {
  336. delCacheObj(storage, srcDirPath, srcRawObj)
  337. if !utils.IsBool(lazyCache...) {
  338. ClearCache(storage, dstDirPath)
  339. }
  340. }
  341. default:
  342. return errors.New("not implement")
  343. }
  344. return errors.WithStack(err)
  345. }
  346. func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string, lazyCache ...bool) error {
  347. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  348. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  349. }
  350. srcPath = utils.FixAndCleanPath(srcPath)
  351. srcRawObj, err := Get(ctx, storage, srcPath)
  352. if err != nil {
  353. return errors.WithMessage(err, "failed to get src object")
  354. }
  355. srcObj := model.UnwrapObjs(srcRawObj)
  356. srcDirPath := stdpath.Dir(srcPath)
  357. switch s := storage.(type) {
  358. case driver.RenameResult:
  359. var newObj model.Obj
  360. newObj, err = s.Rename(ctx, srcObj, dstName)
  361. if err == nil {
  362. if newObj != nil {
  363. updateCacheObj(storage, srcDirPath, srcRawObj, model.WrapObjName(newObj))
  364. } else if !utils.IsBool(lazyCache...) {
  365. ClearCache(storage, srcDirPath)
  366. }
  367. }
  368. case driver.Rename:
  369. err = s.Rename(ctx, srcObj, dstName)
  370. if err == nil && !utils.IsBool(lazyCache...) {
  371. ClearCache(storage, srcDirPath)
  372. }
  373. default:
  374. return errors.New("not implement")
  375. }
  376. return errors.WithStack(err)
  377. }
  378. // Copy Just copy file[s] in a storage
  379. func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
  380. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  381. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  382. }
  383. srcPath = utils.FixAndCleanPath(srcPath)
  384. dstDirPath = utils.FixAndCleanPath(dstDirPath)
  385. srcObj, err := GetUnwrap(ctx, storage, srcPath)
  386. if err != nil {
  387. return errors.WithMessage(err, "failed to get src object")
  388. }
  389. dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
  390. if err != nil {
  391. return errors.WithMessage(err, "failed to get dst dir")
  392. }
  393. switch s := storage.(type) {
  394. case driver.CopyResult:
  395. var newObj model.Obj
  396. newObj, err = s.Copy(ctx, srcObj, dstDir)
  397. if err == nil {
  398. if newObj != nil {
  399. addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
  400. } else if !utils.IsBool(lazyCache...) {
  401. ClearCache(storage, dstDirPath)
  402. }
  403. }
  404. case driver.Copy:
  405. err = s.Copy(ctx, srcObj, dstDir)
  406. if err == nil && !utils.IsBool(lazyCache...) {
  407. ClearCache(storage, dstDirPath)
  408. }
  409. default:
  410. return errors.New("not implement")
  411. }
  412. return errors.WithStack(err)
  413. }
  414. func Remove(ctx context.Context, storage driver.Driver, path string) error {
  415. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  416. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  417. }
  418. path = utils.FixAndCleanPath(path)
  419. rawObj, err := Get(ctx, storage, path)
  420. if err != nil {
  421. // if object not found, it's ok
  422. if errors.Is(pkgerr.Cause(err), errors.New("object not found")) {
  423. return nil
  424. }
  425. return errors.WithMessage(err, "failed to get object")
  426. }
  427. dirPath := stdpath.Dir(path)
  428. switch s := storage.(type) {
  429. case driver.Remove:
  430. err = s.Remove(ctx, model.UnwrapObjs(rawObj))
  431. if err == nil {
  432. delCacheObj(storage, dirPath, rawObj)
  433. }
  434. default:
  435. return errors.New("not implement")
  436. }
  437. return errors.WithStack(err)
  438. }
  439. func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *model.FileStream, up driver.UpdateProgress, lazyCache ...bool) error {
  440. if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
  441. return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
  442. }
  443. defer func() {
  444. if f, ok := file.GetReadCloser().(*os.File); ok {
  445. err := os.RemoveAll(f.Name())
  446. if err != nil {
  447. log.Errorf("failed to remove file [%s]", f.Name())
  448. }
  449. }
  450. }()
  451. defer func() {
  452. if err := file.Close(); err != nil {
  453. log.Errorf("failed to close file streamer, %v", err)
  454. }
  455. }()
  456. // if file exist and size = 0, delete it
  457. dstDirPath = utils.FixAndCleanPath(dstDirPath)
  458. dstPath := stdpath.Join(dstDirPath, file.GetName())
  459. fi, err := GetUnwrap(ctx, storage, dstPath)
  460. if err == nil {
  461. if fi.GetSize() == 0 {
  462. err = Remove(ctx, storage, dstPath)
  463. if err != nil {
  464. return errors.WithMessagef(err, "failed remove file that exist and have size 0")
  465. }
  466. } else {
  467. file.Old = fi
  468. }
  469. }
  470. err = MakeDir(ctx, storage, dstDirPath)
  471. if err != nil {
  472. return errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath)
  473. }
  474. parentDir, err := GetUnwrap(ctx, storage, dstDirPath)
  475. // this should not happen
  476. if err != nil {
  477. return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)
  478. }
  479. // if up is nil, set a default to prevent panic
  480. if up == nil {
  481. up = func(p int) {}
  482. }
  483. switch s := storage.(type) {
  484. case driver.PutResult:
  485. var newObj model.Obj
  486. newObj, err = s.Put(ctx, parentDir, file, up)
  487. if err == nil {
  488. if newObj != nil {
  489. addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
  490. } else if !utils.IsBool(lazyCache...) {
  491. ClearCache(storage, dstDirPath)
  492. }
  493. }
  494. case driver.Put:
  495. err = s.Put(ctx, parentDir, file, up)
  496. if err == nil && !utils.IsBool(lazyCache...) {
  497. ClearCache(storage, dstDirPath)
  498. }
  499. default:
  500. return errors.New("not implement")
  501. }
  502. log.Debugf("put file [%s] done", file.GetName())
  503. //if err == nil {
  504. // //clear cache
  505. // key := stdpath.Join(storage.GetStorage().MountPath, dstDirPath)
  506. // listCache.Del(key)
  507. //}
  508. return errors.WithStack(err)
  509. }