63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// PathInfo describes a source/dest path as seen locally by whichever
|
|
// process (control agent, sink, source-stream) actually opens it.
|
|
type PathInfo struct {
|
|
Exists bool
|
|
IsDevice bool
|
|
Size int64
|
|
}
|
|
|
|
func statPath(path string) (PathInfo, error) {
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return PathInfo{Exists: false}, nil
|
|
}
|
|
return PathInfo{}, err
|
|
}
|
|
mode := fi.Mode()
|
|
isDevice := mode&os.ModeDevice != 0 && mode&os.ModeCharDevice == 0
|
|
size := fi.Size()
|
|
if isDevice {
|
|
sz, err := blockDeviceSize(path)
|
|
if err != nil {
|
|
return PathInfo{}, fmt.Errorf("stat block device %s: %w", path, err)
|
|
}
|
|
size = sz
|
|
}
|
|
return PathInfo{Exists: true, IsDevice: isDevice, Size: size}, nil
|
|
}
|
|
|
|
// prepareDest makes the destination ready to receive exactly targetSize
|
|
// bytes: a regular file is created if missing and truncated (grown or
|
|
// shrunk) to targetSize; a block device is only validated to be large
|
|
// enough (it can't be resized) — the caller is expected to have already
|
|
// capped targetSize at the device's own size when it is the limiting side.
|
|
func prepareDest(path string, targetSize int64) error {
|
|
info, err := statPath(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.Exists && info.IsDevice {
|
|
if targetSize > info.Size {
|
|
return fmt.Errorf("destination device %s is only %d bytes, need %d", path, info.Size, targetSize)
|
|
}
|
|
return nil
|
|
}
|
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return fmt.Errorf("open/create %s: %w", path, err)
|
|
}
|
|
defer f.Close()
|
|
if err := f.Truncate(targetSize); err != nil {
|
|
return fmt.Errorf("truncate %s to %d: %w", path, targetSize, err)
|
|
}
|
|
return nil
|
|
}
|