| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9c9f085 commit d08fb25
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -752,6 +752,20 @@ def test_nonexisting_script(self): | |||
| 752 | 752 | self.assertIn(": can't open file ", err) | |
| 753 | 753 | self.assertNotEqual(proc.returncode, 0) | |
| 754 | 754 | ||
| 755 | + @unittest.skipUnless(os.path.exists('/dev/fd/0'), 'requires /dev/fd platform') | ||
| 756 | + def test_script_as_dev_fd(self): | ||
| 757 | + # GH-87235: On macOS passing a non-trivial script to /dev/fd/N can cause | ||
| 758 | + # problems because all open /dev/fd/N file descriptors share the same | ||
| 759 | + # offset. | ||
| 760 | + script = 'print("12345678912345678912345")' | ||
| 761 | + with os_helper.temp_dir() as work_dir: | ||
| 762 | + script_name = _make_test_script(work_dir, 'script.py', script) | ||
| 763 | + with open(script_name, "r") as fp: | ||
| 764 | + p = spawn_python(f"/dev/fd/{fp.fileno()}", close_fds=False, pass_fds=(0,1,2,fp.fileno())) | ||
| 765 | + out, err = p.communicate() | ||
| 766 | + self.assertEqual(out, b"12345678912345678912345\n") | ||
| 767 | + | ||
| 768 | + | ||
| 755 | 769 | ||
| 756 | 770 | def tearDownModule(): | |
| 757 | 771 | support.reap_children() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -347,114 +347,121 @@ def _read_directory(archive): | |||
| 347 | 347 | raise ZipImportError(f"can't open Zip file: {archive!r}", path=archive) | |
| 348 | 348 | ||
| 349 | 349 | with fp: | |
| 350 | + # GH-87235: On macOS all file descriptors for /dev/fd/N share the same | ||
| 351 | + # file offset, reset the file offset after scanning the zipfile diretory | ||
| 352 | + # to not cause problems when some runs 'python3 /dev/fd/9 9<some_script' | ||
| 353 | + start_offset = fp.tell() | ||
| 350 | 354 | try: | |
| 351 | - fp.seek(-END_CENTRAL_DIR_SIZE, 2) | ||
| 352 | - header_position = fp.tell() | ||
| 353 | - buffer = fp.read(END_CENTRAL_DIR_SIZE) | ||
| 354 | - except OSError: | ||
| 355 | - raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 356 | - if len(buffer) != END_CENTRAL_DIR_SIZE: | ||
| 357 | - raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 358 | - if buffer[:4] != STRING_END_ARCHIVE: | ||
| 359 | - # Bad: End of Central Dir signature | ||
| 360 | - # Check if there's a comment. | ||
| 361 | 355 | try: | |
| 362 | - fp.seek(0, 2) | ||
| 363 | - file_size = fp.tell() | ||
| 364 | - except OSError: | ||
| 365 | - raise ZipImportError(f"can't read Zip file: {archive!r}", | ||
| 366 | - path=archive) | ||
| 367 | - max_comment_start = max(file_size - MAX_COMMENT_LEN - | ||
| 368 | - END_CENTRAL_DIR_SIZE, 0) | ||
| 369 | - try: | ||
| 370 | - fp.seek(max_comment_start) | ||
| 371 | - data = fp.read() | ||
| 372 | - except OSError: | ||
| 373 | - raise ZipImportError(f"can't read Zip file: {archive!r}", | ||
| 374 | - path=archive) | ||
| 375 | - pos = data.rfind(STRING_END_ARCHIVE) | ||
| 376 | - if pos < 0: | ||
| 377 | - raise ZipImportError(f'not a Zip file: {archive!r}', | ||
| 378 | - path=archive) | ||
| 379 | - buffer = data[pos:pos+END_CENTRAL_DIR_SIZE] | ||
| 380 | - if len(buffer) != END_CENTRAL_DIR_SIZE: | ||
| 381 | - raise ZipImportError(f"corrupt Zip file: {archive!r}", | ||
| 382 | - path=archive) | ||
| 383 | - header_position = file_size - len(data) + pos | ||
| 384 | - | ||
| 385 | - header_size = _unpack_uint32(buffer[12:16]) | ||
| 386 | - header_offset = _unpack_uint32(buffer[16:20]) | ||
| 387 | - if header_position < header_size: | ||
| 388 | - raise ZipImportError(f'bad central directory size: {archive!r}', path=archive) | ||
| 389 | - if header_position < header_offset: | ||
| 390 | - raise ZipImportError(f'bad central directory offset: {archive!r}', path=archive) | ||
| 391 | - header_position -= header_size | ||
| 392 | - arc_offset = header_position - header_offset | ||
| 393 | - if arc_offset < 0: | ||
| 394 | - raise ZipImportError(f'bad central directory size or offset: {archive!r}', path=archive) | ||
| 395 | - | ||
| 396 | - files = {} | ||
| 397 | - # Start of Central Directory | ||
| 398 | - count = 0 | ||
| 399 | - try: | ||
| 400 | - fp.seek(header_position) | ||
| 401 | - except OSError: | ||
| 402 | - raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 403 | - while True: | ||
| 404 | - buffer = fp.read(46) | ||
| 405 | - if len(buffer) < 4: | ||
| 406 | - raise EOFError('EOF read where not expected') | ||
| 407 | - # Start of file header | ||
| 408 | - if buffer[:4] != b'PK\x01\x02': | ||
| 409 | - break # Bad: Central Dir File Header | ||
| 410 | - if len(buffer) != 46: | ||
| 411 | - raise EOFError('EOF read where not expected') | ||
| 412 | - flags = _unpack_uint16(buffer[8:10]) | ||
| 413 | - compress = _unpack_uint16(buffer[10:12]) | ||
| 414 | - time = _unpack_uint16(buffer[12:14]) | ||
| 415 | - date = _unpack_uint16(buffer[14:16]) | ||
| 416 | - crc = _unpack_uint32(buffer[16:20]) | ||
| 417 | - data_size = _unpack_uint32(buffer[20:24]) | ||
| 418 | - file_size = _unpack_uint32(buffer[24:28]) | ||
| 419 | - name_size = _unpack_uint16(buffer[28:30]) | ||
| 420 | - extra_size = _unpack_uint16(buffer[30:32]) | ||
| 421 | - comment_size = _unpack_uint16(buffer[32:34]) | ||
| 422 | - file_offset = _unpack_uint32(buffer[42:46]) | ||
| 423 | - header_size = name_size + extra_size + comment_size | ||
| 424 | - if file_offset > header_offset: | ||
| 425 | - raise ZipImportError(f'bad local header offset: {archive!r}', path=archive) | ||
| 426 | - file_offset += arc_offset | ||
| 427 | - | ||
| 428 | - try: | ||
| 429 | - name = fp.read(name_size) | ||
| 356 | + fp.seek(-END_CENTRAL_DIR_SIZE, 2) | ||
| 357 | + header_position = fp.tell() | ||
| 358 | + buffer = fp.read(END_CENTRAL_DIR_SIZE) | ||
| 430 | 359 | except OSError: | |
| 431 | 360 | raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | |
| 432 | - if len(name) != name_size: | ||
| 361 | + if len(buffer) != END_CENTRAL_DIR_SIZE: | ||
| 433 | 362 | raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | |
| 434 | - # On Windows, calling fseek to skip over the fields we don't use is | ||
| 435 | - # slower than reading the data because fseek flushes stdio's | ||
| 436 | - # internal buffers. See issue #8745. | ||
| 363 | + if buffer[:4] != STRING_END_ARCHIVE: | ||
| 364 | + # Bad: End of Central Dir signature | ||
| 365 | + # Check if there's a comment. | ||
| 366 | + try: | ||
| 367 | + fp.seek(0, 2) | ||
| 368 | + file_size = fp.tell() | ||
| 369 | + except OSError: | ||
| 370 | + raise ZipImportError(f"can't read Zip file: {archive!r}", | ||
| 371 | + path=archive) | ||
| 372 | + max_comment_start = max(file_size - MAX_COMMENT_LEN - | ||
| 373 | + END_CENTRAL_DIR_SIZE, 0) | ||
| 374 | + try: | ||
| 375 | + fp.seek(max_comment_start) | ||
| 376 | + data = fp.read() | ||
| 377 | + except OSError: | ||
| 378 | + raise ZipImportError(f"can't read Zip file: {archive!r}", | ||
| 379 | + path=archive) | ||
| 380 | + pos = data.rfind(STRING_END_ARCHIVE) | ||
| 381 | + if pos < 0: | ||
| 382 | + raise ZipImportError(f'not a Zip file: {archive!r}', | ||
| 383 | + path=archive) | ||
| 384 | + buffer = data[pos:pos+END_CENTRAL_DIR_SIZE] | ||
| 385 | + if len(buffer) != END_CENTRAL_DIR_SIZE: | ||
| 386 | + raise ZipImportError(f"corrupt Zip file: {archive!r}", | ||
| 387 | + path=archive) | ||
| 388 | + header_position = file_size - len(data) + pos | ||
| 389 | + | ||
| 390 | + header_size = _unpack_uint32(buffer[12:16]) | ||
| 391 | + header_offset = _unpack_uint32(buffer[16:20]) | ||
| 392 | + if header_position < header_size: | ||
| 393 | + raise ZipImportError(f'bad central directory size: {archive!r}', path=archive) | ||
| 394 | + if header_position < header_offset: | ||
| 395 | + raise ZipImportError(f'bad central directory offset: {archive!r}', path=archive) | ||
| 396 | + header_position -= header_size | ||
| 397 | + arc_offset = header_position - header_offset | ||
| 398 | + if arc_offset < 0: | ||
| 399 | + raise ZipImportError(f'bad central directory size or offset: {archive!r}', path=archive) | ||
| 400 | + | ||
| 401 | + files = {} | ||
| 402 | + # Start of Central Directory | ||
| 403 | + count = 0 | ||
| 437 | 404 | try: | |
| 438 | - if len(fp.read(header_size - name_size)) != header_size - name_size: | ||
| 439 | - raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 405 | + fp.seek(header_position) | ||
| 440 | 406 | except OSError: | |
| 441 | 407 | raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | |
| 408 | + while True: | ||
| 409 | + buffer = fp.read(46) | ||
| 410 | + if len(buffer) < 4: | ||
| 411 | + raise EOFError('EOF read where not expected') | ||
| 412 | + # Start of file header | ||
| 413 | + if buffer[:4] != b'PK\x01\x02': | ||
| 414 | + break # Bad: Central Dir File Header | ||
| 415 | + if len(buffer) != 46: | ||
| 416 | + raise EOFError('EOF read where not expected') | ||
| 417 | + flags = _unpack_uint16(buffer[8:10]) | ||
| 418 | + compress = _unpack_uint16(buffer[10:12]) | ||
| 419 | + time = _unpack_uint16(buffer[12:14]) | ||
| 420 | + date = _unpack_uint16(buffer[14:16]) | ||
| 421 | + crc = _unpack_uint32(buffer[16:20]) | ||
| 422 | + data_size = _unpack_uint32(buffer[20:24]) | ||
| 423 | + file_size = _unpack_uint32(buffer[24:28]) | ||
| 424 | + name_size = _unpack_uint16(buffer[28:30]) | ||
| 425 | + extra_size = _unpack_uint16(buffer[30:32]) | ||
| 426 | + comment_size = _unpack_uint16(buffer[32:34]) | ||
| 427 | + file_offset = _unpack_uint32(buffer[42:46]) | ||
| 428 | + header_size = name_size + extra_size + comment_size | ||
| 429 | + if file_offset > header_offset: | ||
| 430 | + raise ZipImportError(f'bad local header offset: {archive!r}', path=archive) | ||
| 431 | + file_offset += arc_offset | ||
| 442 | 432 | ||
| 443 | - if flags & 0x800: | ||
| 444 | - # UTF-8 file names extension | ||
| 445 | - name = name.decode() | ||
| 446 | - else: | ||
| 447 | - # Historical ZIP filename encoding | ||
| 448 | 433 | try: | |
| 449 | - name = name.decode('ascii') | ||
| 450 | - except UnicodeDecodeError: | ||
| 451 | - name = name.decode('latin1').translate(cp437_table) | ||
| 452 | - | ||
| 453 | - name = name.replace('/', path_sep) | ||
| 454 | - path = _bootstrap_external._path_join(archive, name) | ||
| 455 | - t = (path, compress, data_size, file_size, file_offset, time, date, crc) | ||
| 456 | - files[name] = t | ||
| 457 | - count += 1 | ||
| 434 | + name = fp.read(name_size) | ||
| 435 | + except OSError: | ||
| 436 | + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 437 | + if len(name) != name_size: | ||
| 438 | + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 439 | + # On Windows, calling fseek to skip over the fields we don't use is | ||
| 440 | + # slower than reading the data because fseek flushes stdio's | ||
| 441 | + # internal buffers. See issue #8745. | ||
| 442 | + try: | ||
| 443 | + if len(fp.read(header_size - name_size)) != header_size - name_size: | ||
| 444 | + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 445 | + except OSError: | ||
| 446 | + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) | ||
| 447 | + | ||
| 448 | + if flags & 0x800: | ||
| 449 | + # UTF-8 file names extension | ||
| 450 | + name = name.decode() | ||
| 451 | + else: | ||
| 452 | + # Historical ZIP filename encoding | ||
| 453 | + try: | ||
| 454 | + name = name.decode('ascii') | ||
| 455 | + except UnicodeDecodeError: | ||
| 456 | + name = name.decode('latin1').translate(cp437_table) | ||
| 457 | + | ||
| 458 | + name = name.replace('/', path_sep) | ||
| 459 | + path = _bootstrap_external._path_join(archive, name) | ||
| 460 | + t = (path, compress, data_size, file_size, file_offset, time, date, crc) | ||
| 461 | + files[name] = t | ||
| 462 | + count += 1 | ||
| 463 | + finally: | ||
| 464 | + fp.seek(start_offset) | ||
| 458 | 465 | _bootstrap._verbose_message('zipimport: found {} names in {!r}', count, archive) | |
| 459 | 466 | return files | |
| 460 | 467 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + On macOS ``python3 /dev/fd/9 9</path/to/script.py`` failed for any script longer than a couple of bytes. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments