Example #1
0
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
}
Example #2
0
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
}