github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/cmd/restic/cmd_mount.go (about)

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