Handle sparse files (fixes #245)

This commit is contained in:
Jakob Borg
2015-11-21 16:30:53 +01:00
parent f7ad97918a
commit d46f267663
6 changed files with 136 additions and 1 deletions

View File

@@ -91,6 +91,7 @@ type rwFolder struct {
maxConflicts int
sleep time.Duration
pause time.Duration
allowSparse bool
stop chan struct{}
queue *jobQueue
@@ -125,6 +126,7 @@ func newRWFolder(m *Model, shortID uint64, cfg config.FolderConfiguration) *rwFo
shortID: shortID,
order: cfg.Order,
maxConflicts: cfg.MaxConflicts,
allowSparse: !cfg.DisableSparseFiles,
stop: make(chan struct{}),
queue: newJobQueue(),
@@ -1027,6 +1029,7 @@ func (p *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
ignorePerms: p.ignorePermissions(file),
version: curFile.Version,
mut: sync.NewMutex(),
sparse: p.allowSparse,
}
l.Debugf("%v need file %s; copy %d, reused %v", p, file.Name, len(blocks), reused)
@@ -1113,6 +1116,18 @@ func (p *rwFolder) copierRoutine(in <-chan copyBlocksState, pullChan chan<- pull
p.model.fmut.RUnlock()
for _, block := range state.blocks {
if p.allowSparse && state.reused == 0 && block.IsEmpty() {
// The block is a block of all zeroes, and we are not reusing
// a temp file, so there is no need to do anything with it.
// If we were reusing a temp file and had this block to copy,
// it would be because the block in the temp file was *not* a
// block of all zeroes, so then we should not skip it.
// Pretend we copied it.
state.copiedFromOrigin()
continue
}
buf = buf[:int(block.Size)]
found := p.model.finder.Iterate(folders, block.Hash, func(folder, file string, index int32) bool {
fd, err := os.Open(filepath.Join(folderRoots[folder], file))
@@ -1185,6 +1200,14 @@ func (p *rwFolder) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPul
continue
}
if p.allowSparse && state.reused == 0 && state.block.IsEmpty() {
// There is no need to request a block of all zeroes. Pretend we
// requested it and handled it correctly.
state.pullDone()
out <- state.sharedPullerState
continue
}
var lastError error
potentialDevices := p.model.Availability(p.folder, state.file.Name)
for {

View File

@@ -27,6 +27,7 @@ type sharedPullerState struct {
reused int // Number of blocks reused from temporary file
ignorePerms bool
version protocol.Vector // The current (old) version
sparse bool
// Mutable, must be locked for access
err error // The first error we hit
@@ -138,6 +139,15 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
return nil, err
}
if s.sparse {
// Truncate sets the size of the file. This creates a sparse file or a
// space reservation, depending on the underlying filesystem.
if err := fd.Truncate(s.file.Size()); err != nil {
s.failLocked("dst truncate", err)
return nil, err
}
}
// Same fd will be used by all writers
s.fd = fd