From ed021b554eabc4666b13cb219fb878fdf6bc8d10 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 31 Aug 2024 15:12:24 +0800 Subject: [PATCH] feat: init commit --- .gitignore | 1 + README.md | 5 ++ encfs/file.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ encfs/fs.go | 95 +++++++++++++++++++++++++++++++++++++ go.mod | 8 ++++ go.sum | 4 ++ main.go | 35 ++++++++++++++ 7 files changed, 276 insertions(+) create mode 100644 encfs/file.go create mode 100644 encfs/fs.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore index adc6aea..0b67be9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +main # ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore diff --git a/README.md b/README.md index 01c609e..a76605e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # go-afero-encfs +`go-afero-encfs` is a afero encrypted fs implementation + +In stage 1 we only encrypt file content, the file name is not encrypted. + +In the future we are going to encrypt the file name also. diff --git a/encfs/file.go b/encfs/file.go new file mode 100644 index 0000000..2cf6455 --- /dev/null +++ b/encfs/file.go @@ -0,0 +1,128 @@ +package encfs + +type EncFile struct {} + +func (f *File) Close() error { + if f.closed { + return afero.ErrFileClosed + } + + f.closed = true + f.h = nil + f.data = nil + f.fs = nil + + return nil +} + +func (f *File) Read(p []byte) (n int, err error) { + if f.closed { + return 0, afero.ErrFileClosed + } + + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return f.data.Read(p) +} + +func (f *File) ReadAt(p []byte, off int64) (n int, err error) { + if f.closed { + return 0, afero.ErrFileClosed + } + + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return f.data.ReadAt(p, off) +} + +func (f *File) Seek(offset int64, whence int) (int64, error) { + if f.closed { + return 0, afero.ErrFileClosed + } + + if f.h.Typeflag == tar.TypeDir { + return 0, syscall.EISDIR + } + + return f.data.Seek(offset, whence) +} + +func (f *File) Write(p []byte) (n int, err error) { return 0, syscall.EROFS } + +func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, syscall.EROFS } + +func (f *File) Name() string { + return filepath.Join(splitpath(f.h.Name)) +} + +func (f *File) getDirectoryNames() ([]string, error) { + d, ok := f.fs.files[f.Name()] + if !ok { + return nil, &os.PathError{Op: "readdir", Path: f.Name(), Err: syscall.ENOENT} + } + + var names []string + for n := range d { + names = append(names, n) + } + sort.Strings(names) + + return names, nil +} + +func (f *File) Readdir(count int) ([]os.FileInfo, error) { + if f.closed { + return nil, afero.ErrFileClosed + } + + if !f.h.FileInfo().IsDir() { + return nil, syscall.ENOTDIR + } + + names, err := f.getDirectoryNames() + if err != nil { + return nil, err + } + + d := f.fs.files[f.Name()] + var fi []os.FileInfo + for _, n := range names { + if n == "" { + continue + } + + f := d[n] + fi = append(fi, f.h.FileInfo()) + if count > 0 && len(fi) >= count { + break + } + } + + return fi, nil +} + +func (f *File) Readdirnames(n int) ([]string, error) { + fi, err := f.Readdir(n) + if err != nil { + return nil, err + } + + var names []string + for _, f := range fi { + names = append(names, f.Name()) + } + + return names, nil +} + +func (f *File) Stat() (os.FileInfo, error) { return f.h.FileInfo(), nil } + +func (f *File) Sync() error { return nil } + +func (f *File) Truncate(size int64) error { return syscall.EROFS } + +func (f *File) WriteString(s string) (ret int, err error) { return 0, syscall.EROFS } diff --git a/encfs/fs.go b/encfs/fs.go new file mode 100644 index 0000000..f1c1f1c --- /dev/null +++ b/encfs/fs.go @@ -0,0 +1,95 @@ +package encfs + +import ( + "os" + "time" +) + +// copied from afero/os.go + +type EncFs struct{} + +func NewEncFs() Fs { + return &EncFs{} +} + +func (EncFs) Name() string { return "EncFs" } + +func (EncFs) Create(name string) (File, error) { + f, e := os.Create(name) + if f == nil { + // while this looks strange, we need to return a bare nil (of type nil) not + // a nil value of type *os.File or nil won't be nil + return nil, e + } + return f, e +} + +func (EncFs) Mkdir(name string, perm os.FileMode) error { + return os.Mkdir(name, perm) +} + +func (EncFs) MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +func (EncFs) Open(name string) (File, error) { + f, e := os.Open(name) + if f == nil { + // while this looks strange, we need to return a bare nil (of type nil) not + // a nil value of type *os.File or nil won't be nil + return nil, e + } + return f, e +} + +func (EncFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { + f, e := os.OpenFile(name, flag, perm) + if f == nil { + // while this looks strange, we need to return a bare nil (of type nil) not + // a nil value of type *os.File or nil won't be nil + return nil, e + } + return f, e +} + +func (EncFs) Remove(name string) error { + return os.Remove(name) +} + +func (EncFs) RemoveAll(path string) error { + return os.RemoveAll(path) +} + +func (EncFs) Rename(oldname, newname string) error { + return os.Rename(oldname, newname) +} + +func (EncFs) Stat(name string) (os.FileInfo, error) { + return os.Stat(name) +} + +func (EncFs) Chmod(name string, mode os.FileMode) error { + return os.Chmod(name, mode) +} + +func (EncFs) Chown(name string, uid, gid int) error { + return os.Chown(name, uid, gid) +} + +func (EncFs) Chtimes(name string, atime time.Time, mtime time.Time) error { + return os.Chtimes(name, atime, mtime) +} + +func (EncFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { + fi, err := os.Lstat(name) + return fi, true, err +} + +func (EncFs) SymlinkIfPossible(oldname, newname string) error { + return os.Symlink(oldname, newname) +} + +func (EncFs) ReadlinkIfPossible(name string) (string, error) { + return os.Readlink(name) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b6132e3 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module git.hatter.ink/hatter/go-afero-encfs + +go 1.23.0 + +require ( + github.com/spf13/afero v1.11.0 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b1d2ab7 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/main.go b/main.go new file mode 100644 index 0000000..1e821b8 --- /dev/null +++ b/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "encoding/hex" + "crypto/aes" + "github.com/spf13/afero" +) + +func main() { + fmt.Println("Hello Go.") + osFs := afero.NewOsFs() + fmt.Println(osFs) + + key := []byte { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + cipher, err := aes.NewCipher(key) + if err != nil { + fmt.Println(err) + return + } + + dest := []byte { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + src := []byte { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + } + cipher.Encrypt(dest, src) + + fmt.Println(hex.EncodeToString(key)) + fmt.Println(hex.EncodeToString(src)) + fmt.Println(hex.EncodeToString(dest)) +}