mount: fix retry on network failure when reading off crypt - fixes #1042
This commit is contained in:
parent
07ebf35987
commit
a6b4065e13
1 changed files with 42 additions and 34 deletions
|
@ -20,7 +20,6 @@ type ReadFileHandle struct {
|
||||||
o fs.Object
|
o fs.Object
|
||||||
readCalled bool // set if read has been called
|
readCalled bool // set if read has been called
|
||||||
offset int64
|
offset int64
|
||||||
retries int // number of times we have retried
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) {
|
func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) {
|
||||||
|
@ -44,11 +43,13 @@ var _ fusefs.HandleReader = (*ReadFileHandle)(nil)
|
||||||
|
|
||||||
// seek to a new offset
|
// seek to a new offset
|
||||||
//
|
//
|
||||||
|
// if reopen is true, then we won't attempt to use an io.Seeker interface
|
||||||
|
//
|
||||||
// Must be called with fh.mu held
|
// Must be called with fh.mu held
|
||||||
func (fh *ReadFileHandle) seek(offset int64) error {
|
func (fh *ReadFileHandle) seek(offset int64, reopen bool) error {
|
||||||
// Can we seek it directly?
|
// Can we seek it directly?
|
||||||
oldReader := fh.r.GetReader()
|
oldReader := fh.r.GetReader()
|
||||||
if do, ok := oldReader.(io.Seeker); ok {
|
if do, ok := oldReader.(io.Seeker); !reopen && ok {
|
||||||
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d (io.Seeker)", fh.offset, offset)
|
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d (io.Seeker)", fh.offset, offset)
|
||||||
_, err := do.Seek(offset, 0)
|
_, err := do.Seek(offset, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,7 +76,7 @@ func (fh *ReadFileHandle) seek(offset int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from the file handle
|
// Read from the file handle
|
||||||
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) {
|
||||||
fh.mu.Lock()
|
fh.mu.Lock()
|
||||||
defer fh.mu.Unlock()
|
defer fh.mu.Unlock()
|
||||||
fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset)
|
fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset)
|
||||||
|
@ -84,10 +85,11 @@ func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp
|
||||||
return errClosedFileHandle
|
return errClosedFileHandle
|
||||||
}
|
}
|
||||||
doSeek := req.Offset != fh.offset
|
doSeek := req.Offset != fh.offset
|
||||||
var err error
|
|
||||||
var n int
|
var n int
|
||||||
var newOffset int64
|
var newOffset int64
|
||||||
|
retries := 0
|
||||||
buf := make([]byte, req.Size)
|
buf := make([]byte, req.Size)
|
||||||
|
doReopen := false
|
||||||
for {
|
for {
|
||||||
if doSeek {
|
if doSeek {
|
||||||
// Are we attempting to seek beyond the end of the
|
// Are we attempting to seek beyond the end of the
|
||||||
|
@ -98,41 +100,47 @@ func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp
|
||||||
resp.Data = nil
|
resp.Data = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := fh.seek(req.Offset)
|
// Otherwise do the seek
|
||||||
if err != nil {
|
err = fh.seek(req.Offset, doReopen)
|
||||||
return err
|
} else {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if req.Size > 0 {
|
||||||
|
fh.readCalled = true
|
||||||
|
}
|
||||||
|
// One exception to the above is if we fail to fully populate a
|
||||||
|
// page cache page; a read into page cache is always page aligned.
|
||||||
|
// Make sure we never serve a partial read, to avoid that.
|
||||||
|
n, err = io.ReadFull(fh.r, buf)
|
||||||
|
newOffset = fh.offset + int64(n)
|
||||||
|
// if err == nil && rand.Intn(10) == 0 {
|
||||||
|
// err = errors.New("random error")
|
||||||
|
// }
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
} else if (err == io.ErrUnexpectedEOF || err == io.EOF) && newOffset == fh.o.Size() {
|
||||||
|
// Have read to end of file - reset error
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Size > 0 {
|
if retries >= fs.Config.LowLevelRetries {
|
||||||
fh.readCalled = true
|
|
||||||
}
|
|
||||||
// One exception to the above is if we fail to fully populate a
|
|
||||||
// page cache page; a read into page cache is always page aligned.
|
|
||||||
// Make sure we never serve a partial read, to avoid that.
|
|
||||||
n, err = io.ReadFull(fh.r, buf)
|
|
||||||
newOffset = fh.offset + int64(n)
|
|
||||||
// if err == nil && rand.Intn(10) == 0 {
|
|
||||||
// err = errors.New("random error")
|
|
||||||
// }
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
} else if (err == io.ErrUnexpectedEOF || err == io.EOF) && newOffset == fh.o.Size() {
|
|
||||||
// Read to end of file
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if fh.retries >= fs.Config.LowLevelRetries {
|
retries++
|
||||||
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err)
|
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: low level retry %d/%d: %v", retries, fs.Config.LowLevelRetries, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
fh.retries++
|
|
||||||
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: low level retry %d/%d: %v", fh.retries, fs.Config.LowLevelRetries, err)
|
|
||||||
doSeek = true
|
doSeek = true
|
||||||
|
doReopen = true
|
||||||
}
|
}
|
||||||
fh.retries = 0 // reset retries
|
if err != nil {
|
||||||
resp.Data = buf[:n]
|
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err)
|
||||||
fh.offset = newOffset
|
} else {
|
||||||
fs.Debug(fh.o, "ReadFileHandle.Read OK")
|
resp.Data = buf[:n]
|
||||||
return nil
|
fh.offset = newOffset
|
||||||
|
fs.Debug(fh.o, "ReadFileHandle.Read OK")
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the file handle returning errClosedFileHandle if it has been
|
// close the file handle returning errClosedFileHandle if it has been
|
||||||
|
|
Loading…
Reference in a new issue