blob: 2af2c0abf81ffb1e49ae6a4f5c53d8d8d165f82c [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package state
2
3import (
4 "reflect"
5
6 "veyron/services/store/memstore/acl"
7 "veyron/services/store/memstore/field"
8 "veyron/services/store/memstore/refs"
9
10 "veyron2/security"
11 "veyron2/storage"
12)
13
14type Snapshot interface {
Tilak Sharma61380d32014-07-06 10:06:05 -070015 // NewIterator returns an Iterator that starts with the value at <path>.
16 // pathFilter is used to automatically limit traversal of certain paths.
17 // If filter is given, it is used to limit traversal beneath certain paths
18 // and limit the results of the iteration. If filter is nil, all decendents
19 // of the specified path are returned.
20 NewIterator(pid security.PublicID, path storage.PathName, pathFilter PathFilter, filter IterFilter) Iterator
Jiri Simsa5293dcb2014-05-10 09:56:38 -070021
22 // PathMatch returns true iff there is a name for the store value that
23 // matches the pathRegex.
24 PathMatch(pid security.PublicID, id storage.ID, regex *PathRegex) bool
25
26 // Find performs a lookup based on storage.ID, returning nil if the cell is not found.
27 Find(id storage.ID) *Cell
28
29 // Get returns the value for a path.
30 Get(pid security.PublicID, path storage.PathName) (*storage.Entry, error)
31}
32
33// Snapshot keeps the state for the store. The snapshot contains a dictionary
34// and a root,
35//
36// idTable : storage.ID -> storage.Value
37// rootID : storage.ID
38//
39// Snapshots support isolation by using a functional/immutable dictionary for the
40// idTable.
41//
42// Paths are resolved by traversing the snapshot from the root, using reflection to
43// traverse fields within each of the values. For example, to resolve a path
44// /a/b/c/d/e/f/g/h, we perform the following steps.
45//
46// id1 := idTable[rootID].a.b.c
47// id2 := idTable[id1].d.e
48// id3 := idTable[id2].f.g.h
49// return id3
50//
51// If any of those resolution steps fails (if the idTable doesn't contain an
52// entry, or a field path like .a.b.c doesn't exist), then the resolution fails.
53type snapshot struct {
54 // idTable is the dictionary of values. We use functional sets to make it
55 // easy to perform snapshotting.
56 idTable cellSet
57
58 // rootID is the identifier of the root object.
59 rootID storage.ID
60
61 // aclCache caches a set of ACLs.
62 aclCache acl.Cache
63
64 // defaultACLSet is the ACLSet used to access the root directory.
65 defaultACLSet acl.Set
66}
67
68// newSnapshot returns an empty snapshot.
69func newSnapshot(admin security.PublicID) snapshot {
70 sn := snapshot{
71 idTable: emptyIDTable,
72 defaultACLSet: makeDefaultACLSet(admin),
73 }
74 sn.aclCache = acl.NewCache(sn.makeFindACLFunc())
75 return sn
76}
77
78// resetACLCache resets the aclCache.
79func (sn *snapshot) resetACLCache() {
80 sn.aclCache.UpdateFinder(sn.makeFindACLFunc())
81}
82
83// Find performs a lookup based on storage.ID, returning nil if the cell is not found.
84func (sn *snapshot) Find(id storage.ID) *Cell {
85 v, ok := sn.idTable.Get(&Cell{ID: id})
86 if !ok {
87 return nil
88 }
89 return v.(*Cell)
90}
91
92// Get implements the Snapshot method.
93func (sn *snapshot) Get(pid security.PublicID, path storage.PathName) (*storage.Entry, error) {
94 checker := sn.newPermChecker(pid)
95 // Pass nil for 'mutations' since the snapshot is immutable.
96 cell, suffix, v := sn.resolveCell(checker, path, nil)
97 if cell == nil {
98 return nil, ErrNotFound
99 }
100 var e *storage.Entry
101 if len(suffix) == 0 {
Tilak Sharmadfba8ac2014-06-18 13:49:32 -0700102 e = cell.GetEntry()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700103 } else {
104 e = newSubfieldEntry(v)
105 }
106 return e, nil
107}
108
109// resolveCell performs a path-based lookup, traversing the state from the
110// root.
111//
112// Returns (cell, suffix, v), where cell contains the value, suffix is the path
113// to the value, v is the value itself. If the operation failed, the returned
114// cell is nil.
115func (sn *snapshot) resolveCell(checker *acl.Checker, path storage.PathName, mu *Mutations) (*Cell, storage.PathName, interface{}) {
116 // Get the starting object.
117 id, suffix, ok := path.GetID()
118 if ok {
119 path = suffix
120 checker.Update(uidTagList)
121 } else {
122 id = sn.rootID
123 }
124
125 return sn.resolve(checker, id, path, mu)
126}
127
128func (sn *snapshot) resolve(checker *acl.Checker, id storage.ID, path storage.PathName, mu *Mutations) (*Cell, storage.PathName, interface{}) {
129 cell := sn.Find(id)
130 if cell == nil {
131 return nil, nil, nil
132 }
133 for {
134 if mu != nil {
135 mu.addPrecondition(cell)
136 }
137 checker.Update(cell.Tags)
138 var v reflect.Value
139 var suffix storage.PathName
140 if len(path) > 0 && path[0] == refs.TagsDirName {
141 if !checker.IsAllowed(security.AdminLabel) {
142 // Access to .tags requires admin priviledges.
143 return nil, nil, ErrPermissionDenied
144 }
145 v, suffix = field.Get(cell.Tags, path[1:])
146 } else {
147 if !checker.IsAllowed(security.ReadLabel) {
148 // Do not return errPermissionDenied because that would leak the
149 // existence of the inaccessible value.
150 return nil, nil, nil
151 }
152 v, suffix = field.Get(cell.Value, path)
153 }
154 x := v.Interface()
155 if id, ok := x.(storage.ID); ok {
156 // Always dereference IDs.
157 cell = sn.Find(id)
158 path = suffix
159 continue
160 }
161 switch len(suffix) {
162 case 0:
163 // The path is fully resolved. We're done.
164 return cell, path, x
165 case len(path):
166 // The path couldn't be resolved at all. It must be an entry in the
167 // implicit directory.
168 r, ok := cell.Dir.Get(&refs.Ref{Path: refs.NewSingletonPath(path[0])})
169 if !ok {
170 return nil, nil, nil
171 }
172 cell = sn.Find(r.(*refs.Ref).ID)
173 path = path[1:]
174 default:
175 // The path is partially resolved, but it does not resolve to a
176 // storage.ID. This is an error.
177 return nil, nil, nil
178 }
179 }
180}