package storage import ( "crypto/sha256" "encoding/hex" "fmt" "io" "os" "path/filepath" "mstore/app/logger" "mstore/pkg/auxuuid" ) type Storage struct { basepath string logg *logger.Logger } func NewStorage(basepath string) *Storage { res := &Storage{ basepath: basepath, } res.logg = logger.NewLogger("storage") return res } const ( 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) HardlinkFile(tmpname, collection, filename string) error { var err error dirname := store.makeCollecionpath(collection) err = os.MkdirAll(dirname, 0750) if err != nil { return err } filename = store.makeFilepath(collection, filename) os.Remove(filename) // TODO 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) WriteTempFile(source io.Reader) (string, int64, string, error) { var err error var size int64 var digest string tmpname := auxuuid.NewUUID() tmpname = fmt.Sprintf("file-%s.tmp", tmpname) tmppath := store.makeTmppath(tmpname) tmpdirpath := store.makeTmpsubdir() err = os.MkdirAll(tmpdirpath, 0750) if err != nil { return tmpname, size, digest, err } file, err := os.OpenFile(tmppath, os.O_WRONLY|os.O_CREATE, 0640) if err != nil { return tmpname, size, digest, err } defer file.Close() hasher := sha256.New() writer := io.MultiWriter(file, hasher) size, err = io.Copy(writer, source) if err != nil { return tmpname, size, digest, err } digest = hex.EncodeToString(hasher.Sum(nil)) digest = fmt.Sprintf("sha256:%s", digest) return tmpname, size, digest, 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: more safe removing dirname := store.makeCollecionpath(collection) err = os.RemoveAll(dirname) if err != nil { return err } return err } const ( upsubdir = "uploads" blobsubdir = "blobs" ) func (store *Storage) makeUppath(upname string) string { return filepath.Join(store.basepath, upsubdir, upname) + ".bin" } func (store *Storage) makeUpsubdir() string { return filepath.Join(store.basepath, upsubdir) } func (store *Storage) makeBlobpath(upname string) string { return filepath.Join(store.basepath, blobsubdir, upname) + ".bin" } func (store *Storage) makeBlobsubdir() string { return filepath.Join(store.basepath, blobsubdir) } func (store *Storage) WriteUpload(digest string, source io.Reader) (int64, error) { var err error var recsize int64 uploadDir := store.makeUpsubdir() err = os.MkdirAll(uploadDir, 0750) if err != nil { return recsize, err } uploadPath := store.makeUppath(digest) uploadFile, err := os.OpenFile(uploadPath, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return recsize, err } defer uploadFile.Close() hasher := sha256.New() // TODO: upload cheking streamWriter := io.MultiWriter(uploadFile, hasher) recsize, err = io.Copy(streamWriter, source) if err != nil { return recsize, err } return recsize, err } func (store *Storage) LinkUpload(reference, digest string) error { var err error uploadPath := store.makeUppath(reference) blobPath := store.makeBlobpath(digest) blobdir := store.makeBlobsubdir() err = os.MkdirAll(blobdir, 0750) if err != nil { return err } err = os.Link(uploadPath, blobPath) if err != nil { return err } err = os.Remove(uploadPath) if err != nil { return err } return err } func (store *Storage) RemoveUpload(digest string) error { var err error uploadPath := store.makeUppath(digest) err = os.Remove(uploadPath) if err != nil { return err } return err }