package blobtree import ( "errors" "path/filepath" "strings" "sync/atomic" "syscall" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/common" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/blobstor/compression" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util" utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" ) var _ common.Storage = &BlobTree{} type BlobTree struct { cfg cfg dirLock *utilSync.KeyLocker[string] fileLock *utilSync.KeyLocker[string] compressor *compression.Config dispatcher *rootDispatcher suffix atomic.Uint64 } func New(opts ...Option) *BlobTree { b := &BlobTree{ cfg: cfg{ targetFileSizeBytes: 4 * 1024 * 1024, rootPath: "./", depth: 3, permissions: 0700, initWorkersCount: 1000, metrics: &noopMetrics{}, }, dirLock: utilSync.NewKeyLocker[string](), fileLock: utilSync.NewKeyLocker[string](), } for _, opt := range opts { opt(&b.cfg) } b.dispatcher = newRootDispatcher() return b } func (b *BlobTree) getDir(addr oid.Address) string { sAddr := addr.Object().EncodeToString() + "." + addr.Container().EncodeToString() var sb strings.Builder size := int(b.cfg.depth * (directoryLength + 1)) // (character + slash for every level) sb.Grow(size) for i := uint64(0); i < b.cfg.depth; i++ { if i > 0 { sb.WriteRune(filepath.Separator) } sb.WriteString(sAddr[:directoryLength]) sAddr = sAddr[directoryLength:] } return sb.String() } func (b *BlobTree) createDir(dir string, isSystemPath bool) error { b.dirLock.Lock(dir) defer b.dirLock.Unlock(dir) if !isSystemPath { dir = b.getSystemPath(dir) } if err := util.MkdirAllX(dir, b.cfg.permissions); err != nil { if errors.Is(err, syscall.ENOSPC) { err = common.ErrNoSpace return err } return err } return nil }