233 lines
4.3 KiB
Go
233 lines
4.3 KiB
Go
package encfs
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
const EncFileExt = ".__encfile"
|
|
|
|
var (
|
|
ErrFileForbiddenFileExt = errors.New("File ext is forbidden")
|
|
)
|
|
|
|
type EncFileMeta struct {
|
|
Name string `json:"name"`
|
|
Iv []byte `json:"iv"`
|
|
}
|
|
|
|
func newEncFileMeta(name string) (*EncFileMeta, error) {
|
|
iv := make([]byte, 16)
|
|
_, err := rand.Read(iv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
encFileMeta := &EncFileMeta{
|
|
Name: name,
|
|
Iv: iv,
|
|
}
|
|
encFileMetaName := name + EncFileExt
|
|
encFileMetaFile, err := os.Create(encFileMetaName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
_ = encFileMetaFile.Close()
|
|
}()
|
|
encFileMetaBytes, err := marshalEncFileMeta(encFileMeta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, err = encFileMetaFile.Write(encFileMetaBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return encFileMeta, nil
|
|
}
|
|
|
|
func openEncFileMeta(name string) (*EncFileMeta, error) {
|
|
encFileMetaName := name + "." + EncFileExt
|
|
encFileMetaFile, err := os.Open(encFileMetaName)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
_ = encFileMetaFile.Close()
|
|
}()
|
|
encFileMetaBytes, err := io.ReadAll(encFileMetaFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
encFileMeta, err := unmarchalEncFileMeta(encFileMetaBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return encFileMeta, nil
|
|
}
|
|
|
|
func marshalEncFileMeta(encFileMeata *EncFileMeta) ([]byte, error) {
|
|
return json.Marshal(encFileMeata)
|
|
}
|
|
|
|
func unmarchalEncFileMeta(data []byte) (*EncFileMeta, error) {
|
|
var encFileMeta EncFileMeta
|
|
err := json.Unmarshal(data, &encFileMeta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &encFileMeta, nil
|
|
}
|
|
|
|
type EncFile struct {
|
|
isDir bool
|
|
closed bool
|
|
encFileMeta *EncFileMeta
|
|
file *os.File
|
|
}
|
|
|
|
func NewEncFile(name string, file *os.File, isCreate bool) (*EncFile, error) {
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
isDir := fileInfo.IsDir()
|
|
var encFileMeta *EncFileMeta = nil
|
|
if !isDir {
|
|
if isCreate {
|
|
encFileMeta, err = newEncFileMeta(name)
|
|
} else {
|
|
encFileMeta, err = openEncFileMeta(name)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &EncFile{
|
|
isDir: isDir,
|
|
closed: false,
|
|
encFileMeta: encFileMeta,
|
|
file: file,
|
|
}, nil
|
|
}
|
|
|
|
func (f *EncFile) Close() error {
|
|
if f.closed {
|
|
return afero.ErrFileClosed
|
|
}
|
|
|
|
f.closed = true
|
|
return f.file.Close()
|
|
}
|
|
|
|
func (f *EncFile) Read(p []byte) (n int, err error) {
|
|
checkIsFileErr := f.checkIsFile()
|
|
if checkIsFileErr != nil {
|
|
return 0, checkIsFileErr
|
|
}
|
|
|
|
// TODO decrypt
|
|
return f.file.Read(p)
|
|
}
|
|
|
|
func (f *EncFile) ReadAt(p []byte, off int64) (n int, err error) {
|
|
checkIsFileErr := f.checkIsFile()
|
|
if checkIsFileErr != nil {
|
|
return 0, checkIsFileErr
|
|
}
|
|
|
|
// TODO decrypt
|
|
return f.file.ReadAt(p, off)
|
|
}
|
|
|
|
func (f *EncFile) Seek(offset int64, whence int) (int64, error) {
|
|
checkIsFileErr := f.checkIsFile()
|
|
if checkIsFileErr != nil {
|
|
return 0, checkIsFileErr
|
|
}
|
|
|
|
// TODO decrypt
|
|
return f.file.Seek(offset, whence)
|
|
}
|
|
|
|
func (f *EncFile) Write(p []byte) (n int, err error) {
|
|
checkIsFileErr := f.checkIsFile()
|
|
if checkIsFileErr != nil {
|
|
return 0, checkIsFileErr
|
|
}
|
|
|
|
// TODO encrypt
|
|
return f.file.Write(p)
|
|
}
|
|
|
|
func (f *EncFile) WriteAt(p []byte, off int64) (n int, err error) {
|
|
checkIsFileErr := f.checkIsFile()
|
|
if checkIsFileErr != nil {
|
|
return 0, checkIsFileErr
|
|
}
|
|
|
|
// TODO encrypt
|
|
return f.file.WriteAt(p, off)
|
|
}
|
|
|
|
func (f *EncFile) Name() string {
|
|
return f.file.Name()
|
|
}
|
|
|
|
func (f *EncFile) Readdir(count int) ([]os.FileInfo, error) {
|
|
if f.closed {
|
|
return nil, afero.ErrFileClosed
|
|
}
|
|
|
|
return f.file.Readdir(count)
|
|
}
|
|
|
|
func (f *EncFile) 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 *EncFile) Stat() (os.FileInfo, error) {
|
|
return f.file.Stat()
|
|
}
|
|
|
|
func (f *EncFile) Sync() error {
|
|
return f.file.Sync()
|
|
}
|
|
|
|
func (f *EncFile) Truncate(size int64) error {
|
|
return f.file.Truncate(size)
|
|
}
|
|
|
|
func (f *EncFile) WriteString(s string) (ret int, err error) {
|
|
// TODO encrypt
|
|
return f.file.WriteString(s)
|
|
}
|
|
|
|
func (f *EncFile) checkIsFile() error {
|
|
if f.closed {
|
|
return afero.ErrFileClosed
|
|
}
|
|
if f.isDir {
|
|
return syscall.EISDIR
|
|
}
|
|
return nil
|
|
}
|