mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-11 13:18:53 +00:00
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:

committed by
GitHub

parent
1c259100b0
commit
14683371ee
50
drive/driveimpl/shared/pathutil.go
Normal file
50
drive/driveimpl/shared/pathutil.go
Normal 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)
|
||||
}
|
57
drive/driveimpl/shared/pathutil_test.go
Normal file
57
drive/driveimpl/shared/pathutil_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
111
drive/driveimpl/shared/readonlydir.go
Normal file
111
drive/driveimpl/shared/readonlydir.go
Normal 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"),
|
||||
}
|
||||
}
|
73
drive/driveimpl/shared/stat.go
Normal file
73
drive/driveimpl/shared/stat.go
Normal 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,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user