Skip to content
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

Add os/file.Truncate #4209

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
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 @@ -283,16 +283,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
20 changes: 20 additions & 0 deletions src/os/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,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.
Expand Down
4 changes: 4 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
58 changes: 58 additions & 0 deletions src/os/truncate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//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) {
if runtime.GOOS == "windows" {
t.Skip()
}

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)
}

// 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())
}
}
14 changes: 14 additions & 0 deletions src/syscall/syscall_libc.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ func Unlink(path 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 @@ -431,5 +440,10 @@ func libc_readlink(path *byte, buf *byte, count uint) int
//export unlink
func libc_unlink(pathname *byte) int32

// 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