Skip to content

Add os/file.Truncate #4209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/os/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
19 changes: 19 additions & 0 deletions src/os/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,25 @@ 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.
Expand Down
14 changes: 14 additions & 0 deletions src/os/file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
61 changes: 61 additions & 0 deletions src/os/truncate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//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"
"runtime"
"testing"
)

func TestTruncate(t *testing.T) {
// 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()
file := filepath.Join(tmpDir, "truncate_test")

fd, err := Create(file)
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 {
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: got %v, want nil", 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: got %v, want nil", file, err)
}

// check if size is 0x80
fi, err = Stat(file)
if err != nil {
t.Fatalf("stat %q: got %v, want nil", file, err)
}

if fi.Size() != 0x80 {
t.Fatalf("size of %q is %d; want 0x80", file, fi.Size())
}
}
10 changes: 8 additions & 2 deletions src/syscall/libc_wasip2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -773,7 +772,6 @@ var libcCWD wasiDir
var wasiPreopens map[string]types.Descriptor

func populatePreopens() {

var cwd string

// find CWD
Expand Down Expand Up @@ -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
}
14 changes: 14 additions & 0 deletions src/syscall/syscall_libc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -451,3 +460,8 @@ 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
Loading