Copy data from one regular mmap-like fd to another by using high-performance sendfile(2) syscall. This should work on Linux >= 2.6.33 only.
(fsrc, fdst)
| 104 | raise err from None |
| 105 | |
| 106 | def _fastcopy_sendfile(fsrc, fdst): |
| 107 | """Copy data from one regular mmap-like fd to another by using |
| 108 | high-performance sendfile(2) syscall. |
| 109 | This should work on Linux >= 2.6.33 only. |
| 110 | """ |
| 111 | # Note: copyfileobj() is left alone in order to not introduce any |
| 112 | # unexpected breakage. Possible risks by using zero-copy calls |
| 113 | # in copyfileobj() are: |
| 114 | # - fdst cannot be open in "a"(ppend) mode |
| 115 | # - fsrc and fdst may be open in "t"(ext) mode |
| 116 | # - fsrc may be a BufferedReader (which hides unread data in a buffer), |
| 117 | # GzipFile (which decompresses data), HTTPResponse (which decodes |
| 118 | # chunks). |
| 119 | # - possibly others (e.g. encrypted fs/partition?) |
| 120 | global _USE_CP_SENDFILE |
| 121 | try: |
| 122 | infd = fsrc.fileno() |
| 123 | outfd = fdst.fileno() |
| 124 | except Exception as err: |
| 125 | raise _GiveupOnFastCopy(err) # not a regular file |
| 126 | |
| 127 | # Hopefully the whole file will be copied in a single call. |
| 128 | # sendfile() is called in a loop 'till EOF is reached (0 return) |
| 129 | # so a bufsize smaller or bigger than the actual file size |
| 130 | # should not make any difference, also in case the file content |
| 131 | # changes while being copied. |
| 132 | try: |
| 133 | blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB |
| 134 | except OSError: |
| 135 | blocksize = 2 ** 27 # 128MiB |
| 136 | # On 32-bit architectures truncate to 1GiB to avoid OverflowError, |
| 137 | # see bpo-38319. |
| 138 | if sys.maxsize < 2 ** 32: |
| 139 | blocksize = min(blocksize, 2 ** 30) |
| 140 | |
| 141 | offset = 0 |
| 142 | while True: |
| 143 | try: |
| 144 | sent = os.sendfile(outfd, infd, offset, blocksize) |
| 145 | except OSError as err: |
| 146 | # ...in oder to have a more informative exception. |
| 147 | err.filename = fsrc.name |
| 148 | err.filename2 = fdst.name |
| 149 | |
| 150 | if err.errno == errno.ENOTSOCK: |
| 151 | # sendfile() on this platform (probably Linux < 2.6.33) |
| 152 | # does not support copies between regular files (only |
| 153 | # sockets). |
| 154 | _USE_CP_SENDFILE = False |
| 155 | raise _GiveupOnFastCopy(err) |
| 156 | |
| 157 | if err.errno == errno.ENOSPC: # filesystem is full |
| 158 | raise err from None |
| 159 | |
| 160 | # Give up on first call and if no data was copied. |
| 161 | if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0: |
| 162 | raise _GiveupOnFastCopy(err) |
| 163 |