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