tailscale/tailfs/tailfsimpl/webdavfs/writeonly_file.go

90 lines
1.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package webdavfs
import (
"context"
"errors"
"io"
"io/fs"
"os"
"tailscale.com/tailfs/tailfsimpl/shared"
)
type writeOnlyFile struct {
io.WriteCloser
name string
perm os.FileMode
fs *webdavFS
finalError chan error
}
// Readdir implements webdav.File. As this is a file, this always fails with an
// os.PathError.
func (f *writeOnlyFile) Readdir(count int) ([]fs.FileInfo, error) {
return nil, &os.PathError{
Op: "readdir",
Path: f.name,
Err: errors.New("is a file"), // TODO(oxtoacart): make sure this and below errors match what a regular os.File does
}
}
// Seek implements webdav.File. This always fails with an os.PathError.
func (f *writeOnlyFile) Seek(offset int64, whence int) (int64, error) {
return 0, &os.PathError{
Op: "seek",
Path: f.name,
Err: errors.New("seek not supported"),
}
}
// Stat implements webdav.File.
func (f *writeOnlyFile) Stat() (fs.FileInfo, error) {
fi, err := f.fs.Stat(context.Background(), f.name)
if err != nil {
// use static info for newly created file
now := f.fs.now()
fi = &shared.StaticFileInfo{
Named: f.name,
Sized: 0,
Moded: f.perm,
BirthedTime: now,
ModdedTime: now,
Dir: false,
}
}
return fi, nil
}
// Read implements webdav.File. As this is a write-only file, it always fails
// with an os.PathError.
func (f *writeOnlyFile) Read(p []byte) (int, error) {
return 0, &os.PathError{
Op: "write",
Path: f.name,
Err: errors.New("write-only"),
}
}
// Write implements webdav.File.
func (f *writeOnlyFile) Write(p []byte) (int, error) {
select {
case err := <-f.finalError:
return 0, err
default:
return f.WriteCloser.Write(p)
}
}
// Close implements webdav.File.
func (f *writeOnlyFile) Close() error {
err := f.WriteCloser.Close()
writeErr := <-f.finalError
if writeErr != nil {
return writeErr
}
return err
}