erigon-pulse/common/dir/rw_dir.go
2022-08-03 15:07:36 +07:00

74 lines
1.7 KiB
Go

package dir
import (
"errors"
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/gofrs/flock"
"github.com/ledgerwatch/log/v3"
)
// Rw - type to represent Read-Write access to directory
// if some code accept this typ - it can be sure: dir exists, no other process now write there
// We have no specific Ro type, just use `string` for Ro directory path
type Rw struct {
dirLock *flock.Flock // prevents concurrent use of instance directory
Path string
}
func convertFileLockError(err error, dir string) error {
if errno, ok := err.(syscall.Errno); ok && dirInUseErrnos[uint(errno)] { //nolint
return fmt.Errorf("%w: %s", ErrDirUsed, dir)
}
return err
}
var dirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
var (
ErrDirUsed = errors.New("datadir already used by another process")
)
func OpenRw(dir string) (*Rw, error) {
MustExist(dir)
// Lock the instance directory to prevent concurrent use by another instance as well as
// accidental use of the instance directory as a database.
l := flock.New(filepath.Join(dir, "LOCK"))
locked, err := l.TryLock()
if err != nil {
return nil, convertFileLockError(err, dir)
}
if !locked {
return nil, fmt.Errorf("%w: %s", ErrDirUsed, dir)
}
return &Rw{dirLock: l, Path: dir}, nil
}
func (t *Rw) Close() {
// Release instance directory lock.
if t.dirLock != nil {
if err := t.dirLock.Unlock(); err != nil {
log.Error("Can't release snapshot dir lock", "err", err)
}
t.dirLock = nil
}
}
func MustExist(path string) {
const perm = 0764 // user rwx, group rw, other r
if err := os.MkdirAll(path, perm); err != nil {
panic(err)
}
}
func Exist(path string) bool {
_, err := os.Stat(path)
if err != nil && os.IsNotExist(err) {
return false
}
return true
}