2022-03-28 22:23:47 +02:00
//go:build darwin || freebsd || linux
2020-05-12 11:30:41 +02:00
// +build darwin freebsd linux
2015-07-18 23:19:50 +02:00
2015-04-07 21:10:53 +02:00
package main
import (
"os"
2020-09-03 20:31:57 +02:00
"strings"
2018-01-14 14:22:08 +01:00
"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
== == == == == == == == == ==
2020-09-03 20:31:57 +02:00
If you need a different template for directories that contain snapshots ,
you can pass a time template via -- time - template and path templates via
-- path - template .
2018-01-14 14:22:08 +01:00
2020-09-03 20:31:57 +02:00
Example time template without colons :
-- time - template "2006-01-02_15-04-05"
2018-01-14 14:22:08 +01:00
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
2020-09-03 20:31:57 +02:00
For path templates , you can use the following patterns which will be replaced :
% i by short snapshot ID
% I by long snapshot ID
% u by username
% h by hostname
% t by tags
% T by timestamp as specified by -- time - template
The default path templates are :
"ids/%i"
"snapshots/%T"
"hosts/%h/%T"
"tags/%t/%T"
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
2020-09-03 20:31:57 +02:00
TimeTemplate string
PathTemplates [ ] 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
2020-09-03 20:31:57 +02:00
mountFlags . StringArrayVar ( & mountOptions . PathTemplates , "path-template" , nil , "set `template` for path names (can be specified multiple times)" )
mountFlags . StringVar ( & mountOptions . TimeTemplate , "snapshot-template" , time . RFC3339 , "set `template` to use for snapshot dirs" )
mountFlags . StringVar ( & mountOptions . TimeTemplate , "time-template" , time . RFC3339 , "set `template` to use for times" )
_ = mountFlags . MarkDeprecated ( "snapshot-template" , "use --time-template" )
2015-04-07 21:10:53 +02:00
}
2020-10-21 11:04:02 +02:00
func runMount ( opts MountOptions , gopts GlobalOptions , args [ ] string ) error {
2020-09-03 20:31:57 +02:00
if opts . TimeTemplate == "" {
return errors . Fatal ( "time template string cannot be empty" )
2020-10-21 11:04:02 +02:00
}
2020-09-03 20:31:57 +02:00
if strings . HasPrefix ( opts . TimeTemplate , "/" ) || strings . HasSuffix ( opts . TimeTemplate , "/" ) {
return errors . Fatal ( "time template string cannot start or end with '/'" )
}
2020-10-21 11:04:02 +02:00
if len ( args ) == 0 {
return errors . Fatal ( "wrong number of parameters" )
}
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
}
2020-07-06 02:26:21 +02:00
if ! gopts . NoLock {
2020-08-09 13:24:47 +02:00
lock , err := lockRepo ( gopts . ctx , repo )
2020-07-06 02:26:21 +02:00
defer unlockRepo ( lock )
if err != nil {
return err
}
2017-10-05 14:55:55 +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
err = repo . LoadIndex ( gopts . ctx )
2015-04-07 21:10:53 +02:00
if err != nil {
return err
}
2020-10-21 11:04:02 +02:00
mountpoint := args [ 0 ]
2022-06-13 20:35:37 +02:00
if _ , err := resticfs . Stat ( mountpoint ) ; errors . Is ( err , os . ErrNotExist ) {
2020-10-11 11:39:35 +02:00
Verbosef ( "Mountpoint %s doesn't exist\n" , mountpoint )
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" ) ,
2021-06-12 17:01:51 +02:00
systemFuse . MaxReadahead ( 128 * 1024 ) ,
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
2020-10-21 11:04:02 +02:00
AddCleanupHandler ( func ( ) error {
debug . Log ( "running umount cleanup handler for mount at %v" , mountpoint )
err := umount ( mountpoint )
if err != nil {
Warnf ( "unable to umount (maybe already umounted or still in use?): %v\n" , err )
}
return nil
} )
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 {
2020-09-03 20:31:57 +02:00
OwnerIsRoot : opts . OwnerRoot ,
Hosts : opts . Hosts ,
Tags : opts . Tags ,
Paths : opts . Paths ,
TimeTemplate : opts . TimeTemplate ,
PathTemplates : opts . PathTemplates ,
2017-06-18 14:59:44 +02:00
}
2020-06-14 12:49:39 +02:00
root := fuse . NewRoot ( 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 )
2021-11-01 20:59:20 +01:00
Printf ( "Use another terminal or tool to browse the contents of this folder.\n" )
Printf ( "When finished, quit with Ctrl-c here 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 )
}