Files
mstore/app/storage/file.go
T
2026-03-30 23:12:02 +02:00

184 lines
4.1 KiB
Go

/*
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
*/
package storage
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"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 dirs removing
return err
}
func (store *Storage) ListAllFiles() ([]string, error) {
names := make([]string, 0)
var err error
rootdir := store.makeCollecionpath(string(filepath.Separator))
walker := func(filename string, fileInfo os.FileInfo, err error) error {
if err != nil {
return err
}
if !fileInfo.Mode().IsRegular() {
return nil
}
name := strings.TrimPrefix(filename, filepath.Clean(rootdir))
name = filepath.Join(string(filepath.Separator), name)
names = append(names, name)
return nil
}
err = filepath.Walk(rootdir, walker)
if err != nil {
return names, err
}
return names, err
}