cachefs/pkg/provider/crypto/fs.go

118 lines
2.3 KiB
Go

// Copyright (C) 2022 Marius Schellenberger
package crypto
import (
"cachefs/pkg/provider"
osp "cachefs/pkg/provider/os"
"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(fd uintptr) (int64, error) {
osp, ok := fs.fs.(*osp.FS)
if ok {
return osp.Fstat(fd)
}
return 0, provider.ErrFstat
}
func (fs *FS) Close() error {
return nil
}