(writer io.Writer, reader io.Reader)
| 83 | } |
| 84 | |
| 85 | func CloneFile(writer io.Writer, reader io.Reader) (success bool, err error) { |
| 86 | dst, dstIsFile := writer.(*os.File) |
| 87 | src, srcIsFile := reader.(*os.File) |
| 88 | if !(dstIsFile && srcIsFile) { |
| 89 | return false, nil |
| 90 | } |
| 91 | |
| 92 | srcStat, err := src.Stat() |
| 93 | if err != nil { |
| 94 | return |
| 95 | } |
| 96 | |
| 97 | fileSize := srcStat.Size() |
| 98 | |
| 99 | err = dst.Truncate(fileSize) // set file size. There is a requirement "The destination region must not extend past the end of file." |
| 100 | if err != nil { |
| 101 | return |
| 102 | } |
| 103 | |
| 104 | offset := int64(0) |
| 105 | |
| 106 | // Requirement |
| 107 | // * The source and destination regions must begin and end at a cluster boundary. (4KiB or 64KiB) |
| 108 | // * cloneRegionSize less than 4GiB. |
| 109 | // see https://docs.microsoft.com/windows/win32/fileio/block-cloning |
| 110 | |
| 111 | // Clone first xGiB region. |
| 112 | for ; offset+GiB < fileSize; offset += GiB { |
| 113 | err = callDuplicateExtentsToFile(dst, src, offset, GiB) |
| 114 | if err != nil { |
| 115 | return false, err |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | // Clone tail. First try with 64KiB round up, then fallback to 4KiB. |
| 120 | for _, cloneRegionSize := range availableClusterSize { |
| 121 | err = callDuplicateExtentsToFile(dst, src, offset, roundUp(fileSize-offset, cloneRegionSize)) |
| 122 | if err != nil { |
| 123 | continue |
| 124 | } |
| 125 | break |
| 126 | } |
| 127 | |
| 128 | return err == nil, err |
| 129 | } |
| 130 | |
| 131 | // call FSCTL_DUPLICATE_EXTENTS_TO_FILE IOCTL |
| 132 | // see https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_duplicate_extents_to_file |
no test coverage detected