cachefs/pkg/provider/crypto/fs.go
2022-04-06 01:50:28 +02:00

111 lines
2.1 KiB
Go

package crypto
import (
"cachefs/pkg/provider"
"encoding/base64"
"errors"
"io/fs"
"os"
"path/filepath"
"strings"
)
var _ provider.FS = (*FS)(nil)
type FS struct {
fs provider.FS
cbc *aesCbc
key []byte
}
func NewFS(fs provider.FS, key []byte) (*FS, error) {
if len(key) != 32 {
return nil, errors.New("encryption key length must be 32 bytes")
}
cbc, err := newAesCbc(key)
if err != nil {
return nil, err
}
return &FS{
fs: fs,
cbc: cbc,
key: key,
}, nil
}
func (fs *FS) encPath(path string) string {
parts := strings.Split(path, "/")
cparts := make([]string, len(parts))
for i, p := range parts {
if p == "" {
continue
}
cparts[i] = base64.RawURLEncoding.EncodeToString(
fs.cbc.encrypt([]byte(p)),
)
}
return filepath.Join(cparts...)
}
func (fs *FS) decPath(path string) (string, error) {
parts := strings.Split(path, "/")
pparts := make([]string, len(parts))
for i, p := range parts {
b, err := base64.RawURLEncoding.DecodeString(p)
if err != nil {
return "", err
}
pparts[i] = string(fs.cbc.decrypt(b))
}
return filepath.Join(pparts...), nil
}
func (fs *FS) name(p string) string {
return filepath.Join(fs.Root(), p)
}
func (fs *FS) Stat(p string) (fs.FileInfo, error) {
fi, err := fs.fs.Stat(fs.encPath(p))
return stat{FileInfo: fi, name: fs.name(p)}, err
}
func (fs *FS) Remove(p string) error {
return fs.fs.Remove(fs.encPath(p))
}
func (fs *FS) Open(p string) (provider.File, error) {
c := fs.encPath(p)
fi, err := fs.fs.Stat(c)
if err != nil {
return nil, err
}
f, err := fs.fs.Open(c)
if err != nil {
return nil, err
}
return newFile(fs.key, p, fs, f, fi.IsDir())
}
func (fs *FS) OpenFile(p string, flags int, mode os.FileMode) (provider.File, error) {
f, err := fs.fs.OpenFile(fs.encPath(p), flags, mode)
if err != nil {
return nil, err
}
return newFile(fs.key, p, fs, f, false)
}
func (fs *FS) MkdirAll(p string, mode os.FileMode) error {
return fs.fs.MkdirAll(fs.encPath(p), mode)
}
func (fs *FS) Root() string {
return fs.fs.Root()
}
func (fs *FS) Fstat(_ uintptr) (int64, error) {
return 0, provider.ErrFstat
}
func (fs *FS) Close() error {
return nil
}