/* * Copyright 2026 Oleg Borodin * * This work is published and licensed under a Creative Commons * Attribution-NonCommercial-NoDerivatives 4.0 International License. * * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ 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) 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) 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 }