commit abfbca08f68bb71ab01d50755b54460e0684834e
parent 98b71a76f774959bc123b708dd769fdd4eaa2f62
Author: gearsix <gearsix@tuta.io>
Date: Fri, 29 Jul 2022 17:11:47 +0100
bugfix(copy.go): improved reliability & portability.
Added back in the old golang copy code to handle files larger than
2GB (the limit on the count that `fseek` can return).
Diffstat:
M | copy.go | | | 60 | ++++++++++++++++++++++++++++++++++++++++-------------------- |
1 file changed, 40 insertions(+), 20 deletions(-)
diff --git a/copy.go b/copy.go
@@ -6,32 +6,29 @@ package main
// copyf copies data from file at `src` to the file at `dst`
// in 4kb chunks`.
-// any existing file @ `dst` will be overwritten.
+// The file @ `src` *must* be **less than 2GB**. This is a
+// limitation of libc (staying portable).
+// Any existing file @ `dst` will be overwritten.
// returns EXIT_SUCCESS or EXIT_FAILURE.
int copyf(const char *src, const char *dst)
{
int ret = EXIT_FAILURE;
- FILE *srcf, *dstf;
- if ((!(srcf = fopen(src, "rb"))) ||
- (!(dstf = fopen(dst, "wb"))))
- goto ABORT;
+ FILE *srcf = fopen(src, "rb"), *dstf = fopen(dst, "wb");
+ if (!src || !dst) goto ABORT;
fseek(srcf, 0, SEEK_END);
- size_t siz = ftell(srcf);
+ size_t siz = ftell(srcf); // 2GB limit, returns long int
rewind(srcf);
char buf[4096]; // 4kb chunks
size_t r, w, total = 0;
- do {
- r = fread(buf, sizeof(char), sizeof(buf), srcf);
+ while ((r = fread(buf, sizeof(char), sizeof(buf), srcf)) > 0) {
if (ferror(srcf)) goto ABORT;
- else {
- w = fwrite(buf, sizeof(char), r, dstf);
- if (ferror(dstf)) goto ABORT;
- total += w;
- }
- } while (!feof(srcf));
+ w = fwrite(buf, sizeof(char), r, dstf);
+ if (ferror(dstf)) goto ABORT;
+ total += w;
+ }
if (total == siz) ret = EXIT_SUCCESS;
@@ -44,11 +41,29 @@ ABORT:
import "C"
import (
"fmt"
+ "io"
"os"
"path/filepath"
"unsafe"
)
+func copyFile(src, dst string) (err error) {
+ var srcf, dstf *os.File
+ if srcf, err = os.Open(src); err != nil {
+ return err
+ }
+ defer srcf.Close()
+ if dstf, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0644); err != nil {
+ return err
+ }
+ defer dstf.Close()
+
+ if _, err = io.Copy(dstf, srcf); err != nil {
+ return err
+ }
+ return dstf.Sync()
+}
+
func CopyFile(src, dst string) (err error) {
var srcfi, dstfi os.FileInfo
@@ -73,15 +88,20 @@ func CopyFile(src, dst string) (err error) {
}
// only copy if dst doesnt exist or has different name/size/modtime
+ // and has a size less than 2GB (libc limit)
if dstfi == nil || srcfi.Name() != dstfi.Name() ||
srcfi.Size() != dstfi.Size() || srcfi.ModTime() != dstfi.ModTime() {
- cSrc := C.CString(src)
- cDst := C.CString(dst)
- if uint32(C.copyf(cSrc, cDst)) != 0 {
- err = fmt.Errorf("copyf failed ('%s' -> '%s')", src, dst)
+ if srcfi.Size() > 2000000000 {
+ copyFile(src, dst)
+ } else {
+ cSrc := C.CString(src)
+ cDst := C.CString(dst)
+ if uint32(C.copyf(cSrc, cDst)) != 0 {
+ err = fmt.Errorf("copyf failed ('%s' -> '%s')", src, dst)
+ }
+ C.free(unsafe.Pointer(cSrc))
+ C.free(unsafe.Pointer(cDst))
}
- C.free(unsafe.Pointer(cSrc))
- C.free(unsafe.Pointer(cDst))
}
return