mirror of
https://github.com/X-Cli/large-file-decrypt.git
synced 2024-11-09 23:42:09 +00:00
111 lines
3.6 KiB
Go
111 lines
3.6 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func CreatePrivateCopyOf(inputPath, tempDir string) (privateFile *os.File, errStack *multierror.Error) {
|
|
inputFile, err := os.Open(inputPath)
|
|
if err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to open file %q: %w", inputPath, err))
|
|
return
|
|
}
|
|
defer func() {
|
|
if err := inputFile.Close(); err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to close file %q: %w", inputPath, err))
|
|
}
|
|
}()
|
|
|
|
privateFileFd, err := unix.Open(tempDir, unix.O_RDWR|unix.O_TMPFILE, 0o600)
|
|
if err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to open private file: %w", err))
|
|
return
|
|
}
|
|
|
|
privateFile = os.NewFile(uintptr(privateFileFd), "")
|
|
defer func() {
|
|
if errStack.ErrorOrNil() != nil {
|
|
if err := privateFile.Close(); err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to close private copy: %w", err))
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err := unix.IoctlFileClone(int(privateFile.Fd()), int(inputFile.Fd())); err != nil {
|
|
if err != syscall.EOPNOTSUPP && err != syscall.EINVAL {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to clone file: %w", err))
|
|
return
|
|
}
|
|
if _, err := io.Copy(privateFile, inputFile); err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to copy file: %w", err))
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func GetDataFromFile(privateEncryptedFile *os.File) (data []byte, err error) {
|
|
fs, err := privateEncryptedFile.Stat()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to stat(2) private copy of encrypted file: %w", err)
|
|
}
|
|
data, err = syscall.Mmap(int(privateEncryptedFile.Fd()), 0, int(fs.Size()), syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to mmap(2) private copy of encrypted file: %w", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func CreatePrivateFile(tempDir string, size int64) (*os.File, error) {
|
|
privateFileFd, err := unix.Open(tempDir, unix.O_RDWR|unix.O_TMPFILE, 0o600)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create private file: %w", err)
|
|
}
|
|
privateFile := os.NewFile(uintptr(privateFileFd), "")
|
|
|
|
if err := syscall.Fallocate(int(privateFile.Fd()), 0, 0, size); err != nil {
|
|
var errStack error = err
|
|
if err := privateFile.Close(); err != nil {
|
|
errStack = multierror.Append(errStack, err)
|
|
}
|
|
return nil, errStack
|
|
}
|
|
return privateFile, nil
|
|
}
|
|
|
|
func PublishFile(privateDecryptedFile *os.File, outputPath string) (errStack *multierror.Error) {
|
|
if err := unix.Linkat(int(privateDecryptedFile.Fd()), "", 0, outputPath, unix.AT_EMPTY_PATH); err == nil {
|
|
return
|
|
} else if err != syscall.ENOENT {
|
|
// ENOENT is returned if CAP_DAC_READ_SEARCH is not effective
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to call linkat: %w", err))
|
|
return
|
|
}
|
|
outputFile, err := os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE, 0o600)
|
|
if err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to create output file: %w", err))
|
|
return
|
|
}
|
|
defer func() {
|
|
if err := outputFile.Close(); err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to close file %q: %w", outputPath, err))
|
|
}
|
|
}()
|
|
if err := unix.IoctlFileClone(int(outputFile.Fd()), int(privateDecryptedFile.Fd())); err == nil {
|
|
return
|
|
} else if err != syscall.EOPNOTSUPP && err != syscall.EINVAL {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to clone file %w", err))
|
|
return
|
|
}
|
|
if _, err := io.Copy(outputFile, privateDecryptedFile); err != nil {
|
|
errStack = multierror.Append(errStack, fmt.Errorf("failed to copy into public decrypted file: %w", err))
|
|
return
|
|
}
|
|
return
|
|
}
|