Add version and invalid bit to protocol
This commit is contained in:
parent
d4fe1400d2
commit
2cfb24892f
@ -56,8 +56,6 @@ type Model struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FlagDeleted = 1 << 12
|
|
||||||
|
|
||||||
idxBcastHoldtime = 15 * time.Second // Wait at least this long after the last index modification
|
idxBcastHoldtime = 15 * time.Second // Wait at least this long after the last index modification
|
||||||
idxBcastMaxDelay = 120 * time.Second // Unless we've already waited this long
|
idxBcastMaxDelay = 120 * time.Second // Unless we've already waited this long
|
||||||
|
|
||||||
@ -65,7 +63,10 @@ const (
|
|||||||
maxFileHoldTimeS = 600 // Always allow file changes at least this often
|
maxFileHoldTimeS = 600 // Always allow file changes at least this often
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoSuchFile = errors.New("no such file")
|
var (
|
||||||
|
ErrNoSuchFile = errors.New("no such file")
|
||||||
|
ErrInvalid = errors.New("file is invalid")
|
||||||
|
)
|
||||||
|
|
||||||
// NewModel creates and starts a new model. The model starts in read-only mode,
|
// NewModel creates and starts a new model. The model starts in read-only mode,
|
||||||
// where it sends index information to connected peers and responds to requests
|
// where it sends index information to connected peers and responds to requests
|
||||||
@ -159,7 +160,7 @@ func (m *Model) GlobalSize() (files, deleted, bytes int) {
|
|||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
for _, f := range m.global {
|
for _, f := range m.global {
|
||||||
if f.Flags&FlagDeleted == 0 {
|
if f.Flags&protocol.FlagDeleted == 0 {
|
||||||
files++
|
files++
|
||||||
bytes += f.Size()
|
bytes += f.Size()
|
||||||
} else {
|
} else {
|
||||||
@ -176,7 +177,7 @@ func (m *Model) LocalSize() (files, deleted, bytes int) {
|
|||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
for _, f := range m.local {
|
for _, f := range m.local {
|
||||||
if f.Flags&FlagDeleted == 0 {
|
if f.Flags&protocol.FlagDeleted == 0 {
|
||||||
files++
|
files++
|
||||||
bytes += f.Size()
|
bytes += f.Size()
|
||||||
} else {
|
} else {
|
||||||
@ -193,7 +194,7 @@ func (m *Model) InSyncSize() (files, bytes int) {
|
|||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
for n, f := range m.local {
|
for n, f := range m.local {
|
||||||
if gf, ok := m.global[n]; ok && f.Modified == gf.Modified {
|
if gf, ok := m.global[n]; ok && f.Equals(gf) {
|
||||||
files++
|
files++
|
||||||
bytes += f.Size()
|
bytes += f.Size()
|
||||||
}
|
}
|
||||||
@ -249,7 +250,7 @@ func (m *Model) IndexUpdate(nodeID string, fs []protocol.FileInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
if f.Flags&FlagDeleted != 0 && !m.delete {
|
if f.Flags&protocol.FlagDeleted != 0 && !m.delete {
|
||||||
// Files marked as deleted do not even enter the model
|
// Files marked as deleted do not even enter the model
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -284,13 +285,16 @@ func (m *Model) Close(node string, err error) {
|
|||||||
func (m *Model) Request(nodeID, name string, offset uint64, size uint32, hash []byte) ([]byte, error) {
|
func (m *Model) Request(nodeID, name string, offset uint64, size uint32, hash []byte) ([]byte, error) {
|
||||||
// Verify that the requested file exists in the local and global model.
|
// Verify that the requested file exists in the local and global model.
|
||||||
m.RLock()
|
m.RLock()
|
||||||
_, localOk := m.local[name]
|
lf, localOk := m.local[name]
|
||||||
_, globalOk := m.global[name]
|
_, globalOk := m.global[name]
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
if !localOk || !globalOk {
|
if !localOk || !globalOk {
|
||||||
log.Printf("SECURITY (nonexistent file) REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
|
log.Printf("SECURITY (nonexistent file) REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
|
||||||
return nil, ErrNoSuchFile
|
return nil, ErrNoSuchFile
|
||||||
}
|
}
|
||||||
|
if lf.Flags&protocol.FlagInvalid != 0 {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
if m.trace["net"] && nodeID != "<local>" {
|
if m.trace["net"] && nodeID != "<local>" {
|
||||||
log.Printf("NET REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
|
log.Printf("NET REQ(in): %s: %q o=%d s=%d h=%x", nodeID, name, offset, size, hash)
|
||||||
@ -322,7 +326,7 @@ func (m *Model) ReplaceLocal(fs []File) {
|
|||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
newLocal[f.Name] = f
|
newLocal[f.Name] = f
|
||||||
if ef := m.local[f.Name]; ef.Modified != f.Modified {
|
if ef := m.local[f.Name]; !ef.Equals(f) {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,7 +434,7 @@ func (m *Model) protocolIndex() []protocol.FileInfo {
|
|||||||
mf := fileInfoFromFile(f)
|
mf := fileInfoFromFile(f)
|
||||||
if m.trace["idx"] {
|
if m.trace["idx"] {
|
||||||
var flagComment string
|
var flagComment string
|
||||||
if mf.Flags&FlagDeleted != 0 {
|
if mf.Flags&protocol.FlagDeleted != 0 {
|
||||||
flagComment = " (deleted)"
|
flagComment = " (deleted)"
|
||||||
}
|
}
|
||||||
log.Printf("IDX: %q m=%d f=%o%s (%d blocks)", mf.Name, mf.Modified, mf.Flags, flagComment, len(mf.Blocks))
|
log.Printf("IDX: %q m=%d f=%o%s (%d blocks)", mf.Name, mf.Modified, mf.Flags, flagComment, len(mf.Blocks))
|
||||||
@ -496,10 +500,10 @@ func (m *Model) markDeletedLocals(newLocal map[string]File) bool {
|
|||||||
var updated bool
|
var updated bool
|
||||||
for n, f := range m.local {
|
for n, f := range m.local {
|
||||||
if _, ok := newLocal[n]; !ok {
|
if _, ok := newLocal[n]; !ok {
|
||||||
if gf := m.global[n]; gf.Modified <= f.Modified {
|
if gf := m.global[n]; !gf.NewerThan(f) {
|
||||||
if f.Flags&FlagDeleted == 0 {
|
if f.Flags&protocol.FlagDeleted == 0 {
|
||||||
f.Flags = FlagDeleted
|
f.Flags = protocol.FlagDeleted
|
||||||
f.Modified = f.Modified + 1
|
f.Version++
|
||||||
f.Blocks = nil
|
f.Blocks = nil
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
@ -511,7 +515,7 @@ func (m *Model) markDeletedLocals(newLocal map[string]File) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) updateLocal(f File) {
|
func (m *Model) updateLocal(f File) {
|
||||||
if ef, ok := m.local[f.Name]; !ok || ef.Modified != f.Modified {
|
if ef, ok := m.local[f.Name]; !ok || !ef.Equals(f) {
|
||||||
m.local[f.Name] = f
|
m.local[f.Name] = f
|
||||||
m.recomputeGlobal()
|
m.recomputeGlobal()
|
||||||
m.recomputeNeed()
|
m.recomputeNeed()
|
||||||
@ -530,7 +534,7 @@ func (m *Model) recomputeGlobal() {
|
|||||||
|
|
||||||
for _, fs := range m.remote {
|
for _, fs := range m.remote {
|
||||||
for n, f := range fs {
|
for n, f := range fs {
|
||||||
if cf, ok := newGlobal[n]; !ok || cf.Modified < f.Modified {
|
if cf, ok := newGlobal[n]; !ok || f.NewerThan(cf) {
|
||||||
newGlobal[n] = f
|
newGlobal[n] = f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -543,7 +547,7 @@ func (m *Model) recomputeGlobal() {
|
|||||||
updated = true
|
updated = true
|
||||||
} else {
|
} else {
|
||||||
for n, f0 := range newGlobal {
|
for n, f0 := range newGlobal {
|
||||||
if f1, ok := m.global[n]; !ok || f0.Modified != f1.Modified {
|
if f1, ok := m.global[n]; !ok || !f0.Equals(f1) {
|
||||||
updated = true
|
updated = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -561,12 +565,16 @@ func (m *Model) recomputeNeed() {
|
|||||||
m.need = make(map[string]bool)
|
m.need = make(map[string]bool)
|
||||||
for n, f := range m.global {
|
for n, f := range m.global {
|
||||||
hf, ok := m.local[n]
|
hf, ok := m.local[n]
|
||||||
if !ok || f.Modified > hf.Modified {
|
if !ok || f.NewerThan(hf) {
|
||||||
if f.Flags&FlagDeleted != 0 && !m.delete {
|
if f.Flags&protocol.FlagInvalid != 0 {
|
||||||
|
// Never attempt to sync invalid files
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if f.Flags&protocol.FlagDeleted != 0 && !m.delete {
|
||||||
// Don't want to delete files, so forget this need
|
// Don't want to delete files, so forget this need
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if f.Flags&FlagDeleted != 0 && !ok {
|
if f.Flags&protocol.FlagDeleted != 0 && !ok {
|
||||||
// Don't have the file, so don't need to delete it
|
// Don't have the file, so don't need to delete it
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -584,7 +592,7 @@ func (m *Model) whoHas(name string) []string {
|
|||||||
|
|
||||||
gf := m.global[name]
|
gf := m.global[name]
|
||||||
for node, files := range m.remote {
|
for node, files := range m.remote {
|
||||||
if file, ok := files[name]; ok && file.Modified == gf.Modified {
|
if file, ok := files[name]; ok && file.Equals(gf) {
|
||||||
remote = append(remote, node)
|
remote = append(remote, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -607,6 +615,7 @@ func fileFromFileInfo(f protocol.FileInfo) File {
|
|||||||
Name: f.Name,
|
Name: f.Name,
|
||||||
Flags: f.Flags,
|
Flags: f.Flags,
|
||||||
Modified: int64(f.Modified),
|
Modified: int64(f.Modified),
|
||||||
|
Version: f.Version,
|
||||||
Blocks: blocks,
|
Blocks: blocks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,6 +632,7 @@ func fileInfoFromFile(f File) protocol.FileInfo {
|
|||||||
Name: f.Name,
|
Name: f.Name,
|
||||||
Flags: f.Flags,
|
Flags: f.Flags,
|
||||||
Modified: int64(f.Modified),
|
Modified: int64(f.Modified),
|
||||||
|
Version: f.Version,
|
||||||
Blocks: blocks,
|
Blocks: blocks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/calmh/syncthing/buffers"
|
"github.com/calmh/syncthing/buffers"
|
||||||
|
"github.com/calmh/syncthing/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Model) pullFile(name string) error {
|
func (m *Model) pullFile(name string) error {
|
||||||
@ -171,7 +172,7 @@ func (m *Model) puller() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if f.Flags&FlagDeleted == 0 {
|
if f.Flags&protocol.FlagDeleted == 0 {
|
||||||
if m.trace["file"] {
|
if m.trace["file"] {
|
||||||
log.Printf("FILE: Pull %q", n)
|
log.Printf("FILE: Pull %q", n)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -228,9 +228,12 @@ func TestDelete(t *testing.T) {
|
|||||||
if len(m.local["a new file"].Blocks) != 0 {
|
if len(m.local["a new file"].Blocks) != 0 {
|
||||||
t.Error("Unexpected non-zero blocks for deleted file in local")
|
t.Error("Unexpected non-zero blocks for deleted file in local")
|
||||||
}
|
}
|
||||||
if ft := m.local["a new file"].Modified; ft != ot+1 {
|
if ft := m.local["a new file"].Modified; ft != ot {
|
||||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
||||||
}
|
}
|
||||||
|
if fv := m.local["a new file"].Version; fv != 1 {
|
||||||
|
t.Errorf("Unexpected version %d != 1 for deleted file in local", fv)
|
||||||
|
}
|
||||||
|
|
||||||
if m.global["a new file"].Flags&(1<<12) == 0 {
|
if m.global["a new file"].Flags&(1<<12) == 0 {
|
||||||
t.Error("Unexpected deleted flag = 0 in global table")
|
t.Error("Unexpected deleted flag = 0 in global table")
|
||||||
@ -238,8 +241,11 @@ func TestDelete(t *testing.T) {
|
|||||||
if len(m.global["a new file"].Blocks) != 0 {
|
if len(m.global["a new file"].Blocks) != 0 {
|
||||||
t.Error("Unexpected non-zero blocks for deleted file in global")
|
t.Error("Unexpected non-zero blocks for deleted file in global")
|
||||||
}
|
}
|
||||||
if ft := m.local["a new file"].Modified; ft != ot+1 {
|
if ft := m.global["a new file"].Modified; ft != ot {
|
||||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
t.Errorf("Unexpected time %d != %d for deleted file in global", ft, ot+1)
|
||||||
|
}
|
||||||
|
if fv := m.local["a new file"].Version; fv != 1 {
|
||||||
|
t.Errorf("Unexpected version %d != 1 for deleted file in global", fv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Another update should change nothing
|
// Another update should change nothing
|
||||||
@ -259,8 +265,11 @@ func TestDelete(t *testing.T) {
|
|||||||
if len(m.local["a new file"].Blocks) != 0 {
|
if len(m.local["a new file"].Blocks) != 0 {
|
||||||
t.Error("Unexpected non-zero blocks for deleted file in local")
|
t.Error("Unexpected non-zero blocks for deleted file in local")
|
||||||
}
|
}
|
||||||
if ft := m.local["a new file"].Modified; ft != ot+1 {
|
if ft := m.local["a new file"].Modified; ft != ot {
|
||||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot)
|
||||||
|
}
|
||||||
|
if fv := m.local["a new file"].Version; fv != 1 {
|
||||||
|
t.Errorf("Unexpected version %d != 1 for deleted file in local", fv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.global["a new file"].Flags&(1<<12) == 0 {
|
if m.global["a new file"].Flags&(1<<12) == 0 {
|
||||||
@ -269,8 +278,11 @@ func TestDelete(t *testing.T) {
|
|||||||
if len(m.global["a new file"].Blocks) != 0 {
|
if len(m.global["a new file"].Blocks) != 0 {
|
||||||
t.Error("Unexpected non-zero blocks for deleted file in global")
|
t.Error("Unexpected non-zero blocks for deleted file in global")
|
||||||
}
|
}
|
||||||
if ft := m.local["a new file"].Modified; ft != ot+1 {
|
if ft := m.global["a new file"].Modified; ft != ot {
|
||||||
t.Errorf("Unexpected time %d != %d for deleted file in local", ft, ot+1)
|
t.Errorf("Unexpected time %d != %d for deleted file in global", ft, ot)
|
||||||
|
}
|
||||||
|
if fv := m.local["a new file"].Version; fv != 1 {
|
||||||
|
t.Errorf("Unexpected version %d != 1 for deleted file in global", fv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/calmh/syncthing/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BlockSize = 128 * 1024
|
const BlockSize = 128 * 1024
|
||||||
@ -18,6 +20,7 @@ type File struct {
|
|||||||
Name string
|
Name string
|
||||||
Flags uint32
|
Flags uint32
|
||||||
Modified int64
|
Modified int64
|
||||||
|
Version uint32
|
||||||
Blocks []Block
|
Blocks []Block
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +31,14 @@ func (f File) Size() (bytes int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f File) Equals(o File) bool {
|
||||||
|
return f.Modified == o.Modified && f.Version == o.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f File) NewerThan(o File) bool {
|
||||||
|
return f.Modified > o.Modified || (f.Modified == o.Modified && f.Version > o.Version)
|
||||||
|
}
|
||||||
|
|
||||||
func isTempName(name string) bool {
|
func isTempName(name string) bool {
|
||||||
return strings.HasPrefix(path.Base(name), ".syncthing.")
|
return strings.HasPrefix(path.Base(name), ".syncthing.")
|
||||||
}
|
}
|
||||||
@ -79,7 +90,10 @@ func (m *Model) genWalker(res *[]File, ign map[string][]string) filepath.WalkFun
|
|||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
if ok && hf.Modified == modified {
|
if ok && hf.Modified == modified {
|
||||||
// No change
|
if nf := uint32(info.Mode()); nf != hf.Flags {
|
||||||
|
hf.Flags = nf
|
||||||
|
hf.Version++
|
||||||
|
}
|
||||||
*res = append(*res, hf)
|
*res = append(*res, hf)
|
||||||
} else {
|
} else {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
@ -89,7 +103,8 @@ func (m *Model) genWalker(res *[]File, ign map[string][]string) filepath.WalkFun
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
// Files that are ignored will be suppressed but don't actually exist in the local model
|
hf.Flags = protocol.FlagInvalid
|
||||||
|
hf.Version++
|
||||||
*res = append(*res, hf)
|
*res = append(*res, hf)
|
||||||
}
|
}
|
||||||
m.Unlock()
|
m.Unlock()
|
||||||
|
|||||||
@ -62,9 +62,8 @@ reserved bits must be set to zero.
|
|||||||
All data following the message header is in XDR (RFC 1014) encoding.
|
All data following the message header is in XDR (RFC 1014) encoding.
|
||||||
The actual data types in use by BEP, in XDR naming convention, are:
|
The actual data types in use by BEP, in XDR naming convention, are:
|
||||||
|
|
||||||
- unsigned int -- unsigned 32 bit integer
|
- (unsigned) int -- (unsigned) 32 bit integer
|
||||||
- hyper -- signed 64 bit integer
|
- (unsigned) hyper -- (unsigned) 64 bit integer
|
||||||
- unsigned hyper -- signed 64 bit integer
|
|
||||||
- opaque<> -- variable length opaque data
|
- opaque<> -- variable length opaque data
|
||||||
- string<> -- variable length string
|
- string<> -- variable length string
|
||||||
|
|
||||||
@ -92,6 +91,7 @@ message.
|
|||||||
string Name<>;
|
string Name<>;
|
||||||
unsigned int Flags;
|
unsigned int Flags;
|
||||||
hyper Modified;
|
hyper Modified;
|
||||||
|
unsigned int Version;
|
||||||
BlockInfo Blocks<>;
|
BlockInfo Blocks<>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,15 +102,19 @@ message.
|
|||||||
|
|
||||||
The file name is the part relative to the repository root. The
|
The file name is the part relative to the repository root. The
|
||||||
modification time is expressed as the number of seconds since the Unix
|
modification time is expressed as the number of seconds since the Unix
|
||||||
Epoch. The hash algorithm is implied by the hash length. Currently, the
|
Epoch. The version field is a counter that increments each time the file
|
||||||
hash must be 32 bytes long and computed by SHA256.
|
changes but resets to zero each time the modification is updated. This
|
||||||
|
is used to signal changes to the file (or file metadata) while the
|
||||||
|
modification time remains unchanged. The hash algorithm is implied by
|
||||||
|
the hash length. Currently, the hash must be 32 bytes long and computed
|
||||||
|
by SHA256.
|
||||||
|
|
||||||
The flags field is made up of the following single bit flags:
|
The flags field is made up of the following single bit flags:
|
||||||
|
|
||||||
0 1 2 3
|
0 1 2 3
|
||||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
| Reserved |D| Unix Perm. & Mode |
|
| Reserved |I|D| Unix Perm. & Mode |
|
||||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
- The lower 12 bits hold the common Unix permission and mode bits.
|
- The lower 12 bits hold the common Unix permission and mode bits.
|
||||||
@ -118,9 +122,13 @@ The flags field is made up of the following single bit flags:
|
|||||||
- Bit 19 ("D") is set when the file has been deleted. The block list
|
- Bit 19 ("D") is set when the file has been deleted. The block list
|
||||||
shall contain zero blocks and the modification time indicates the
|
shall contain zero blocks and the modification time indicates the
|
||||||
time of deletion or, if deletion time is not reliably determinable,
|
time of deletion or, if deletion time is not reliably determinable,
|
||||||
one second past the last know modification time.
|
the last known modification time and a higher version number.
|
||||||
|
|
||||||
- Bit 0 through 18 are reserved for future use and shall be set to
|
- Bit 18 ("I") is set when the file is invalid and unavailable for
|
||||||
|
synchronization. A peer may set this bit to indicate that it can
|
||||||
|
temporarily not serve data for the file.
|
||||||
|
|
||||||
|
- Bit 0 through 17 are reserved for future use and shall be set to
|
||||||
zero.
|
zero.
|
||||||
|
|
||||||
### Request (Type = 2)
|
### Request (Type = 2)
|
||||||
|
|||||||
@ -39,6 +39,7 @@ func (w *marshalWriter) writeIndex(idx []FileInfo) {
|
|||||||
w.writeString(f.Name)
|
w.writeString(f.Name)
|
||||||
w.writeUint32(f.Flags)
|
w.writeUint32(f.Flags)
|
||||||
w.writeUint64(uint64(f.Modified))
|
w.writeUint64(uint64(f.Modified))
|
||||||
|
w.writeUint32(f.Version)
|
||||||
w.writeUint32(uint32(len(f.Blocks)))
|
w.writeUint32(uint32(len(f.Blocks)))
|
||||||
for _, b := range f.Blocks {
|
for _, b := range f.Blocks {
|
||||||
w.writeUint32(b.Length)
|
w.writeUint32(b.Length)
|
||||||
@ -77,6 +78,7 @@ func (r *marshalReader) readIndex() []FileInfo {
|
|||||||
files[i].Name = r.readString()
|
files[i].Name = r.readString()
|
||||||
files[i].Flags = r.readUint32()
|
files[i].Flags = r.readUint32()
|
||||||
files[i].Modified = int64(r.readUint64())
|
files[i].Modified = int64(r.readUint64())
|
||||||
|
files[i].Version = r.readUint32()
|
||||||
nblocks := r.readUint32()
|
nblocks := r.readUint32()
|
||||||
blocks := make([]BlockInfo, nblocks)
|
blocks := make([]BlockInfo, nblocks)
|
||||||
for j := range blocks {
|
for j := range blocks {
|
||||||
|
|||||||
@ -12,8 +12,9 @@ func TestIndex(t *testing.T) {
|
|||||||
idx := []FileInfo{
|
idx := []FileInfo{
|
||||||
{
|
{
|
||||||
"Foo",
|
"Foo",
|
||||||
0755,
|
FlagInvalid & FlagDeleted & 0755,
|
||||||
1234567890,
|
1234567890,
|
||||||
|
142,
|
||||||
[]BlockInfo{
|
[]BlockInfo{
|
||||||
{12345678, []byte("hash hash hash")},
|
{12345678, []byte("hash hash hash")},
|
||||||
{23456781, []byte("ash hash hashh")},
|
{23456781, []byte("ash hash hashh")},
|
||||||
@ -23,6 +24,7 @@ func TestIndex(t *testing.T) {
|
|||||||
"Quux/Quux",
|
"Quux/Quux",
|
||||||
0644,
|
0644,
|
||||||
2345678901,
|
2345678901,
|
||||||
|
232323232,
|
||||||
[]BlockInfo{
|
[]BlockInfo{
|
||||||
{45678123, []byte("4321 hash hash hash")},
|
{45678123, []byte("4321 hash hash hash")},
|
||||||
{56781234, []byte("3214 ash hash hashh")},
|
{56781234, []byte("3214 ash hash hashh")},
|
||||||
@ -81,6 +83,7 @@ func BenchmarkWriteIndex(b *testing.B) {
|
|||||||
"Foo",
|
"Foo",
|
||||||
0777,
|
0777,
|
||||||
1234567890,
|
1234567890,
|
||||||
|
424242,
|
||||||
[]BlockInfo{
|
[]BlockInfo{
|
||||||
{12345678, []byte("hash hash hash")},
|
{12345678, []byte("hash hash hash")},
|
||||||
{23456781, []byte("ash hash hashh")},
|
{23456781, []byte("ash hash hashh")},
|
||||||
@ -90,6 +93,7 @@ func BenchmarkWriteIndex(b *testing.B) {
|
|||||||
"Quux/Quux",
|
"Quux/Quux",
|
||||||
0644,
|
0644,
|
||||||
2345678901,
|
2345678901,
|
||||||
|
323232,
|
||||||
[]BlockInfo{
|
[]BlockInfo{
|
||||||
{45678123, []byte("4321 hash hash hash")},
|
{45678123, []byte("4321 hash hash hash")},
|
||||||
{56781234, []byte("3214 ash hash hashh")},
|
{56781234, []byte("3214 ash hash hashh")},
|
||||||
|
|||||||
@ -20,10 +20,16 @@ const (
|
|||||||
messageTypeIndexUpdate = 6
|
messageTypeIndexUpdate = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagDeleted = 1 << 12
|
||||||
|
FlagInvalid = 1 << 13
|
||||||
|
)
|
||||||
|
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
Name string
|
Name string
|
||||||
Flags uint32
|
Flags uint32
|
||||||
Modified int64
|
Modified int64
|
||||||
|
Version uint32
|
||||||
Blocks []BlockInfo
|
Blocks []BlockInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user