mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-27 23:40:41 +01:00
* mount: fail reads with error when chunk manifest resolution fails When SetChunks fails to resolve a chunk manifest (e.g. the volume is on a remote tier with reads disabled), the sections map stays empty and readDataAtSequential/readDataAtParallel zero-fill every missing section as if it were a sparse hole. Reads then return all-zero data with no error, so a plain cp of a large manifest-based file silently produces a completely zero-filled file. Remember the resolve error in ChunkGroup (guarded by sectionsLock) and return it from ReadDataAt. A later successful SetChunks clears it. Fixes the mount path of #11286. * filer: propagate manifest resolve errors in streaming read paths ViewFromChunks discards the chunk manifest resolve error returned by NonOverlappingVisibleIntervals. On failure the chunk views come back empty, and the streaming paths zero-fill the entire requested range, serving HTTP 200 / WebDAV 200 responses whose body is all zeros. Propagate the error in PrepareStreamContentWithThrottler, PrepareStreamContentWithPrefetch and the WebDAV read path so these requests fail with 500 instead. Fixes the filer HTTP and WebDAV paths of #11286. * mount: fail lseek with EIO when chunk manifest resolution fails SearchChunks still consulted the stale section map after SetChunks recorded a manifest resolution failure, so SEEK_DATA/SEEK_HOLE would describe the unresolved regions as sparse holes or return ENXIO. Return the recorded error from SearchChunks and map it to EIO in Lseek. Also add regression tests for the stream preparation error paths. Addresses review feedback on #11287.
92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package mount
|
|
|
|
import (
|
|
"context"
|
|
"syscall"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
|
|
"github.com/seaweedfs/go-fuse/v2/fuse"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
)
|
|
|
|
// These are non-POSIX extensions
|
|
const (
|
|
SEEK_DATA uint32 = 3 // seek to next data after the offset
|
|
SEEK_HOLE uint32 = 4 // seek to next hole after the offset
|
|
ENXIO = fuse.Status(syscall.ENXIO)
|
|
)
|
|
|
|
// Lseek finds next data or hole segments after the specified offset
|
|
// See https://man7.org/linux/man-pages/man2/lseek.2.html
|
|
func (wfs *WFS) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
|
|
// not a documented feature
|
|
if in.Padding != 0 {
|
|
return fuse.EINVAL
|
|
}
|
|
|
|
if in.Whence != SEEK_DATA && in.Whence != SEEK_HOLE {
|
|
return fuse.EINVAL
|
|
}
|
|
|
|
// file must exist
|
|
fh := wfs.GetHandle(FileHandleId(in.Fh))
|
|
if fh == nil {
|
|
return fuse.EBADF
|
|
}
|
|
|
|
// lock the file until the proper offset was calculated
|
|
fhActiveLock := fh.wfs.fhLockTable.AcquireLock("Lseek", fh.fh, util.SharedLock)
|
|
defer fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
|
|
|
|
fileSize := int64(filer.FileSize(fh.GetEntry().GetEntry()))
|
|
offset := max(int64(in.Offset), 0)
|
|
|
|
glog.V(4).Infof(
|
|
"Lseek %s fh %d in [%d,%d], whence %d",
|
|
fh.FullPath(), fh.fh, offset, fileSize, in.Whence,
|
|
)
|
|
|
|
// can neither seek beyond file nor seek to a hole at the end of the file with SEEK_DATA
|
|
if offset > fileSize {
|
|
return ENXIO
|
|
} else if in.Whence == SEEK_DATA && offset == fileSize {
|
|
return ENXIO
|
|
}
|
|
|
|
// Create a context that will be cancelled when the cancel channel receives a signal
|
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
|
defer cancelFunc() // Ensure cleanup
|
|
|
|
go func() {
|
|
select {
|
|
case <-cancel:
|
|
cancelFunc()
|
|
case <-ctx.Done():
|
|
// Clean exit when lseek operation completes
|
|
}
|
|
}()
|
|
|
|
// search chunks for the offset
|
|
found, offset, err := fh.entryChunkGroup.SearchChunks(ctx, offset, fileSize, in.Whence)
|
|
if err != nil {
|
|
glog.Errorf("Lseek %s: %v", fh.FullPath(), err)
|
|
return fuse.EIO
|
|
}
|
|
if found {
|
|
out.Offset = uint64(offset)
|
|
return fuse.OK
|
|
}
|
|
|
|
if in.Whence == SEEK_DATA {
|
|
return ENXIO
|
|
}
|
|
|
|
// If no explicit hole was found, the hole after the final data range is
|
|
// implicit and starts at the end of the file.
|
|
out.Offset = uint64(fileSize)
|
|
return fuse.OK
|
|
}
|