system/tmpfiles: do not fail for smaller files

The limit is meant to be an upper bound. Handle EOF and print verbose message for it instead of failing.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2025-09-08 03:22:10 +09:00
parent 323d132c40
commit da2b9c01ce
4 changed files with 218 additions and 42 deletions

View File

@@ -1,44 +1,142 @@
package system
import (
"strconv"
"bytes"
"errors"
"os"
"strings"
"syscall"
"testing"
"hakurei.app/container/stub"
)
func TestCopyFile(t *testing.T) {
testCases := []struct {
tcOp
cap int
n int64
}{
{tcOp{Process, "/home/ophestra/xdg/config/pulse/cookie"}, 256, 256},
}
for _, tc := range testCases {
t.Run("copy file "+tc.path+" with cap = "+strconv.Itoa(tc.cap)+" n = "+strconv.Itoa(int(tc.n)), func(t *testing.T) {
sys := New(t.Context(), 150)
sys.CopyFile(new([]byte), tc.path, tc.cap, tc.n)
tc.test(t, sys.ops, []Op{
&tmpfileOp{nil, tc.path, tc.n, nil},
}, "CopyFile")
})
}
}
type errorReader struct{}
func TestTmpfile_String(t *testing.T) {
testCases := []struct {
src string
n int64
want string
}{
{"/home/ophestra/xdg/config/pulse/cookie", 256,
func (errorReader) Read([]byte) (int, error) { return 0, stub.UniqueError(0xdeadbeef) }
func TestTmpfileOp(t *testing.T) {
// 255 bytes
const paSample = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
checkOpBehaviour(t, []opBehaviourTestCase{
{"payload", 0xdead, 0xff, &tmpfileOp{
nil, "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, nil, errors.New("invalid payload"), nil, nil},
{"stat", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, nil, stub.UniqueError(1)),
}, &OpError{Op: "tmpfile", Err: stub.UniqueError(1)}, nil, nil},
{"stat EISDIR", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, true}, nil),
}, &OpError{Op: "tmpfile", Err: &os.PathError{Op: "stat", Path: "/home/ophestra/xdg/config/pulse/cookie", Err: syscall.EISDIR}}, nil, nil},
{"stat ENOMEM", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1<<8 + 1, false}, nil),
}, &OpError{Op: "tmpfile", Err: &os.PathError{Op: "stat", Path: "/home/ophestra/xdg/config/pulse/cookie", Err: syscall.ENOMEM}}, nil, nil},
{"open", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, false}, nil),
call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, nil, stub.UniqueError(0)),
}, &OpError{Op: "tmpfile", Err: stub.UniqueError(0)}, nil, nil},
{"reader", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, false}, nil),
call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &readerOsFile{true, errorReader{}}, nil),
}, &OpError{Op: "tmpfile", Err: stub.UniqueError(0xdeadbeef)}, nil, nil},
{"closed", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, false}, nil),
call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &readerOsFile{true, strings.NewReader(paSample + "=")}, nil),
}, &OpError{Op: "tmpfile", Err: os.ErrClosed}, nil, nil},
{"success full", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, false}, nil),
call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &readerOsFile{false, strings.NewReader(paSample + "=")}, nil),
}, nil, nil, nil},
{"success", 0xdead, 0xff, &tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"copying", &tmpfileOp{new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }()}}}, nil, nil),
call("stat", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, stubFi{1 << 8, false}, nil),
call("open", stub.ExpectArgs{"/home/ophestra/xdg/config/pulse/cookie"}, &readerOsFile{false, strings.NewReader(paSample)}, nil),
call("verbosef", stub.ExpectArgs{"copied %d bytes from %q", []any{int64(1<<8 - 1), "/home/ophestra/xdg/config/pulse/cookie"}}, nil, nil),
}, nil, nil, nil},
})
checkOpsBuilder(t, "CopyFile", []opsBuilderTestCase{
{"pulse", 0xcafebabe, func(_ *testing.T, sys *I) {
sys.CopyFile(new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1<<8, 1<<8)
}, []Op{&tmpfileOp{
new([]byte), "/home/ophestra/xdg/config/pulse/cookie", 1 << 8,
func() *bytes.Buffer { buf := new(bytes.Buffer); buf.Grow(1 << 8); return buf }(),
}}, stub.Expect{}},
})
checkOpIs(t, []opIsTestCase{
{"nil", (*tmpfileOp)(nil), (*tmpfileOp)(nil), false},
{"zero", new(tmpfileOp), new(tmpfileOp), true},
{"n differs", &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse/cookie",
n: 1 << 7,
}, &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse/cookie",
n: 1 << 8,
}, false},
{"src differs", &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse",
n: 1 << 8,
}, &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse/cookie",
n: 1 << 8,
}, false},
{"equals", &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse/cookie",
n: 1 << 8,
}, &tmpfileOp{
src: "/home/ophestra/xdg/config/pulse/cookie",
n: 1 << 8,
}, true},
})
checkOpMeta(t, []opMetaTestCase{
{"pulse", &tmpfileOp{nil, "/home/ophestra/xdg/config/pulse/cookie", 1 << 8, nil},
Process, "/home/ophestra/xdg/config/pulse/cookie",
`up to 256 bytes from "/home/ophestra/xdg/config/pulse/cookie"`},
}
for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) {
if got := (&tmpfileOp{src: tc.src, n: tc.n}).String(); got != tc.want {
t.Errorf("String() = %v, want %v", got, tc.want)
}
})
}
})
}