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, Android and Solaris.
(fsrc, fdst)
| 176 | offset += n_copied |
| 177 | |
| 178 | def _fastcopy_sendfile(fsrc, fdst): |
| 179 | """Copy data from one regular mmap-like fd to another by using |
| 180 | high-performance sendfile(2) syscall. |
| 181 | This should work on Linux >= 2.6.33, Android and Solaris. |
| 182 | """ |
| 183 | # Note: copyfileobj() is left alone in order to not introduce any |
| 184 | # unexpected breakage. Possible risks by using zero-copy calls |
| 185 | # in copyfileobj() are: |
| 186 | # - fdst cannot be open in "a"(ppend) mode |
| 187 | # - fsrc and fdst may be open in "t"(ext) mode |
| 188 | # - fsrc may be a BufferedReader (which hides unread data in a buffer), |
| 189 | # GzipFile (which decompresses data), HTTPResponse (which decodes |
| 190 | # chunks). |
| 191 | # - possibly others (e.g. encrypted fs/partition?) |
| 192 | global _USE_CP_SENDFILE |
| 193 | try: |
| 194 | infd = fsrc.fileno() |
| 195 | outfd = fdst.fileno() |
| 196 | except Exception as err: |
| 197 | raise _GiveupOnFastCopy(err) # not a regular file |
| 198 | |
| 199 | blocksize = _determine_linux_fastcopy_blocksize(infd) |
| 200 | offset = 0 |
| 201 | while True: |
| 202 | try: |
| 203 | sent = os.sendfile(outfd, infd, offset, blocksize) |
| 204 | except OSError as err: |
| 205 | # ...in order to have a more informative exception. |
| 206 | err.filename = fsrc.name |
| 207 | err.filename2 = fdst.name |
| 208 | |
| 209 | if err.errno == errno.ENOTSOCK: |
| 210 | # sendfile() on this platform (probably Linux < 2.6.33) |
| 211 | # does not support copies between regular files (only |
| 212 | # sockets). |
| 213 | _USE_CP_SENDFILE = False |
| 214 | raise _GiveupOnFastCopy(err) |
| 215 | |
| 216 | if err.errno == errno.ENOSPC: # filesystem is full |
| 217 | raise err from None |
| 218 | |
| 219 | # Give up on first call and if no data was copied. |
| 220 | if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0: |
| 221 | raise _GiveupOnFastCopy(err) |
| 222 | |
| 223 | raise err |
| 224 | else: |
| 225 | if sent == 0: |
| 226 | break # EOF |
| 227 | offset += sent |
| 228 | |
| 229 | def _copyfileobj_readinto(fsrc, fdst, length=COPY_BUFSIZE): |
| 230 | """readinto()/memoryview() based variant of copyfileobj(). |
no test coverage detected
searching dependent graphs…