diff --git a/cmd/stsrv/main.go b/cmd/stsrv/main.go index 416afc0..c914d78 100644 --- a/cmd/stsrv/main.go +++ b/cmd/stsrv/main.go @@ -4,21 +4,98 @@ import ( "flag" "log" "net/http" + "os" + "path/filepath" "github.com/pojntfx/stfs/internal/fs" "github.com/pojntfx/stfs/internal/handlers" + "github.com/pojntfx/stfs/internal/logging" + "github.com/pojntfx/stfs/internal/persisters" + "github.com/pojntfx/stfs/pkg/config" + "github.com/pojntfx/stfs/pkg/operations" + "github.com/pojntfx/stfs/pkg/tape" "github.com/spf13/afero" ) func main() { + home, err := os.UserHomeDir() + if err != nil { + panic(err) + } + laddr := flag.String("laddr", "localhost:1337", "Listen address") dir := flag.String("dir", "/", "Directory to use as the root directory") + drive := flag.String("drive", "/dev/nst0", "Tape or tar file to use") + metadata := flag.String("metadata", filepath.Join(home, ".local", "share", "stbak", "var", "lib", "stbak", "metadata.sqlite"), "Metadata database to use") + recordSize := flag.Int("recordSize", 20, "Amount of 512-bit blocks per record") flag.Parse() - stfs := afero.NewHttpFs(fs.NewSTFS()) + tm := tape.NewTapeManager( + *drive, + *recordSize, + false, + ) + + metadataPersister := persisters.NewMetadataPersister(*metadata) + if err := metadataPersister.Open(); err != nil { + panic(err) + } + + logger := logging.NewLogger() + + ops := operations.NewOperations( + config.BackendConfig{ + GetWriter: tm.GetWriter, + CloseWriter: tm.Close, + + GetReader: tm.GetReader, + CloseReader: tm.Close, + + GetDrive: tm.GetDrive, + CloseDrive: tm.Close, + }, + config.MetadataConfig{ + Metadata: metadataPersister, + }, + + config.PipeConfig{ + Compression: config.NoneKey, + Encryption: config.NoneKey, + Signature: config.NoneKey, + RecordSize: *recordSize, + }, + config.CryptoConfig{ + Recipient: []byte{}, + Identity: []byte{}, + Password: "", + }, + + logger.PrintHeaderEvent, + ) + + stfs := afero.NewHttpFs( + fs.NewFileSystem( + ops, + + config.MetadataConfig{ + Metadata: metadataPersister, + }, + + logger.PrintHeader, + ), + ) log.Println("Listening on", *laddr) - log.Fatal(http.ListenAndServe(*laddr, handlers.PanicHandler(http.FileServer(stfs.Dir(*dir))))) + panic( + http.ListenAndServe( + *laddr, + handlers.PanicHandler( + http.FileServer( + stfs.Dir(*dir), + ), + ), + ), + ) } diff --git a/internal/fs/afero.go b/internal/fs/afero.go deleted file mode 100644 index 27ae66e..0000000 --- a/internal/fs/afero.go +++ /dev/null @@ -1,124 +0,0 @@ -package fs - -import ( - "errors" - "os" - "time" - - "github.com/spf13/afero" -) - -var ( - ErrNotImplemented = errors.New("not implemented") -) - -type STFS struct{} - -func NewSTFS() afero.Fs { - return &STFS{} -} - -func (STFS) Name() string { return "STFS" } - -func (STFS) Create(name string) (afero.File, error) { - panic(ErrNotImplemented) - - // f, e := os.Create(name) - // if f == nil { - // return nil, e - // } - // return f, e -} - -func (STFS) Mkdir(name string, perm os.FileMode) error { - panic(ErrNotImplemented) - - // return os.Mkdir(name, perm) -} - -func (STFS) MkdirAll(path string, perm os.FileMode) error { - panic(ErrNotImplemented) - - // return os.MkdirAll(path, perm) -} - -func (STFS) Open(name string) (afero.File, error) { - panic(ErrNotImplemented) - - // f, e := os.Open(name) - // if f == nil { - // return nil, e - // } - // return f, e -} - -func (STFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { - panic(ErrNotImplemented) - - // f, e := os.OpenFile(name, flag, perm) - // if f == nil { - // return nil, e - // } - // return f, e -} - -func (STFS) Remove(name string) error { - panic(ErrNotImplemented) - - // return os.Remove(name) -} - -func (STFS) RemoveAll(path string) error { - panic(ErrNotImplemented) - - // return os.RemoveAll(path) -} - -func (STFS) Rename(oldname, newname string) error { - panic(ErrNotImplemented) - - // return os.Rename(oldname, newname) -} - -func (STFS) Stat(name string) (os.FileInfo, error) { - panic(ErrNotImplemented) - - // return os.Stat(name) -} - -func (STFS) Chmod(name string, mode os.FileMode) error { - panic(ErrNotImplemented) - - // return os.Chmod(name, mode) -} - -func (STFS) Chown(name string, uid, gid int) error { - panic(ErrNotImplemented) - - // return os.Chown(name, uid, gid) -} - -func (STFS) Chtimes(name string, atime time.Time, mtime time.Time) error { - panic(ErrNotImplemented) - - // return os.Chtimes(name, atime, mtime) -} - -func (STFS) LstatIfPossible(name string) (os.FileInfo, bool, error) { - panic(ErrNotImplemented) - - // fi, err := os.Lstat(name) - // return fi, true, err -} - -func (STFS) SymlinkIfPossible(oldname, newname string) error { - panic(ErrNotImplemented) - - // return os.Symlink(oldname, newname) -} - -func (STFS) ReadlinkIfPossible(name string) (string, error) { - panic(ErrNotImplemented) - - // return os.Readlink(name) -} diff --git a/internal/fs/file.go b/internal/fs/file.go new file mode 100644 index 0000000..db63093 --- /dev/null +++ b/internal/fs/file.go @@ -0,0 +1,103 @@ +package fs + +import ( + "log" + "os" + + "github.com/spf13/afero" +) + +type File struct { + afero.File + + name string + info os.FileInfo +} + +func NewFile( + name string, + stat os.FileInfo, +) *File { + return &File{ + name: name, + info: stat, + } +} + +func (f File) Name() string { + log.Println("File.Name", f.name) + + return f.name +} + +func (f File) Stat() (os.FileInfo, error) { + log.Println("File.Stat", f.name) + + return f.info, nil +} + +func (f File) Readdir(count int) ([]os.FileInfo, error) { + log.Println("File.Readdir", f.name, count) + + return nil, ErrNotImplemented +} + +func (f File) Readdirnames(n int) ([]string, error) { + log.Println("File.Readdirnames", f.name, n) + + return nil, ErrNotImplemented +} + +func (f File) Sync() error { + log.Println("File.Sync", f.name) + + return ErrNotImplemented +} + +func (f File) Truncate(size int64) error { + log.Println("File.Truncate", f.name, size) + + return ErrNotImplemented +} + +func (f File) WriteString(s string) (ret int, err error) { + log.Println("File.WriteString", f.name, s) + + return -1, ErrNotImplemented +} + +func (f File) Close() error { + log.Println("File.Close", f.name) + + return ErrNotImplemented +} + +func (f File) Read(p []byte) (n int, err error) { + log.Println("File.Read", f.name, p) + + return -1, ErrNotImplemented +} + +func (f File) ReadAt(p []byte, off int64) (n int, err error) { + log.Println("File.ReadAt", f.name, p, off) + + return -1, ErrNotImplemented +} + +func (f File) Seek(offset int64, whence int) (int64, error) { + log.Println("File.Seek", f.name, offset, whence) + + return -1, ErrNotImplemented +} + +func (f File) Write(p []byte) (n int, err error) { + log.Println("File.Write", f.name, p) + + return -1, ErrNotImplemented +} + +func (f File) WriteAt(p []byte, off int64) (n int, err error) { + log.Println("File.WriteAt", f.name, p, off) + + return -1, ErrNotImplemented +} diff --git a/internal/fs/fileinfo.go b/internal/fs/fileinfo.go new file mode 100644 index 0000000..6545a77 --- /dev/null +++ b/internal/fs/fileinfo.go @@ -0,0 +1,69 @@ +package fs + +import ( + "log" + "os" + "time" +) + +type FileInfo struct { + os.FileInfo + + name string + size int64 + mode int64 + modTime time.Time + isDir bool +} + +func NewFileInfo( + name string, + size int64, + mode int64, + modTime time.Time, + isDir bool, +) *FileInfo { + return &FileInfo{ + name: name, + size: size, + mode: mode, + modTime: modTime, + isDir: isDir, + } +} + +func (f FileInfo) Name() string { + log.Println("FileInfo.Name", f.name) + + return f.name +} + +func (f FileInfo) Size() int64 { + log.Println("FileInfo.Size", f.name) + + return f.size +} + +func (f FileInfo) Mode() os.FileMode { + log.Println("FileInfo.Mode", f.name) + + return os.FileMode(f.mode) +} + +func (f FileInfo) ModTime() time.Time { + log.Println("FileInfo.ModTime", f.name) + + return f.modTime +} + +func (f FileInfo) IsDir() bool { + log.Println("FileInfo.IsDir", f.name) + + return f.isDir +} + +func (f FileInfo) Sys() interface{} { + log.Println("FileInfo.Sys", f.name) + + return nil +} diff --git a/internal/fs/fs.go b/internal/fs/fs.go new file mode 100644 index 0000000..84e9915 --- /dev/null +++ b/internal/fs/fs.go @@ -0,0 +1,165 @@ +package fs + +import ( + "archive/tar" + "database/sql" + "errors" + "log" + "os" + "path/filepath" + "time" + + models "github.com/pojntfx/stfs/internal/db/sqlite/models/metadata" + "github.com/pojntfx/stfs/pkg/config" + "github.com/pojntfx/stfs/pkg/inventory" + "github.com/pojntfx/stfs/pkg/operations" + "github.com/spf13/afero" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +type FileSystem struct { + ops *operations.Operations + + metadata config.MetadataConfig + + onHeader func(hdr *models.Header) +} + +func NewFileSystem( + ops *operations.Operations, + + metadata config.MetadataConfig, + + onHeader func(hdr *models.Header), +) afero.Fs { + return &FileSystem{ + ops: ops, + + metadata: metadata, + + onHeader: onHeader, + } +} + +func (S *FileSystem) Name() string { + log.Println("FileSystem.Name") + + return "STFS" +} + +func (S *FileSystem) Create(name string) (afero.File, error) { + log.Println("FileSystem.Name", name) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Mkdir(name string, perm os.FileMode) error { + log.Println("FileSystem.Mkdir", name, perm) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) MkdirAll(path string, perm os.FileMode) error { + log.Println("FileSystem.MkdirAll", path, perm) + + panic(ErrNotImplemented) +} + +func (s *FileSystem) Open(name string) (afero.File, error) { + log.Println("FileSystem.Open", name) + + hdr, err := inventory.Stat( + s.metadata, + + name, + + s.onHeader, + ) + if err != nil { + if err == sql.ErrNoRows { + return nil, os.ErrNotExist + } + + return nil, err + } + + return NewFile( + filepath.Base(name), + NewFileInfo( + filepath.Base(name), + hdr.Size, + hdr.Mode, + hdr.ModTime, + hdr.Typeflag == tar.TypeDir, + ), + ), nil +} + +func (S *FileSystem) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { + log.Println("FileSystem.OpenFile", name, flag, perm) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Remove(name string) error { + log.Println("FileSystem.Remove", name) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) RemoveAll(path string) error { + log.Println("FileSystem.RemoveAll", path) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Rename(oldname, newname string) error { + log.Println("FileSystem.Rename", oldname, newname) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Stat(name string) (os.FileInfo, error) { + log.Println("FileSystem.Stat", name) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Chmod(name string, mode os.FileMode) error { + log.Println("FileSystem.Chmod", name, mode) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Chown(name string, uid, gid int) error { + log.Println("FileSystem.Chown", name, uid, gid) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) Chtimes(name string, atime time.Time, mtime time.Time) error { + log.Println("FileSystem.Chtimes", name, atime, mtime) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) LstatIfPossible(name string) (os.FileInfo, bool, error) { + log.Println("FileSystem.LstatIfPossible", name) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) SymlinkIfPossible(oldname, newname string) error { + log.Println("FileSystem.SymlinkIfPossible", oldname, newname) + + panic(ErrNotImplemented) +} + +func (S *FileSystem) ReadlinkIfPossible(name string) (string, error) { + log.Println("FileSystem.ReadlinkIfPossible", name) + + panic(ErrNotImplemented) +}