v2api: implement Lseek

This also fixes the last remaining tests/fsck failure.
libgocryptfs
Jakob Unterwurzacher 2 years ago
parent 8915785acf
commit 9cd24d79a2
  1. 2
      internal/fusefrontend/file2_api_check.go
  2. 40
      internal/fusefrontend/file2_holes.go
  3. 32
      tests/defaults/main_test.go

@ -13,11 +13,11 @@ var _ = (fs.FileWriter)((*File2)(nil))
var _ = (fs.FileFsyncer)((*File2)(nil))
var _ = (fs.FileFlusher)((*File2)(nil))
var _ = (fs.FileAllocater)((*File2)(nil))
var _ = (fs.FileLseeker)((*File2)(nil))
/* TODO
var _ = (fs.FileHandle)((*File2)(nil))
var _ = (fs.FileGetlker)((*File2)(nil))
var _ = (fs.FileSetlker)((*File2)(nil))
var _ = (fs.FileSetlkwer)((*File2)(nil))
var _ = (fs.FileLseeker)((*File2)(nil))
*/

@ -3,7 +3,7 @@ package fusefrontend
// Helper functions for sparse files (files with holes)
import (
"runtime"
"context"
"syscall"
"github.com/hanwen/go-fuse/v2/fs"
@ -56,37 +56,13 @@ func (f *File2) zeroPad(plainSize uint64) syscall.Errno {
return errno
}
// SeekData calls the lseek syscall with SEEK_DATA. It returns the offset of the
// next data bytes, skipping over file holes.
func (f *File2) SeekData(oldOffset int64) (int64, error) {
if runtime.GOOS != "linux" {
// Does MacOS support something like this?
return 0, syscall.EOPNOTSUPP
}
const SEEK_DATA = 3
// Convert plaintext offset to ciphertext offset and round down to the
// start of the current block. File holes smaller than a full block will
// be ignored.
blockNo := f.contentEnc.PlainOffToBlockNo(uint64(oldOffset))
oldCipherOff := int64(f.contentEnc.BlockNoToCipherOff(blockNo))
// Determine the next data offset. If the old offset points to (or beyond)
// the end of the file, the Seek syscall fails with syscall.ENXIO.
newCipherOff, err := syscall.Seek(f.intFd(), oldCipherOff, SEEK_DATA)
// Lseek - FUSE call.
func (f *File2) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
cipherOff := f.rootNode.contentEnc.PlainSizeToCipherSize(off)
newCipherOff, err := syscall.Seek(f.intFd(), int64(cipherOff), int(whence))
if err != nil {
return 0, err
return uint64(newCipherOff), fs.ToErrno(err)
}
// Convert ciphertext offset back to plaintext offset. At this point,
// newCipherOff should always be >= contentenc.HeaderLen. Round down,
// but ensure that the result is never smaller than the initial offset
// (to avoid endless loops).
blockNo = f.contentEnc.CipherOffToBlockNo(uint64(newCipherOff))
newOffset := int64(f.contentEnc.BlockNoToPlainOff(blockNo))
if newOffset < oldOffset {
newOffset = oldOffset
}
return newOffset, nil
newOff := f.contentEnc.CipherSizeToPlainSize(uint64(newCipherOff))
return newOff, 0
}

@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
@ -261,3 +262,34 @@ func TestCpWarnings(t *testing.T) {
t.Fatalf("Got warnings from cp -a:\n%s", string(out))
}
}
func TestSeekData(t *testing.T) {
fn := filepath.Join(test_helpers.DefaultPlainDir, t.Name())
f, err := os.Create(fn)
if err != nil {
t.Fatal(err)
}
var oneTiB int64 = 1024 * 1024 * 1024 * 1024
if _, err = f.Seek(oneTiB, 0); err != nil {
t.Fatal(err)
}
if _, err = f.Write([]byte("foo")); err != nil {
t.Fatal(err)
}
f.Close()
const SEEK_DATA = 3
f, err = os.Open(fn)
if err != nil {
t.Fatal(err)
}
off, err := f.Seek(1024*1024, SEEK_DATA)
if err != nil {
t.Fatal(err)
}
if off < oneTiB-1024*1024 {
t.Errorf("off=%d, expected=%d\n", off, oneTiB)
}
f.Close()
}

Loading…
Cancel
Save