mirror of
https://github.com/datarhei/core.git
synced 2025-09-27 20:32:35 +08:00

If purging is enabled, overwriting a file with a file of the same or smaller size will not result in an error. It is now possible to change the purging mode on an existing sized filesystem.
183 lines
3.9 KiB
Go
183 lines
3.9 KiB
Go
package fs
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
type SizedFilesystem interface {
|
|
Filesystem
|
|
|
|
// Resize resizes the filesystem to the new size. Files may need to be deleted.
|
|
Resize(size int64, purge bool) error
|
|
}
|
|
|
|
type PurgeFilesystem interface {
|
|
// Purge will free up at least size number of bytes and returns the actual
|
|
// freed space in bytes.
|
|
Purge(size int64) int64
|
|
}
|
|
|
|
type sizedFilesystem struct {
|
|
Filesystem
|
|
|
|
// Size is the capacity of the filesystem in bytes
|
|
maxSize int64
|
|
|
|
// Set true to automatically delete the oldest files until there's
|
|
// enough space to store a new file
|
|
purge bool
|
|
}
|
|
|
|
var _ PurgeFilesystem = &sizedFilesystem{}
|
|
|
|
func NewSizedFilesystem(fs Filesystem, maxSize int64, purge bool) (SizedFilesystem, error) {
|
|
r := &sizedFilesystem{
|
|
Filesystem: fs,
|
|
maxSize: maxSize,
|
|
purge: purge,
|
|
}
|
|
|
|
return r, nil
|
|
}
|
|
|
|
func (r *sizedFilesystem) Size() (int64, int64) {
|
|
currentSize, _ := r.Filesystem.Size()
|
|
|
|
return currentSize, r.maxSize
|
|
}
|
|
|
|
func (r *sizedFilesystem) Resize(size int64, purge bool) error {
|
|
r.purge = purge
|
|
|
|
currentSize, _ := r.Size()
|
|
if size >= currentSize {
|
|
// If the new size is the same or larger than the current size,
|
|
// nothing to do.
|
|
r.maxSize = size
|
|
return nil
|
|
}
|
|
|
|
// If the new size is less than the current size, purge some files.
|
|
r.Purge(currentSize - size)
|
|
|
|
r.maxSize = size
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *sizedFilesystem) WriteFileReader(path string, rd io.Reader) (int64, bool, error) {
|
|
currentSize, maxSize := r.Size()
|
|
if maxSize <= 0 {
|
|
return r.Filesystem.WriteFileReader(path, rd)
|
|
}
|
|
|
|
data := bytes.Buffer{}
|
|
size, err := data.ReadFrom(rd)
|
|
if err != nil {
|
|
return -1, false, err
|
|
}
|
|
|
|
// reject if the new file is larger than the available space
|
|
if size > maxSize {
|
|
return -1, false, fmt.Errorf("File is too big")
|
|
}
|
|
|
|
newSize := currentSize + size
|
|
|
|
// Calculate the new size of the filesystem
|
|
finfo, err := r.Filesystem.Stat(path)
|
|
if err == nil {
|
|
// If the file already exist, take it's size into account
|
|
newSize -= finfo.Size()
|
|
}
|
|
|
|
// If the the new size is larger than the allowed size, we have to free
|
|
// some space.
|
|
if newSize > maxSize {
|
|
if !r.purge {
|
|
return -1, false, fmt.Errorf("not enough space on device")
|
|
}
|
|
|
|
if r.Purge(size) < size {
|
|
return -1, false, fmt.Errorf("not enough space on device")
|
|
}
|
|
}
|
|
|
|
return r.Filesystem.WriteFileReader(path, &data)
|
|
}
|
|
|
|
func (r *sizedFilesystem) WriteFile(path string, data []byte) (int64, bool, error) {
|
|
return r.WriteFileReader(path, bytes.NewBuffer(data))
|
|
}
|
|
|
|
func (r *sizedFilesystem) WriteFileSafe(path string, data []byte) (int64, bool, error) {
|
|
currentSize, maxSize := r.Size()
|
|
if maxSize <= 0 {
|
|
return r.Filesystem.WriteFile(path, data)
|
|
}
|
|
|
|
size := int64(len(data))
|
|
|
|
// reject if the new file is larger than the available space
|
|
if size > maxSize {
|
|
return -1, false, fmt.Errorf("File is too big")
|
|
}
|
|
|
|
newSize := currentSize + size
|
|
|
|
// Calculate the new size of the filesystem
|
|
finfo, err := r.Filesystem.Stat(path)
|
|
if err == nil {
|
|
// If the file already exist, take it's size into account
|
|
newSize -= finfo.Size()
|
|
}
|
|
|
|
// If the the new size is larger than the allowed size, we have to free
|
|
// some space.
|
|
if newSize > maxSize {
|
|
if !r.purge {
|
|
return -1, false, fmt.Errorf("not enough space on device")
|
|
}
|
|
|
|
if r.Purge(size) < size {
|
|
return -1, false, fmt.Errorf("not enough space on device")
|
|
}
|
|
}
|
|
|
|
return r.Filesystem.WriteFileSafe(path, data)
|
|
}
|
|
|
|
func (r *sizedFilesystem) Purge(size int64) int64 {
|
|
if purger, ok := r.Filesystem.(PurgeFilesystem); ok {
|
|
return purger.Purge(size)
|
|
}
|
|
|
|
return 0
|
|
/*
|
|
files := r.Filesystem.List("/", "")
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
return files[i].ModTime().Before(files[j].ModTime())
|
|
})
|
|
|
|
var freed int64 = 0
|
|
|
|
for _, f := range files {
|
|
r.Filesystem.Remove(f.Name())
|
|
size -= f.Size()
|
|
freed += f.Size()
|
|
r.currentSize -= f.Size()
|
|
|
|
if size <= 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
files = nil
|
|
|
|
return freed
|
|
*/
|
|
}
|