Simple file versioning (fixes #218)
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/calmh/syncthing/config"
|
||||
"github.com/calmh/syncthing/files"
|
||||
"github.com/calmh/syncthing/lamport"
|
||||
"github.com/calmh/syncthing/osutil"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
@@ -716,7 +717,7 @@ func (m *Model) saveIndex(repo string, dir string, fs []protocol.FileInfo) {
|
||||
gzw.Close()
|
||||
idxf.Close()
|
||||
|
||||
Rename(name+".tmp", name)
|
||||
osutil.Rename(name+".tmp", name)
|
||||
}
|
||||
|
||||
func (m *Model) loadIndex(repo string, dir string) []protocol.FileInfo {
|
||||
|
||||
@@ -10,8 +10,10 @@ import (
|
||||
"github.com/calmh/syncthing/buffers"
|
||||
"github.com/calmh/syncthing/cid"
|
||||
"github.com/calmh/syncthing/config"
|
||||
"github.com/calmh/syncthing/osutil"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
"github.com/calmh/syncthing/versioner"
|
||||
)
|
||||
|
||||
type requestResult struct {
|
||||
@@ -71,6 +73,7 @@ type puller struct {
|
||||
requestSlots chan bool
|
||||
blocks chan bqBlock
|
||||
requestResults chan requestResult
|
||||
versioner versioner.Versioner
|
||||
}
|
||||
|
||||
func newPuller(repoCfg config.RepositoryConfiguration, model *Model, slots int, cfg *config.Configuration) *puller {
|
||||
@@ -86,6 +89,14 @@ func newPuller(repoCfg config.RepositoryConfiguration, model *Model, slots int,
|
||||
requestResults: make(chan requestResult),
|
||||
}
|
||||
|
||||
if len(repoCfg.Versioning.Type) > 0 {
|
||||
factory, ok := versioner.Factories[repoCfg.Versioning.Type]
|
||||
if !ok {
|
||||
l.Fatalf("Requested versioning type %q that does not exist", repoCfg.Versioning.Type)
|
||||
}
|
||||
p.versioner = factory(repoCfg.Versioning.Params)
|
||||
}
|
||||
|
||||
if slots > 0 {
|
||||
// Read/write
|
||||
for i := 0; i < slots; i++ {
|
||||
@@ -221,6 +232,10 @@ func (p *puller) fixupDirectories() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if filepath.Base(rn) == ".stversions" {
|
||||
return nil
|
||||
}
|
||||
|
||||
cur := p.model.CurrentRepoFile(p.repoCfg.ID, rn)
|
||||
if cur.Name != rn {
|
||||
// No matching dir in current list; weird
|
||||
@@ -284,10 +299,10 @@ func (p *puller) fixupDirectories() {
|
||||
l.Debugln("delete dir:", dir)
|
||||
}
|
||||
err := os.Remove(dir)
|
||||
if err != nil {
|
||||
l.Warnln(err)
|
||||
} else {
|
||||
if err == nil {
|
||||
deleted++
|
||||
} else if p.versioner == nil { // Failures are expected in the presence of versioning
|
||||
l.Warnln(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +400,7 @@ func (p *puller) handleBlock(b bqBlock) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
defTempNamer.Hide(of.temp)
|
||||
osutil.HideFile(of.temp)
|
||||
}
|
||||
|
||||
if of.err != nil {
|
||||
@@ -524,7 +539,11 @@ func (p *puller) handleEmptyBlock(b bqBlock) {
|
||||
}
|
||||
os.Remove(of.temp)
|
||||
os.Chmod(of.filepath, 0666)
|
||||
if err := os.Remove(of.filepath); err == nil || os.IsNotExist(err) {
|
||||
if p.versioner != nil {
|
||||
if err := p.versioner.Archive(of.filepath); err == nil {
|
||||
p.model.updateLocal(p.repoCfg.ID, f)
|
||||
}
|
||||
} else if err := os.Remove(of.filepath); err == nil || os.IsNotExist(err) {
|
||||
p.model.updateLocal(p.repoCfg.ID, f)
|
||||
}
|
||||
} else {
|
||||
@@ -540,8 +559,8 @@ func (p *puller) handleEmptyBlock(b bqBlock) {
|
||||
delete(p.openFiles, f.Name)
|
||||
return
|
||||
}
|
||||
defTempNamer.Show(of.temp)
|
||||
if Rename(of.temp, of.filepath) == nil {
|
||||
osutil.ShowFile(of.temp)
|
||||
if osutil.Rename(of.temp, of.filepath) == nil {
|
||||
p.model.updateLocal(p.repoCfg.ID, f)
|
||||
}
|
||||
}
|
||||
@@ -614,11 +633,23 @@ func (p *puller) closeFile(f scanner.File) {
|
||||
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
|
||||
}
|
||||
}
|
||||
defTempNamer.Show(of.temp)
|
||||
|
||||
osutil.ShowFile(of.temp)
|
||||
|
||||
if p.versioner != nil {
|
||||
err := p.versioner.Archive(of.filepath)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugf("pull: rename %q / %q: %q", p.repoCfg.ID, f.Name, of.filepath)
|
||||
}
|
||||
if err := Rename(of.temp, of.filepath); err == nil {
|
||||
if err := osutil.Rename(of.temp, of.filepath); err == nil {
|
||||
p.model.updateLocal(p.repoCfg.ID, f)
|
||||
} else {
|
||||
l.Debugf("pull: error: %q / %q: %v", p.repoCfg.ID, f.Name, err)
|
||||
|
||||
@@ -23,11 +23,3 @@ func (t tempNamer) TempName(name string) string {
|
||||
tname := fmt.Sprintf("%s.%s", t.prefix, filepath.Base(name))
|
||||
return filepath.Join(tdir, tname)
|
||||
}
|
||||
|
||||
func (t tempNamer) Hide(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t tempNamer) Show(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type tempNamer struct {
|
||||
@@ -24,33 +23,3 @@ func (t tempNamer) TempName(name string) string {
|
||||
tname := fmt.Sprintf("%s.%s.tmp", t.prefix, filepath.Base(name))
|
||||
return filepath.Join(tdir, tname)
|
||||
}
|
||||
|
||||
func (t tempNamer) Hide(path string) error {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs, err := syscall.GetFileAttributes(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs |= syscall.FILE_ATTRIBUTE_HIDDEN
|
||||
return syscall.SetFileAttributes(p, attrs)
|
||||
}
|
||||
|
||||
func (t tempNamer) Show(path string) error {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs, err := syscall.GetFileAttributes(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attrs &^= syscall.FILE_ATTRIBUTE_HIDDEN
|
||||
return syscall.SetFileAttributes(p, attrs)
|
||||
}
|
||||
|
||||
@@ -2,26 +2,12 @@ package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
func Rename(from, to string) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
os.Chmod(to, 0666) // Make sure the file is user writeable
|
||||
err := os.Remove(to)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
l.Warnln(err)
|
||||
}
|
||||
}
|
||||
defer os.Remove(from) // Don't leave a dangling temp file in case of rename error
|
||||
return os.Rename(from, to)
|
||||
}
|
||||
|
||||
func fileFromFileInfo(f protocol.FileInfo) scanner.File {
|
||||
var blocks = make([]scanner.Block, len(f.Blocks))
|
||||
var offset int64
|
||||
|
||||
Reference in New Issue
Block a user