mirror of
https://github.com/restic/rest-server.git
synced 2025-10-19 15:43:21 +00:00
Copy errors & fs packages from the restic repo and fix import paths
Tedious, but I see no better way...
This commit is contained in:
parent
80196e6df6
commit
93d8c2beba
11 changed files with 328 additions and 2 deletions
2
errors/doc.go
Normal file
2
errors/doc.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package errors provides custom error types used within restic.
|
||||
package errors
|
38
errors/fatal.go
Normal file
38
errors/fatal.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// fatalError is an error that should be printed to the user, then the program
|
||||
// should exit with an error code.
|
||||
type fatalError string
|
||||
|
||||
func (e fatalError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e fatalError) Fatal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Fataler is an error which should be printed to the user directly.
|
||||
// Afterwards, the program should exit with an error.
|
||||
type Fataler interface {
|
||||
Fatal() bool
|
||||
}
|
||||
|
||||
// IsFatal returns true if err is a fatal message that should be printed to the
|
||||
// user. Then, the program should exit.
|
||||
func IsFatal(err error) bool {
|
||||
e, ok := err.(Fataler)
|
||||
return ok && e.Fatal()
|
||||
}
|
||||
|
||||
// Fatal returns a wrapped error which implements the Fataler interface.
|
||||
func Fatal(s string) error {
|
||||
return Wrap(fatalError(s), "Fatal")
|
||||
}
|
||||
|
||||
// Fatalf returns an error which implements the Fataler interface.
|
||||
func Fatalf(s string, data ...interface{}) error {
|
||||
return fatalError(fmt.Sprintf(s, data...))
|
||||
}
|
20
errors/wrap.go
Normal file
20
errors/wrap.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package errors
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// Cause returns the cause of an error.
|
||||
func Cause(err error) error {
|
||||
return errors.Cause(err)
|
||||
}
|
||||
|
||||
// New creates a new error based on message. Wrapped so that this package does
|
||||
// not appear in the stack trace.
|
||||
var New = errors.New
|
||||
|
||||
// Errorf creates an error based on a format string and values. Wrapped so that
|
||||
// this package does not appear in the stack trace.
|
||||
var Errorf = errors.Errorf
|
||||
|
||||
// Wrap wraps an error retrieved from outside of restic. Wrapped so that this
|
||||
// package does not appear in the stack trace.
|
||||
var Wrap = errors.Wrap
|
30
fs/deviceid_unix.go
Normal file
30
fs/deviceid_unix.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// +build !windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/zcalusic/restic-server/errors"
|
||||
)
|
||||
|
||||
// DeviceID extracts the device ID from an os.FileInfo object by casting it
|
||||
// to syscall.Stat_t
|
||||
func DeviceID(fi os.FileInfo) (deviceID uint64, err error) {
|
||||
if fi == nil {
|
||||
return 0, errors.New("unable to determine device: fi is nil")
|
||||
}
|
||||
|
||||
if fi.Sys() == nil {
|
||||
return 0, errors.New("unable to determine device: fi.Sys() is nil")
|
||||
}
|
||||
|
||||
if st, ok := fi.Sys().(*syscall.Stat_t); ok {
|
||||
// st.Dev is uint32 on Darwin and uint64 on Linux. Just cast
|
||||
// everything to uint64.
|
||||
return uint64(st.Dev), nil
|
||||
}
|
||||
|
||||
return 0, errors.New("Could not cast to syscall.Stat_t")
|
||||
}
|
15
fs/deviceid_windows.go
Normal file
15
fs/deviceid_windows.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// +build windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/zcalusic/restic-server/errors"
|
||||
)
|
||||
|
||||
// DeviceID extracts the device ID from an os.FileInfo object by casting it
|
||||
// to syscall.Stat_t
|
||||
func DeviceID(fi os.FileInfo) (deviceID uint64, err error) {
|
||||
return 0, errors.New("Device IDs are not supported on Windows")
|
||||
}
|
3
fs/doc.go
Normal file
3
fs/doc.go
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Package fs implements an OS independend abstraction of a file system
|
||||
// suitable for backup purposes.
|
||||
package fs
|
145
fs/file.go
Normal file
145
fs/file.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// File is an open file on a file system.
|
||||
type File interface {
|
||||
io.Reader
|
||||
io.Writer
|
||||
io.Closer
|
||||
|
||||
Fd() uintptr
|
||||
Readdirnames(n int) ([]string, error)
|
||||
Readdir(int) ([]os.FileInfo, error)
|
||||
Seek(int64, int) (int64, error)
|
||||
Stat() (os.FileInfo, error)
|
||||
}
|
||||
|
||||
// fixpath returns an absolute path on windows, so restic can open long file
|
||||
// names.
|
||||
func fixpath(name string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
abspath, err := filepath.Abs(name)
|
||||
if err == nil {
|
||||
// Check if \\?\UNC\ already exist
|
||||
if strings.HasPrefix(abspath, `\\?\UNC\`) {
|
||||
return abspath
|
||||
}
|
||||
// Check if \\?\ already exist
|
||||
if strings.HasPrefix(abspath, `\\?\`) {
|
||||
return abspath
|
||||
}
|
||||
// Check if path starts with \\
|
||||
if strings.HasPrefix(abspath, `\\`) {
|
||||
return strings.Replace(abspath, `\\`, `\\?\UNC\`, 1)
|
||||
}
|
||||
// Normal path
|
||||
return `\\?\` + abspath
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// Chmod changes the mode of the named file to mode.
|
||||
func Chmod(name string, mode os.FileMode) error {
|
||||
return os.Chmod(fixpath(name), mode)
|
||||
}
|
||||
|
||||
// Mkdir creates a new directory with the specified name and permission bits.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Mkdir(name string, perm os.FileMode) error {
|
||||
return os.Mkdir(fixpath(name), perm)
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory named path,
|
||||
// along with any necessary parents, and returns nil,
|
||||
// or else returns an error.
|
||||
// The permission bits perm are used for all
|
||||
// directories that MkdirAll creates.
|
||||
// If path is already a directory, MkdirAll does nothing
|
||||
// and returns nil.
|
||||
func MkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(fixpath(path), perm)
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Readlink(name string) (string, error) {
|
||||
return os.Readlink(fixpath(name))
|
||||
}
|
||||
|
||||
// Remove removes the named file or directory.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Remove(name string) error {
|
||||
return os.Remove(fixpath(name))
|
||||
}
|
||||
|
||||
// RemoveAll removes path and any children it contains.
|
||||
// It removes everything it can but returns the first error
|
||||
// it encounters. If the path does not exist, RemoveAll
|
||||
// returns nil (no error).
|
||||
func RemoveAll(path string) error {
|
||||
return os.RemoveAll(fixpath(path))
|
||||
}
|
||||
|
||||
// Rename renames (moves) oldpath to newpath.
|
||||
// If newpath already exists, Rename replaces it.
|
||||
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
|
||||
// If there is an error, it will be of type *LinkError.
|
||||
func Rename(oldpath, newpath string) error {
|
||||
return os.Rename(fixpath(oldpath), fixpath(newpath))
|
||||
}
|
||||
|
||||
// Symlink creates newname as a symbolic link to oldname.
|
||||
// If there is an error, it will be of type *LinkError.
|
||||
func Symlink(oldname, newname string) error {
|
||||
return os.Symlink(fixpath(oldname), fixpath(newname))
|
||||
}
|
||||
|
||||
// Stat returns a FileInfo structure describing the named file.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Stat(name string) (os.FileInfo, error) {
|
||||
return os.Stat(fixpath(name))
|
||||
}
|
||||
|
||||
// Lstat returns the FileInfo structure describing the named file.
|
||||
// If the file is a symbolic link, the returned FileInfo
|
||||
// describes the symbolic link. Lstat makes no attempt to follow the link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Lstat(name string) (os.FileInfo, error) {
|
||||
return os.Lstat(fixpath(name))
|
||||
}
|
||||
|
||||
// Create creates the named file with mode 0666 (before umask), truncating
|
||||
// it if it already exists. If successful, methods on the returned
|
||||
// File can be used for I/O; the associated file descriptor has mode
|
||||
// O_RDWR.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Create(name string) (*os.File, error) {
|
||||
return os.Create(fixpath(name))
|
||||
}
|
||||
|
||||
// OpenFile is the generalized open call; most users will use Open
|
||||
// or Create instead. It opens the named file with specified flag
|
||||
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
|
||||
// methods on the returned File can be used for I/O.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||
return os.OpenFile(fixpath(name), flag, perm)
|
||||
}
|
||||
|
||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
||||
// directory in the tree, including root. All errors that arise visiting files
|
||||
// and directories are filtered by walkFn. The files are walked in lexical
|
||||
// order, which makes the output deterministic but means that for very
|
||||
// large directories Walk can be inefficient.
|
||||
// Walk does not follow symbolic links.
|
||||
func Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return filepath.Walk(fixpath(root), walkFn)
|
||||
}
|
58
fs/file_linux.go
Normal file
58
fs/file_linux.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// +build linux,go1.4
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/zcalusic/restic-server/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Open opens a file for reading, without updating the atime and without caching data on read.
|
||||
func Open(name string) (File, error) {
|
||||
file, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NOATIME, 0)
|
||||
if os.IsPermission(errors.Cause(err)) {
|
||||
file, err = os.OpenFile(name, os.O_RDONLY, 0)
|
||||
}
|
||||
return &nonCachingFile{File: file}, err
|
||||
}
|
||||
|
||||
// nonCachingFile wraps an *os.File and calls fadvise() to instantly forget
|
||||
// data that has been read or written.
|
||||
type nonCachingFile struct {
|
||||
*os.File
|
||||
readOffset int64
|
||||
}
|
||||
|
||||
func (f *nonCachingFile) Read(p []byte) (int, error) {
|
||||
n, err := f.File.Read(p)
|
||||
|
||||
if n > 0 {
|
||||
ferr := unix.Fadvise(int(f.File.Fd()), f.readOffset, int64(n), unix.FADV_DONTNEED)
|
||||
|
||||
f.readOffset += int64(n)
|
||||
|
||||
if err == nil {
|
||||
err = ferr
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ClearCache syncs and then removes the file's content from the OS cache.
|
||||
func ClearCache(file File) error {
|
||||
f, ok := file.(*os.File)
|
||||
if !ok {
|
||||
panic("ClearCache called for file not *os.File")
|
||||
}
|
||||
|
||||
err := f.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return unix.Fadvise(int(f.Fd()), 0, 0, unix.FADV_DONTNEED)
|
||||
}
|
15
fs/file_nonlinux.go
Normal file
15
fs/file_nonlinux.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// +build !linux !go1.4
|
||||
|
||||
package fs
|
||||
|
||||
import "os"
|
||||
|
||||
// Open opens a file for reading.
|
||||
func Open(name string) (File, error) {
|
||||
return os.OpenFile(fixpath(name), os.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// ClearCache syncs and then removes the file's content from the OS cache.
|
||||
func ClearCache(f File) error {
|
||||
return nil
|
||||
}
|
|
@ -13,7 +13,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"restic/fs"
|
||||
"github.com/zcalusic/restic-server/fs"
|
||||
)
|
||||
|
||||
// Context contains repository meta-data.
|
||||
|
|
|
@ -33,7 +33,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
|
||||
"restic/fs"
|
||||
"github.com/zcalusic/restic-server/fs"
|
||||
)
|
||||
|
||||
// lookup passwords in a htpasswd file
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue