2015-07-18 21:19:50 +00:00
|
|
|
// +build !openbsd
|
2015-08-14 13:33:11 +00:00
|
|
|
// +build !windows
|
2015-07-18 21:19:50 +00:00
|
|
|
|
2015-04-07 19:10:53 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-06-04 09:16:55 +00:00
|
|
|
"context"
|
2015-04-07 19:10:53 +00:00
|
|
|
"os"
|
2017-07-09 07:47:41 +00:00
|
|
|
"restic"
|
2016-08-21 15:48:36 +00:00
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2016-09-15 19:17:20 +00:00
|
|
|
"restic/debug"
|
2016-09-01 20:17:37 +00:00
|
|
|
"restic/errors"
|
2016-08-29 17:18:57 +00:00
|
|
|
|
Fix 567 (#570)
* Patch for https://github.com/restic/restic/issues/567
Backup also files on windows with longer pathnames than 255 chars (e.g. from node).
as fd0 says "So, as far as I can see, we need to have custom methods for all functions that accept a path, so that on Windows we can substitute the normal (possibly relative) path used within restic by an (absolute) UNC path, and only then call the underlying functions like os.Stat(), os.Lstat(), os.Open() and so on.
I've already thought about adding a generic abstraction for the file system (so we can mock this easier in tests), and this looks like a good opportunity to build it."
* fixed building tests
* Restructured patches
Add Wrapper for filepath.Walk
* using \\?\ requires absolute pathes to be used.
Now all tests run
* used gofmt on the code
* Restructured Code. No patches dir, integrate the file functions into restic/fs/
There is still an issue, because restic.fs.Open has a different api the os.Open, which returns the result of OpenFile, but takes only a string
* Changed the last os.Open() calls to fs.Open() after extending the File interface
* fixed name-clash of restic.fs and fuse.fs detected by travis
* fixed fmt with gofmt
* c&p failure: removed fixpath() call.
* missing include
* fixed includes in linux variant
* Fix for Linux. Fd() is required on File interface
* done gofmt
2016-08-15 19:59:13 +00:00
|
|
|
resticfs "restic/fs"
|
2016-02-14 14:29:28 +00:00
|
|
|
"restic/fuse"
|
2015-04-07 19:10:53 +00:00
|
|
|
|
2015-07-19 12:28:11 +00:00
|
|
|
systemFuse "bazil.org/fuse"
|
2015-04-07 19:10:53 +00:00
|
|
|
"bazil.org/fuse/fs"
|
|
|
|
)
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
var cmdMount = &cobra.Command{
|
|
|
|
Use: "mount [flags] mountpoint",
|
|
|
|
Short: "mount the repository",
|
|
|
|
Long: `
|
|
|
|
The "mount" command mounts the repository via fuse to a directory. This is a
|
|
|
|
read-only mount.
|
|
|
|
`,
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
return runMount(mountOptions, globalOptions, args)
|
|
|
|
},
|
|
|
|
}
|
2015-07-26 18:41:29 +00:00
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
// MountOptions collects all options for the mount command.
|
|
|
|
type MountOptions struct {
|
2017-02-10 20:56:25 +00:00
|
|
|
OwnerRoot bool
|
|
|
|
AllowRoot bool
|
|
|
|
AllowOther bool
|
2017-03-08 18:59:19 +00:00
|
|
|
Host string
|
|
|
|
Tags []string
|
|
|
|
Paths []string
|
2015-04-07 19:10:53 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
var mountOptions MountOptions
|
|
|
|
|
2015-04-07 19:10:53 +00:00
|
|
|
func init() {
|
2016-09-17 10:36:05 +00:00
|
|
|
cmdRoot.AddCommand(cmdMount)
|
2015-04-07 19:10:53 +00:00
|
|
|
|
2017-03-08 18:59:19 +00:00
|
|
|
mountFlags := cmdMount.Flags()
|
|
|
|
mountFlags.BoolVar(&mountOptions.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs")
|
|
|
|
mountFlags.BoolVar(&mountOptions.AllowRoot, "allow-root", false, "allow root user to access the data in the mounted directory")
|
|
|
|
mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory")
|
|
|
|
|
|
|
|
mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
|
2017-07-07 01:19:06 +00:00
|
|
|
mountFlags.StringArrayVar(&mountOptions.Tags, "tag", nil, "only consider snapshots which include this `tag`")
|
|
|
|
mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`")
|
2015-04-07 19:10:53 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
|
2016-09-27 20:35:08 +00:00
|
|
|
debug.Log("start mount")
|
|
|
|
defer debug.Log("finish mount")
|
2015-04-07 19:10:53 +00:00
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
repo, err := OpenRepository(gopts)
|
2015-04-07 19:10:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-04 09:16:55 +00:00
|
|
|
err = repo.LoadIndex(context.TODO())
|
2015-04-07 19:10:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-08-29 17:18:57 +00:00
|
|
|
if _, err := resticfs.Stat(mountpoint); os.IsNotExist(errors.Cause(err)) {
|
2016-09-17 10:36:05 +00:00
|
|
|
Verbosef("Mountpoint %s doesn't exist, creating it\n", mountpoint)
|
Fix 567 (#570)
* Patch for https://github.com/restic/restic/issues/567
Backup also files on windows with longer pathnames than 255 chars (e.g. from node).
as fd0 says "So, as far as I can see, we need to have custom methods for all functions that accept a path, so that on Windows we can substitute the normal (possibly relative) path used within restic by an (absolute) UNC path, and only then call the underlying functions like os.Stat(), os.Lstat(), os.Open() and so on.
I've already thought about adding a generic abstraction for the file system (so we can mock this easier in tests), and this looks like a good opportunity to build it."
* fixed building tests
* Restructured patches
Add Wrapper for filepath.Walk
* using \\?\ requires absolute pathes to be used.
Now all tests run
* used gofmt on the code
* Restructured Code. No patches dir, integrate the file functions into restic/fs/
There is still an issue, because restic.fs.Open has a different api the os.Open, which returns the result of OpenFile, but takes only a string
* Changed the last os.Open() calls to fs.Open() after extending the File interface
* fixed name-clash of restic.fs and fuse.fs detected by travis
* fixed fmt with gofmt
* c&p failure: removed fixpath() call.
* missing include
* fixed includes in linux variant
* Fix for Linux. Fd() is required on File interface
* done gofmt
2016-08-15 19:59:13 +00:00
|
|
|
err = resticfs.Mkdir(mountpoint, os.ModeDir|0700)
|
2015-07-19 12:08:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-04-07 19:10:53 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 20:56:25 +00:00
|
|
|
|
|
|
|
mountOptions := []systemFuse.MountOption{
|
2015-07-19 12:28:11 +00:00
|
|
|
systemFuse.ReadOnly(),
|
|
|
|
systemFuse.FSName("restic"),
|
2017-02-10 20:56:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if opts.AllowRoot {
|
|
|
|
mountOptions = append(mountOptions, systemFuse.AllowRoot())
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.AllowOther {
|
|
|
|
mountOptions = append(mountOptions, systemFuse.AllowOther())
|
|
|
|
}
|
|
|
|
|
|
|
|
c, err := systemFuse.Mount(mountpoint, mountOptions...)
|
2015-04-07 19:10:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-18 12:59:44 +00:00
|
|
|
systemFuse.Debug = func(msg interface{}) {
|
|
|
|
debug.Log("fuse: %v", msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg := fuse.Config{
|
|
|
|
OwnerIsRoot: opts.OwnerRoot,
|
|
|
|
Host: opts.Host,
|
2017-07-09 07:47:41 +00:00
|
|
|
Tags: restic.SplitTagLists(opts.Tags),
|
2017-06-18 12:59:44 +00:00
|
|
|
Paths: opts.Paths,
|
|
|
|
}
|
|
|
|
root, err := fuse.NewRoot(context.TODO(), repo, cfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
Printf("Now serving the repository at %s\n", mountpoint)
|
|
|
|
Printf("Don't forget to umount after quitting!\n")
|
|
|
|
|
2016-09-27 20:35:08 +00:00
|
|
|
debug.Log("serving mount at %v", mountpoint)
|
2017-06-18 12:59:44 +00:00
|
|
|
err = fs.Serve(c, root)
|
2016-09-15 19:17:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
<-c.Ready
|
|
|
|
return c.MountError
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
func umount(mountpoint string) error {
|
2016-09-15 19:17:20 +00:00
|
|
|
return systemFuse.Unmount(mountpoint)
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
|
2016-09-15 19:17:20 +00:00
|
|
|
if len(args) == 0 {
|
2017-02-10 18:39:49 +00:00
|
|
|
return errors.Fatal("wrong number of parameters")
|
2016-09-15 19:17:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mountpoint := args[0]
|
2015-04-07 19:10:53 +00:00
|
|
|
|
2015-07-19 21:02:02 +00:00
|
|
|
AddCleanupHandler(func() error {
|
2016-09-27 20:35:08 +00:00
|
|
|
debug.Log("running umount cleanup handler for mount at %v", mountpoint)
|
2016-09-17 10:36:05 +00:00
|
|
|
err := umount(mountpoint)
|
2015-07-19 21:02:48 +00:00
|
|
|
if err != nil {
|
2016-09-17 10:36:05 +00:00
|
|
|
Warnf("unable to umount (maybe already umounted?): %v\n", err)
|
2015-07-19 21:02:48 +00:00
|
|
|
}
|
2016-09-15 17:59:07 +00:00
|
|
|
return nil
|
|
|
|
})
|
2015-07-19 21:02:48 +00:00
|
|
|
|
2016-09-17 10:36:05 +00:00
|
|
|
return mount(opts, gopts, mountpoint)
|
2015-04-07 19:10:53 +00:00
|
|
|
}
|