github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/cmd/restic/cmd_mount.go (about)

     1  // +build !openbsd
     2  // +build !windows
     3  
     4  package main
     5  
     6  import (
     7  	"os"
     8  
     9  	"github.com/spf13/cobra"
    10  
    11  	"github.com/restic/restic/internal/debug"
    12  	"github.com/restic/restic/internal/errors"
    13  	"github.com/restic/restic/internal/restic"
    14  
    15  	resticfs "github.com/restic/restic/internal/fs"
    16  	"github.com/restic/restic/internal/fuse"
    17  
    18  	systemFuse "bazil.org/fuse"
    19  	"bazil.org/fuse/fs"
    20  )
    21  
    22  var cmdMount = &cobra.Command{
    23  	Use:   "mount [flags] mountpoint",
    24  	Short: "Mount the repository",
    25  	Long: `
    26  The "mount" command mounts the repository via fuse to a directory. This is a
    27  read-only mount.
    28  `,
    29  	DisableAutoGenTag: true,
    30  	RunE: func(cmd *cobra.Command, args []string) error {
    31  		return runMount(mountOptions, globalOptions, args)
    32  	},
    33  }
    34  
    35  // MountOptions collects all options for the mount command.
    36  type MountOptions struct {
    37  	OwnerRoot  bool
    38  	AllowRoot  bool
    39  	AllowOther bool
    40  	Host       string
    41  	Tags       restic.TagLists
    42  	Paths      []string
    43  }
    44  
    45  var mountOptions MountOptions
    46  
    47  func init() {
    48  	cmdRoot.AddCommand(cmdMount)
    49  
    50  	mountFlags := cmdMount.Flags()
    51  	mountFlags.BoolVar(&mountOptions.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs")
    52  	mountFlags.BoolVar(&mountOptions.AllowRoot, "allow-root", false, "allow root user to access the data in the mounted directory")
    53  	mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory")
    54  
    55  	mountFlags.StringVarP(&mountOptions.Host, "host", "H", "", `only consider snapshots for this host`)
    56  	mountFlags.Var(&mountOptions.Tags, "tag", "only consider snapshots which include this `taglist`")
    57  	mountFlags.StringArrayVar(&mountOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`")
    58  }
    59  
    60  func mount(opts MountOptions, gopts GlobalOptions, mountpoint string) error {
    61  	debug.Log("start mount")
    62  	defer debug.Log("finish mount")
    63  
    64  	repo, err := OpenRepository(gopts)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	lock, err := lockRepo(repo)
    70  	defer unlockRepo(lock)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	err = repo.LoadIndex(gopts.ctx)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	if _, err := resticfs.Stat(mountpoint); os.IsNotExist(errors.Cause(err)) {
    81  		Verbosef("Mountpoint %s doesn't exist, creating it\n", mountpoint)
    82  		err = resticfs.Mkdir(mountpoint, os.ModeDir|0700)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  
    88  	mountOptions := []systemFuse.MountOption{
    89  		systemFuse.ReadOnly(),
    90  		systemFuse.FSName("restic"),
    91  	}
    92  
    93  	if opts.AllowRoot {
    94  		mountOptions = append(mountOptions, systemFuse.AllowRoot())
    95  	}
    96  
    97  	if opts.AllowOther {
    98  		mountOptions = append(mountOptions, systemFuse.AllowOther())
    99  	}
   100  
   101  	c, err := systemFuse.Mount(mountpoint, mountOptions...)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	systemFuse.Debug = func(msg interface{}) {
   107  		debug.Log("fuse: %v", msg)
   108  	}
   109  
   110  	cfg := fuse.Config{
   111  		OwnerIsRoot: opts.OwnerRoot,
   112  		Host:        opts.Host,
   113  		Tags:        opts.Tags,
   114  		Paths:       opts.Paths,
   115  	}
   116  	root, err := fuse.NewRoot(gopts.ctx, repo, cfg)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	Printf("Now serving the repository at %s\n", mountpoint)
   122  	Printf("Don't forget to umount after quitting!\n")
   123  
   124  	debug.Log("serving mount at %v", mountpoint)
   125  	err = fs.Serve(c, root)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	<-c.Ready
   131  	return c.MountError
   132  }
   133  
   134  func umount(mountpoint string) error {
   135  	return systemFuse.Unmount(mountpoint)
   136  }
   137  
   138  func runMount(opts MountOptions, gopts GlobalOptions, args []string) error {
   139  	if len(args) == 0 {
   140  		return errors.Fatal("wrong number of parameters")
   141  	}
   142  
   143  	mountpoint := args[0]
   144  
   145  	AddCleanupHandler(func() error {
   146  		debug.Log("running umount cleanup handler for mount at %v", mountpoint)
   147  		err := umount(mountpoint)
   148  		if err != nil {
   149  			Warnf("unable to umount (maybe already umounted?): %v\n", err)
   150  		}
   151  		return nil
   152  	})
   153  
   154  	return mount(opts, gopts, mountpoint)
   155  }