restic/cmd/restic/cmd_mount.go

187 lines
5.0 KiB
Go
Raw Normal View History

// +build darwin freebsd linux
2015-04-07 21:10:53 +02:00
package main
import (
"os"
"strings"
"time"
2017-07-23 14:21:03 +02:00
2016-09-17 12:36:05 +02:00
"github.com/spf13/cobra"
2017-07-23 14:21:03 +02:00
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors"
2017-07-24 17:42:25 +02:00
"github.com/restic/restic/internal/restic"
2017-07-23 14:21:03 +02:00
resticfs "github.com/restic/restic/internal/fs"
"github.com/restic/restic/internal/fuse"
2015-04-07 21:10:53 +02:00
2015-07-19 14:28:11 +02:00
systemFuse "bazil.org/fuse"
2015-04-07 21:10:53 +02:00
"bazil.org/fuse/fs"
)
2016-09-17 12:36:05 +02:00
var cmdMount = &cobra.Command{
Use: "mount [flags] mountpoint",
Short: "Mount the repository",
2016-09-17 12:36:05 +02:00
Long: `
The "mount" command mounts the repository via fuse to a directory. This is a
read-only mount.
Snapshot Directories
====================
If you need a different template for all directories that contain snapshots,
you can pass a template via --snapshot-template. Example without colons:
--snapshot-template "2006-01-02_15-04-05"
You need to specify a sample format for exactly the following timestamp:
Mon Jan 2 15:04:05 -0700 MST 2006
For details please see the documentation for time.Format() at:
https://godoc.org/time#Time.Format
EXIT STATUS
===========
Exit status is 0 if the command was successful, and non-zero if there was any error.
2016-09-17 12:36:05 +02:00
`,
DisableAutoGenTag: true,
2016-09-17 12:36:05 +02:00
RunE: func(cmd *cobra.Command, args []string) error {
return runMount(mountOptions, globalOptions, args)
},
}
2016-09-17 12:36:05 +02:00
// MountOptions collects all options for the mount command.
type MountOptions struct {
OwnerRoot bool
AllowOther bool
NoDefaultPermissions bool
Hosts []string
Tags restic.TagLists
Paths []string
SnapshotTemplate string
2015-04-07 21:10:53 +02:00
}
2016-09-17 12:36:05 +02:00
var mountOptions MountOptions
2015-04-07 21:10:53 +02:00
func init() {
2016-09-17 12:36:05 +02:00
cmdRoot.AddCommand(cmdMount)
2015-04-07 21:10:53 +02:00
2017-03-08 19:59:19 +01:00
mountFlags := cmdMount.Flags()
mountFlags.BoolVar(&mountOptions.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs")
mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory")
mountFlags.BoolVar(&mountOptions.NoDefaultPermissions, "no-default-permissions", false, "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files")
2017-03-08 19:59:19 +01:00
mountFlags.StringArrayVarP(&mountOptions.Hosts, "host", "H", nil, `only consider snapshots for this host (can be specified multiple times)`)
mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`")
mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`")
mountFlags.StringVar(&mountOptions.SnapshotTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs")
2015-04-07 21:10:53 +02:00
}
2016-09-17 12:36:05 +02:00
func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
2016-09-27 22:35:08 +02:00
debug.Log("start mount")
defer debug.Log("finish mount")
2015-04-07 21:10:53 +02:00
2016-09-17 12:36:05 +02:00
repo, err := OpenRepository(gopts)
2015-04-07 21:10:53 +02:00
if err != nil {
return err
}
lock, err := lockRepo(repo)
defer unlockRepo(lock)
if err != nil {
return err
}
err = repo.LoadIndex(gopts.ctx)
2015-04-07 21:10:53 +02:00
if err != nil {
return err
}
if _, err := resticfs.Stat(mountpoint); os.IsNotExist(errors.Cause(err)) {
2016-09-17 12:36:05 +02:00
Verbosef("Mountpoint %s doesn't exist, creating it\n", mountpoint)
err = resticfs.Mkdir(mountpoint, os.ModeDir|0700)
2015-07-19 14:08:34 +02:00
if err != nil {
return err
2015-04-07 21:10:53 +02:00
}
}
mountOptions := []systemFuse.MountOption{
2015-07-19 14:28:11 +02:00
systemFuse.ReadOnly(),
systemFuse.FSName("restic"),
}
if opts.AllowOther {
mountOptions = append(mountOptions, systemFuse.AllowOther())
// let the kernel check permissions unless it is explicitly disabled
if !opts.NoDefaultPermissions {
mountOptions = append(mountOptions, systemFuse.DefaultPermissions())
}
}
c, err := systemFuse.Mount(mountpoint, mountOptions...)
2015-04-07 21:10:53 +02:00
if err != nil {
return err
}
2017-06-18 14:59:44 +02:00
systemFuse.Debug = func(msg interface{}) {
debug.Log("fuse: %v", msg)
}
cfg := fuse.Config{
OwnerIsRoot: opts.OwnerRoot,
Hosts: opts.Hosts,
Tags: opts.Tags,
Paths: opts.Paths,
SnapshotTemplate: opts.SnapshotTemplate,
2017-06-18 14:59:44 +02:00
}
root := fuse.NewRoot(gopts.ctx, repo, cfg)
2017-06-18 14:59:44 +02:00
2016-09-17 12:36:05 +02:00
Printf("Now serving the repository at %s\n", mountpoint)
Printf("When finished, quit with Ctrl-c or umount the mountpoint.\n")
2016-09-17 12:36:05 +02:00
2016-09-27 22:35:08 +02:00
debug.Log("serving mount at %v", mountpoint)
2017-06-18 14:59:44 +02:00
err = fs.Serve(c, root)
2016-09-15 21:17:20 +02:00
if err != nil {
return err
}
<-c.Ready
return c.MountError
}
2016-09-17 12:36:05 +02:00
func umount(mountpoint string) error {
2016-09-15 21:17:20 +02:00
return systemFuse.Unmount(mountpoint)
}
2016-09-17 12:36:05 +02:00
func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
if opts.SnapshotTemplate == "" {
return errors.Fatal("snapshot template string cannot be empty")
}
if strings.ContainsAny(opts.SnapshotTemplate, `\/`) {
return errors.Fatal("snapshot template string contains a slash (/) or backslash (\\) character")
}
2016-09-15 21:17:20 +02:00
if len(args) == 0 {
return errors.Fatal("wrong number of parameters")
2016-09-15 21:17:20 +02:00
}
mountpoint := args[0]
2015-04-07 21:10:53 +02:00
AddCleanupHandler(func() error {
2016-09-27 22:35:08 +02:00
debug.Log("running umount cleanup handler for mount at %v", mountpoint)
2016-09-17 12:36:05 +02:00
err := umount(mountpoint)
if err != nil {
Warnf("unable to umount (maybe already umounted or still in use?): %v\n", err)
}
2016-09-15 19:59:07 +02:00
return nil
})
2016-09-17 12:36:05 +02:00
return mount(opts, gopts, mountpoint)
2015-04-07 21:10:53 +02:00
}