mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-06-02 04:30:21 +00:00
fe39d50e09
Bumps codeberg.org/gruf/go-store/v2 from 2.0.9 to 2.0.10. --- updated-dependencies: - dependency-name: codeberg.org/gruf/go-store/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
222 lines
4.5 KiB
Go
222 lines
4.5 KiB
Go
package storage
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"syscall"
|
|
|
|
"codeberg.org/gruf/go-fastpath/v2"
|
|
"codeberg.org/gruf/go-store/v2/util"
|
|
)
|
|
|
|
const (
|
|
// default file permission bits
|
|
defaultDirPerms = 0o755
|
|
defaultFilePerms = 0o644
|
|
|
|
// default file open flags
|
|
defaultFileROFlags = syscall.O_RDONLY
|
|
defaultFileRWFlags = syscall.O_CREAT | syscall.O_RDWR
|
|
defaultFileLockFlags = syscall.O_RDONLY | syscall.O_CREAT
|
|
)
|
|
|
|
// NOTE:
|
|
// These functions are for opening storage files,
|
|
// not necessarily for e.g. initial setup (OpenFile)
|
|
|
|
// walkDir traverses the dir tree of the supplied path, performing the supplied walkFn on each entry
|
|
func walkDir(pb *fastpath.Builder, path string, walkFn func(string, fs.DirEntry) error) error {
|
|
// Read directory entries
|
|
entries, err := readDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// frame represents a directory entry
|
|
// walk-loop snapshot, taken when a sub
|
|
// directory requiring iteration is found
|
|
type frame struct {
|
|
path string
|
|
entries []fs.DirEntry
|
|
}
|
|
|
|
// stack contains a list of held snapshot
|
|
// frames, representing unfinished upper
|
|
// layers of a directory structure yet to
|
|
// be traversed.
|
|
var stack []frame
|
|
|
|
outer:
|
|
for {
|
|
if len(entries) == 0 {
|
|
if len(stack) == 0 {
|
|
// Reached end
|
|
break outer
|
|
}
|
|
|
|
// Pop frame from stack
|
|
frame := stack[len(stack)-1]
|
|
stack = stack[:len(stack)-1]
|
|
|
|
// Update loop vars
|
|
entries = frame.entries
|
|
path = frame.path
|
|
}
|
|
|
|
for len(entries) > 0 {
|
|
// Pop next entry from queue
|
|
entry := entries[0]
|
|
entries = entries[1:]
|
|
|
|
// Pass to provided walk function
|
|
if err := walkFn(path, entry); err != nil {
|
|
return err
|
|
}
|
|
|
|
if entry.IsDir() {
|
|
// Push current frame to stack
|
|
stack = append(stack, frame{
|
|
path: path,
|
|
entries: entries,
|
|
})
|
|
|
|
// Update current directory path
|
|
path = pb.Join(path, entry.Name())
|
|
|
|
// Read next directory entries
|
|
next, err := readDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Set next entries
|
|
entries = next
|
|
|
|
continue outer
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// cleanDirs traverses the dir tree of the supplied path, removing any folders with zero children
|
|
func cleanDirs(path string) error {
|
|
// Acquire path builder
|
|
pb := util.GetPathBuilder()
|
|
defer util.PutPathBuilder(pb)
|
|
|
|
// Get top-level dir entries
|
|
entries, err := readDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
// Recursively clean sub-directory entries
|
|
if err := cleanDir(pb, pb.Join(path, entry.Name())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// cleanDir performs the actual dir cleaning logic for the above top-level version.
|
|
func cleanDir(pb *fastpath.Builder, path string) error {
|
|
// Get dir entries
|
|
entries, err := readDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If no entries, delete
|
|
if len(entries) < 1 {
|
|
return rmdir(path)
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
// Recursively clean sub-directory entries
|
|
if err := cleanDir(pb, pb.Join(path, entry.Name())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// readDir will open file at path, read the unsorted list of entries, then close.
|
|
func readDir(path string) ([]fs.DirEntry, error) {
|
|
// Open file at path
|
|
file, err := open(path, defaultFileROFlags)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Read directory entries
|
|
entries, err := file.ReadDir(-1)
|
|
|
|
// Done with file
|
|
_ = file.Close()
|
|
|
|
return entries, err
|
|
}
|
|
|
|
// open will open a file at the given path with flags and default file perms.
|
|
func open(path string, flags int) (*os.File, error) {
|
|
var fd int
|
|
err := retryOnEINTR(func() (err error) {
|
|
fd, err = syscall.Open(path, flags, defaultFilePerms)
|
|
return
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return os.NewFile(uintptr(fd), path), nil
|
|
}
|
|
|
|
// stat checks for a file on disk.
|
|
func stat(path string) (bool, error) {
|
|
var stat syscall.Stat_t
|
|
err := retryOnEINTR(func() error {
|
|
return syscall.Stat(path, &stat)
|
|
})
|
|
if err != nil {
|
|
if err == syscall.ENOENT {
|
|
// not-found is no error
|
|
err = nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// unlink removes a file (not dir!) on disk.
|
|
func unlink(path string) error {
|
|
return retryOnEINTR(func() error {
|
|
return syscall.Unlink(path)
|
|
})
|
|
}
|
|
|
|
// rmdir removes a dir (not file!) on disk.
|
|
func rmdir(path string) error {
|
|
return retryOnEINTR(func() error {
|
|
return syscall.Rmdir(path)
|
|
})
|
|
}
|
|
|
|
// retryOnEINTR is a low-level filesystem function for retrying syscalls on O_EINTR received.
|
|
func retryOnEINTR(do func() error) error {
|
|
for {
|
|
err := do()
|
|
if err == syscall.EINTR {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
}
|