2024-02-02 18:45:32 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
2024-04-03 18:09:58 +01:00
|
|
|
// Package shared contains types and functions shared by different drive
|
2024-02-02 18:45:32 +00:00
|
|
|
// packages.
|
|
|
|
package shared
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DirFile implements webdav.File for a virtual directory.
|
|
|
|
// It mimics the behavior of an os.File that is pointing at a real directory.
|
|
|
|
type DirFile struct {
|
|
|
|
// Info provides the fs.FileInfo for this directory
|
|
|
|
Info fs.FileInfo
|
|
|
|
// LoadChildren is used to load the fs.FileInfos for this directory's
|
|
|
|
// children. It is called at most once in order to support listing
|
|
|
|
// children.
|
|
|
|
LoadChildren func() ([]fs.FileInfo, error)
|
|
|
|
|
|
|
|
// loadChildrenMu guards children and loadedChildren.
|
|
|
|
loadChildrenMu sync.Mutex
|
|
|
|
children []fs.FileInfo
|
|
|
|
loadedChildren bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Readdir implements interface webdav.File. It lazily loads information about
|
|
|
|
// children when it is called.
|
|
|
|
func (d *DirFile) Readdir(count int) ([]fs.FileInfo, error) {
|
|
|
|
err := d.loadChildrenIfNecessary()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if count <= 0 {
|
|
|
|
result := d.children
|
|
|
|
d.children = nil
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
n := len(d.children)
|
|
|
|
if count < n {
|
|
|
|
n = count
|
|
|
|
}
|
|
|
|
result := d.children[:n]
|
|
|
|
d.children = d.children[n:]
|
|
|
|
if len(d.children) == 0 {
|
|
|
|
err = io.EOF
|
|
|
|
}
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *DirFile) loadChildrenIfNecessary() error {
|
|
|
|
d.loadChildrenMu.Lock()
|
|
|
|
defer d.loadChildrenMu.Unlock()
|
|
|
|
|
|
|
|
if !d.loadedChildren {
|
|
|
|
var err error
|
|
|
|
d.children, err = d.LoadChildren()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.loadedChildren = true
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stat implements interface webdav.File.
|
|
|
|
func (d *DirFile) Stat() (fs.FileInfo, error) {
|
|
|
|
return d.Info, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close implements interface webdav.File. It does nothing and never returns an
|
|
|
|
// error.
|
|
|
|
func (d *DirFile) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read implements interface webdav.File. As this is a directory, it always
|
|
|
|
// fails with an fs.PathError.
|
|
|
|
func (d *DirFile) Read(b []byte) (int, error) {
|
|
|
|
return 0, &fs.PathError{
|
|
|
|
Op: "read",
|
|
|
|
Path: d.Info.Name(),
|
|
|
|
Err: errors.New("is a directory"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements interface webdav.File. As this is a directory, it always
|
|
|
|
// fails with an fs.PathError.
|
|
|
|
func (d *DirFile) Write(b []byte) (int, error) {
|
|
|
|
return 0, &fs.PathError{
|
|
|
|
Op: "write",
|
|
|
|
Path: d.Info.Name(),
|
|
|
|
Err: errors.New("bad file descriptor"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seek implements interface webdav.File. As this is a directory, it always
|
|
|
|
// fails with an fs.PathError.
|
|
|
|
func (d *DirFile) Seek(offset int64, whence int) (int64, error) {
|
|
|
|
return 0, &fs.PathError{
|
|
|
|
Op: "seek",
|
|
|
|
Path: d.Info.Name(),
|
|
|
|
Err: errors.New("invalid argument"),
|
|
|
|
}
|
|
|
|
}
|