206 lines
4.1 KiB
Go
206 lines
4.1 KiB
Go
/*
|
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
*/
|
|
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
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(name, digstr string) string {
|
|
return filepath.Join(store.basepath, blobsubdir, name, digstr) + ".bin"
|
|
}
|
|
|
|
func (store *Storage) makeBlobsubdir(name string) string {
|
|
return filepath.Join(store.basepath, blobsubdir, name)
|
|
}
|
|
|
|
func (store *Storage) WriteUpload(upID string, source io.Reader) (int64, error) {
|
|
var err error
|
|
var recsize int64
|
|
|
|
uploadDir := store.makeUpsubdir()
|
|
_, err = os.Stat(uploadDir)
|
|
if os.IsNotExist(err) {
|
|
err = os.MkdirAll(uploadDir, 0750)
|
|
if err != nil {
|
|
return recsize, err
|
|
}
|
|
}
|
|
if err != nil {
|
|
return recsize, err
|
|
}
|
|
upPath := store.makeUppath(upID)
|
|
upFile, err := os.OpenFile(upPath, os.O_WRONLY|os.O_CREATE, 0644)
|
|
if err != nil {
|
|
return recsize, err
|
|
}
|
|
defer upFile.Close()
|
|
recsize, err = io.Copy(upFile, source)
|
|
if err != nil {
|
|
return recsize, err
|
|
}
|
|
return recsize, err
|
|
}
|
|
|
|
func (store *Storage) LinkUpload(reference, name, digest string) error {
|
|
var err error
|
|
upPath := store.makeUppath(reference)
|
|
|
|
blobdir := store.makeBlobsubdir(name)
|
|
_, err = os.Stat(blobdir)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
err = os.MkdirAll(blobdir, 0750)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
blobPath := store.makeBlobpath(name, digest)
|
|
_, err = os.Stat(blobPath)
|
|
if err == nil {
|
|
err = os.Remove(blobPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.Link(upPath, blobPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = os.Remove(upPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (store *Storage) RemoveUpload(digest string) error {
|
|
var err error
|
|
upPath := store.makeUppath(digest)
|
|
err = os.Remove(upPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (st *Storage) UploadExists(name, reference string) (bool, int64, error) {
|
|
var err error
|
|
var size int64
|
|
upPath := st.makeUppath(reference)
|
|
fileStat, err := os.Stat(upPath)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return false, 0, nil
|
|
}
|
|
if err != nil {
|
|
return false, 0, err
|
|
}
|
|
size = fileStat.Size()
|
|
return true, size, err
|
|
}
|
|
|
|
func (store *Storage) WriteBlob(name, digstr string, source io.Reader) (int64, error) {
|
|
var err error
|
|
var size int64
|
|
blobDir := store.makeBlobsubdir(name)
|
|
_, err = os.Stat(blobDir)
|
|
if os.IsNotExist(err) {
|
|
err = os.MkdirAll(blobDir, 0750)
|
|
if err != nil {
|
|
return size, err
|
|
}
|
|
}
|
|
if err != nil {
|
|
return size, err
|
|
}
|
|
blobPath := store.makeBlobpath(name, digstr)
|
|
blobFile, err := os.OpenFile(blobPath, os.O_WRONLY|os.O_CREATE, 0644)
|
|
if err != nil {
|
|
return size, err
|
|
}
|
|
defer blobFile.Close()
|
|
size, err = io.Copy(blobFile, source)
|
|
if err != nil {
|
|
return size, err
|
|
}
|
|
return size, err
|
|
}
|
|
|
|
func (st *Storage) BlobExists(name, digest string) (bool, int64, error) {
|
|
var err error
|
|
var size int64
|
|
|
|
blobPath := st.makeBlobpath(name, digest)
|
|
fileStat, err := os.Stat(blobPath)
|
|
if os.IsNotExist(err) {
|
|
return false, 0, nil
|
|
}
|
|
if err != nil {
|
|
return false, 0, err
|
|
}
|
|
size = fileStat.Size()
|
|
return true, size, err
|
|
}
|
|
|
|
func (store *Storage) BlobReader(name, digest string) (int64, io.ReadCloser, error) {
|
|
var err error
|
|
var size int64
|
|
blobPath := store.makeBlobpath(name, digest)
|
|
nop := io.NopCloser(bytes.NewReader(nil))
|
|
file, err := os.OpenFile(blobPath, os.O_RDONLY, 0)
|
|
if err != nil {
|
|
return size, nop, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
file.Close()
|
|
}
|
|
}()
|
|
filestat, err := file.Stat()
|
|
if err != nil {
|
|
return size, nop, err
|
|
}
|
|
size = filestat.Size()
|
|
return size, file, err
|
|
}
|
|
|
|
func (store *Storage) DeleteBlob(name, digest string) error {
|
|
var err error
|
|
blobPath := store.makeBlobpath(name, digest)
|
|
err = os.Remove(blobPath)
|
|
if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// TODO: remove dirs
|
|
return err
|
|
}
|