tailscale: update tailfs file and package names (#11590)

This change updates the tailfs file and package names to their new
naming convention.

Updates #tailscale/corp#16827

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
This commit is contained in:
Charlotte Brandhorst-Satzkorn
2024-04-02 13:32:30 -07:00
committed by GitHub
parent 1c259100b0
commit 14683371ee
58 changed files with 180 additions and 180 deletions

View File

@@ -0,0 +1,50 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package shared
import (
"path"
"strings"
)
// This file provides utility functions for working with URL paths. These are
// similar to functions in package path in the standard library, but differ in
// ways that are documented on the relevant functions.
const (
sepString = "/"
sepStringAndDot = "/."
sep = '/'
)
// CleanAndSplit cleans the provided path p and splits it into its constituent
// parts. This is different from path.Split which just splits a path into prefix
// and suffix.
func CleanAndSplit(p string) []string {
return strings.Split(strings.Trim(path.Clean(p), sepStringAndDot), sepString)
}
// Join behaves like path.Join() but also includes a leading slash.
func Join(parts ...string) string {
fullParts := make([]string, 0, len(parts))
fullParts = append(fullParts, sepString)
for _, part := range parts {
fullParts = append(fullParts, part)
}
return path.Join(fullParts...)
}
// IsRoot determines whether a given path p is the root path, defined as either
// empty or "/".
func IsRoot(p string) bool {
return p == "" || p == sepString
}
// Base is like path.Base except that it returns "" for the root folder
func Base(p string) string {
if IsRoot(p) {
return ""
}
return path.Base(p)
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package shared
import (
"reflect"
"testing"
)
func TestCleanAndSplit(t *testing.T) {
tests := []struct {
path string
want []string
}{
{"", []string{""}},
{"/", []string{""}},
{"//", []string{""}},
{"a", []string{"a"}},
{"/a", []string{"a"}},
{"a/", []string{"a"}},
{"/a/", []string{"a"}},
{"a/b", []string{"a", "b"}},
{"/a/b", []string{"a", "b"}},
{"a/b/", []string{"a", "b"}},
{"/a/b/", []string{"a", "b"}},
{"/a/../b", []string{"b"}},
}
for _, tt := range tests {
t.Run(tt.path, func(t *testing.T) {
if got := CleanAndSplit(tt.path); !reflect.DeepEqual(tt.want, got) {
t.Errorf("CleanAndSplit(%q) = %v; want %v", tt.path, got, tt.want)
}
})
}
}
func TestJoin(t *testing.T) {
tests := []struct {
parts []string
want string
}{
{[]string{""}, "/"},
{[]string{"a"}, "/a"},
{[]string{"/a"}, "/a"},
{[]string{"/a/"}, "/a"},
{[]string{"/a/", "/b/"}, "/a/b"},
{[]string{"/a/../b", "c"}, "/b/c"},
}
for _, tt := range tests {
t.Run(Join(tt.parts...), func(t *testing.T) {
if got := Join(tt.parts...); !reflect.DeepEqual(tt.want, got) {
t.Errorf("Join(%v) = %q; want %q", tt.parts, got, tt.want)
}
})
}
}

View File

@@ -0,0 +1,111 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package shared contains types and functions shared by different tailfs
// 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"),
}
}

View File

@@ -0,0 +1,73 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package shared
import (
"context"
"io/fs"
"os"
"time"
"github.com/tailscale/xnet/webdav"
)
// StaticFileInfo implements a static fs.FileInfo
type StaticFileInfo struct {
// Named controls Name()
Named string
// Sized controls Size()
Sized int64
// Moded controls Mode()
Moded os.FileMode
// BirthedTime controls BirthTime()
BirthedTime time.Time
// BirthedTimeErr stores any error encountered when trying to get BirthTime
BirthedTimeErr error
// ModdedTime controls ModTime()
ModdedTime time.Time
// Dir controls IsDir()
Dir bool
}
// BirthTime implements webdav.BirthTimer
func (fi *StaticFileInfo) BirthTime(_ context.Context) (time.Time, error) {
return fi.BirthedTime, fi.BirthedTimeErr
}
func (fi *StaticFileInfo) Name() string { return fi.Named }
func (fi *StaticFileInfo) Size() int64 { return fi.Sized }
func (fi *StaticFileInfo) Mode() os.FileMode { return fi.Moded }
func (fi *StaticFileInfo) ModTime() time.Time { return fi.ModdedTime }
func (fi *StaticFileInfo) IsDir() bool { return fi.Dir }
func (fi *StaticFileInfo) Sys() any { return nil }
func RenamedFileInfo(ctx context.Context, name string, fi fs.FileInfo) *StaticFileInfo {
var birthTime time.Time
var birthTimeErr error
birthTimer, ok := fi.(webdav.BirthTimer)
if ok {
birthTime, birthTimeErr = birthTimer.BirthTime(ctx)
}
return &StaticFileInfo{
Named: Base(name),
Sized: fi.Size(),
Moded: fi.Mode(),
BirthedTime: birthTime,
BirthedTimeErr: birthTimeErr,
ModdedTime: fi.ModTime(),
Dir: fi.IsDir(),
}
}
// ReadOnlyDirInfo returns a static fs.FileInfo for a read-only directory
func ReadOnlyDirInfo(name string, ts time.Time) *StaticFileInfo {
return &StaticFileInfo{
Named: Base(name),
Sized: 0,
Moded: 0555,
BirthedTime: ts,
ModdedTime: ts,
Dir: true,
}
}