Skip to content

Commit 998f016

Browse files
dkegel-fastlydeadprogram
authored andcommitted
os: implement file.Seek, add smoke test
1 parent 84279ab commit 998f016

File tree

7 files changed

+91
-5
lines changed

7 files changed

+91
-5
lines changed

src/os/file.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,17 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
176176
return nil, &PathError{"readdirnames", f.name, ErrNotImplemented}
177177
}
178178

179-
// Seek is a stub, not yet implemented
179+
// Seek sets the offset for the next Read or Write on file to offset, interpreted
180+
// according to whence: 0 means relative to the origin of the file, 1 means
181+
// relative to the current offset, and 2 means relative to the end.
182+
// It returns the new offset and an error, if any.
183+
// The behavior of Seek on a file opened with O_APPEND is not specified.
184+
//
185+
// If f is a directory, the behavior of Seek varies by operating
186+
// system; you can seek to the beginning of the directory on Unix-like
187+
// operating systems, but not on Windows.
180188
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
181-
return 0, &PathError{"seek", f.name, ErrNotImplemented}
189+
return f.handle.Seek(offset, whence)
182190
}
183191

184192
// Stat is a stub, not yet implemented

src/os/file_other.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ func (f stdioFileHandle) Close() error {
5050
return ErrUnsupported
5151
}
5252

53+
// Seek wraps syscall.Seek.
54+
func (f stdioFileHandle) Seek(offset int64, whence int) (int64, error) {
55+
return -1, ErrUnsupported
56+
}
57+
5358
//go:linkname putchar runtime.putchar
5459
func putchar(c byte)
5560

src/os/file_unix.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,9 @@ func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
6464
}
6565
return
6666
}
67+
68+
// Seek wraps syscall.Seek.
69+
func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
70+
newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
71+
return newoffset, handleSyscallError(err)
72+
}

src/os/file_windows.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ func (f unixFileHandle) ReadAt(b []byte, offset int64) (n int, err error) {
6666
return -1, ErrNotImplemented
6767
}
6868

69+
// Seek wraps syscall.Seek.
70+
func (f unixFileHandle) Seek(offset int64, whence int) (int64, error) {
71+
newoffset, err := syscall.Seek(syscallFd(f), offset, whence)
72+
return newoffset, handleSyscallError(err)
73+
}
74+
6975
// isWindowsNulName reports whether name is os.DevNull ('NUL') on Windows.
7076
// True is returned if name is 'NUL' whatever the case.
7177
func isWindowsNulName(name string) bool {

src/os/filesystem.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type FileHandle interface {
4949
// ReadAt reads up to len(b) bytes from the file starting at the given absolute offset
5050
ReadAt(b []byte, offset int64) (n int, err error)
5151

52+
// Seek resets the file pointer relative to start, current position, or end
53+
Seek(offset int64, whence int) (newoffset int64, err error)
54+
5255
// Write writes up to len(b) bytes to the file.
5356
Write(b []byte) (n int, err error)
5457

src/os/os_test.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ package os_test
66

77
import (
88
"io"
9+
"io/ioutil"
910
. "os"
1011
"runtime"
1112
"strings"
13+
"syscall"
1214
"testing"
1315
)
1416

@@ -20,7 +22,7 @@ func localTmp() string {
2022
func newFile(testName string, t *testing.T) (f *File) {
2123
f, err := CreateTemp("", testName)
2224
if err != nil {
23-
t.Fatalf("TempFile %s: %s", testName, err)
25+
t.Fatalf("newFile %s: CreateTemp fails with %s", testName, err)
2426
}
2527
return
2628
}
@@ -86,6 +88,54 @@ func checkMode(t *testing.T, path string, mode FileMode) {
8688
}
8789
}
8890

91+
func TestSeek(t *testing.T) {
92+
f := newFile("TestSeek", t)
93+
if f == nil {
94+
t.Fatalf("f is nil")
95+
return // TODO: remove
96+
}
97+
defer Remove(f.Name())
98+
defer f.Close()
99+
100+
const data = "hello, world\n"
101+
io.WriteString(f, data)
102+
103+
type test struct {
104+
in int64
105+
whence int
106+
out int64
107+
}
108+
var tests = []test{
109+
{0, io.SeekCurrent, int64(len(data))},
110+
{0, io.SeekStart, 0},
111+
{5, io.SeekStart, 5},
112+
{0, io.SeekEnd, int64(len(data))},
113+
{0, io.SeekStart, 0},
114+
{-1, io.SeekEnd, int64(len(data)) - 1},
115+
{1 << 33, io.SeekStart, 1 << 33},
116+
{1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
117+
118+
// Issue 21681, Windows 4G-1, etc:
119+
{1<<32 - 1, io.SeekStart, 1<<32 - 1},
120+
{0, io.SeekCurrent, 1<<32 - 1},
121+
{2<<32 - 1, io.SeekStart, 2<<32 - 1},
122+
{0, io.SeekCurrent, 2<<32 - 1},
123+
}
124+
for i, tt := range tests {
125+
off, err := f.Seek(tt.in, tt.whence)
126+
if off != tt.out || err != nil {
127+
if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" {
128+
mounts, _ := ioutil.ReadFile("/proc/mounts")
129+
if strings.Contains(string(mounts), "reiserfs") {
130+
// Reiserfs rejects the big seeks.
131+
t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91")
132+
}
133+
}
134+
t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
135+
}
136+
}
137+
}
138+
89139
func TestReadAt(t *testing.T) {
90140
if runtime.GOOS == "windows" {
91141
t.Log("TODO: implement Pread for Windows")

src/syscall/syscall_libc.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {
4646
return
4747
}
4848

49-
func Seek(fd int, offset int64, whence int) (off int64, err error) {
50-
return 0, ENOSYS // TODO
49+
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
50+
newoffset = libc_lseek(int32(fd), offset, whence)
51+
if newoffset < 0 {
52+
err = getErrno()
53+
}
54+
return
5155
}
5256

5357
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
248252
//export pread
249253
func libc_pread(fd int32, buf *byte, count uint, offset int64) int
250254

255+
// ssize_t lseek(int fd, off_t offset, int whence);
256+
//export lseek
257+
func libc_lseek(fd int32, offset int64, whence int) int64
258+
251259
// int open(const char *pathname, int flags, mode_t mode);
252260
//export open
253261
func libc_open(pathname *byte, flags int32, mode uint32) int32

0 commit comments

Comments
 (0)