diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-02-17 19:00:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-02-17 19:00:43 +0900 |
| commit | 90cb01b2748550228d0bc179691a19ebb996fc36 (patch) | |
| tree | 06a2e17b578312c3f0734bf4c476e3c2eb5f6908 /system/tmpfiles.go | |
| parent | b1e1d5627e6532ec719c761846d7f38fe93fcadd (diff) | |
system: move out of internal
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'system/tmpfiles.go')
| -rw-r--r-- | system/tmpfiles.go | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/system/tmpfiles.go b/system/tmpfiles.go new file mode 100644 index 00000000..636943bc --- /dev/null +++ b/system/tmpfiles.go @@ -0,0 +1,76 @@ +package system + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "syscall" +) + +// CopyFile registers an Op that copies from src. +// A buffer is initialised with size cap and the Op faults if bytes read exceed n. +func (sys *I) CopyFile(payload *[]byte, src string, cap int, n int64) *I { + buf := new(bytes.Buffer) + buf.Grow(cap) + + sys.lock.Lock() + sys.ops = append(sys.ops, &Tmpfile{payload, src, n, buf}) + sys.lock.Unlock() + + return sys +} + +type Tmpfile struct { + payload *[]byte + src string + + n int64 + buf *bytes.Buffer +} + +func (t *Tmpfile) Type() Enablement { return Process } +func (t *Tmpfile) apply(sys *I) error { + sys.println("copying", t) + + if t.payload == nil { + // this is a misuse of the API; do not return an error message + return errors.New("invalid payload") + } + + if b, err := os.Stat(t.src); err != nil { + return sys.wrapErrSuffix(err, + fmt.Sprintf("cannot stat %q:", t.src)) + } else { + if b.IsDir() { + return sys.wrapErrSuffix(syscall.EISDIR, + fmt.Sprintf("%q is a directory", t.src)) + } + if s := b.Size(); s > t.n { + return sys.wrapErrSuffix(syscall.ENOMEM, + fmt.Sprintf("file %q is too long: %d > %d", + t.src, s, t.n)) + } + } + + if f, err := os.Open(t.src); err != nil { + return sys.wrapErrSuffix(err, + fmt.Sprintf("cannot open %q:", t.src)) + } else if _, err = io.CopyN(t.buf, f, t.n); err != nil { + return sys.wrapErrSuffix(err, + fmt.Sprintf("cannot read from %q:", t.src)) + } + + *t.payload = t.buf.Bytes() + return nil +} +func (t *Tmpfile) revert(*I, *Criteria) error { t.buf.Reset(); return nil } + +func (t *Tmpfile) Is(o Op) bool { + t0, ok := o.(*Tmpfile) + return ok && t0 != nil && + t.src == t0.src && t.n == t0.n +} +func (t *Tmpfile) Path() string { return t.src } +func (t *Tmpfile) String() string { return fmt.Sprintf("up to %d bytes from %q", t.n, t.src) } |
