mirror of
https://github.com/restic/rest-server.git
synced 2026-09-26 14:01:21 -07: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:
@@ -0,0 +1,2 @@
|
|||||||
|
// Package errors provides custom error types used within restic.
|
||||||
|
package errors
|
||||||
@@ -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...))
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Package fs implements an OS independend abstraction of a file system
|
||||||
|
// suitable for backup purposes.
|
||||||
|
package fs
|
||||||
+145
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+1
-1
@@ -13,7 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"restic/fs"
|
"github.com/zcalusic/restic-server/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context contains repository meta-data.
|
// Context contains repository meta-data.
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"restic/fs"
|
"github.com/zcalusic/restic-server/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// lookup passwords in a htpasswd file
|
// lookup passwords in a htpasswd file
|
||||||
|
|||||||
Reference in New Issue
Block a user