Skip to content

Commit a8a532f

Browse files
authored
os: add file.Truncate
1 parent 417a26d commit a8a532f

File tree

6 files changed

+116
-12
lines changed

6 files changed

+116
-12
lines changed

src/os/file.go

-10
Original file line numberDiff line numberDiff line change
@@ -290,16 +290,6 @@ func (f *File) Sync() (err error) {
290290
return
291291
}
292292

293-
// Truncate is a stub, not yet implemented
294-
func (f *File) Truncate(size int64) (err error) {
295-
if f.handle == nil {
296-
err = ErrClosed
297-
} else {
298-
err = ErrNotImplemented
299-
}
300-
return &PathError{Op: "truncate", Path: f.name, Err: err}
301-
}
302-
303293
// LinkError records an error during a link or symlink or rename system call and
304294
// the paths that caused it.
305295
type LinkError struct {

src/os/file_unix.go

+19
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ func Readlink(name string) (string, error) {
125125
}
126126
}
127127

128+
// Truncate changes the size of the file.
129+
// It does not change the I/O offset.
130+
// If there is an error, it will be of type *PathError.
131+
// Alternatively just use 'raw' syscall by file name
132+
func (f *File) Truncate(size int64) (err error) {
133+
if f.handle == nil {
134+
return ErrClosed
135+
}
136+
137+
e := ignoringEINTR(func() error {
138+
return syscall.Truncate(f.name, size)
139+
})
140+
141+
if e != nil {
142+
return &PathError{Op: "truncate", Path: f.name, Err: e}
143+
}
144+
return
145+
}
146+
128147
// ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
129148
// It returns the number of bytes read and any error encountered, possibly io.EOF.
130149
// At end of file, Pread returns 0, io.EOF.

src/os/file_windows.go

+14
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ func Pipe() (r *File, w *File, err error) {
5959
return
6060
}
6161

62+
func (f *unixFileHandle) Truncate(size int64) error {
63+
return ErrNotImplemented
64+
}
65+
6266
func tempDir() string {
6367
n := uint32(syscall.MAX_PATH)
6468
for {
@@ -106,6 +110,16 @@ func (f unixFileHandle) Sync() error {
106110
return ErrNotImplemented
107111
}
108112

113+
func (f *File) Truncate(size int64) error {
114+
var err error
115+
if f.handle == nil {
116+
err = ErrClosed
117+
} else {
118+
err = ErrNotImplemented
119+
}
120+
return &PathError{Op: "truncate", Path: f.name, Err: err}
121+
}
122+
109123
// isWindowsNulName reports whether name is os.DevNull ('NUL') on Windows.
110124
// True is returned if name is 'NUL' whatever the case.
111125
func isWindowsNulName(name string) bool {

src/os/truncate_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//go:build darwin || (linux && !baremetal && !js && !wasi)
2+
3+
// Copyright 2024 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package os_test
8+
9+
import (
10+
. "os"
11+
"path/filepath"
12+
"runtime"
13+
"testing"
14+
)
15+
16+
func TestTruncate(t *testing.T) {
17+
// Truncate is not supported on Windows or wasi at the moment
18+
if runtime.GOOS == "windows" || runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
19+
t.Logf("skipping test on %s", runtime.GOOS)
20+
return
21+
}
22+
23+
tmpDir := t.TempDir()
24+
file := filepath.Join(tmpDir, "truncate_test")
25+
26+
fd, err := Create(file)
27+
if err != nil {
28+
t.Fatalf("create %q: got %v, want nil", file, err)
29+
}
30+
defer fd.Close()
31+
32+
// truncate up to 0x100
33+
if err := fd.Truncate(0x100); err != nil {
34+
t.Fatalf("truncate %q: got %v, want nil", file, err)
35+
}
36+
37+
// check if size is 0x100
38+
fi, err := Stat(file)
39+
if err != nil {
40+
t.Fatalf("stat %q: got %v, want nil", file, err)
41+
}
42+
43+
if fi.Size() != 0x100 {
44+
t.Fatalf("size of %q is %d; want 0x100", file, fi.Size())
45+
}
46+
47+
// truncate down to 0x80
48+
if err := fd.Truncate(0x80); err != nil {
49+
t.Fatalf("truncate %q: got %v, want nil", file, err)
50+
}
51+
52+
// check if size is 0x80
53+
fi, err = Stat(file)
54+
if err != nil {
55+
t.Fatalf("stat %q: got %v, want nil", file, err)
56+
}
57+
58+
if fi.Size() != 0x80 {
59+
t.Fatalf("size of %q is %d; want 0x80", file, fi.Size())
60+
}
61+
}

src/syscall/libc_wasip2.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,6 @@ func chmod(pathname *byte, mode uint32) int32 {
432432
//
433433
//export mkdir
434434
func mkdir(pathname *byte, mode uint32) int32 {
435-
436435
path := goString(pathname)
437436
dir, relPath := findPreopenForPath(path)
438437
if dir.d == cm.ResourceNone {
@@ -773,7 +772,6 @@ var libcCWD wasiDir
773772
var wasiPreopens map[string]types.Descriptor
774773

775774
func populatePreopens() {
776-
777775
var cwd string
778776

779777
// find CWD
@@ -1354,3 +1352,11 @@ func getcwd(buf *byte, size uint) *byte {
13541352
copy(s, cwd)
13551353
return buf
13561354
}
1355+
1356+
// int truncate(const char *path, off_t length);
1357+
//
1358+
//export truncate
1359+
func truncate(path *byte, length int64) int32 {
1360+
libcErrno = ENOSYS
1361+
return -1
1362+
}

src/syscall/syscall_libc.go

+14
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,15 @@ func Execve(pathname string, argv []string, envv []string) (err error) {
203203
return
204204
}
205205

206+
func Truncate(path string, length int64) (err error) {
207+
data := cstring(path)
208+
fail := int(libc_truncate(&data[0], length))
209+
if fail < 0 {
210+
err = getErrno()
211+
}
212+
return
213+
}
214+
206215
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
207216

208217
func Kill(pid int, sig Signal) (err error) {
@@ -451,3 +460,8 @@ func libc_fork() int32
451460
//
452461
//export execve
453462
func libc_execve(filename *byte, argv **byte, envp **byte) int
463+
464+
// int truncate(const char *path, off_t length);
465+
//
466+
//export truncate
467+
func libc_truncate(path *byte, length int64) int32

0 commit comments

Comments
 (0)