summaryrefslogtreecommitdiff
path: root/internal/fuse/snapshots_dir.go
blob: c1915574110fb28fbca1c1436bf4a193b90a8ffe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//go:build darwin || freebsd || linux
// +build darwin freebsd linux

package fuse

import (
	"context"
	"os"

	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/restic"

	"github.com/anacrolix/fuse"
	"github.com/anacrolix/fuse/fs"
)

// SnapshotsDir is a actual fuse directory generated from SnapshotsDirStructure
// It uses the saved prefix to select the corresponding MetaDirData.
type SnapshotsDir struct {
	root        *Root
	inode       uint64
	parentInode uint64
	dirStruct   *SnapshotsDirStructure
	prefix      string
}

// ensure that *SnapshotsDir implements these interfaces
var _ = fs.HandleReadDirAller(&SnapshotsDir{})
var _ = fs.NodeStringLookuper(&SnapshotsDir{})

// NewSnapshotsDir returns a new directory structure containing snapshots and "latest" links
func NewSnapshotsDir(root *Root, inode, parentInode uint64, dirStruct *SnapshotsDirStructure, prefix string) *SnapshotsDir {
	debug.Log("create snapshots dir, inode %d", inode)
	return &SnapshotsDir{
		root:        root,
		inode:       inode,
		parentInode: parentInode,
		dirStruct:   dirStruct,
		prefix:      prefix,
	}
}

// Attr returns the attributes for any dir in the snapshots directory structure
func (d *SnapshotsDir) Attr(ctx context.Context, attr *fuse.Attr) error {
	attr.Inode = d.inode
	attr.Mode = os.ModeDir | 0555
	attr.Uid = d.root.uid
	attr.Gid = d.root.gid

	debug.Log("attr: %v", attr)
	return nil
}

// ReadDirAll returns all entries of the SnapshotsDir.
func (d *SnapshotsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	debug.Log("ReadDirAll()")

	// update snapshots
	meta, err := d.dirStruct.UpdatePrefix(ctx, d.prefix)
	if err != nil {
		return nil, unwrapCtxCanceled(err)
	} else if meta == nil {
		return nil, fuse.ENOENT
	}

	items := []fuse.Dirent{
		{
			Inode: d.inode,
			Name:  ".",
			Type:  fuse.DT_Dir,
		},
		{
			Inode: d.parentInode,
			Name:  "..",
			Type:  fuse.DT_Dir,
		},
	}

	for name, entry := range meta.names {
		d := fuse.Dirent{
			Inode: inodeFromName(d.inode, name),
			Name:  name,
			Type:  fuse.DT_Dir,
		}
		if entry.linkTarget != "" {
			d.Type = fuse.DT_Link
		}
		items = append(items, d)
	}

	return items, nil
}

// 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)

	meta, err := d.dirStruct.UpdatePrefix(ctx, d.prefix)
	if err != nil {
		return nil, unwrapCtxCanceled(err)
	} else if meta == nil {
		return nil, fuse.ENOENT
	}

	entry := meta.names[name]
	if entry != nil {
		inode := inodeFromName(d.inode, name)
		if entry.linkTarget != "" {
			return newSnapshotLink(d.root, inode, entry.linkTarget, entry.snapshot)
		} else if entry.snapshot != nil {
			return newDirFromSnapshot(d.root, inode, entry.snapshot)
		} else {
			return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
		}
	}

	return nil, fuse.ENOENT
}

// SnapshotLink
type snapshotLink struct {
	root     *Root
	inode    uint64
	target   string
	snapshot *restic.Snapshot
}

var _ = fs.NodeReadlinker(&snapshotLink{})

// newSnapshotLink
func newSnapshotLink(root *Root, inode uint64, target string, snapshot *restic.Snapshot) (*snapshotLink, error) {
	return &snapshotLink{root: root, inode: inode, target: target, snapshot: snapshot}, nil
}

// Readlink
func (l *snapshotLink) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
	return l.target, nil
}

// Attr
func (l *snapshotLink) Attr(ctx context.Context, a *fuse.Attr) error {
	a.Inode = l.inode
	a.Mode = os.ModeSymlink | 0777
	a.Size = uint64(len(l.target))
	a.Blocks = (a.Size + blockSize - 1) / blockSize
	a.Uid = l.root.uid
	a.Gid = l.root.gid
	a.Atime = l.snapshot.Time
	a.Ctime = l.snapshot.Time
	a.Mtime = l.snapshot.Time

	a.Nlink = 1

	return nil
}