Initial DDC release
This commit is contained in:
commit
434f6441da
57 files changed
+4606
No files matched your search
Executable
+101
@@ -0,0 +1,101 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"ddc/internal/diskspace"
|
||||
)
|
||||
|
||||
var ErrDestinationExists = errors.New("an item with this name already exists in the destination")
|
||||
|
||||
func MoveInto(source, destinationDir string, reservedBytes int64, progress func(int64)) error {
|
||||
name := filepath.Base(source)
|
||||
target := filepath.Join(destinationDir, name)
|
||||
if exists(target) {
|
||||
return fmt.Errorf("%w: %s", ErrDestinationExists, name)
|
||||
}
|
||||
|
||||
size, err := TreeSize(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
renameErr := os.Rename(source, target)
|
||||
if renameErr == nil {
|
||||
progress(size)
|
||||
return nil
|
||||
}
|
||||
if !errors.Is(renameErr, syscall.EXDEV) {
|
||||
return renameErr
|
||||
}
|
||||
|
||||
if err := diskspace.EnsureFree(destinationDir, size, reservedBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyTree(source, target, progress); err != nil {
|
||||
os.RemoveAll(target)
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(source)
|
||||
}
|
||||
|
||||
func copyTree(source, target string, progress func(int64)) error {
|
||||
return filepath.WalkDir(source, func(current string, entry fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
relative, err := filepath.Rel(source, current)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destination := filepath.Join(target, relative)
|
||||
|
||||
switch {
|
||||
case entry.IsDir():
|
||||
return os.MkdirAll(destination, 0o755)
|
||||
case entry.Type().IsRegular():
|
||||
return copyFile(current, destination, progress)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func copyFile(source, destination string, progress func(int64)) error {
|
||||
input, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer input.Close()
|
||||
|
||||
output, err := os.OpenFile(destination, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, copyErr := io.Copy(output, &progressReader{reader: input, progress: progress})
|
||||
closeErr := output.Close()
|
||||
if copyErr != nil {
|
||||
return copyErr
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
|
||||
type progressReader struct {
|
||||
reader io.Reader
|
||||
progress func(int64)
|
||||
}
|
||||
|
||||
func (reader *progressReader) Read(buffer []byte) (int, error) {
|
||||
count, err := reader.reader.Read(buffer)
|
||||
if count > 0 {
|
||||
reader.progress(int64(count))
|
||||
}
|
||||
return count, err
|
||||
}
|
||||
Reference in new issue
Block a user