2018-06-16 15:48:45 +09:00
// +build !netbsd
2015-07-18 23:19:50 +02:00
// +build !openbsd
2018-03-04 13:30:06 +00:00
// +build !solaris
2015-08-14 15:33:11 +02:00
// +build !windows
2015-07-18 23:19:50 +02:00
2015-04-07 21:10:53 +02:00
package main
import (
"os"
2018-01-14 14:22:08 +01:00
"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"
2016-08-29 19:18:57 +02:00
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" ,
2017-09-11 09:32:44 -07:00
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 .
2018-01-14 14:22:08 +01:00
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
2019-11-04 22:03:38 -08:00
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
` ,
2017-08-06 21:02:16 +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 )
} ,
}
2015-07-26 20:41:29 +02:00
2016-09-17 12:36:05 +02:00
// MountOptions collects all options for the mount command.
type MountOptions struct {
2018-11-26 21:06:47 -08:00
OwnerRoot bool
AllowOther bool
NoDefaultPermissions bool
2020-02-26 22:17:59 +01:00
Hosts [ ] string
2018-11-26 21:06:47 -08:00
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" )
2018-11-26 21:06:47 -08:00
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
2020-02-26 22:17:59 +01:00
mountFlags . StringArrayVarP ( & mountOptions . Hosts , "host" , "H" , nil , ` only consider snapshots for this host (can be specified multiple times) ` )
2017-07-09 12:45:49 +02:00
mountFlags . Var ( & mountOptions . Tags , "tag" , "only consider snapshots which include this `taglist`" )
2017-07-07 03:19:06 +02:00
mountFlags . StringArrayVar ( & mountOptions . Paths , "path" , nil , "only consider snapshots which include this (absolute) `path`" )
2018-01-14 14:22:08 +01:00
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
}
2017-10-05 14:55:55 +02:00
lock , err := lockRepo ( repo )
defer unlockRepo ( lock )
if err != nil {
return err
}
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 07:02:55 -05:00
err = repo . LoadIndex ( gopts . ctx )
2015-04-07 21:10:53 +02:00
if err != nil {
return err
}
2016-08-29 19:18:57 +02:00
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 )
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 21:59:13 +02:00
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
}
}
2017-02-10 21:56:25 +01:00
mountOptions := [ ] systemFuse . MountOption {
2015-07-19 14:28:11 +02:00
systemFuse . ReadOnly ( ) ,
systemFuse . FSName ( "restic" ) ,
2017-02-10 21:56:25 +01:00
}
if opts . AllowOther {
mountOptions = append ( mountOptions , systemFuse . AllowOther ( ) )
2019-01-06 20:55:49 +01:00
// let the kernel check permissions unless it is explicitly disabled
if ! opts . NoDefaultPermissions {
mountOptions = append ( mountOptions , systemFuse . DefaultPermissions ( ) )
}
2018-11-26 21:06:47 -08:00
}
2018-10-02 21:34:28 -07:00
2017-02-10 21:56:25 +01:00
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 {
2018-01-14 14:22:08 +01:00
OwnerIsRoot : opts . OwnerRoot ,
2020-02-26 22:17:59 +01:00
Hosts : opts . Hosts ,
2018-01-14 14:22:08 +01:00
Tags : opts . Tags ,
Paths : opts . Paths ,
SnapshotTemplate : opts . SnapshotTemplate ,
2017-06-18 14:59:44 +02:00
}
use global context for check, debug, dump, find, forget, init, key,
list, mount, tag, unlock commands
gh-1434
2017-12-06 07:02:55 -05:00
root , err := fuse . NewRoot ( gopts . ctx , repo , cfg )
2017-06-18 14:59:44 +02:00
if err != nil {
return err
}
2016-09-17 12:36:05 +02:00
Printf ( "Now serving the repository at %s\n" , mountpoint )
2019-01-28 16:53:35 -05:00
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 {
2018-01-14 14:22:08 +01:00
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 {
2017-02-10 19:39:49 +01:00
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
2015-07-19 23:02:02 +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 )
2015-07-19 23:02:48 +02:00
if err != nil {
2016-09-17 12:36:05 +02:00
Warnf ( "unable to umount (maybe already umounted?): %v\n" , err )
2015-07-19 23:02:48 +02:00
}
2016-09-15 19:59:07 +02:00
return nil
} )
2015-07-19 23:02:48 +02:00
2016-09-17 12:36:05 +02:00
return mount ( opts , gopts , mountpoint )
2015-04-07 21:10:53 +02:00
}