Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | package state |
| 2 | |
| 3 | import ( |
| 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 | |
| 14 | type Snapshot interface { |
Tilak Sharma | 61380d3 | 2014-07-06 10:06:05 -0700 | [diff] [blame^] | 15 | // 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 Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 21 | |
| 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. |
| 53 | type 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. |
| 69 | func 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. |
| 79 | func (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. |
| 84 | func (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. |
| 93 | func (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 Sharma | dfba8ac | 2014-06-18 13:49:32 -0700 | [diff] [blame] | 102 | e = cell.GetEntry() |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 103 | } 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. |
| 115 | func (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 | |
| 128 | func (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 | } |