package local import ( "crypto/sha256" "io" "os" "path/filepath" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/utils/io2" stgtypes "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types" jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types" ) type BaseStore struct { root string detail *jcstypes.UserSpaceDetail } func NewBaseStore(root string, detail *jcstypes.UserSpaceDetail) (*BaseStore, error) { return &BaseStore{ root: root, detail: detail, }, nil } func (s *BaseStore) Write(pat jcstypes.JPath, stream io.Reader, opt stgtypes.WriteOption) (stgtypes.FileInfo, error) { log := s.getLogger() absObjPath := filepath.Join(s.root, pat.String()) err := os.MkdirAll(filepath.Dir(absObjPath), 0755) if err != nil { return stgtypes.FileInfo{}, err } f, err := os.Create(absObjPath) if err != nil { return stgtypes.FileInfo{}, err } defer f.Close() counter := io2.Counter(stream) hasher := io2.NewReadHasher(sha256.New(), counter) _, err = io.Copy(f, hasher) if err != nil { return stgtypes.FileInfo{}, err } if !opt.ModTime.IsZero() { err := os.Chtimes(absObjPath, opt.ModTime, opt.ModTime) if err != nil { log.Warnf("change file %v mod time: %v", absObjPath, err) } } return stgtypes.FileInfo{ Path: pat, Size: counter.Count(), Hash: jcstypes.NewFullHash(hasher.Sum()), }, nil } func (s *BaseStore) Read(objPath jcstypes.JPath, opt stgtypes.OpenOption) (io.ReadCloser, error) { absObjPath := filepath.Join(s.root, objPath.JoinOSPath()) file, err := os.Open(absObjPath) if err != nil { return nil, err } if opt.Offset > 0 { _, err = file.Seek(opt.Offset, io.SeekStart) if err != nil { file.Close() return nil, err } } var ret io.ReadCloser = file if opt.Length >= 0 { ret = io2.Length(ret, opt.Length) } return ret, nil } func (s *BaseStore) Mkdir(path jcstypes.JPath) error { absObjPath := filepath.Join(s.root, path.JoinOSPath()) err := os.MkdirAll(absObjPath, 0755) if err != nil { return err } return nil } func (s *BaseStore) ReadDir(pat jcstypes.JPath) stgtypes.DirReader { return &DirReader{ absRootPath: filepath.Join(s.root, pat.JoinOSPath()), rootJPath: pat.Clone(), } } func (s *BaseStore) CleanTemps() { log := s.getLogger() tempDir := filepath.Join(s.root, s.detail.UserSpace.WorkingDir.JoinOSPath(), stgtypes.TempWorkingDir) entries, err := os.ReadDir(tempDir) if err != nil { log.Warnf("read temp dir: %v", err) return } for _, entry := range entries { if entry.IsDir() { continue } info, err := entry.Info() if err != nil { log.Warnf("get temp file %v info: %v", entry.Name(), err) continue } path := filepath.Join(tempDir, entry.Name()) err = os.Remove(path) if err != nil { log.Warnf("remove temp file %v: %v", path, err) } else { log.Infof("remove unused temp file %v, size: %v, last mod time: %v", path, info.Size(), info.ModTime()) } } } func (s *BaseStore) Test() error { _, err := os.Stat(s.root) return err } func (s *BaseStore) getLogger() logger.Logger { return logger.WithField("BaseStore", "Local").WithField("UserSpace", s.detail.UserSpace) }