copy.go (2587B)
1 package main 2 3 /* 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 // copyf copies data from file at `src` to the file at `dst` 8 // in 4kb chunks`. 9 // The file @ `src` *must* be **less than 2GB**. This is a 10 // limitation of libc (staying portable). 11 // Any existing file @ `dst` will be overwritten. 12 // returns EXIT_SUCCESS or EXIT_FAILURE. 13 int copyf(const char *src, const char *dst) 14 { 15 int ret = EXIT_FAILURE; 16 17 FILE *srcf = fopen(src, "rb"), *dstf = fopen(dst, "wb"); 18 if (!src || !dst) goto ABORT; 19 20 fseek(srcf, 0, SEEK_END); 21 size_t siz = ftell(srcf); // 2GB limit, returns long int 22 rewind(srcf); 23 24 char buf[4096]; // 4kb chunks 25 size_t r, w, total = 0; 26 while ((r = fread(buf, sizeof(char), sizeof(buf), srcf)) > 0) { 27 if (ferror(srcf)) goto ABORT; 28 w = fwrite(buf, sizeof(char), r, dstf); 29 if (ferror(dstf)) goto ABORT; 30 total += w; 31 } 32 33 if (total == siz) ret = EXIT_SUCCESS; 34 35 ABORT: 36 if (srcf) fclose(srcf); 37 if (dstf) fclose(dstf); 38 return ret; 39 } 40 */ 41 import "C" 42 import ( 43 "fmt" 44 "io" 45 "os" 46 "path/filepath" 47 "unsafe" 48 ) 49 50 func copyFile(src, dst string) (err error) { 51 var srcf, dstf *os.File 52 if srcf, err = os.Open(src); err != nil { 53 return err 54 } 55 defer srcf.Close() 56 if dstf, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0644); err != nil { 57 return err 58 } 59 defer dstf.Close() 60 61 if _, err = io.Copy(dstf, srcf); err != nil { 62 return err 63 } 64 return dstf.Sync() 65 } 66 67 func CopyFile(src, dst string) (err error) { 68 var srcfi, dstfi os.FileInfo 69 70 if srcfi, err = os.Stat(src); err != nil { 71 return err 72 } else if !srcfi.Mode().IsRegular() { 73 return fmt.Errorf("cannot copy from non-regular source file %s (%q)", 74 srcfi.Name(), srcfi.Mode().String()) 75 } 76 77 if dstfi, err = os.Stat(dst); err != nil && !os.IsNotExist(err) { 78 return err 79 } else if dstfi != nil && !dstfi.Mode().IsRegular() { 80 return fmt.Errorf("cannot copy to non-regular destination file %s (%q)", 81 dstfi.Name(), dstfi.Mode().String()) 82 } else if os.SameFile(srcfi, dstfi) { 83 return nil 84 } 85 86 if err = os.MkdirAll(filepath.Dir(dst), 0777); err != nil { 87 return err 88 } 89 90 // only copy if dst doesnt exist or has different name/size/modtime 91 // and has a size less than 2GB (libc limit) 92 if dstfi == nil || srcfi.Name() != dstfi.Name() || 93 srcfi.Size() != dstfi.Size() || srcfi.ModTime() != dstfi.ModTime() { 94 if srcfi.Size() > 2000000000 { 95 copyFile(src, dst) 96 } else { 97 cSrc := C.CString(src) 98 cDst := C.CString(dst) 99 if uint32(C.copyf(cSrc, cDst)) != 0 { 100 err = fmt.Errorf("copyf failed ('%s' -> '%s')", src, dst) 101 } 102 C.free(unsafe.Pointer(cSrc)) 103 C.free(unsafe.Pointer(cDst)) 104 } 105 } 106 107 return 108 }