1
0
Fork 0
mirror of https://github.com/X-Cli/large-file-decrypt.git synced 2024-09-20 20:12:10 +00:00
large-file-crypto/utils/utils.go
Florian Maury b1f60357fa initial
2022-01-10 12:21:33 +01:00

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
}