func (dl directoryLayer) checkVersion(rtr fdb.ReadTransaction, tr *fdb.Transaction) error { version := rtr.Get(dl.rootNode.Sub([]byte("version"))).MustGet() if version == nil { if tr != nil { dl.initializeDirectory(*tr) } return nil } var versions []int32 buf := bytes.NewBuffer(version) for i := 0; i < 3; i++ { var v int32 err := binary.Read(buf, binary.LittleEndian, &v) if err != nil { return errors.New("cannot determine directory version present in database") } versions = append(versions, v) } if versions[0] > _MAJORVERSION { return fmt.Errorf("cannot load directory with version %d.%d.%d using directory layer %d.%d.%d", versions[0], versions[1], versions[2], _MAJORVERSION, _MINORVERSION, _MICROVERSION) } if versions[1] > _MINORVERSION && tr != nil /* aka write access allowed */ { return fmt.Errorf("directory with version %d.%d.%d is read-only when opened using directory layer %d.%d.%d", versions[0], versions[1], versions[2], _MAJORVERSION, _MINORVERSION, _MICROVERSION) } return nil }
func (n *node) layer(rtr fdb.ReadTransaction) fdb.FutureByteSlice { if n._layer == nil { fv := rtr.Get(n.subspace.Sub([]byte("layer"))) n._layer = fv } return n._layer }
func (dl directoryLayer) find(rtr fdb.ReadTransaction, path []string) *node { n := &node{dl.rootNode, []string{}, path, nil} for i := range path { n = &node{dl.nodeWithPrefix(rtr.Get(n.subspace.Sub(_SUBDIRS, path[i])).MustGet()), path[:i+1], path, nil} if !n.exists() || bytes.Compare(n.layer(rtr).MustGet(), []byte("partition")) == 0 { return n } } return n }
func (dl directoryLayer) subdirNames(rtr fdb.ReadTransaction, node subspace.Subspace) ([]string, error) { sd := node.Sub(_SUBDIRS) rr := rtr.GetRange(sd, fdb.RangeOptions{}) ri := rr.Iterator() var ret []string for ri.Advance() { kv := ri.MustGet() p, e := sd.Unpack(kv.Key) if e != nil { return nil, e } ret = append(ret, p[0].(string)) } return ret, nil }
func (dl directoryLayer) nodeContainingKey(rtr fdb.ReadTransaction, key []byte) (subspace.Subspace, error) { if bytes.HasPrefix(key, dl.nodeSS.Bytes()) { return dl.rootNode, nil } bk, _ := dl.nodeSS.FDBRangeKeys() kr := fdb.KeyRange{bk, fdb.Key(append(dl.nodeSS.Pack(tuple.Tuple{key}), 0x00))} kvs := rtr.GetRange(kr, fdb.RangeOptions{Reverse: true, Limit: 1}).GetSliceOrPanic() if len(kvs) == 1 { pp, e := dl.nodeSS.Unpack(kvs[0].Key) if e != nil { return nil, e } prevPrefix := pp[0].([]byte) if bytes.HasPrefix(key, prevPrefix) { return dl.nodeWithPrefix(prevPrefix), nil } } return nil, nil }
func (dl directoryLayer) createOrOpen(rtr fdb.ReadTransaction, tr *fdb.Transaction, path []string, layer []byte, prefix []byte, allowCreate, allowOpen bool) (DirectorySubspace, error) { if e := dl.checkVersion(rtr, nil); e != nil { return nil, e } if prefix != nil && !dl.allowManualPrefixes { if len(dl.path) == 0 { return nil, errors.New("cannot specify a prefix unless manual prefixes are enabled") } else { return nil, errors.New("cannot specify a prefix in a partition") } } if len(path) == 0 { return nil, errors.New("the root directory cannot be opened") } existingNode := dl.find(rtr, path).prefetchMetadata(rtr) if existingNode.exists() { if existingNode.isInPartition(nil, false) { subpath := existingNode.getPartitionSubpath() enc, e := existingNode.getContents(dl, nil) if e != nil { return nil, e } return enc.(directoryPartition).createOrOpen(rtr, tr, subpath, layer, prefix, allowCreate, allowOpen) } if !allowOpen { return nil, errors.New("the directory already exists") } if layer != nil && bytes.Compare(existingNode._layer.MustGet(), layer) != 0 { return nil, errors.New("the directory was created with an incompatible layer") } return existingNode.getContents(dl, nil) } if !allowCreate { return nil, errors.New("the directory does not exist") } if e := dl.checkVersion(rtr, tr); e != nil { return nil, e } if prefix == nil { newss, e := dl.allocator.allocate(*tr, dl.contentSS) if e != nil { return nil, fmt.Errorf("unable to allocate new directory prefix (%s)", e.Error()) } if !isRangeEmpty(rtr, newss) { return nil, fmt.Errorf("the database has keys stored at the prefix chosen by the automatic prefix allocator: %v", prefix) } prefix = newss.Bytes() pf, e := dl.isPrefixFree(rtr.Snapshot(), prefix) if e != nil { return nil, e } if !pf { return nil, errors.New("the directory layer has manually allocated prefixes that conflict with the automatic prefix allocator") } } else { pf, e := dl.isPrefixFree(rtr, prefix) if e != nil { return nil, e } if !pf { return nil, errors.New("the given prefix is already in use") } } var parentNode subspace.Subspace if len(path) > 1 { pd, e := dl.createOrOpen(rtr, tr, path[:len(path)-1], nil, nil, true, true) if e != nil { return nil, e } parentNode = dl.nodeWithPrefix(pd.Bytes()) } else { parentNode = dl.rootNode } if parentNode == nil { return nil, errors.New("the parent directory does not exist") } node := dl.nodeWithPrefix(prefix) tr.Set(parentNode.Sub(_SUBDIRS, path[len(path)-1]), prefix) if layer == nil { layer = []byte{} } tr.Set(node.Sub([]byte("layer")), layer) return dl.contentsOfNode(node, path, layer) }
func isRangeEmpty(rtr fdb.ReadTransaction, r fdb.Range) bool { kvs := rtr.GetRange(r, fdb.RangeOptions{Limit: 1}).GetSliceOrPanic() return len(kvs) == 0 }