From 1c9450e89c159a5bbf50ae4f17cb3b7eca76ed26 Mon Sep 17 00:00:00 2001 From: leongross Date: Wed, 27 Mar 2024 14:34:10 +0100 Subject: [PATCH 1/4] add file.Truncate Signed-off-by: leongross --- src/os/file.go | 10 ------- src/os/file_unix.go | 20 ++++++++++++++ src/os/truncate_test.go | 53 +++++++++++++++++++++++++++++++++++++ src/syscall/syscall_libc.go | 19 +++++++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/os/truncate_test.go diff --git a/src/os/file.go b/src/os/file.go index acf33f850d..7c3c0db125 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -290,16 +290,6 @@ func (f *File) Sync() (err error) { return } -// Truncate is a stub, not yet implemented -func (f *File) Truncate(size int64) (err error) { - if f.handle == nil { - err = ErrClosed - } else { - err = ErrNotImplemented - } - return &PathError{Op: "truncate", Path: f.name, Err: err} -} - // LinkError records an error during a link or symlink or rename system call and // the paths that caused it. type LinkError struct { diff --git a/src/os/file_unix.go b/src/os/file_unix.go index badfc71ff9..ebf3f32bd6 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -125,6 +125,26 @@ func Readlink(name string) (string, error) { } } +// Truncate changes the size of the file. +// It does not change the I/O offset. +// If there is an error, it will be of type *PathError. + +// Alternatively just use 'raw' syscall by file name +func (f *File) Truncate(size int64) (err error) { + if f.handle == nil { + return ErrClosed + } + + e := ignoringEINTR(func() error { + return syscall.Truncate(f.name, size) + }) + + if e != nil { + return &PathError{Op: "truncate", Path: f.name, Err: e} + } + return +} + // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset. // It returns the number of bytes read and any error encountered, possibly io.EOF. // At end of file, Pread returns 0, io.EOF. diff --git a/src/os/truncate_test.go b/src/os/truncate_test.go new file mode 100644 index 0000000000..a84706a35b --- /dev/null +++ b/src/os/truncate_test.go @@ -0,0 +1,53 @@ +//go:build darwin || (linux && !baremetal && !js && !wasi) + +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os_test + +import ( + . "os" + "path/filepath" + "testing" +) + +func TestTruncate(t *testing.T) { + tmpDir := t.TempDir() + file := filepath.Join(tmpDir, "truncate_0x100") + + fd, err := Create(file) + if err != nil { + t.Fatalf("create %q: %s", file, err) + } + + // truncate up to 0x100 + if err := fd.Truncate(0x100); err != nil { + t.Fatalf("truncate %q: %s", file, err) + } + + // check if size is 0x100 + fi, err := Stat(file) + if err != nil { + t.Fatalf("stat %q: %s", file, err) + } + + if fi.Size() != 0x100 { + t.Fatalf("size of %q is %d; want 0x100", file, fi.Size()) + } + + // truncate down to 0x80 + if err := fd.Truncate(0x80); err != nil { + t.Fatalf("truncate %q: %s", file, err) + } + + // check if size is 0x80 + fi, err = Stat(file) + if err != nil { + t.Fatalf("stat %q: %s", file, err) + } + + if fi.Size() != 0x80 { + t.Fatalf("size of %q is %d; want 0x80", file, fi.Size()) + } +} diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 0dec4c74d5..a3fb6ee4f7 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -203,6 +203,15 @@ func Execve(pathname string, argv []string, envv []string) (err error) { return } +func Truncate(path string, length int64) (err error) { + data := cstring(path) + fail := int(libc_truncate(&data[0], length)) + if fail < 0 { + err = getErrno() + } + return +} + func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) func Kill(pid int, sig Signal) (err error) { @@ -442,6 +451,7 @@ func libc_readlink(path *byte, buf *byte, count uint) int //export unlink func libc_unlink(pathname *byte) int32 +<<<<<<< HEAD // pid_t fork(void); // //export fork @@ -451,3 +461,12 @@ func libc_fork() int32 // //export execve func libc_execve(filename *byte, argv **byte, envp **byte) int +======= +// int truncate(const char *path, off_t length); +// +//export truncate +func libc_truncate(path *byte, length int64) int32 + +//go:extern environ +var libc_environ *unsafe.Pointer +>>>>>>> 2bb9c18a (add file.Truncate) From dad802246485be75615fe814d6884c30753f8aa3 Mon Sep 17 00:00:00 2001 From: leongross Date: Thu, 4 Apr 2024 10:18:47 +0200 Subject: [PATCH 2/4] adjust test error messages Signed-off-by: leongross --- src/os/truncate_test.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/os/truncate_test.go b/src/os/truncate_test.go index a84706a35b..507fbd91e9 100644 --- a/src/os/truncate_test.go +++ b/src/os/truncate_test.go @@ -9,27 +9,32 @@ package os_test import ( . "os" "path/filepath" + "runtime" "testing" ) func TestTruncate(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip() + } + tmpDir := t.TempDir() - file := filepath.Join(tmpDir, "truncate_0x100") + file := filepath.Join(tmpDir, "truncate_test") fd, err := Create(file) if err != nil { - t.Fatalf("create %q: %s", file, err) + t.Fatalf("create %q: got %v, want nil", file, err) } // truncate up to 0x100 if err := fd.Truncate(0x100); err != nil { - t.Fatalf("truncate %q: %s", file, err) + t.Fatalf("truncate %q: got %v, want nil", file, err) } // check if size is 0x100 fi, err := Stat(file) if err != nil { - t.Fatalf("stat %q: %s", file, err) + t.Fatalf("stat %q: got %v, want nil", file, err) } if fi.Size() != 0x100 { @@ -38,13 +43,13 @@ func TestTruncate(t *testing.T) { // truncate down to 0x80 if err := fd.Truncate(0x80); err != nil { - t.Fatalf("truncate %q: %s", file, err) + t.Fatalf("truncate %q: got %v, want nil", file, err) } // check if size is 0x80 fi, err = Stat(file) if err != nil { - t.Fatalf("stat %q: %s", file, err) + t.Fatalf("stat %q: got %v, want nil", file, err) } if fi.Size() != 0x80 { From 138d6413f4650f4cf232ec8104ceb3b9b16d84e8 Mon Sep 17 00:00:00 2001 From: leongross Date: Fri, 5 Apr 2024 18:01:26 +0200 Subject: [PATCH 3/4] add windows stub Signed-off-by: leongross --- src/os/file_windows.go | 14 ++++++++++++++ src/syscall/syscall_libc.go | 4 +--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 70f3a3dd0c..5860c76b04 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -59,6 +59,10 @@ func Pipe() (r *File, w *File, err error) { return } +func (f *unixFileHandle) Truncate(size int64) error { + return ErrNotImplemented +} + func tempDir() string { n := uint32(syscall.MAX_PATH) for { @@ -106,6 +110,16 @@ func (f unixFileHandle) Sync() error { return ErrNotImplemented } +func (f *File) Truncate(size int64) error { + var err error + if f.handle == nil { + err = ErrClosed + } else { + err = ErrNotImplemented + } + return &PathError{Op: "truncate", Path: f.name, Err: 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 { diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index a3fb6ee4f7..1e21e4d176 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -451,7 +451,6 @@ func libc_readlink(path *byte, buf *byte, count uint) int //export unlink func libc_unlink(pathname *byte) int32 -<<<<<<< HEAD // pid_t fork(void); // //export fork @@ -461,7 +460,7 @@ func libc_fork() int32 // //export execve func libc_execve(filename *byte, argv **byte, envp **byte) int -======= + // int truncate(const char *path, off_t length); // //export truncate @@ -469,4 +468,3 @@ func libc_truncate(path *byte, length int64) int32 //go:extern environ var libc_environ *unsafe.Pointer ->>>>>>> 2bb9c18a (add file.Truncate) From f37e3f5456453025fc30baea3ca58434a1f18ee8 Mon Sep 17 00:00:00 2001 From: leongross Date: Wed, 10 Jul 2024 11:24:46 +0200 Subject: [PATCH 4/4] stub truncate for wasip2 Signed-off-by: leongross --- src/os/file_unix.go | 1 - src/os/truncate_test.go | 7 +++++-- src/syscall/libc_wasip2.go | 10 ++++++++-- src/syscall/syscall_libc.go | 3 --- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/os/file_unix.go b/src/os/file_unix.go index ebf3f32bd6..ef7abcbac8 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -128,7 +128,6 @@ func Readlink(name string) (string, error) { // Truncate changes the size of the file. // It does not change the I/O offset. // If there is an error, it will be of type *PathError. - // Alternatively just use 'raw' syscall by file name func (f *File) Truncate(size int64) (err error) { if f.handle == nil { diff --git a/src/os/truncate_test.go b/src/os/truncate_test.go index 507fbd91e9..2b1d982ba2 100644 --- a/src/os/truncate_test.go +++ b/src/os/truncate_test.go @@ -14,8 +14,10 @@ import ( ) func TestTruncate(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip() + // Truncate is not supported on Windows or wasi at the moment + if runtime.GOOS == "windows" || runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" { + t.Logf("skipping test on %s", runtime.GOOS) + return } tmpDir := t.TempDir() @@ -25,6 +27,7 @@ func TestTruncate(t *testing.T) { if err != nil { t.Fatalf("create %q: got %v, want nil", file, err) } + defer fd.Close() // truncate up to 0x100 if err := fd.Truncate(0x100); err != nil { diff --git a/src/syscall/libc_wasip2.go b/src/syscall/libc_wasip2.go index 1e985c1da9..3aadf877f3 100644 --- a/src/syscall/libc_wasip2.go +++ b/src/syscall/libc_wasip2.go @@ -432,7 +432,6 @@ func chmod(pathname *byte, mode uint32) int32 { // //export mkdir func mkdir(pathname *byte, mode uint32) int32 { - path := goString(pathname) dir, relPath := findPreopenForPath(path) if dir.d == cm.ResourceNone { @@ -773,7 +772,6 @@ var libcCWD wasiDir var wasiPreopens map[string]types.Descriptor func populatePreopens() { - var cwd string // find CWD @@ -1354,3 +1352,11 @@ func getcwd(buf *byte, size uint) *byte { copy(s, cwd) return buf } + +// int truncate(const char *path, off_t length); +// +//export truncate +func truncate(path *byte, length int64) int32 { + libcErrno = ENOSYS + return -1 +} diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 1e21e4d176..2321292d98 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -465,6 +465,3 @@ func libc_execve(filename *byte, argv **byte, envp **byte) int // //export truncate func libc_truncate(path *byte, length int64) int32 - -//go:extern environ -var libc_environ *unsafe.Pointer