2023-11-27 22:18:22 +01:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-01-10 17:57:21 +01:00
|
|
|
"github.com/openziti/zrok/drives/davServer"
|
2023-11-27 23:31:13 +01:00
|
|
|
"io"
|
2023-11-27 22:18:22 +01:00
|
|
|
"io/fs"
|
|
|
|
"os"
|
2023-11-27 23:31:13 +01:00
|
|
|
"path/filepath"
|
2023-11-28 03:11:19 +01:00
|
|
|
"time"
|
2023-11-27 22:18:22 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type FilesystemTargetConfig struct {
|
|
|
|
Root string
|
|
|
|
}
|
|
|
|
|
|
|
|
type FilesystemTarget struct {
|
2023-11-27 23:31:13 +01:00
|
|
|
cfg *FilesystemTargetConfig
|
2023-11-27 22:18:22 +01:00
|
|
|
root fs.FS
|
|
|
|
tree []*Object
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFilesystemTarget(cfg *FilesystemTargetConfig) *FilesystemTarget {
|
|
|
|
root := os.DirFS(cfg.Root)
|
2023-11-27 23:31:13 +01:00
|
|
|
return &FilesystemTarget{cfg: cfg, root: root}
|
2023-11-27 22:18:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *FilesystemTarget) Inventory() ([]*Object, error) {
|
2023-12-15 18:10:35 +01:00
|
|
|
fi, err := os.Stat(t.cfg.Root)
|
|
|
|
if os.IsNotExist(err) {
|
2023-11-27 23:31:13 +01:00
|
|
|
return nil, nil
|
|
|
|
}
|
2023-12-15 18:10:35 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !fi.IsDir() {
|
2024-01-10 19:38:35 +01:00
|
|
|
t.cfg.Root = filepath.Dir(t.cfg.Root)
|
|
|
|
return []*Object{{
|
|
|
|
Path: "/" + fi.Name(),
|
|
|
|
IsDir: false,
|
|
|
|
Size: fi.Size(),
|
|
|
|
Modified: fi.ModTime(),
|
|
|
|
}}, nil
|
2023-12-15 18:10:35 +01:00
|
|
|
}
|
|
|
|
|
2023-11-27 22:18:22 +01:00
|
|
|
t.tree = nil
|
|
|
|
if err := fs.WalkDir(t.root, ".", t.recurse); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return t.tree, nil
|
|
|
|
}
|
|
|
|
|
2024-01-10 20:35:27 +01:00
|
|
|
func (t *FilesystemTarget) Dir(path string) ([]*Object, error) {
|
|
|
|
des, err := os.ReadDir(t.cfg.Root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var objects []*Object
|
|
|
|
for _, de := range des {
|
|
|
|
fi, err := de.Info()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
objects = append(objects, &Object{
|
|
|
|
Path: de.Name(),
|
|
|
|
IsDir: de.IsDir(),
|
|
|
|
Size: fi.Size(),
|
|
|
|
Modified: fi.ModTime(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
2024-01-09 19:58:38 +01:00
|
|
|
func (t *FilesystemTarget) Mkdir(path string) error {
|
|
|
|
return os.MkdirAll(filepath.Join(t.cfg.Root, path), os.ModePerm)
|
2023-12-15 18:10:35 +01:00
|
|
|
}
|
|
|
|
|
2023-11-27 22:18:22 +01:00
|
|
|
func (t *FilesystemTarget) recurse(path string, d fs.DirEntry, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-09 19:58:38 +01:00
|
|
|
fi, err := d.Info()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
etag := ""
|
2024-01-10 17:57:21 +01:00
|
|
|
if v, ok := fi.(davServer.ETager); ok {
|
2024-01-09 19:58:38 +01:00
|
|
|
etag, err = v.ETag(context.Background())
|
2023-11-27 22:18:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-09 19:58:38 +01:00
|
|
|
} else {
|
|
|
|
etag = fmt.Sprintf(`"%x%x"`, fi.ModTime().UTC().UnixNano(), fi.Size())
|
|
|
|
}
|
|
|
|
if path != "." {
|
|
|
|
outPath := "/" + path
|
|
|
|
if fi.IsDir() {
|
|
|
|
outPath = outPath + "/"
|
2023-11-27 22:18:22 +01:00
|
|
|
}
|
2024-01-09 19:58:38 +01:00
|
|
|
t.tree = append(t.tree, &Object{
|
|
|
|
Path: outPath,
|
|
|
|
IsDir: fi.IsDir(),
|
|
|
|
Size: fi.Size(),
|
|
|
|
Modified: fi.ModTime(),
|
|
|
|
ETag: etag,
|
|
|
|
})
|
2023-11-27 22:18:22 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-11-27 23:31:13 +01:00
|
|
|
|
|
|
|
func (t *FilesystemTarget) ReadStream(path string) (io.ReadCloser, error) {
|
2023-12-01 18:36:06 +01:00
|
|
|
return os.Open(filepath.Join(t.cfg.Root, path))
|
2023-11-27 23:31:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *FilesystemTarget) WriteStream(path string, stream io.Reader, mode os.FileMode) error {
|
|
|
|
targetPath := filepath.Join(t.cfg.Root, path)
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(targetPath), mode); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f, err := os.Create(targetPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-05-08 12:10:01 +02:00
|
|
|
defer f.Close()
|
2023-11-27 23:31:13 +01:00
|
|
|
_, err = io.Copy(f, stream)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2023-11-28 03:11:19 +01:00
|
|
|
|
2024-01-16 19:54:01 +01:00
|
|
|
func (t *FilesystemTarget) WriteStreamWithModTime(path string, stream io.Reader, mode os.FileMode, modTime time.Time) error {
|
|
|
|
return t.WriteStream(path, stream, mode)
|
|
|
|
}
|
|
|
|
|
2024-01-11 19:19:20 +01:00
|
|
|
func (t *FilesystemTarget) Move(src, dest string) error {
|
|
|
|
return os.Rename(filepath.Join(t.cfg.Root, src), filepath.Join(filepath.Dir(t.cfg.Root), dest))
|
|
|
|
}
|
|
|
|
|
2024-01-11 18:11:21 +01:00
|
|
|
func (t *FilesystemTarget) Rm(path string) error {
|
|
|
|
return os.RemoveAll(filepath.Join(t.cfg.Root, path))
|
|
|
|
}
|
|
|
|
|
2023-11-28 03:11:19 +01:00
|
|
|
func (t *FilesystemTarget) SetModificationTime(path string, mtime time.Time) error {
|
|
|
|
targetPath := filepath.Join(t.cfg.Root, path)
|
|
|
|
if err := os.Chtimes(targetPath, time.Now(), mtime); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|