summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-store/storage/disk.go
diff options
context:
space:
mode:
authorLibravatar Terin Stock <terinjokes@gmail.com>2022-04-26 20:30:25 -0700
committerLibravatar Terin Stock <terinjokes@gmail.com>2023-01-31 15:16:42 +0100
commit83b4c9ebc87d0fddf4e638f13e3af1483912e3a5 (patch)
tree47840b84c0fd3cb226eab2ecb3dbce0617163406 /vendor/codeberg.org/gruf/go-store/storage/disk.go
parent[chore] update URLs to forked source (diff)
downloadgotosocial-83b4c9ebc87d0fddf4e638f13e3af1483912e3a5.tar.xz
[chore] remove vendor
Diffstat (limited to 'vendor/codeberg.org/gruf/go-store/storage/disk.go')
-rw-r--r--vendor/codeberg.org/gruf/go-store/storage/disk.go400
1 files changed, 0 insertions, 400 deletions
diff --git a/vendor/codeberg.org/gruf/go-store/storage/disk.go b/vendor/codeberg.org/gruf/go-store/storage/disk.go
deleted file mode 100644
index 457cc6364..000000000
--- a/vendor/codeberg.org/gruf/go-store/storage/disk.go
+++ /dev/null
@@ -1,400 +0,0 @@
-package storage
-
-import (
- "io"
- "io/fs"
- "os"
- "path"
- _path "path"
- "strings"
- "syscall"
-
- "codeberg.org/gruf/go-bytes"
- "codeberg.org/gruf/go-fastcopy"
- "codeberg.org/gruf/go-store/util"
-)
-
-// DefaultDiskConfig is the default DiskStorage configuration
-var DefaultDiskConfig = &DiskConfig{
- Overwrite: true,
- WriteBufSize: 4096,
- Transform: NopTransform(),
- Compression: NoCompression(),
-}
-
-// DiskConfig defines options to be used when opening a DiskStorage
-type DiskConfig struct {
- // Transform is the supplied key<-->path KeyTransform
- Transform KeyTransform
-
- // WriteBufSize is the buffer size to use when writing file streams (PutStream)
- WriteBufSize int
-
- // Overwrite allows overwriting values of stored keys in the storage
- Overwrite bool
-
- // LockFile allows specifying the filesystem path to use for the lockfile,
- // providing only a filename it will store the lockfile within provided store
- // path and nest the store under `path/store` to prevent access to lockfile
- LockFile string
-
- // Compression is the Compressor to use when reading / writing files, default is no compression
- Compression Compressor
-}
-
-// getDiskConfig returns a valid DiskConfig for supplied ptr
-func getDiskConfig(cfg *DiskConfig) DiskConfig {
- // If nil, use default
- if cfg == nil {
- cfg = DefaultDiskConfig
- }
-
- // Assume nil transform == none
- if cfg.Transform == nil {
- cfg.Transform = NopTransform()
- }
-
- // Assume nil compress == none
- if cfg.Compression == nil {
- cfg.Compression = NoCompression()
- }
-
- // Assume 0 buf size == use default
- if cfg.WriteBufSize < 1 {
- cfg.WriteBufSize = DefaultDiskConfig.WriteBufSize
- }
-
- // Assume empty lockfile path == use default
- if len(cfg.LockFile) < 1 {
- cfg.LockFile = LockFile
- }
-
- // Return owned config copy
- return DiskConfig{
- Transform: cfg.Transform,
- WriteBufSize: cfg.WriteBufSize,
- Overwrite: cfg.Overwrite,
- LockFile: cfg.LockFile,
- Compression: cfg.Compression,
- }
-}
-
-// DiskStorage is a Storage implementation that stores directly to a filesystem
-type DiskStorage struct {
- path string // path is the root path of this store
- cppool fastcopy.CopyPool // cppool is the prepared io copier with buffer pool
- config DiskConfig // cfg is the supplied configuration for this store
- lock *Lock // lock is the opened lockfile for this storage instance
-}
-
-// OpenFile opens a DiskStorage instance for given folder path and configuration
-func OpenFile(path string, cfg *DiskConfig) (*DiskStorage, error) {
- // Get checked config
- config := getDiskConfig(cfg)
-
- // Acquire path builder
- pb := util.GetPathBuilder()
- defer util.PutPathBuilder(pb)
-
- // Clean provided store path, ensure
- // ends in '/' to help later path trimming
- storePath := pb.Clean(path) + "/"
-
- // Clean provided lockfile path
- lockfile := pb.Clean(config.LockFile)
-
- // Check if lockfile is an *actual* path or just filename
- if lockDir, _ := _path.Split(lockfile); len(lockDir) < 1 {
- // Lockfile is a filename, store must be nested under
- // $storePath/store to prevent access to the lockfile
- storePath += "store/"
- lockfile = pb.Join(path, lockfile)
- }
-
- // Attempt to open dir path
- file, err := os.OpenFile(storePath, defaultFileROFlags, defaultDirPerms)
- if err != nil {
- // If not a not-exist error, return
- if !os.IsNotExist(err) {
- return nil, err
- }
-
- // Attempt to make store path dirs
- err = os.MkdirAll(storePath, defaultDirPerms)
- if err != nil {
- return nil, err
- }
-
- // Reopen dir now it's been created
- file, err = os.OpenFile(storePath, defaultFileROFlags, defaultDirPerms)
- if err != nil {
- return nil, err
- }
- }
- defer file.Close()
-
- // Double check this is a dir (NOT a file!)
- stat, err := file.Stat()
- if err != nil {
- return nil, err
- } else if !stat.IsDir() {
- return nil, errPathIsFile
- }
-
- // Open and acquire storage lock for path
- lock, err := OpenLock(lockfile)
- if err != nil {
- return nil, err
- }
-
- // Prepare DiskStorage
- st := &DiskStorage{
- path: storePath,
- config: config,
- lock: lock,
- }
-
- // Set copypool buffer size
- st.cppool.Buffer(config.WriteBufSize)
-
- return st, nil
-}
-
-// Clean implements Storage.Clean()
-func (st *DiskStorage) Clean() error {
- st.lock.Add()
- defer st.lock.Done()
- if st.lock.Closed() {
- return ErrClosed
- }
- return util.CleanDirs(st.path)
-}
-
-// ReadBytes implements Storage.ReadBytes()
-func (st *DiskStorage) ReadBytes(key string) ([]byte, error) {
- // Get stream reader for key
- rc, err := st.ReadStream(key)
- if err != nil {
- return nil, err
- }
- defer rc.Close()
-
- // Read all bytes and return
- return io.ReadAll(rc)
-}
-
-// ReadStream implements Storage.ReadStream()
-func (st *DiskStorage) ReadStream(key string) (io.ReadCloser, error) {
- // Get file path for key
- kpath, err := st.filepath(key)
- if err != nil {
- return nil, err
- }
-
- // Track open
- st.lock.Add()
-
- // Check if open
- if st.lock.Closed() {
- return nil, ErrClosed
- }
-
- // Attempt to open file (replace ENOENT with our own)
- file, err := open(kpath, defaultFileROFlags)
- if err != nil {
- st.lock.Done()
- return nil, errSwapNotFound(err)
- }
-
- // Wrap the file in a compressor
- cFile, err := st.config.Compression.Reader(file)
- if err != nil {
- file.Close() // close this here, ignore error
- st.lock.Done()
- return nil, err
- }
-
- // Wrap compressor to ensure file close
- return util.ReadCloserWithCallback(cFile, func() {
- file.Close()
- st.lock.Done()
- }), nil
-}
-
-// WriteBytes implements Storage.WriteBytes()
-func (st *DiskStorage) WriteBytes(key string, value []byte) error {
- return st.WriteStream(key, bytes.NewReader(value))
-}
-
-// WriteStream implements Storage.WriteStream()
-func (st *DiskStorage) WriteStream(key string, r io.Reader) error {
- // Get file path for key
- kpath, err := st.filepath(key)
- if err != nil {
- return err
- }
-
- // Track open
- st.lock.Add()
- defer st.lock.Done()
-
- // Check if open
- if st.lock.Closed() {
- return ErrClosed
- }
-
- // Ensure dirs leading up to file exist
- err = os.MkdirAll(path.Dir(kpath), defaultDirPerms)
- if err != nil {
- return err
- }
-
- // Prepare to swap error if need-be
- errSwap := errSwapNoop
-
- // Build file RW flags
- flags := defaultFileRWFlags
- if !st.config.Overwrite {
- flags |= syscall.O_EXCL
-
- // Catch + replace err exist
- errSwap = errSwapExist
- }
-
- // Attempt to open file
- file, err := open(kpath, flags)
- if err != nil {
- return errSwap(err)
- }
- defer file.Close()
-
- // Wrap the file in a compressor
- cFile, err := st.config.Compression.Writer(file)
- if err != nil {
- return err
- }
- defer cFile.Close()
-
- // Copy provided reader to file
- _, err = st.cppool.Copy(cFile, r)
- return err
-}
-
-// Stat implements Storage.Stat()
-func (st *DiskStorage) Stat(key string) (bool, error) {
- // Get file path for key
- kpath, err := st.filepath(key)
- if err != nil {
- return false, err
- }
-
- // Track open
- st.lock.Add()
- defer st.lock.Done()
-
- // Check if open
- if st.lock.Closed() {
- return false, ErrClosed
- }
-
- // Check for file on disk
- return stat(kpath)
-}
-
-// Remove implements Storage.Remove()
-func (st *DiskStorage) Remove(key string) error {
- // Get file path for key
- kpath, err := st.filepath(key)
- if err != nil {
- return err
- }
-
- // Track open
- st.lock.Add()
- defer st.lock.Done()
-
- // Check if open
- if st.lock.Closed() {
- return ErrClosed
- }
-
- // Remove at path (we know this is file)
- if err := unlink(kpath); err != nil {
- return errSwapNotFound(err)
- }
-
- return nil
-}
-
-// Close implements Storage.Close()
-func (st *DiskStorage) Close() error {
- return st.lock.Close()
-}
-
-// WalkKeys implements Storage.WalkKeys()
-func (st *DiskStorage) WalkKeys(opts WalkKeysOptions) error {
- // Track open
- st.lock.Add()
- defer st.lock.Done()
-
- // Check if open
- if st.lock.Closed() {
- return ErrClosed
- }
-
- // Acquire path builder
- pb := util.GetPathBuilder()
- defer util.PutPathBuilder(pb)
-
- // Walk dir for entries
- return util.WalkDir(pb, st.path, func(kpath string, fsentry fs.DirEntry) {
- if fsentry.Type().IsRegular() {
- // Only deal with regular files
-
- // Get full item path (without root)
- kpath = pb.Join(kpath, fsentry.Name())[len(st.path):]
-
- // Perform provided walk function
- opts.WalkFn(entry(st.config.Transform.PathToKey(kpath)))
- }
- })
-}
-
-// filepath checks and returns a formatted filepath for given key
-func (st *DiskStorage) filepath(key string) (string, error) {
- // Calculate transformed key path
- key = st.config.Transform.KeyToPath(key)
-
- // Acquire path builder
- pb := util.GetPathBuilder()
- defer util.PutPathBuilder(pb)
-
- // Generated joined root path
- pb.AppendString(st.path)
- pb.AppendString(key)
-
- // Check for dir traversal outside of root
- if isDirTraversal(st.path, pb.StringPtr()) {
- return "", ErrInvalidKey
- }
-
- return pb.String(), nil
-}
-
-// isDirTraversal will check if rootPlusPath is a dir traversal outside of root,
-// assuming that both are cleaned and that rootPlusPath is path.Join(root, somePath)
-func isDirTraversal(root, rootPlusPath string) bool {
- switch {
- // Root is $PWD, check for traversal out of
- case root == ".":
- return strings.HasPrefix(rootPlusPath, "../")
-
- // The path MUST be prefixed by root
- case !strings.HasPrefix(rootPlusPath, root):
- return true
-
- // In all other cases, check not equal
- default:
- return len(root) == len(rootPlusPath)
- }
-}