tailfs: initial implementation
Add a WebDAV-based folder sharing mechanism that is exposed to local clients at 100.100.100.100:8080 and to remote peers via a new peerapi endpoint at /v0/tailfs. Add the ability to manage folder sharing via the new 'share' CLI sub-command. Updates tailscale/corp#16827 Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
committed by
Percy Wegmann
parent
2e404b769d
commit
993acf4475
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package webdavfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"tailscale.com/tailfs/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
|
||||
}
|
||||
Reference in New Issue
Block a user