84 lines
2.6 KiB
Go
84 lines
2.6 KiB
Go
|
// Package filestore provide a storage backend based on the local file system.
|
||
|
//
|
||
|
// FileStore is a storage backend used as a handler.DataStore in handler.NewHandler.
|
||
|
// It stores the uploads in a directory specified in two different files: The
|
||
|
// `[id].info` files are used to store the fileinfo in JSON format. The
|
||
|
// `[id]` files without an extension contain the raw binary data uploaded.
|
||
|
// No cleanup is performed so you may want to run a cronjob to ensure your disk
|
||
|
// is not filled up with old and finished uploads.
|
||
|
//
|
||
|
// In addition, it provides an exclusive upload locking mechanism using lock files
|
||
|
// which are stored on disk. Each of them stores the PID of the process which
|
||
|
// acquired the lock. This allows locks to be automatically freed when a process
|
||
|
// is unable to release it on its own because the process is not alive anymore.
|
||
|
// For more information, consult the documentation for handler.LockerDataStore
|
||
|
// interface, which is implemented by FileStore
|
||
|
package filestore
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
|
||
|
"github.com/tus/tusd/pkg/handler"
|
||
|
|
||
|
"gopkg.in/Acconut/lockfile.v1"
|
||
|
)
|
||
|
|
||
|
var defaultFilePerm = os.FileMode(0664)
|
||
|
|
||
|
// See the handler.DataStore interface for documentation about the different
|
||
|
// methods.
|
||
|
type FileLocker struct {
|
||
|
// Relative or absolute path to store files in. FileStore does not check
|
||
|
// whether the path exists, use os.MkdirAll in this case on your own.
|
||
|
Path string
|
||
|
}
|
||
|
|
||
|
// New creates a new file based storage backend. The directory specified will
|
||
|
// be used as the only storage entry. This method does not check
|
||
|
// whether the path exists, use os.MkdirAll to ensure.
|
||
|
// In addition, a locking mechanism is provided.
|
||
|
func New(path string) FileLocker {
|
||
|
return FileLocker{path}
|
||
|
}
|
||
|
|
||
|
func (locker FileLocker) NewLock(id string) (handler.UploadLock, error) {
|
||
|
path, err := filepath.Abs(filepath.Join(locker.Path, id+".lock"))
|
||
|
if err != nil {
|
||
|
return lockfile.Lockfile(""), err
|
||
|
}
|
||
|
|
||
|
// We use Lockfile directly instead of lockfile.New to bypass the unnecessary
|
||
|
// check whether the provided path is absolute since we just resolved it
|
||
|
// on our own.
|
||
|
return fileUploadLock{
|
||
|
file: lockfile.Lockfile(path),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
type fileUploadLock struct {
|
||
|
file lockfile.Lockfile
|
||
|
}
|
||
|
|
||
|
func (lock fileUploadLock) Lock() error {
|
||
|
err = lock.file.TryLock()
|
||
|
if err == lockfile.ErrBusy {
|
||
|
return handler.ErrFileLocked
|
||
|
}
|
||
|
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (lock fileUploadLock) Unlock() error {
|
||
|
err = lock.file.Unlock()
|
||
|
|
||
|
// A "no such file or directory" will be returned if no lockfile was found.
|
||
|
// Since this means that the file has never been locked, we drop the error
|
||
|
// and continue as if nothing happened.
|
||
|
if os.IsNotExist(err) {
|
||
|
err = nil
|
||
|
}
|
||
|
|
||
|
return err
|
||
|
}
|