Browse Source

os: implement file.Seek, add smoke test

pull/2508/head
Dan Kegel 3 years ago
committed by Ron Evans
parent
commit
998f01608c
  1. 12
      src/os/file.go
  2. 5
      src/os/file_other.go
  3. 6
      src/os/file_unix.go
  4. 6
      src/os/file_windows.go
  5. 3
      src/os/filesystem.go
  6. 52
      src/os/os_test.go
  7. 12
      src/syscall/syscall_libc.go

12
src/os/file.go

@ -176,9 +176,17 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
return nil, &PathError{"readdirnames", f.name, ErrNotImplemented}
}
// Seek is a stub, not yet implemented
// Seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
//
// If f is a directory, the behavior of Seek varies by operating
// system; you can seek to the beginning of the directory on Unix-like
// operating systems, but not on Windows.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
return 0, &PathError{"seek", f.name, ErrNotImplemented}
return f.handle.Seek(offset, whence)
}
// Stat is a stub, not yet implemented

5
src/os/file_other.go

@ -50,6 +50,11 @@ func (f stdioFileHandle) Close() error {
return ErrUnsupported
}
// Seek wraps syscall.Seek.
func (f stdioFileHandle) Seek(offset int64, whence int) (int64, error) {
return -1, ErrUnsupported
}
//go:linkname putchar runtime.putchar
func putchar(c byte)

6
src/os/file_unix.go

@ -64,3 +64,9 @@ func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
}
return
}
// Seek wraps syscall.Seek.
func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
return newoffset, handleSyscallError(err)
}

6
src/os/file_windows.go

@ -66,6 +66,12 @@ func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
return -1, ErrNotImplemented
}
// Seek wraps syscall.Seek.
func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
return newoffset, handleSyscallError(err)
}
// isWindowsNulName reports whether name is os.DevNull ('NUL') on Windows.
// True is returned if name is 'NUL' whatever the case.
func isWindowsNulName(name string) bool {

3
src/os/filesystem.go

@ -49,6 +49,9 @@ type FileHandle interface {
// ReadAt reads up to len(b) bytes from the file starting at the given absolute offset
ReadAt(b []byte, offset int64) (n int, err error)
// Seek resets the file pointer relative to start, current position, or end
Seek(offset int64, whence int) (newoffset int64, err error)
// Write writes up to len(b) bytes to the file.
Write(b []byte) (n int, err error)

52
src/os/os_test.go

@ -6,9 +6,11 @@ package os_test
import (
"io"
"io/ioutil"
. "os"
"runtime"
"strings"
"syscall"
"testing"
)
@ -20,7 +22,7 @@ func localTmp() string {
func newFile(testName string, t *testing.T) (f *File) {
f, err := CreateTemp("", testName)
if err != nil {
t.Fatalf("TempFile %s: %s", testName, err)
t.Fatalf("newFile %s: CreateTemp fails with %s", testName, err)
}
return
}
@ -86,6 +88,54 @@ func checkMode(t *testing.T, path string, mode FileMode) {
}
}
func TestSeek(t *testing.T) {
f := newFile("TestSeek", t)
if f == nil {
t.Fatalf("f is nil")
return // TODO: remove
}
defer Remove(f.Name())
defer f.Close()
const data = "hello, world\n"
io.WriteString(f, data)
type test struct {
in int64
whence int
out int64
}
var tests = []test{
{0, io.SeekCurrent, int64(len(data))},
{0, io.SeekStart, 0},
{5, io.SeekStart, 5},
{0, io.SeekEnd, int64(len(data))},
{0, io.SeekStart, 0},
{-1, io.SeekEnd, int64(len(data)) - 1},
{1 << 33, io.SeekStart, 1 << 33},
{1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
// Issue 21681, Windows 4G-1, etc:
{1<<32 - 1, io.SeekStart, 1<<32 - 1},
{0, io.SeekCurrent, 1<<32 - 1},
{2<<32 - 1, io.SeekStart, 2<<32 - 1},
{0, io.SeekCurrent, 2<<32 - 1},
}
for i, tt := range tests {
off, err := f.Seek(tt.in, tt.whence)
if off != tt.out || err != nil {
if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" {
mounts, _ := ioutil.ReadFile("/proc/mounts")
if strings.Contains(string(mounts), "reiserfs") {
// Reiserfs rejects the big seeks.
t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91")
}
}
t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
}
}
}
func TestReadAt(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("TODO: implement Pread for Windows")

12
src/syscall/syscall_libc.go

@ -46,8 +46,12 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
return
}
func Seek(fd int, offset int64, whence int) (off int64, err error) {
return 0, ENOSYS // TODO
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
newoffset = libc_lseek(int32(fd), offset, whence)
if newoffset < 0 {
err = getErrno()
}
return
}
func Open(path string, flag int, mode uint32) (fd int, err error) {
@ -248,6 +252,10 @@ func libc_read(fd int32, buf *byte, count uint) int
//export pread
func libc_pread(fd int32, buf *byte, count uint, offset int64) int
// ssize_t lseek(int fd, off_t offset, int whence);
//export lseek
func libc_lseek(fd int32, offset int64, whence int) int64
// int open(const char *pathname, int flags, mode_t mode);
//export open
func libc_open(pathname *byte, flags int32, mode uint32) int32

Loading…
Cancel
Save