166 lines
3.6 KiB
Go
166 lines
3.6 KiB
Go
/*
|
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
*/
|
|
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"mstore/pkg/auxuuid"
|
|
)
|
|
|
|
const (
|
|
sha256prefix = "sha256:"
|
|
fileSubdir = "files"
|
|
tmpSubdir = "tmps"
|
|
)
|
|
|
|
func (store *Storage) makeCollecionpath(collection string) string {
|
|
return filepath.Join(store.basepath, fileSubdir, collection)
|
|
}
|
|
|
|
func (store *Storage) makeFilepath(collection, filename string) string {
|
|
return filepath.Join(store.basepath, fileSubdir, collection, filename)
|
|
}
|
|
|
|
func (store *Storage) makeTmppath(tmpName string) string {
|
|
return filepath.Join(store.basepath, tmpSubdir, tmpName)
|
|
}
|
|
|
|
func (store *Storage) makeTmpsubdir() string {
|
|
return filepath.Join(store.basepath, tmpSubdir)
|
|
}
|
|
|
|
func (store *Storage) makeFilesubdir(collection, filename string) string {
|
|
return filepath.Join(store.basepath, fileSubdir)
|
|
}
|
|
|
|
func (store *Storage) FileExists(collection, filename string) (bool, int64, error) {
|
|
var err error
|
|
var exists bool
|
|
var size int64
|
|
filename = store.makeFilepath(collection, filename)
|
|
stat, err := os.Stat(filename)
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
return exists, size, err
|
|
}
|
|
if err != nil {
|
|
return exists, size, err
|
|
}
|
|
size = stat.Size()
|
|
exists = true
|
|
return exists, size, err
|
|
}
|
|
|
|
func (store *Storage) GetFileReader(collection, filename string) (io.ReadCloser, error) {
|
|
var err error
|
|
var res io.ReadCloser
|
|
|
|
filename = store.makeFilepath(collection, filename)
|
|
file, err := os.OpenFile(filename, os.O_RDONLY, 0)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
res = file
|
|
return res, err
|
|
}
|
|
|
|
func (store *Storage) GetFileCheksum(collection, filename string) (string, error) {
|
|
var err error
|
|
var res string
|
|
|
|
filename = store.makeFilepath(collection, filename)
|
|
file, err := os.OpenFile(filename, os.O_RDONLY, 0)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
defer file.Close()
|
|
|
|
hasher := NewHasher()
|
|
_, err = io.Copy(hasher.Writer(), file)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
res = hasher.Hex()
|
|
return res, err
|
|
}
|
|
|
|
func (store *Storage) WriteTempFile(source io.Reader) (string, int64, string, error) {
|
|
var err error
|
|
var size int64
|
|
var csum string
|
|
|
|
tmpName := fmt.Sprintf("%s.tmp", auxuuid.NewUUID())
|
|
tmpPath := store.makeTmppath(tmpName)
|
|
|
|
tmpdirpath := store.makeTmpsubdir()
|
|
err = os.MkdirAll(tmpdirpath, 0750)
|
|
if err != nil {
|
|
return tmpName, size, csum, err
|
|
}
|
|
|
|
file, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE, 0640)
|
|
if err != nil {
|
|
return tmpName, size, csum, err
|
|
}
|
|
defer file.Close()
|
|
|
|
hasher := NewHasher()
|
|
mWriter := io.MultiWriter(file, hasher.Writer())
|
|
size, err = io.Copy(mWriter, source)
|
|
if err != nil {
|
|
return tmpName, size, csum, err
|
|
}
|
|
csum = hasher.Hex()
|
|
|
|
return tmpName, size, csum, err
|
|
}
|
|
|
|
func (store *Storage) HardlinkFile(tmpName, collection, filename string) error {
|
|
var err error
|
|
|
|
dirname := store.makeCollecionpath(collection)
|
|
_, err = os.Stat(dirname)
|
|
if os.IsNotExist(err) {
|
|
err = os.MkdirAll(dirname, 0750)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filename = store.makeFilepath(collection, filename)
|
|
_, err = os.Stat(dirname)
|
|
if err == nil {
|
|
os.Remove(filename) // TODO: safe removing
|
|
}
|
|
tmpName = store.makeTmppath(tmpName)
|
|
err = os.Link(tmpName, filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.Remove(tmpName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (store *Storage) DeleteFile(collection, filename string) error {
|
|
var err error
|
|
filename = store.makeFilepath(collection, filename)
|
|
err = os.Remove(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// TODO: clean removing
|
|
dirname := store.makeCollecionpath(collection)
|
|
os.RemoveAll(dirname)
|
|
return err
|
|
}
|