2022-03-28 20:23:47 +00:00
|
|
|
//go:build darwin || freebsd || linux
|
2020-05-12 09:30:41 +00:00
|
|
|
// +build darwin freebsd linux
|
2017-06-18 15:02:07 +00:00
|
|
|
|
2017-06-18 12:59:44 +00:00
|
|
|
package fuse
|
|
|
|
|
|
|
|
import (
|
2020-10-05 21:09:52 +00:00
|
|
|
"context"
|
2017-06-18 12:59:44 +00:00
|
|
|
"os"
|
2023-05-18 15:47:42 +00:00
|
|
|
"syscall"
|
2017-06-18 12:59:44 +00:00
|
|
|
|
2017-07-23 12:21:03 +00:00
|
|
|
"github.com/restic/restic/internal/debug"
|
2017-07-24 15:42:25 +00:00
|
|
|
"github.com/restic/restic/internal/restic"
|
2017-07-23 12:21:03 +00:00
|
|
|
|
2022-11-12 13:52:37 +00:00
|
|
|
"github.com/anacrolix/fuse"
|
|
|
|
"github.com/anacrolix/fuse/fs"
|
2017-06-18 12:59:44 +00:00
|
|
|
)
|
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
// SnapshotsDir is a actual fuse directory generated from SnapshotsDirStructure
|
2022-07-30 18:45:51 +00:00
|
|
|
// It uses the saved prefix to select the corresponding MetaDirData.
|
2017-06-18 15:06:27 +00:00
|
|
|
type SnapshotsDir struct {
|
2022-08-07 11:02:40 +00:00
|
|
|
root *Root
|
|
|
|
inode uint64
|
|
|
|
parentInode uint64
|
|
|
|
dirStruct *SnapshotsDirStructure
|
|
|
|
prefix string
|
2024-09-09 20:15:30 +00:00
|
|
|
cache treeCache
|
2017-06-18 12:59:44 +00:00
|
|
|
}
|
|
|
|
|
2017-09-17 15:34:19 +00:00
|
|
|
// ensure that *SnapshotsDir implements these interfaces
|
2017-06-18 15:06:27 +00:00
|
|
|
var _ = fs.HandleReadDirAller(&SnapshotsDir{})
|
|
|
|
var _ = fs.NodeStringLookuper(&SnapshotsDir{})
|
2017-06-18 12:59:44 +00:00
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
// NewSnapshotsDir returns a new directory structure containing snapshots and "latest" links
|
2022-08-07 11:02:40 +00:00
|
|
|
func NewSnapshotsDir(root *Root, inode, parentInode uint64, dirStruct *SnapshotsDirStructure, prefix string) *SnapshotsDir {
|
2017-10-05 12:55:55 +00:00
|
|
|
debug.Log("create snapshots dir, inode %d", inode)
|
2020-09-02 19:27:24 +00:00
|
|
|
return &SnapshotsDir{
|
2022-08-07 11:02:40 +00:00
|
|
|
root: root,
|
|
|
|
inode: inode,
|
|
|
|
parentInode: parentInode,
|
|
|
|
dirStruct: dirStruct,
|
|
|
|
prefix: prefix,
|
2024-09-09 20:15:30 +00:00
|
|
|
cache: *newTreeCache(),
|
2017-10-05 12:55:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
// Attr returns the attributes for any dir in the snapshots directory structure
|
2023-05-18 17:18:09 +00:00
|
|
|
func (d *SnapshotsDir) Attr(_ context.Context, attr *fuse.Attr) error {
|
2017-06-18 12:59:44 +00:00
|
|
|
attr.Inode = d.inode
|
|
|
|
attr.Mode = os.ModeDir | 0555
|
2020-02-17 15:26:53 +00:00
|
|
|
attr.Uid = d.root.uid
|
|
|
|
attr.Gid = d.root.gid
|
2017-06-18 12:59:44 +00:00
|
|
|
|
|
|
|
debug.Log("attr: %v", attr)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-05 12:55:55 +00:00
|
|
|
// ReadDirAll returns all entries of the SnapshotsDir.
|
2017-06-18 15:06:27 +00:00
|
|
|
func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
2017-06-18 12:59:44 +00:00
|
|
|
debug.Log("ReadDirAll()")
|
2017-10-05 12:55:55 +00:00
|
|
|
|
2017-10-08 15:47:10 +00:00
|
|
|
// update snapshots
|
2022-07-30 18:45:51 +00:00
|
|
|
meta, err := d.dirStruct.UpdatePrefix(ctx, d.prefix)
|
2021-01-30 15:32:00 +00:00
|
|
|
if err != nil {
|
2022-08-19 18:26:35 +00:00
|
|
|
return nil, unwrapCtxCanceled(err)
|
2022-07-30 18:45:51 +00:00
|
|
|
} else if meta == nil {
|
2023-05-18 15:47:42 +00:00
|
|
|
return nil, syscall.ENOENT
|
2021-01-30 15:32:00 +00:00
|
|
|
}
|
2017-10-05 12:55:55 +00:00
|
|
|
|
2017-06-18 12:59:44 +00:00
|
|
|
items := []fuse.Dirent{
|
|
|
|
{
|
|
|
|
Inode: d.inode,
|
|
|
|
Name: ".",
|
|
|
|
Type: fuse.DT_Dir,
|
|
|
|
},
|
|
|
|
{
|
2022-08-07 11:02:40 +00:00
|
|
|
Inode: d.parentInode,
|
2017-06-18 12:59:44 +00:00
|
|
|
Name: "..",
|
|
|
|
Type: fuse.DT_Dir,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-07-30 18:45:51 +00:00
|
|
|
for name, entry := range meta.names {
|
2024-07-31 17:30:47 +00:00
|
|
|
if ctx.Err() != nil {
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
|
2022-07-30 18:45:51 +00:00
|
|
|
d := fuse.Dirent{
|
2022-11-11 09:52:47 +00:00
|
|
|
Inode: inodeFromName(d.inode, name),
|
2022-07-30 18:45:51 +00:00
|
|
|
Name: name,
|
|
|
|
Type: fuse.DT_Dir,
|
2020-09-02 19:27:24 +00:00
|
|
|
}
|
2022-07-30 18:45:51 +00:00
|
|
|
if entry.linkTarget != "" {
|
|
|
|
d.Type = fuse.DT_Link
|
2020-09-02 19:27:24 +00:00
|
|
|
}
|
2022-07-30 18:45:51 +00:00
|
|
|
items = append(items, d)
|
2017-09-14 17:44:03 +00:00
|
|
|
}
|
2017-10-05 12:55:55 +00:00
|
|
|
|
|
|
|
return items, nil
|
|
|
|
}
|
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
// Lookup returns a specific entry from the SnapshotsDir.
|
|
|
|
func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|
|
|
debug.Log("Lookup(%s)", name)
|
2017-10-05 12:55:55 +00:00
|
|
|
|
2022-07-30 18:45:51 +00:00
|
|
|
meta, err := d.dirStruct.UpdatePrefix(ctx, d.prefix)
|
2021-01-30 15:32:00 +00:00
|
|
|
if err != nil {
|
2022-08-19 18:26:35 +00:00
|
|
|
return nil, unwrapCtxCanceled(err)
|
2022-07-30 18:45:51 +00:00
|
|
|
} else if meta == nil {
|
2023-05-18 15:47:42 +00:00
|
|
|
return nil, syscall.ENOENT
|
2021-01-30 15:32:00 +00:00
|
|
|
}
|
2017-10-05 12:55:55 +00:00
|
|
|
|
2024-09-09 20:15:30 +00:00
|
|
|
return d.cache.lookupOrCreate(name, func() (fs.Node, error) {
|
|
|
|
entry := meta.names[name]
|
|
|
|
if entry == nil {
|
|
|
|
return nil, syscall.ENOENT
|
|
|
|
}
|
|
|
|
|
2022-11-27 12:53:42 +00:00
|
|
|
inode := inodeFromName(d.inode, name)
|
2022-07-30 18:45:51 +00:00
|
|
|
if entry.linkTarget != "" {
|
2022-11-27 12:53:42 +00:00
|
|
|
return newSnapshotLink(d.root, inode, entry.linkTarget, entry.snapshot)
|
2022-07-30 18:45:51 +00:00
|
|
|
} else if entry.snapshot != nil {
|
2022-11-27 12:53:42 +00:00
|
|
|
return newDirFromSnapshot(d.root, inode, entry.snapshot)
|
2020-09-02 19:27:24 +00:00
|
|
|
}
|
2023-12-23 12:40:04 +00:00
|
|
|
return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
2024-09-09 20:15:30 +00:00
|
|
|
})
|
2020-09-02 19:27:24 +00:00
|
|
|
}
|
2017-10-05 12:55:55 +00:00
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
// SnapshotLink
|
|
|
|
type snapshotLink struct {
|
|
|
|
root *Root
|
|
|
|
inode uint64
|
|
|
|
target string
|
|
|
|
snapshot *restic.Snapshot
|
2017-09-14 17:44:03 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 19:27:24 +00:00
|
|
|
var _ = fs.NodeReadlinker(&snapshotLink{})
|
|
|
|
|
2017-10-05 12:55:55 +00:00
|
|
|
// newSnapshotLink
|
2022-08-19 18:29:33 +00:00
|
|
|
func newSnapshotLink(root *Root, inode uint64, target string, snapshot *restic.Snapshot) (*snapshotLink, error) {
|
2017-09-14 17:44:03 +00:00
|
|
|
return &snapshotLink{root: root, inode: inode, target: target, snapshot: snapshot}, nil
|
|
|
|
}
|
|
|
|
|
2017-10-05 12:55:55 +00:00
|
|
|
// Readlink
|
2023-05-18 17:27:38 +00:00
|
|
|
func (l *snapshotLink) Readlink(_ context.Context, _ *fuse.ReadlinkRequest) (string, error) {
|
2017-09-14 17:44:03 +00:00
|
|
|
return l.target, nil
|
|
|
|
}
|
|
|
|
|
2017-10-05 12:55:55 +00:00
|
|
|
// Attr
|
2023-05-18 17:18:09 +00:00
|
|
|
func (l *snapshotLink) Attr(_ context.Context, a *fuse.Attr) error {
|
2017-09-14 17:44:03 +00:00
|
|
|
a.Inode = l.inode
|
|
|
|
a.Mode = os.ModeSymlink | 0777
|
2022-03-18 03:21:47 +00:00
|
|
|
a.Size = uint64(len(l.target))
|
2023-03-07 21:12:08 +00:00
|
|
|
a.Blocks = (a.Size + blockSize - 1) / blockSize
|
2020-02-17 15:26:53 +00:00
|
|
|
a.Uid = l.root.uid
|
|
|
|
a.Gid = l.root.gid
|
2017-09-14 17:44:03 +00:00
|
|
|
a.Atime = l.snapshot.Time
|
|
|
|
a.Ctime = l.snapshot.Time
|
|
|
|
a.Mtime = l.snapshot.Time
|
|
|
|
|
|
|
|
a.Nlink = 1
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|