diff options
author | a <[email protected]> | 2024-01-13 14:12:43 -0600 |
---|---|---|
committer | GitHub <[email protected]> | 2024-01-13 20:12:43 +0000 |
commit | c839a98ff527932fd14460829142c486f4531a7b (patch) | |
tree | 3a4a9745d2bc54ff557b4439ea0fe2dbc58238e0 /internal | |
parent | b359ca565c624b8718eac79058bff0591b250d0e (diff) | |
download | caddy-c839a98ff527932fd14460829142c486f4531a7b.tar.gz caddy-c839a98ff527932fd14460829142c486f4531a7b.zip |
filesystem: Globally declared filesystems, `fs` directive (#5833)
Diffstat (limited to 'internal')
-rw-r--r-- | internal/filesystems/map.go | 77 | ||||
-rw-r--r-- | internal/filesystems/os.go | 29 |
2 files changed, 106 insertions, 0 deletions
diff --git a/internal/filesystems/map.go b/internal/filesystems/map.go new file mode 100644 index 000000000..e795ed1fe --- /dev/null +++ b/internal/filesystems/map.go @@ -0,0 +1,77 @@ +package filesystems + +import ( + "io/fs" + "strings" + "sync" +) + +const ( + DefaultFilesystemKey = "default" +) + +var DefaultFilesystem = &wrapperFs{key: DefaultFilesystemKey, FS: OsFS{}} + +// wrapperFs exists so can easily add to wrapperFs down the line +type wrapperFs struct { + key string + fs.FS +} + +// FilesystemMap stores a map of filesystems +// the empty key will be overwritten to be the default key +// it includes a default filesystem, based off the os fs +type FilesystemMap struct { + m sync.Map +} + +// note that the first invocation of key cannot be called in a racy context. +func (f *FilesystemMap) key(k string) string { + if k == "" { + k = DefaultFilesystemKey + } + return k +} + +// Register will add the filesystem with key to later be retrieved +// A call with a nil fs will call unregister, ensuring that a call to Default() will never be nil +func (f *FilesystemMap) Register(k string, v fs.FS) { + k = f.key(k) + if v == nil { + f.Unregister(k) + return + } + f.m.Store(k, &wrapperFs{key: k, FS: v}) +} + +// Unregister will remove the filesystem with key from the filesystem map +// if the key is the default key, it will set the default to the osFS instead of deleting it +// modules should call this on cleanup to be safe +func (f *FilesystemMap) Unregister(k string) { + k = f.key(k) + if k == DefaultFilesystemKey { + f.m.Store(k, DefaultFilesystem) + } else { + f.m.Delete(k) + } +} + +// Get will get a filesystem with a given key +func (f *FilesystemMap) Get(k string) (v fs.FS, ok bool) { + k = f.key(k) + c, ok := f.m.Load(strings.TrimSpace(k)) + if !ok { + if k == DefaultFilesystemKey { + f.m.Store(k, DefaultFilesystem) + return DefaultFilesystem, true + } + return nil, ok + } + return c.(fs.FS), true +} + +// Default will get the default filesystem in the filesystem map +func (f *FilesystemMap) Default() fs.FS { + val, _ := f.Get(DefaultFilesystemKey) + return val +} diff --git a/internal/filesystems/os.go b/internal/filesystems/os.go new file mode 100644 index 000000000..04b4d5b40 --- /dev/null +++ b/internal/filesystems/os.go @@ -0,0 +1,29 @@ +package filesystems + +import ( + "io/fs" + "os" + "path/filepath" +) + +// OsFS is a simple fs.FS implementation that uses the local +// file system. (We do not use os.DirFS because we do our own +// rooting or path prefixing without being constrained to a single +// root folder. The standard os.DirFS implementation is problematic +// since roots can be dynamic in our application.) +// +// OsFS also implements fs.StatFS, fs.GlobFS, fs.ReadDirFS, and fs.ReadFileFS. +type OsFS struct{} + +func (OsFS) Open(name string) (fs.File, error) { return os.Open(name) } +func (OsFS) Stat(name string) (fs.FileInfo, error) { return os.Stat(name) } +func (OsFS) Glob(pattern string) ([]string, error) { return filepath.Glob(pattern) } +func (OsFS) ReadDir(name string) ([]fs.DirEntry, error) { return os.ReadDir(name) } +func (OsFS) ReadFile(name string) ([]byte, error) { return os.ReadFile(name) } + +var ( + _ fs.StatFS = (*OsFS)(nil) + _ fs.GlobFS = (*OsFS)(nil) + _ fs.ReadDirFS = (*OsFS)(nil) + _ fs.ReadFileFS = (*OsFS)(nil) +) |