func checkMakePeer(tx *db.Tx, pub *peer.PublicKey, id peer.ID) error { peer, err := tx.Peers().Make(pub) if err != nil { return fmt.Errorf("unexpected peers.Make error: %v", err) } if g, e := *peer.Pub(), *pub; g != e { return fmt.Errorf("peer pubkey came back wrong: %v != %v", g, e) } if g, e := peer.ID(), id; g != e { return fmt.Errorf("wrong peer ID: %v != %v", g, e) } return nil }
func (v *Volume) SyncSend(ctx context.Context, dirPath string, send func(*wirepeer.VolumeSyncPullItem) error) error { dirPath = path.Clean("/" + dirPath)[1:] // First, start a new epoch so all mutations happen after the // clocks that are included in the snapshot. // // We hold the lock over to prevent using clocks from using the // new epoch until we have a snapshot started. v.epoch.mu.Lock() locked := true defer func() { if locked { v.epoch.mu.Unlock() } }() if _, err := v.cleanEpoch(); err != nil { return err } sync := func(tx *db.Tx) error { v.epoch.mu.Unlock() locked = false // NOT HOLDING THE LOCK, accessing database snapshot ONLY bucket := v.bucket(tx) dirs := bucket.Dirs() clocks := bucket.Clock() dirInode := v.root.inode // Keep track of the parent of the directory, to access the // clock for the directory itself. Starts off as 0:"", as is // the convention for storing data about the root directory // itself. parentDirInode := uint64(0) dirName := "" var dirDE *wire.Dirent for dirPath != "" { dirName, dirPath = splitPath(dirPath) de, err := dirs.Get(dirInode, dirName) if err != nil { return err } // Might not be a dir anymore but that'll just trigger // ENOENT on the next round. parentDirInode = dirInode dirInode = de.Inode dirDE = de } // If it's not the root, make sure it's a directory; List below doesn't. if dirDE != nil && dirDE.Dir == nil { msg := &wirepeer.VolumeSyncPullItem{ Error: wirepeer.VolumeSyncPullItem_NOT_A_DIRECTORY, } if err := send(msg); err != nil { return err } return nil } // TODO more complex db api would avoid unmarshal-marshal dirClock, err := clocks.Get(parentDirInode, dirName) if err != nil { return err } dirClockBuf, err := dirClock.MarshalBinary() if err != nil { return err } msg := &wirepeer.VolumeSyncPullItem{ Peers: map[uint32][]byte{ // PeerID 0 always refers to myself. 0: v.pubKey[:], }, DirClock: dirClockBuf, } cursor := tx.Peers().Cursor() for peer := cursor.First(); peer != nil; peer = cursor.Next() { // filter what ids are returned here to include only peers // authorized for current volumes; avoids leaking information // about all of our peers. if !peer.Volumes().IsAllowed(bucket) { continue } // TODO hardcoded knowledge of size of peer.ID msg.Peers[uint32(peer.ID())] = peer.Pub()[:] } c := dirs.List(dirInode) const maxBatch = 1000 for item := c.First(); item != nil; item = c.Next() { name := item.Name() var tmp wire.Dirent if err := item.Unmarshal(&tmp); err != nil { return err } de := &wirepeer.Dirent{ Name: name, } switch { case tmp.File != nil: de.File = &wirepeer.File{ Manifest: tmp.File.Manifest, } case tmp.Dir != nil: de.Dir = &wirepeer.Dir{} default: return fmt.Errorf("unknown dirent type: %v", tmp) } clock, err := clocks.Get(dirInode, name) if err != nil { return err } // TODO more complex db api would avoid unmarshal-marshal // hoops clockBuf, err := clock.MarshalBinary() if err != nil { return err } de.Clock = clockBuf // TODO executable, xattr, acl // TODO mtime msg.Children = append(msg.Children, de) if len(msg.Children) > maxBatch { if err := send(msg); err != nil { return err } msg.Reset() } } if len(msg.Children) > 0 || msg.Peers != nil { if err := send(msg); err != nil { return err } } return nil } if err := v.db.View(sync); err != nil { return err } return nil }