mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-24 05:39:02 -05:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c680c955f | ||
|
|
2c8b627008 | ||
|
|
221e3eddd5 | ||
|
|
74c39c677b | ||
|
|
e6558832bf | ||
|
|
9c50625c55 | ||
|
|
d372435e92 | ||
|
|
a53facf709 | ||
|
|
ffc39dfbcb | ||
|
|
cba38b15a9 | ||
|
|
5ac7564bfe | ||
|
|
53cd289b90 | ||
|
|
8dc13bcf1a | ||
|
|
261825a89b | ||
|
|
f47a5a309d | ||
|
|
a40f2b9fa0 | ||
|
|
cfcd3892f7 | ||
|
|
703987f61c | ||
|
|
e50a8917ec | ||
|
|
74d7c8e625 |
@@ -33,8 +33,8 @@ latest info on Transifex.
|
||||
|
||||
Every contribution is welcome. If you want to contribute but are unsure
|
||||
where to start, any open issues are fair game! Be prepared for a
|
||||
[certain amount of review](https://discourse.syncthing.net/t/733); it's
|
||||
all in the name of quality. :) Following the points below will make this
|
||||
[certain amount of review](https://github.com/syncthing/syncthing/wiki/FAQ#why-are-you-being-so-hard-on-my-pull-request);
|
||||
it's all in the name of quality. :) Following the points below will make this
|
||||
a smoother process.
|
||||
|
||||
Individuals making significant and valuable contributions are given
|
||||
@@ -105,8 +105,8 @@ add yourself as a separate commit in your first pull request.
|
||||
|
||||
## Building
|
||||
|
||||
[See the documentation](http://discourse.syncthing.net/t/44) on how to
|
||||
get started with a build environment.
|
||||
[See the documentation](https://github.com/syncthing/syncthing/wiki/Building)
|
||||
on how to get started with a build environment.
|
||||
|
||||
## Branches
|
||||
|
||||
@@ -133,7 +133,7 @@ Yes please!
|
||||
|
||||
## Documentation
|
||||
|
||||
[Over here!](http://discourse.syncthing.net/category/documentation)
|
||||
[Over here!](https://github.com/syncthing/syncthing/wiki)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
12
Godeps/Godeps.json
generated
12
Godeps/Godeps.json
generated
@@ -27,7 +27,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/calmh/xdr",
|
||||
"Rev": "214788d8fedfc310c18eca9ed12be408a5054cd5"
|
||||
"Rev": "ff948d7666c5e0fd18d398f6278881724d36a90b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/juju/ratelimit",
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syncthing/protocol",
|
||||
"Rev": "f76b5d800208131015b14cfc1cfd3e6dd1d5e979"
|
||||
"Rev": "15bf5f583a88b7aaf0a5b810fcf5fb21da0a3b3f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||
@@ -59,19 +59,19 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||
"Rev": "731db29863ea7213d9556d0170afb38987f401d4"
|
||||
"Rev": "4ed45ec682102c643324fae5dff8dab085b6c300"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||
"Rev": "731db29863ea7213d9556d0170afb38987f401d4"
|
||||
"Rev": "4ed45ec682102c643324fae5dff8dab085b6c300"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/transform",
|
||||
"Rev": "985ee5acfaf1ff6712c7c99438752f8e09416ccb"
|
||||
"Rev": "c980adc4a823548817b9c47d38c6ca6b7d7d8b6a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/unicode/norm",
|
||||
"Rev": "985ee5acfaf1ff6712c7c99438752f8e09416ccb"
|
||||
"Rev": "c980adc4a823548817b9c47d38c6ca6b7d7d8b6a"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
32
Godeps/_workspace/src/github.com/syncthing/protocol/counting.go
generated
vendored
32
Godeps/_workspace/src/github.com/syncthing/protocol/counting.go
generated
vendored
@@ -10,25 +10,25 @@ import (
|
||||
|
||||
type countingReader struct {
|
||||
io.Reader
|
||||
tot uint64 // bytes
|
||||
last int64 // unix nanos
|
||||
tot int64 // bytes
|
||||
last int64 // unix nanos
|
||||
}
|
||||
|
||||
var (
|
||||
totalIncoming uint64
|
||||
totalOutgoing uint64
|
||||
totalIncoming int64
|
||||
totalOutgoing int64
|
||||
)
|
||||
|
||||
func (c *countingReader) Read(bs []byte) (int, error) {
|
||||
n, err := c.Reader.Read(bs)
|
||||
atomic.AddUint64(&c.tot, uint64(n))
|
||||
atomic.AddUint64(&totalIncoming, uint64(n))
|
||||
atomic.AddInt64(&c.tot, int64(n))
|
||||
atomic.AddInt64(&totalIncoming, int64(n))
|
||||
atomic.StoreInt64(&c.last, time.Now().UnixNano())
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *countingReader) Tot() uint64 {
|
||||
return atomic.LoadUint64(&c.tot)
|
||||
func (c *countingReader) Tot() int64 {
|
||||
return atomic.LoadInt64(&c.tot)
|
||||
}
|
||||
|
||||
func (c *countingReader) Last() time.Time {
|
||||
@@ -37,26 +37,26 @@ func (c *countingReader) Last() time.Time {
|
||||
|
||||
type countingWriter struct {
|
||||
io.Writer
|
||||
tot uint64 // bytes
|
||||
last int64 // unix nanos
|
||||
tot int64 // bytes
|
||||
last int64 // unix nanos
|
||||
}
|
||||
|
||||
func (c *countingWriter) Write(bs []byte) (int, error) {
|
||||
n, err := c.Writer.Write(bs)
|
||||
atomic.AddUint64(&c.tot, uint64(n))
|
||||
atomic.AddUint64(&totalOutgoing, uint64(n))
|
||||
atomic.AddInt64(&c.tot, int64(n))
|
||||
atomic.AddInt64(&totalOutgoing, int64(n))
|
||||
atomic.StoreInt64(&c.last, time.Now().UnixNano())
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *countingWriter) Tot() uint64 {
|
||||
return atomic.LoadUint64(&c.tot)
|
||||
func (c *countingWriter) Tot() int64 {
|
||||
return atomic.LoadInt64(&c.tot)
|
||||
}
|
||||
|
||||
func (c *countingWriter) Last() time.Time {
|
||||
return time.Unix(0, atomic.LoadInt64(&c.last))
|
||||
}
|
||||
|
||||
func TotalInOut() (uint64, uint64) {
|
||||
return atomic.LoadUint64(&totalIncoming), atomic.LoadUint64(&totalOutgoing)
|
||||
func TotalInOut() (int64, int64) {
|
||||
return atomic.LoadInt64(&totalIncoming), atomic.LoadInt64(&totalOutgoing)
|
||||
}
|
||||
|
||||
17
Godeps/_workspace/src/github.com/syncthing/protocol/message.go
generated
vendored
17
Godeps/_workspace/src/github.com/syncthing/protocol/message.go
generated
vendored
@@ -1,6 +1,5 @@
|
||||
// Copyright (C) 2014 The Protocol Authors.
|
||||
|
||||
//go:generate -command genxdr go run ../../Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go
|
||||
//go:generate genxdr -o message_xdr.go message.go
|
||||
|
||||
package protocol
|
||||
@@ -18,8 +17,8 @@ type FileInfo struct {
|
||||
Name string // max:8192
|
||||
Flags uint32
|
||||
Modified int64
|
||||
Version uint64
|
||||
LocalVersion uint64
|
||||
Version int64
|
||||
LocalVersion int64
|
||||
Blocks []BlockInfo
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ func (f FileInfo) HasPermissionBits() bool {
|
||||
|
||||
type BlockInfo struct {
|
||||
Offset int64 // noencode (cache only)
|
||||
Size uint32
|
||||
Size int32
|
||||
Hash []byte // max:64
|
||||
}
|
||||
|
||||
@@ -71,8 +70,8 @@ func (b BlockInfo) String() string {
|
||||
type RequestMessage struct {
|
||||
Folder string // max:64
|
||||
Name string // max:8192
|
||||
Offset uint64
|
||||
Size uint32
|
||||
Offset int64
|
||||
Size int32
|
||||
Hash []byte // max:64
|
||||
Flags uint32
|
||||
Options []Option // max:64
|
||||
@@ -80,7 +79,7 @@ type RequestMessage struct {
|
||||
|
||||
type ResponseMessage struct {
|
||||
Data []byte
|
||||
Error uint32
|
||||
Error int32
|
||||
}
|
||||
|
||||
type ClusterConfigMessage struct {
|
||||
@@ -107,7 +106,7 @@ type Folder struct {
|
||||
type Device struct {
|
||||
ID []byte // max:32
|
||||
Flags uint32
|
||||
MaxLocalVersion uint64
|
||||
MaxLocalVersion int64
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
@@ -117,7 +116,7 @@ type Option struct {
|
||||
|
||||
type CloseMessage struct {
|
||||
Reason string // max:1024
|
||||
Code uint32
|
||||
Code int32
|
||||
}
|
||||
|
||||
type EmptyMessage struct{}
|
||||
|
||||
48
Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go
generated
vendored
48
Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go
generated
vendored
@@ -168,8 +168,8 @@ struct FileInfo {
|
||||
string Name<8192>;
|
||||
unsigned int Flags;
|
||||
hyper Modified;
|
||||
unsigned hyper Version;
|
||||
unsigned hyper LocalVersion;
|
||||
hyper Version;
|
||||
hyper LocalVersion;
|
||||
BlockInfo Blocks<>;
|
||||
}
|
||||
|
||||
@@ -206,8 +206,8 @@ func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteString(o.Name)
|
||||
xw.WriteUint32(o.Flags)
|
||||
xw.WriteUint64(uint64(o.Modified))
|
||||
xw.WriteUint64(o.Version)
|
||||
xw.WriteUint64(o.LocalVersion)
|
||||
xw.WriteUint64(uint64(o.Version))
|
||||
xw.WriteUint64(uint64(o.LocalVersion))
|
||||
xw.WriteUint32(uint32(len(o.Blocks)))
|
||||
for i := range o.Blocks {
|
||||
_, err := o.Blocks[i].encodeXDR(xw)
|
||||
@@ -233,8 +233,8 @@ func (o *FileInfo) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Name = xr.ReadStringMax(8192)
|
||||
o.Flags = xr.ReadUint32()
|
||||
o.Modified = int64(xr.ReadUint64())
|
||||
o.Version = xr.ReadUint64()
|
||||
o.LocalVersion = xr.ReadUint64()
|
||||
o.Version = int64(xr.ReadUint64())
|
||||
o.LocalVersion = int64(xr.ReadUint64())
|
||||
_BlocksSize := int(xr.ReadUint32())
|
||||
o.Blocks = make([]BlockInfo, _BlocksSize)
|
||||
for i := range o.Blocks {
|
||||
@@ -261,7 +261,7 @@ BlockInfo Structure:
|
||||
|
||||
|
||||
struct BlockInfo {
|
||||
unsigned int Size;
|
||||
int Size;
|
||||
opaque Hash<64>;
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Size)
|
||||
xw.WriteUint32(uint32(o.Size))
|
||||
if l := len(o.Hash); l > 64 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func (o *BlockInfo) UnmarshalXDR(bs []byte) error {
|
||||
}
|
||||
|
||||
func (o *BlockInfo) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Size = xr.ReadUint32()
|
||||
o.Size = int32(xr.ReadUint32())
|
||||
o.Hash = xr.ReadBytesMax(64)
|
||||
return xr.Error()
|
||||
}
|
||||
@@ -361,8 +361,8 @@ RequestMessage Structure:
|
||||
struct RequestMessage {
|
||||
string Folder<64>;
|
||||
string Name<8192>;
|
||||
unsigned hyper Offset;
|
||||
unsigned int Size;
|
||||
hyper Offset;
|
||||
int Size;
|
||||
opaque Hash<64>;
|
||||
unsigned int Flags;
|
||||
Option Options<64>;
|
||||
@@ -403,8 +403,8 @@ func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192)
|
||||
}
|
||||
xw.WriteString(o.Name)
|
||||
xw.WriteUint64(o.Offset)
|
||||
xw.WriteUint32(o.Size)
|
||||
xw.WriteUint64(uint64(o.Offset))
|
||||
xw.WriteUint32(uint32(o.Size))
|
||||
if l := len(o.Hash); l > 64 {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64)
|
||||
}
|
||||
@@ -437,8 +437,8 @@ func (o *RequestMessage) UnmarshalXDR(bs []byte) error {
|
||||
func (o *RequestMessage) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Folder = xr.ReadStringMax(64)
|
||||
o.Name = xr.ReadStringMax(8192)
|
||||
o.Offset = xr.ReadUint64()
|
||||
o.Size = xr.ReadUint32()
|
||||
o.Offset = int64(xr.ReadUint64())
|
||||
o.Size = int32(xr.ReadUint32())
|
||||
o.Hash = xr.ReadBytesMax(64)
|
||||
o.Flags = xr.ReadUint32()
|
||||
_OptionsSize := int(xr.ReadUint32())
|
||||
@@ -471,7 +471,7 @@ ResponseMessage Structure:
|
||||
|
||||
struct ResponseMessage {
|
||||
opaque Data<>;
|
||||
unsigned int Error;
|
||||
int Error;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -502,7 +502,7 @@ func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) {
|
||||
|
||||
func (o ResponseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteBytes(o.Data)
|
||||
xw.WriteUint32(o.Error)
|
||||
xw.WriteUint32(uint32(o.Error))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ func (o *ResponseMessage) UnmarshalXDR(bs []byte) error {
|
||||
|
||||
func (o *ResponseMessage) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Data = xr.ReadBytes()
|
||||
o.Error = xr.ReadUint32()
|
||||
o.Error = int32(xr.ReadUint32())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
@@ -766,7 +766,7 @@ Device Structure:
|
||||
struct Device {
|
||||
opaque ID<32>;
|
||||
unsigned int Flags;
|
||||
unsigned hyper MaxLocalVersion;
|
||||
hyper MaxLocalVersion;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -801,7 +801,7 @@ func (o Device) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
}
|
||||
xw.WriteBytes(o.ID)
|
||||
xw.WriteUint32(o.Flags)
|
||||
xw.WriteUint64(o.MaxLocalVersion)
|
||||
xw.WriteUint64(uint64(o.MaxLocalVersion))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
@@ -819,7 +819,7 @@ func (o *Device) UnmarshalXDR(bs []byte) error {
|
||||
func (o *Device) decodeXDR(xr *xdr.Reader) error {
|
||||
o.ID = xr.ReadBytesMax(32)
|
||||
o.Flags = xr.ReadUint32()
|
||||
o.MaxLocalVersion = xr.ReadUint64()
|
||||
o.MaxLocalVersion = int64(xr.ReadUint64())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
@@ -923,7 +923,7 @@ CloseMessage Structure:
|
||||
|
||||
struct CloseMessage {
|
||||
string Reason<1024>;
|
||||
unsigned int Code;
|
||||
int Code;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -957,7 +957,7 @@ func (o CloseMessage) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024)
|
||||
}
|
||||
xw.WriteString(o.Reason)
|
||||
xw.WriteUint32(o.Code)
|
||||
xw.WriteUint32(uint32(o.Code))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
@@ -974,7 +974,7 @@ func (o *CloseMessage) UnmarshalXDR(bs []byte) error {
|
||||
|
||||
func (o *CloseMessage) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Reason = xr.ReadStringMax(1024)
|
||||
o.Code = xr.ReadUint32()
|
||||
o.Code = int32(xr.ReadUint32())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
|
||||
8
Godeps/_workspace/src/github.com/syncthing/protocol/protocol.go
generated
vendored
8
Godeps/_workspace/src/github.com/syncthing/protocol/protocol.go
generated
vendored
@@ -222,8 +222,8 @@ func (c *rawConnection) Request(folder string, name string, offset int64, size i
|
||||
ok := c.send(id, messageTypeRequest, RequestMessage{
|
||||
Folder: folder,
|
||||
Name: name,
|
||||
Offset: uint64(offset),
|
||||
Size: uint32(size),
|
||||
Offset: offset,
|
||||
Size: int32(size),
|
||||
})
|
||||
if !ok {
|
||||
return nil, ErrClosed
|
||||
@@ -706,8 +706,8 @@ func (c *rawConnection) pingerLoop() {
|
||||
|
||||
type Statistics struct {
|
||||
At time.Time
|
||||
InBytesTotal uint64
|
||||
OutBytesTotal uint64
|
||||
InBytesTotal int64
|
||||
OutBytesTotal int64
|
||||
}
|
||||
|
||||
func (c *rawConnection) Statistics() Statistics {
|
||||
|
||||
2
build.go
2
build.go
@@ -301,7 +301,7 @@ func assets() {
|
||||
}
|
||||
|
||||
func xdr() {
|
||||
runPrint("go", "generate", "./internal/discover", "./internal/db", "./internal/protocol")
|
||||
runPrint("go", "generate", "./internal/discover", "./internal/db")
|
||||
}
|
||||
|
||||
func translate() {
|
||||
|
||||
2
build.sh
2
build.sh
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
DOCKERIMGV=1.4-5
|
||||
DOCKERIMGV=1.4.1-1
|
||||
|
||||
case "${1:-default}" in
|
||||
default)
|
||||
|
||||
@@ -5,5 +5,5 @@ if [[ -z $since ]] ; then
|
||||
since="$(git describe --abbrev=0 HEAD^).."
|
||||
fi
|
||||
|
||||
git log --pretty=format:'* %s \n (%h, @%aN)' "$since" | egrep 'fixes #\d|ref #\d' | sed s/\\\\n/\\$'\n'/
|
||||
git log --reverse --pretty=format:'* %s, @%aN)' "$since" | egrep 'fixes #\d|ref #\d' | sed 's/),/,/' | sed 's/fixes #/#/g' | sed 's/ref #/#/g'
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ no-docs-typos() {
|
||||
grep -v f1120d7aa936c0658429edef0037792520b46334 |\
|
||||
grep -v a9339d0627fff439879d157c75077f02c9fac61b |\
|
||||
grep -v 254c63763a3ad42fd82259f1767db526cff94a14 |\
|
||||
grep -v 4b76ec40c07078beaa2c5e250ed7d9bd6276a718
|
||||
grep -v 4b76ec40c07078beaa2c5e250ed7d9bd6276a718 |\
|
||||
grep -v ffc39dfbcb34eacc3ea12327a02b6e7741a2c207
|
||||
}
|
||||
|
||||
print-missing-authors() {
|
||||
|
||||
@@ -794,7 +794,7 @@ func toNeedSlice(fs []db.FileInfoTruncated) []map[string]interface{} {
|
||||
"Version": file.Version,
|
||||
"LocalVersion": file.LocalVersion,
|
||||
"NumBlocks": file.NumBlocks,
|
||||
"Size": db.BlocksToSize(file.NumBlocks),
|
||||
"Size": db.BlocksToSize(int(file.NumBlocks)),
|
||||
}
|
||||
}
|
||||
return output
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func memorySize() (uint64, error) {
|
||||
func memorySize() (int64, error) {
|
||||
cmd := exec.Command("sysctl", "hw.memsize")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -32,7 +32,7 @@ func memorySize() (uint64, error) {
|
||||
if len(fs) != 2 {
|
||||
return 0, errors.New("sysctl parse error")
|
||||
}
|
||||
bytes, err := strconv.ParseUint(fs[1], 10, 64)
|
||||
bytes, err := strconv.ParseInt(fs[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func memorySize() (uint64, error) {
|
||||
func memorySize() (int64, error) {
|
||||
f, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -40,7 +40,7 @@ func memorySize() (uint64, error) {
|
||||
return 0, errors.New("/proc/meminfo parse error 2")
|
||||
}
|
||||
|
||||
kb, err := strconv.ParseUint(fs[1], 10, 64)
|
||||
kb, err := strconv.ParseInt(fs[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func memorySize() (uint64, error) {
|
||||
func memorySize() (int64, error) {
|
||||
cmd := exec.Command("prtconf", "-m")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
mb, err := strconv.ParseUint(string(out), 10, 64)
|
||||
mb, err := strconv.ParseInt(string(out), 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ package main
|
||||
|
||||
import "errors"
|
||||
|
||||
func memorySize() (uint64, error) {
|
||||
func memorySize() (int64, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
globalMemoryStatusEx, _ = syscall.GetProcAddress(kernel32, "GlobalMemoryStatusEx")
|
||||
)
|
||||
|
||||
func memorySize() (uint64, error) {
|
||||
func memorySize() (int64, error) {
|
||||
var memoryStatusEx [64]byte
|
||||
binary.LittleEndian.PutUint32(memoryStatusEx[:], 64)
|
||||
p := uintptr(unsafe.Pointer(&memoryStatusEx[0]))
|
||||
@@ -36,5 +36,5 @@ func memorySize() (uint64, error) {
|
||||
return 0, callErr
|
||||
}
|
||||
|
||||
return binary.LittleEndian.Uint64(memoryStatusEx[8:]), nil
|
||||
return int64(binary.LittleEndian.Uint64(memoryStatusEx[8:])), nil
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func savePerfStats(file string) {
|
||||
var prevTime int64
|
||||
var rusage syscall.Rusage
|
||||
var memstats runtime.MemStats
|
||||
var prevIn, prevOut uint64
|
||||
var prevIn, prevOut int64
|
||||
|
||||
t0 := time.Now()
|
||||
for t := range time.NewTicker(250 * time.Millisecond).C {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM debian:squeeze
|
||||
MAINTAINER Jakob Borg <jakob@nym.se>
|
||||
|
||||
ENV GOLANG_VERSION 1.4
|
||||
ENV GOLANG_VERSION 1.4.1
|
||||
|
||||
# SCMs for "go get", gcc for cgo
|
||||
RUN apt-get update && apt-get install -y \
|
||||
@@ -34,14 +34,14 @@ RUN go get github.com/calmh/gonative \
|
||||
|
||||
# Rebuild the special and missing versions, using patches as appropriate
|
||||
|
||||
RUN mkdir /tmp/patches
|
||||
ADD *.patch /tmp/patches/
|
||||
RUN bash -xec '\
|
||||
cd /usr/local/go ; \
|
||||
for patch in /tmp/patches/*.patch ; do \
|
||||
patch -p0 < "$patch" ; \
|
||||
done \
|
||||
'
|
||||
# RUN mkdir /tmp/patches
|
||||
# ADD *.patch /tmp/patches/
|
||||
# RUN bash -xec '\
|
||||
# cd /usr/local/go ; \
|
||||
# for patch in /tmp/patches/*.patch ; do \
|
||||
# patch -p0 < "$patch" ; \
|
||||
# done \
|
||||
# '
|
||||
|
||||
RUN bash -xec '\
|
||||
cd /usr/local/go/src; \
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
--- src/syscall/route_openbsd.go.orig Fri Jul 25 23:38:47 2014
|
||||
+++ src/syscall/route_openbsd.go Fri Jul 25 23:39:20 2014
|
||||
@@ -12,16 +12,16 @@ func (any *anyMessage) toRoutingMessage(b []byte) Rout
|
||||
switch any.Type {
|
||||
case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
|
||||
p := (*RouteMessage)(unsafe.Pointer(any))
|
||||
- return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
|
||||
+ return &RouteMessage{Header: p.Header, Data: b[p.Header.Hdrlen:any.Msglen]}
|
||||
case RTM_IFINFO:
|
||||
p := (*InterfaceMessage)(unsafe.Pointer(any))
|
||||
- return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
|
||||
+ return &InterfaceMessage{Header: p.Header, Data: b[p.Header.Hdrlen:any.Msglen]}
|
||||
case RTM_IFANNOUNCE:
|
||||
p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
|
||||
return &InterfaceAnnounceMessage{Header: p.Header}
|
||||
case RTM_NEWADDR, RTM_DELADDR:
|
||||
p := (*InterfaceAddrMessage)(unsafe.Pointer(any))
|
||||
- return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]}
|
||||
+ return &InterfaceAddrMessage{Header: p.Header, Data: b[p.Header.Hdrlen:any.Msglen]}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -150,6 +150,13 @@
|
||||
<th><span class="glyphicon glyphicon-refresh"></span> <span translate>Rescan Interval</span></th>
|
||||
<td class="text-right">{{folder.RescanIntervalS}} s</td>
|
||||
</tr>
|
||||
<tr ng-if="folder.Versioning.Type">
|
||||
<th><span class="glyphicon glyphicon-tags"></span> <span translate>File Versioning</span></th>
|
||||
<td class="text-right" ng-switch="folder.Versioning.Type">
|
||||
<span ng-switch-when="staggered" translate>Staggered File Versioning</span>
|
||||
<span ng-switch-when="simple" translate>Simple File Versioning</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="glyphicon glyphicon-share-alt"></span> <span translate>Shared With</span></th>
|
||||
<td class="text-right">{{sharesFolder(folder)}}</td>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -264,6 +264,7 @@ func (w *Wrapper) Save() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(fd.Name())
|
||||
|
||||
err = w.cfg.WriteXML(fd)
|
||||
if err != nil {
|
||||
|
||||
@@ -160,7 +160,7 @@ func (f *BlockFinder) Changed(cfg config.Configuration) error {
|
||||
// the block) or false to continue iterating for whatever reason.
|
||||
// The iterator finally returns the result, whether or not a satisfying block
|
||||
// was eventually found.
|
||||
func (f *BlockFinder) Iterate(hash []byte, iterFn func(string, string, uint32) bool) bool {
|
||||
func (f *BlockFinder) Iterate(hash []byte, iterFn func(string, string, int32) bool) bool {
|
||||
f.mut.RLock()
|
||||
folders := f.folders
|
||||
f.mut.RUnlock()
|
||||
@@ -171,7 +171,7 @@ func (f *BlockFinder) Iterate(hash []byte, iterFn func(string, string, uint32) b
|
||||
|
||||
for iter.Next() && iter.Error() == nil {
|
||||
folder, file := fromBlockKey(iter.Key())
|
||||
index := binary.BigEndian.Uint32(iter.Value())
|
||||
index := int32(binary.BigEndian.Uint32(iter.Value()))
|
||||
if iterFn(folder, osutil.NativeFilename(file), index) {
|
||||
return true
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func (f *BlockFinder) Iterate(hash []byte, iterFn func(string, string, uint32) b
|
||||
|
||||
// A method for repairing incorrect blockmap entries, removes the old entry
|
||||
// and replaces it with a new entry for the given block
|
||||
func (f *BlockFinder) Fix(folder, file string, index uint32, oldHash, newHash []byte) error {
|
||||
func (f *BlockFinder) Fix(folder, file string, index int32, oldHash, newHash []byte) error {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(index))
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func genBlocks(n int) []protocol.BlockInfo {
|
||||
for j := range h {
|
||||
h[j] = byte(i + j)
|
||||
}
|
||||
b[i].Size = uint32(i)
|
||||
b[i].Size = int32(i)
|
||||
b[i].Hash = h
|
||||
}
|
||||
return b
|
||||
@@ -103,21 +103,21 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
if folder != "folder1" || file != "f1" || index != 0 {
|
||||
t.Fatal("Mismatch")
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
f.Iterate(f2.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f2.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
if folder != "folder1" || file != "f2" || index != 0 {
|
||||
t.Fatal("Mismatch")
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
f.Iterate(f3.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f3.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
t.Fatal("Unexpected block")
|
||||
return true
|
||||
})
|
||||
@@ -132,17 +132,17 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
t.Fatal("Unexpected block")
|
||||
return false
|
||||
})
|
||||
|
||||
f.Iterate(f2.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f2.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
t.Fatal("Unexpected block")
|
||||
return false
|
||||
})
|
||||
|
||||
f.Iterate(f3.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f3.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
if folder != "folder1" || file != "f3" || index != 0 {
|
||||
t.Fatal("Mismatch")
|
||||
}
|
||||
@@ -189,7 +189,7 @@ func TestBlockFinderLookup(t *testing.T) {
|
||||
}
|
||||
|
||||
counter := 0
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
counter++
|
||||
switch counter {
|
||||
case 1:
|
||||
@@ -217,7 +217,7 @@ func TestBlockFinderLookup(t *testing.T) {
|
||||
}
|
||||
|
||||
counter = 0
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index uint32) bool {
|
||||
f.Iterate(f1.Blocks[0].Hash, func(folder, file string, index int32) bool {
|
||||
counter++
|
||||
switch counter {
|
||||
case 1:
|
||||
@@ -239,7 +239,7 @@ func TestBlockFinderLookup(t *testing.T) {
|
||||
func TestBlockFinderFix(t *testing.T) {
|
||||
db, f := setup()
|
||||
|
||||
iterFn := func(folder, file string, index uint32) bool {
|
||||
iterFn := func(folder, file string, index int32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
clockTick uint64
|
||||
clockTick int64
|
||||
clockMut sync.Mutex
|
||||
)
|
||||
|
||||
func clock(v uint64) uint64 {
|
||||
func clock(v int64) int64 {
|
||||
clockMut.Lock()
|
||||
defer clockMut.Unlock()
|
||||
if v > clockTick {
|
||||
@@ -56,7 +56,7 @@ const (
|
||||
)
|
||||
|
||||
type fileVersion struct {
|
||||
version uint64
|
||||
version int64
|
||||
device []byte
|
||||
}
|
||||
|
||||
@@ -164,9 +164,9 @@ func globalKeyFolder(key []byte) []byte {
|
||||
return folder[:izero]
|
||||
}
|
||||
|
||||
type deletionHandler func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) uint64
|
||||
type deletionHandler func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64
|
||||
|
||||
func ldbGenericReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, deleteFn deletionHandler) uint64 {
|
||||
func ldbGenericReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo, deleteFn deletionHandler) int64 {
|
||||
runtime.GC()
|
||||
|
||||
sort.Sort(fileList(fs)) // sort list on name, same as in the database
|
||||
@@ -197,7 +197,7 @@ func ldbGenericReplace(db *leveldb.DB, folder, device []byte, fs []protocol.File
|
||||
|
||||
moreDb := dbi.Next()
|
||||
fsi := 0
|
||||
var maxLocalVer uint64
|
||||
var maxLocalVer int64
|
||||
|
||||
for {
|
||||
var newName, oldName []byte
|
||||
@@ -288,9 +288,9 @@ func ldbGenericReplace(db *leveldb.DB, folder, device []byte, fs []protocol.File
|
||||
return maxLocalVer
|
||||
}
|
||||
|
||||
func ldbReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) uint64 {
|
||||
func ldbReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) int64 {
|
||||
// TODO: Return the remaining maxLocalVer?
|
||||
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) uint64 {
|
||||
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64 {
|
||||
// Database has a file that we are missing. Remove it.
|
||||
if debugDB {
|
||||
l.Debugf("delete; folder=%q device=%v name=%q", folder, protocol.DeviceIDFromBytes(device), name)
|
||||
@@ -304,8 +304,8 @@ func ldbReplace(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) u
|
||||
})
|
||||
}
|
||||
|
||||
func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) uint64 {
|
||||
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) uint64 {
|
||||
func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) int64 {
|
||||
return ldbGenericReplace(db, folder, device, fs, func(db dbReader, batch dbWriter, folder, device, name []byte, dbi iterator.Iterator) int64 {
|
||||
var tf FileInfoTruncated
|
||||
err := tf.UnmarshalXDR(dbi.Value())
|
||||
if err != nil {
|
||||
@@ -335,7 +335,7 @@ func ldbReplaceWithDelete(db *leveldb.DB, folder, device []byte, fs []protocol.F
|
||||
})
|
||||
}
|
||||
|
||||
func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) uint64 {
|
||||
func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) int64 {
|
||||
runtime.GC()
|
||||
|
||||
batch := new(leveldb.Batch)
|
||||
@@ -356,7 +356,7 @@ func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) ui
|
||||
snap.Release()
|
||||
}()
|
||||
|
||||
var maxLocalVer uint64
|
||||
var maxLocalVer int64
|
||||
for _, f := range fs {
|
||||
name := []byte(f.Name)
|
||||
fk := deviceKey(folder, device, name)
|
||||
@@ -406,7 +406,7 @@ func ldbUpdate(db *leveldb.DB, folder, device []byte, fs []protocol.FileInfo) ui
|
||||
return maxLocalVer
|
||||
}
|
||||
|
||||
func ldbInsert(batch dbWriter, folder, device []byte, file protocol.FileInfo) uint64 {
|
||||
func ldbInsert(batch dbWriter, folder, device []byte, file protocol.FileInfo) int64 {
|
||||
if debugDB {
|
||||
l.Debugf("insert; folder=%q device=%v %v", folder, protocol.DeviceIDFromBytes(device), file)
|
||||
}
|
||||
@@ -428,7 +428,7 @@ func ldbInsert(batch dbWriter, folder, device []byte, file protocol.FileInfo) ui
|
||||
// ldbUpdateGlobal adds this device+version to the version list for the given
|
||||
// file. If the device is already present in the list, the version is updated.
|
||||
// If the file does not have an entry in the global list, it is created.
|
||||
func ldbUpdateGlobal(db dbReader, batch dbWriter, folder, device, file []byte, version uint64) bool {
|
||||
func ldbUpdateGlobal(db dbReader, batch dbWriter, folder, device, file []byte, version int64) bool {
|
||||
if debugDB {
|
||||
l.Debugf("update global; folder=%q device=%v file=%q version=%d", folder, protocol.DeviceIDFromBytes(device), file, version)
|
||||
}
|
||||
@@ -798,7 +798,7 @@ outer:
|
||||
|
||||
have := false // If we have the file, any version
|
||||
need := false // If we have a lower version of the file
|
||||
var haveVersion uint64
|
||||
var haveVersion int64
|
||||
for _, v := range vl.versions {
|
||||
if bytes.Compare(v.device, device) == 0 {
|
||||
have = true
|
||||
|
||||
@@ -31,7 +31,7 @@ fileVersion Structure:
|
||||
|
||||
|
||||
struct fileVersion {
|
||||
unsigned hyper version;
|
||||
hyper version;
|
||||
opaque device<>;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (o fileVersion) AppendXDR(bs []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (o fileVersion) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint64(o.version)
|
||||
xw.WriteUint64(uint64(o.version))
|
||||
xw.WriteBytes(o.device)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func (o *fileVersion) UnmarshalXDR(bs []byte) error {
|
||||
}
|
||||
|
||||
func (o *fileVersion) decodeXDR(xr *xdr.Reader) error {
|
||||
o.version = xr.ReadUint64()
|
||||
o.version = int64(xr.ReadUint64())
|
||||
o.device = xr.ReadBytes()
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
)
|
||||
|
||||
type FileSet struct {
|
||||
localVersion map[protocol.DeviceID]uint64
|
||||
localVersion map[protocol.DeviceID]int64
|
||||
mutex sync.Mutex
|
||||
folder string
|
||||
db *leveldb.DB
|
||||
@@ -56,7 +56,7 @@ type Iterator func(f FileIntf) bool
|
||||
|
||||
func NewFileSet(folder string, db *leveldb.DB) *FileSet {
|
||||
var s = FileSet{
|
||||
localVersion: make(map[protocol.DeviceID]uint64),
|
||||
localVersion: make(map[protocol.DeviceID]int64),
|
||||
folder: folder,
|
||||
db: db,
|
||||
blockmap: NewBlockMap(db, folder),
|
||||
@@ -212,7 +212,7 @@ func (s *FileSet) Availability(file string) []protocol.DeviceID {
|
||||
return ldbAvailability(s.db, []byte(s.folder), []byte(osutil.NormalizedFilename(file)))
|
||||
}
|
||||
|
||||
func (s *FileSet) LocalVersion(device protocol.DeviceID) uint64 {
|
||||
func (s *FileSet) LocalVersion(device protocol.DeviceID) int64 {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.localVersion[device]
|
||||
|
||||
@@ -43,7 +43,7 @@ func genBlocks(n int) []protocol.BlockInfo {
|
||||
for j := range h {
|
||||
h[j] = byte(i + j)
|
||||
}
|
||||
b[i].Size = uint32(i)
|
||||
b[i].Size = int32(i)
|
||||
b[i].Hash = h
|
||||
}
|
||||
return b
|
||||
|
||||
@@ -29,9 +29,9 @@ type FileInfoTruncated struct {
|
||||
Name string // max:8192
|
||||
Flags uint32
|
||||
Modified int64
|
||||
Version uint64
|
||||
LocalVersion uint64
|
||||
NumBlocks uint32
|
||||
Version int64
|
||||
LocalVersion int64
|
||||
NumBlocks int32
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) String() string {
|
||||
@@ -44,7 +44,7 @@ func (f FileInfoTruncated) Size() int64 {
|
||||
if f.IsDeleted() || f.IsDirectory() {
|
||||
return 128
|
||||
}
|
||||
return BlocksToSize(f.NumBlocks)
|
||||
return BlocksToSize(int(f.NumBlocks))
|
||||
}
|
||||
|
||||
func (f FileInfoTruncated) IsDeleted() bool {
|
||||
@@ -67,7 +67,7 @@ func (f FileInfoTruncated) HasPermissionBits() bool {
|
||||
return f.Flags&protocol.FlagNoPermBits == 0
|
||||
}
|
||||
|
||||
func BlocksToSize(num uint32) int64 {
|
||||
func BlocksToSize(num int) int64 {
|
||||
if num < 2 {
|
||||
return protocol.BlockSize / 2
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ struct FileInfoTruncated {
|
||||
string Name<8192>;
|
||||
unsigned int Flags;
|
||||
hyper Modified;
|
||||
unsigned hyper Version;
|
||||
unsigned hyper LocalVersion;
|
||||
unsigned int NumBlocks;
|
||||
hyper Version;
|
||||
hyper LocalVersion;
|
||||
int NumBlocks;
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -84,9 +84,9 @@ func (o FileInfoTruncated) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteString(o.Name)
|
||||
xw.WriteUint32(o.Flags)
|
||||
xw.WriteUint64(uint64(o.Modified))
|
||||
xw.WriteUint64(o.Version)
|
||||
xw.WriteUint64(o.LocalVersion)
|
||||
xw.WriteUint32(o.NumBlocks)
|
||||
xw.WriteUint64(uint64(o.Version))
|
||||
xw.WriteUint64(uint64(o.LocalVersion))
|
||||
xw.WriteUint32(uint32(o.NumBlocks))
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ func (o *FileInfoTruncated) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Name = xr.ReadStringMax(8192)
|
||||
o.Flags = xr.ReadUint32()
|
||||
o.Modified = int64(xr.ReadUint64())
|
||||
o.Version = xr.ReadUint64()
|
||||
o.LocalVersion = xr.ReadUint64()
|
||||
o.NumBlocks = xr.ReadUint32()
|
||||
o.Version = int64(xr.ReadUint64())
|
||||
o.LocalVersion = int64(xr.ReadUint64())
|
||||
o.NumBlocks = int32(xr.ReadUint32())
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type EventType uint64
|
||||
type EventType int
|
||||
|
||||
const (
|
||||
Ping EventType = 1 << iota
|
||||
|
||||
@@ -21,11 +21,11 @@ import "sync"
|
||||
var Default = Clock{}
|
||||
|
||||
type Clock struct {
|
||||
val uint64
|
||||
val int64
|
||||
mut sync.Mutex
|
||||
}
|
||||
|
||||
func (c *Clock) Tick(v uint64) uint64 {
|
||||
func (c *Clock) Tick(v int64) int64 {
|
||||
c.mut.Lock()
|
||||
if v > c.val {
|
||||
c.val = v + 1
|
||||
|
||||
@@ -17,12 +17,12 @@ package lamport
|
||||
|
||||
import "testing"
|
||||
|
||||
var inputs = []uint64{0, 42, 2, 3, 4, 8, 9, 33, 44, 112, 100}
|
||||
var inputs = []int64{0, 42, 2, 3, 4, 8, 9, 33, 44, 112, 100}
|
||||
|
||||
func TestClock(t *testing.T) {
|
||||
c := Clock{}
|
||||
|
||||
var prev uint64
|
||||
var prev int64
|
||||
for _, input := range inputs {
|
||||
cur := c.Tick(input)
|
||||
if cur <= prev || cur <= input {
|
||||
|
||||
@@ -298,8 +298,6 @@ func (m *Model) FolderStatistics() map[string]stats.FolderStatistics {
|
||||
|
||||
// Returns the completion status, in percent, for the given device and folder.
|
||||
func (m *Model) Completion(device protocol.DeviceID, folder string) float64 {
|
||||
defer m.leveldbPanicWorkaround()
|
||||
|
||||
var tot int64
|
||||
|
||||
m.fmut.RLock()
|
||||
@@ -359,8 +357,6 @@ func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
|
||||
// GlobalSize returns the number of files, deleted files and total bytes for all
|
||||
// files in the global model.
|
||||
func (m *Model) GlobalSize(folder string) (nfiles, deleted int, bytes int64) {
|
||||
defer m.leveldbPanicWorkaround()
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
if rf, ok := m.folderFiles[folder]; ok {
|
||||
@@ -378,8 +374,6 @@ func (m *Model) GlobalSize(folder string) (nfiles, deleted int, bytes int64) {
|
||||
// LocalSize returns the number of files, deleted files and total bytes for all
|
||||
// files in the local folder.
|
||||
func (m *Model) LocalSize(folder string) (nfiles, deleted int, bytes int64) {
|
||||
defer m.leveldbPanicWorkaround()
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
if rf, ok := m.folderFiles[folder]; ok {
|
||||
@@ -399,8 +393,6 @@ func (m *Model) LocalSize(folder string) (nfiles, deleted int, bytes int64) {
|
||||
|
||||
// NeedSize returns the number and total size of currently needed files.
|
||||
func (m *Model) NeedSize(folder string) (nfiles int, bytes int64) {
|
||||
defer m.leveldbPanicWorkaround()
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
if rf, ok := m.folderFiles[folder]; ok {
|
||||
@@ -422,10 +414,9 @@ func (m *Model) NeedSize(folder string) (nfiles int, bytes int64) {
|
||||
// and to be queued on next puller iteration. Also takes a soft cap which is
|
||||
// only respected when adding files from the model rather than the runner queue.
|
||||
func (m *Model) NeedFolderFiles(folder string, max int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated) {
|
||||
defer m.leveldbPanicWorkaround()
|
||||
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
|
||||
if rf, ok := m.folderFiles[folder]; ok {
|
||||
var progress, queued, rest []db.FileInfoTruncated
|
||||
var seen map[string]bool
|
||||
@@ -710,9 +701,18 @@ func (m *Model) Close(device protocol.DeviceID, err error) {
|
||||
// Request returns the specified data segment by reading it from local disk.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, size int) ([]byte, error) {
|
||||
if offset < 0 || size < 0 {
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
|
||||
if !m.folderSharedWith(folder, deviceID) {
|
||||
l.Warnf("Request from %s for file %s in unshared folder %q", deviceID, name, folder)
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
|
||||
// Verify that the requested file exists in the local model.
|
||||
m.fmut.RLock()
|
||||
r, ok := m.folderFiles[folder]
|
||||
folderFiles, ok := m.folderFiles[folder]
|
||||
m.fmut.RUnlock()
|
||||
|
||||
if !ok {
|
||||
@@ -720,7 +720,7 @@ func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
|
||||
lf, ok := r.Get(protocol.LocalDeviceID, name)
|
||||
lf, ok := folderFiles.Get(protocol.LocalDeviceID, name)
|
||||
if !ok {
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
@@ -974,12 +974,12 @@ func sendIndexes(conn protocol.Connection, folder string, fs *db.FileSet, ignore
|
||||
}
|
||||
}
|
||||
|
||||
func sendIndexTo(initial bool, minLocalVer uint64, conn protocol.Connection, folder string, fs *db.FileSet, ignores *ignore.Matcher) (uint64, error) {
|
||||
func sendIndexTo(initial bool, minLocalVer int64, conn protocol.Connection, folder string, fs *db.FileSet, ignores *ignore.Matcher) (int64, error) {
|
||||
deviceID := conn.ID()
|
||||
name := conn.Name()
|
||||
batch := make([]protocol.FileInfo, 0, indexBatchSize)
|
||||
currentBatchSize := 0
|
||||
maxLocalVer := uint64(0)
|
||||
maxLocalVer := int64(0)
|
||||
var err error
|
||||
|
||||
fs.WithHave(protocol.LocalDeviceID, func(fi db.FileIntf) bool {
|
||||
@@ -1353,7 +1353,7 @@ func (m *Model) Override(folder string) {
|
||||
// CurrentLocalVersion returns the change version for the given folder.
|
||||
// This is guaranteed to increment if the contents of the local folder has
|
||||
// changed.
|
||||
func (m *Model) CurrentLocalVersion(folder string) uint64 {
|
||||
func (m *Model) CurrentLocalVersion(folder string) int64 {
|
||||
m.fmut.RLock()
|
||||
fs, ok := m.folderFiles[folder]
|
||||
m.fmut.RUnlock()
|
||||
@@ -1369,7 +1369,7 @@ func (m *Model) CurrentLocalVersion(folder string) uint64 {
|
||||
// RemoteLocalVersion returns the change version for the given folder, as
|
||||
// sent by remote peers. This is guaranteed to increment if the contents of
|
||||
// the remote or global folder has changed.
|
||||
func (m *Model) RemoteLocalVersion(folder string) uint64 {
|
||||
func (m *Model) RemoteLocalVersion(folder string) int64 {
|
||||
m.fmut.RLock()
|
||||
defer m.fmut.RUnlock()
|
||||
|
||||
@@ -1380,7 +1380,7 @@ func (m *Model) RemoteLocalVersion(folder string) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var ver uint64
|
||||
var ver int64
|
||||
for _, n := range m.folderDevices[folder] {
|
||||
ver += fs.LocalVersion(n)
|
||||
}
|
||||
@@ -1426,27 +1426,6 @@ func (m *Model) String() string {
|
||||
return fmt.Sprintf("model@%p", m)
|
||||
}
|
||||
|
||||
func (m *Model) leveldbPanicWorkaround() {
|
||||
// When an inconsistency is detected in leveldb we panic(). This is
|
||||
// appropriate because it should never happen, but currently it does for
|
||||
// some reason. However it only seems to trigger in the asynchronous full-
|
||||
// database scans that happen due to REST and usage-reporting calls. In
|
||||
// those places we defer to this workaround to catch the panic instead of
|
||||
// taking down syncthing.
|
||||
|
||||
// This is just a band-aid and should be removed as soon as we have found
|
||||
// a real root cause.
|
||||
|
||||
if pnc := recover(); pnc != nil {
|
||||
if err, ok := pnc.(error); ok && strings.Contains(err.Error(), "leveldb") {
|
||||
l.Infoln("recovered:", err)
|
||||
} else {
|
||||
// Any non-leveldb error is genuine and should continue panicing.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func symlinkInvalid(isLink bool) bool {
|
||||
if !symlinks.Supported && isLink {
|
||||
SymlinkWarning.Do(func() {
|
||||
|
||||
@@ -68,18 +68,41 @@ func init() {
|
||||
|
||||
func TestRequest(t *testing.T) {
|
||||
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
|
||||
m := NewModel(config.Wrap("/tmp/test", config.Configuration{}), "device", "syncthing", "dev", db)
|
||||
m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata"})
|
||||
|
||||
// device1 shares default, but device2 doesn't
|
||||
m.AddFolder(config.FolderConfiguration{ID: "default", Path: "testdata", Devices: []config.FolderDeviceConfiguration{{DeviceID: device1}}})
|
||||
m.ScanFolder("default")
|
||||
|
||||
// Existing, shared file
|
||||
bs, err := m.Request(device1, "default", "foo", 0, 6)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Error(err)
|
||||
}
|
||||
if bytes.Compare(bs, []byte("foobar")) != 0 {
|
||||
t.Errorf("Incorrect data from request: %q", string(bs))
|
||||
}
|
||||
|
||||
// Existing, nonshared file
|
||||
bs, err = m.Request(device2, "default", "foo", 0, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
|
||||
// Nonexistent file
|
||||
bs, err = m.Request(device1, "default", "nonexistent", 0, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
|
||||
// Shared folder, but disallowed file name
|
||||
bs, err = m.Request(device1, "default", "../walk.go", 0, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
@@ -87,6 +110,33 @@ func TestRequest(t *testing.T) {
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
|
||||
// Larger block than available
|
||||
bs, err = m.Request(device1, "default", "foo", 0, 42)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
|
||||
// Negative offset
|
||||
bs, err = m.Request(device1, "default", "foo", -4, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
|
||||
// Negative size
|
||||
bs, err = m.Request(device1, "default", "foo", 4, -4)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
if bs != nil {
|
||||
t.Errorf("Unexpected non nil data on insecure file read: %q", string(bs))
|
||||
}
|
||||
}
|
||||
|
||||
func genFiles(n int) []protocol.FileInfo {
|
||||
|
||||
@@ -100,7 +100,7 @@ func (p *Puller) Serve() {
|
||||
p.model.setState(p.folder, FolderIdle)
|
||||
}()
|
||||
|
||||
var prevVer uint64
|
||||
var prevVer int64
|
||||
var prevIgnoreHash string
|
||||
|
||||
// We don't start pulling files until a scan has been completed.
|
||||
@@ -297,7 +297,9 @@ func (p *Puller) pullerIteration(ignores *ignore.Matcher) int {
|
||||
|
||||
changed := 0
|
||||
|
||||
var deletions []protocol.FileInfo
|
||||
fileDeletions := map[string]protocol.FileInfo{}
|
||||
dirDeletions := []protocol.FileInfo{}
|
||||
buckets := map[string][]protocol.FileInfo{}
|
||||
|
||||
folderFiles.WithNeed(protocol.LocalDeviceID, func(intf db.FileIntf) bool {
|
||||
|
||||
@@ -327,7 +329,20 @@ func (p *Puller) pullerIteration(ignores *ignore.Matcher) int {
|
||||
switch {
|
||||
case file.IsDeleted():
|
||||
// A deleted file, directory or symlink
|
||||
deletions = append(deletions, file)
|
||||
if file.IsDirectory() {
|
||||
dirDeletions = append(dirDeletions, file)
|
||||
} else {
|
||||
fileDeletions[file.Name] = file
|
||||
df, ok := p.model.CurrentFolderFile(p.folder, file.Name)
|
||||
// Local file can be already deleted, but with a lower version
|
||||
// number, hence the deletion coming in again as part of
|
||||
// WithNeed
|
||||
if ok && !df.IsDeleted() {
|
||||
// Put files into buckets per first hash
|
||||
key := string(df.Blocks[0].Hash)
|
||||
buckets[key] = append(buckets[key], df)
|
||||
}
|
||||
}
|
||||
case file.IsDirectory() && !file.IsSymlink():
|
||||
// A new or changed directory
|
||||
p.handleDir(file)
|
||||
@@ -341,17 +356,44 @@ func (p *Puller) pullerIteration(ignores *ignore.Matcher) int {
|
||||
return true
|
||||
})
|
||||
|
||||
nextFile:
|
||||
for {
|
||||
fileName, ok := p.queue.Pop()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if f, ok := p.model.CurrentGlobalFile(p.folder, fileName); ok {
|
||||
p.handleFile(f, copyChan, finisherChan)
|
||||
} else {
|
||||
|
||||
f, ok := p.model.CurrentGlobalFile(p.folder, fileName)
|
||||
if !ok {
|
||||
// File is no longer in the index. Mark it as done and drop it.
|
||||
p.queue.Done(fileName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Local file can be already deleted, but with a lower version
|
||||
// number, hence the deletion coming in again as part of
|
||||
// WithNeed
|
||||
if !f.IsSymlink() && !f.IsDeleted() {
|
||||
key := string(f.Blocks[0].Hash)
|
||||
for i, candidate := range buckets[key] {
|
||||
if scanner.BlocksEqual(candidate.Blocks, f.Blocks) {
|
||||
// Remove the candidate from the bucket
|
||||
l := len(buckets[key]) - 1
|
||||
buckets[key][i] = buckets[key][l]
|
||||
buckets[key] = buckets[key][:l]
|
||||
// Remove the pending deletion (as we perform it by renaming)
|
||||
delete(fileDeletions, candidate.Name)
|
||||
|
||||
p.renameFile(candidate, f)
|
||||
|
||||
p.queue.Done(fileName)
|
||||
continue nextFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not a rename or a symlink, deal with it.
|
||||
p.handleFile(f, copyChan, finisherChan)
|
||||
}
|
||||
|
||||
// Signal copy and puller routines that we are done with the in data for
|
||||
@@ -367,13 +409,12 @@ func (p *Puller) pullerIteration(ignores *ignore.Matcher) int {
|
||||
// Wait for the finisherChan to finish.
|
||||
doneWg.Wait()
|
||||
|
||||
for i := range deletions {
|
||||
deletion := deletions[len(deletions)-i-1]
|
||||
if deletion.IsDirectory() {
|
||||
p.deleteDir(deletion)
|
||||
} else {
|
||||
p.deleteFile(deletion)
|
||||
}
|
||||
for _, file := range fileDeletions {
|
||||
p.deleteFile(file)
|
||||
}
|
||||
|
||||
for i := range dirDeletions {
|
||||
p.deleteDir(dirDeletions[len(dirDeletions)-i-1])
|
||||
}
|
||||
|
||||
return changed
|
||||
@@ -479,6 +520,40 @@ func (p *Puller) deleteFile(file protocol.FileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// renameFile attempts to rename an existing file to a destination
|
||||
// and set the right attributes on it.
|
||||
func (p *Puller) renameFile(source, target protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugln(p, "taking rename shortcut", source.Name, "->", target.Name)
|
||||
}
|
||||
|
||||
from := filepath.Join(p.dir, source.Name)
|
||||
to := filepath.Join(p.dir, target.Name)
|
||||
|
||||
var err error
|
||||
if p.versioner != nil {
|
||||
err = osutil.Copy(from, to)
|
||||
if err == nil {
|
||||
err = osutil.InWritableDir(p.versioner.Archive, from)
|
||||
}
|
||||
} else {
|
||||
err = osutil.TryRename(from, to)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
l.Infof("Puller (folder %q, file %q): rename from %q: %v", p.folder, target.Name, source.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Fix-up the metadata, and update the local index of the target file
|
||||
p.shortcutFile(target)
|
||||
|
||||
// Source file already has the delete bit set.
|
||||
// Because we got rid of the file (by renaming it), we just need to update
|
||||
// the index, and we're done with it.
|
||||
p.model.updateLocal(p.folder, source)
|
||||
}
|
||||
|
||||
// handleFile queues the copies and pulls as necessary for a single new or
|
||||
// changed file.
|
||||
func (p *Puller) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksState, finisherChan chan<- *sharedPullerState) {
|
||||
@@ -493,7 +568,7 @@ func (p *Puller) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksSt
|
||||
}
|
||||
p.queue.Done(file.Name)
|
||||
if file.IsSymlink() {
|
||||
p.shortcutSymlink(curFile, file)
|
||||
p.shortcutSymlink(file)
|
||||
} else {
|
||||
p.shortcutFile(file)
|
||||
}
|
||||
@@ -548,9 +623,9 @@ func (p *Puller) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocksSt
|
||||
folder: p.folder,
|
||||
tempName: tempName,
|
||||
realName: realName,
|
||||
copyTotal: uint32(len(blocks)),
|
||||
copyNeeded: uint32(len(blocks)),
|
||||
reused: uint32(reused),
|
||||
copyTotal: len(blocks),
|
||||
copyNeeded: len(blocks),
|
||||
reused: reused,
|
||||
}
|
||||
|
||||
if debug {
|
||||
@@ -595,7 +670,7 @@ func (p *Puller) shortcutFile(file protocol.FileInfo) {
|
||||
}
|
||||
|
||||
// shortcutSymlink changes the symlinks type if necessery.
|
||||
func (p *Puller) shortcutSymlink(curFile, file protocol.FileInfo) {
|
||||
func (p *Puller) shortcutSymlink(file protocol.FileInfo) {
|
||||
err := symlinks.ChangeType(filepath.Join(p.dir, file.Name), file.Flags)
|
||||
if err != nil {
|
||||
l.Infof("Puller (folder %q, file %q): symlink shortcut: %v", p.folder, file.Name, err)
|
||||
@@ -645,7 +720,7 @@ func (p *Puller) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pullBl
|
||||
|
||||
for _, block := range state.blocks {
|
||||
buf = buf[:int(block.Size)]
|
||||
found := p.model.finder.Iterate(block.Hash, func(folder, file string, index uint32) bool {
|
||||
found := p.model.finder.Iterate(block.Hash, func(folder, file string, index int32) bool {
|
||||
path := filepath.Join(folderRoots[folder], file)
|
||||
|
||||
var fd *os.File
|
||||
|
||||
@@ -199,7 +199,7 @@ func TestCopierFinder(t *testing.T) {
|
||||
// Update index
|
||||
m.updateLocal("default", existingFile)
|
||||
|
||||
iterFn := func(folder, file string, index uint32) bool {
|
||||
iterFn := func(folder, file string, index int32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ func TestCopierFinder(t *testing.T) {
|
||||
|
||||
// Test that updating a file removes it's old blocks from the blockmap
|
||||
func TestCopierCleanup(t *testing.T) {
|
||||
iterFn := func(folder, file string, index uint32) bool {
|
||||
iterFn := func(folder, file string, index int32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ func TestLastResortPulling(t *testing.T) {
|
||||
// with a different name (causing to copy that particular block)
|
||||
file.Name = "newfile"
|
||||
|
||||
iterFn := func(folder, file string, index uint32) bool {
|
||||
iterFn := func(folder, file string, index int32) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ func TestDeregisterOnFailInCopy(t *testing.T) {
|
||||
blocks[5], blocks[0], blocks[0], blocks[8],
|
||||
},
|
||||
}
|
||||
defer os.Remove(defTempNamer.TempName("filex"))
|
||||
defer os.Remove("testdata/" + defTempNamer.TempName("filex"))
|
||||
|
||||
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
cw := config.Wrap("/tmp/test", config.Configuration{})
|
||||
@@ -481,7 +481,7 @@ func TestDeregisterOnFailInPull(t *testing.T) {
|
||||
blocks[5], blocks[0], blocks[0], blocks[8],
|
||||
},
|
||||
}
|
||||
defer os.Remove(defTempNamer.TempName("filex"))
|
||||
defer os.Remove("testdata/" + defTempNamer.TempName("filex"))
|
||||
|
||||
db, _ := leveldb.Open(storage.NewMemStorage(), nil)
|
||||
cw := config.Wrap("/tmp/test", config.Configuration{})
|
||||
|
||||
@@ -33,27 +33,27 @@ type sharedPullerState struct {
|
||||
folder string
|
||||
tempName string
|
||||
realName string
|
||||
reused uint32 // Number of blocks reused from temporary file
|
||||
reused int // Number of blocks reused from temporary file
|
||||
|
||||
// Mutable, must be locked for access
|
||||
err error // The first error we hit
|
||||
fd *os.File // The fd of the temp file
|
||||
copyTotal uint32 // Total number of copy actions for the whole job
|
||||
pullTotal uint32 // Total number of pull actions for the whole job
|
||||
copyOrigin uint32 // Number of blocks copied from the original file
|
||||
copyNeeded uint32 // Number of copy actions still pending
|
||||
pullNeeded uint32 // Number of block pulls still pending
|
||||
copyTotal int // Total number of copy actions for the whole job
|
||||
pullTotal int // Total number of pull actions for the whole job
|
||||
copyOrigin int // Number of blocks copied from the original file
|
||||
copyNeeded int // Number of copy actions still pending
|
||||
pullNeeded int // Number of block pulls still pending
|
||||
mut sync.Mutex // Protects the above
|
||||
}
|
||||
|
||||
// A momentary state representing the progress of the puller
|
||||
type pullerProgress struct {
|
||||
Total uint32
|
||||
Reused uint32
|
||||
CopiedFromOrigin uint32
|
||||
CopiedFromElsewhere uint32
|
||||
Pulled uint32
|
||||
Pulling uint32
|
||||
Total int
|
||||
Reused int
|
||||
CopiedFromOrigin int
|
||||
CopiedFromElsewhere int
|
||||
Pulled int
|
||||
Pulling int
|
||||
BytesDone int64
|
||||
BytesTotal int64
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package osutil
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -32,34 +33,36 @@ var ErrNoHome = errors.New("No home directory found - set $HOME (or the platform
|
||||
// often enough that there is any contention on this lock.
|
||||
var renameLock sync.Mutex
|
||||
|
||||
// Rename renames a file, while trying hard to succeed on various systems by
|
||||
// temporarily tweaking directory permissions and removing the destination
|
||||
// file when necessary. Will make sure to delete the from file if the
|
||||
// operation fails, so use only for situations like committing a temp file to
|
||||
// it's final location.
|
||||
func Rename(from, to string) error {
|
||||
// TryRename renames a file, leaving source file intact in case of failure.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func TryRename(from, to string) error {
|
||||
renameLock.Lock()
|
||||
defer renameLock.Unlock()
|
||||
|
||||
// Make sure the destination directory is writeable
|
||||
toDir := filepath.Dir(to)
|
||||
if info, err := os.Stat(toDir); err == nil && info.IsDir() && info.Mode()&0200 == 0 {
|
||||
os.Chmod(toDir, 0755)
|
||||
defer os.Chmod(toDir, info.Mode())
|
||||
}
|
||||
|
||||
// On Windows, make sure the destination file is writeable (or we can't delete it)
|
||||
if runtime.GOOS == "windows" {
|
||||
os.Chmod(to, 0666)
|
||||
err := os.Remove(to)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return withPreparedTarget(to, func() error {
|
||||
return os.Rename(from, to)
|
||||
})
|
||||
}
|
||||
|
||||
// Rename moves a temporary file to it's final place.
|
||||
// Will make sure to delete the from file if the operation fails, so use only
|
||||
// for situations like committing a temp file to it's final location.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func Rename(from, to string) error {
|
||||
// Don't leave a dangling temp file in case of rename error
|
||||
defer os.Remove(from)
|
||||
return os.Rename(from, to)
|
||||
return TryRename(from, to)
|
||||
}
|
||||
|
||||
// Copy copies the file content from source to destination.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func Copy(from, to string) (err error) {
|
||||
return withPreparedTarget(to, func() error {
|
||||
return copyFileContents(from, to)
|
||||
})
|
||||
}
|
||||
|
||||
// InWritableDir calls fn(path), while making sure that the directory
|
||||
@@ -123,3 +126,51 @@ func getHomeDir() (string, error) {
|
||||
|
||||
return home, nil
|
||||
}
|
||||
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func withPreparedTarget(to string, f func() error) error {
|
||||
// Make sure the destination directory is writeable
|
||||
toDir := filepath.Dir(to)
|
||||
if info, err := os.Stat(toDir); err == nil && info.IsDir() && info.Mode()&0200 == 0 {
|
||||
os.Chmod(toDir, 0755)
|
||||
defer os.Chmod(toDir, info.Mode())
|
||||
}
|
||||
|
||||
// On Windows, make sure the destination file is writeable (or we can't delete it)
|
||||
if runtime.GOOS == "windows" {
|
||||
os.Chmod(to, 0666)
|
||||
err := os.Remove(to)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
// copyFileContents copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFileContents(src, dst string) (err error) {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
cerr := out.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return
|
||||
}
|
||||
err = out.Sync()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func Blocks(r io.Reader, blocksize int, sizehint int64) ([]protocol.BlockInfo, e
|
||||
}
|
||||
|
||||
b := protocol.BlockInfo{
|
||||
Size: uint32(n),
|
||||
Size: int32(n),
|
||||
Offset: offset,
|
||||
Hash: hf.Sum(nil),
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func upgradeToURL(binary string, url string) error {
|
||||
}
|
||||
|
||||
old := binary + ".old"
|
||||
_ = os.Remove(old)
|
||||
os.Remove(old)
|
||||
err = os.Rename(binary, old)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -193,20 +193,16 @@ fileLoop:
|
||||
}
|
||||
}
|
||||
|
||||
if tempName != "" && actualMD5 != "" {
|
||||
if tempName != "" {
|
||||
// We found and saved something to disk.
|
||||
if expectedMD5 == "" {
|
||||
if debug {
|
||||
l.Debugln("there is no md5 to compare with")
|
||||
}
|
||||
} else if actualMD5 != expectedMD5 {
|
||||
// There was an md5 file included in the archive, and it doesn't
|
||||
// match what we just wrote to disk.
|
||||
return "", fmt.Errorf("incorrect MD5 checksum")
|
||||
if expectedMD5 == "" || actualMD5 == expectedMD5 {
|
||||
return tempName, nil
|
||||
}
|
||||
return tempName, nil
|
||||
os.Remove(tempName)
|
||||
// There was an md5 file included in the archive, and it doesn't
|
||||
// match what we just wrote to disk.
|
||||
return "", fmt.Errorf("incorrect MD5 checksum")
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no upgrade found")
|
||||
}
|
||||
|
||||
@@ -274,20 +270,16 @@ fileLoop:
|
||||
}
|
||||
}
|
||||
|
||||
if tempName != "" && actualMD5 != "" {
|
||||
if tempName != "" {
|
||||
// We found and saved something to disk.
|
||||
if expectedMD5 == "" {
|
||||
if debug {
|
||||
l.Debugln("there is no md5 to compare with")
|
||||
}
|
||||
} else if actualMD5 != expectedMD5 {
|
||||
// There was an md5 file included in the archive, and it doesn't
|
||||
// match what we just wrote to disk.
|
||||
return "", fmt.Errorf("incorrect MD5 checksum")
|
||||
if expectedMD5 == "" || actualMD5 == expectedMD5 {
|
||||
return tempName, nil
|
||||
}
|
||||
return tempName, nil
|
||||
os.Remove(tempName)
|
||||
// There was an md5 file included in the archive, and it doesn't
|
||||
// match what we just wrote to disk.
|
||||
return "", fmt.Errorf("incorrect MD5 checksum")
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("No upgrade found")
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<lenientMtimes>false</lenientMtimes>
|
||||
<copiers>1</copiers>
|
||||
<pullers>16</pullers>
|
||||
<finishers>1</finishers>
|
||||
<hashers>0</hashers>
|
||||
</folder>
|
||||
<folder id="s12" path="s12-2" ro="false" rescanIntervalS="15" ignorePerms="false">
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU"></device>
|
||||
@@ -16,7 +16,7 @@
|
||||
<lenientMtimes>false</lenientMtimes>
|
||||
<copiers>1</copiers>
|
||||
<pullers>16</pullers>
|
||||
<finishers>1</finishers>
|
||||
<hashers>0</hashers>
|
||||
</folder>
|
||||
<folder id="s23" path="s23-2" ro="false" rescanIntervalS="15" ignorePerms="false">
|
||||
<device id="JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU"></device>
|
||||
@@ -25,7 +25,7 @@
|
||||
<lenientMtimes>false</lenientMtimes>
|
||||
<copiers>1</copiers>
|
||||
<pullers>16</pullers>
|
||||
<finishers>1</finishers>
|
||||
<hashers>0</hashers>
|
||||
</folder>
|
||||
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="true" introducer="false">
|
||||
<address>127.0.0.1:22001</address>
|
||||
|
||||
@@ -105,7 +105,7 @@ func testSyncCluster(t *testing.T) {
|
||||
}
|
||||
|
||||
// We'll use this file for appending data without modifying the time stamp.
|
||||
fd, err := os.Create("s1/appendfile")
|
||||
fd, err := os.Create("s1/test-appendfile")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -207,12 +207,12 @@ func testSyncCluster(t *testing.T) {
|
||||
break
|
||||
}
|
||||
|
||||
// Alter the "appendfile" without changing it's modification time. Sneaky!
|
||||
fi, err := os.Stat("s1/appendfile")
|
||||
// Alter the "test-appendfile" without changing it's modification time. Sneaky!
|
||||
fi, err := os.Stat("s1/test-appendfile")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fd, err := os.OpenFile("s1/appendfile", os.O_APPEND|os.O_WRONLY, 0644)
|
||||
fd, err := os.OpenFile("s1/test-appendfile", os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func testSyncCluster(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Chtimes("s1/appendfile", fi.ModTime(), fi.ModTime())
|
||||
err = os.Chtimes("s1/test-appendfile", fi.ModTime(), fi.ModTime())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
15
test/util.go
15
test/util.go
@@ -116,6 +116,10 @@ func alterFiles(dir string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(filepath.Base(path), "test-") {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch filepath.Base(path) {
|
||||
case ".stfolder":
|
||||
return nil
|
||||
@@ -161,6 +165,17 @@ func alterFiles(dir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case r < 0.3 && comps > 1 && (info.Mode().IsRegular() || rand.Float64() < 0.2):
|
||||
rpath := filepath.Dir(path)
|
||||
if rand.Float64() < 0.2 {
|
||||
for move := rand.Intn(comps - 1); move > 0; move-- {
|
||||
rpath = filepath.Join(rpath, "..")
|
||||
}
|
||||
}
|
||||
err = os.Rename(path, filepath.Join(rpath, randomName()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user