Repository: python-pillow/Pillow

Issue:
Photoshop PSD errors with Truncated File Read 
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

Tried to open a Photoshop `psd` file that loads fine in Photoshop.

This issue came up as I'm processing multiple Photoshop `psd` files at a time on a project. Some files work fine, others do not. All of the files open fine in Photoshop 2023. 

Ultimately for this purpose I'm just looking to get the dimensions of the file and confirm the format. But in the future would like to actually be doing more processing on these files.

### What did you expect to happen?

I expected the file to be opened.

### What actually happened?

The code errors with `OSError: Truncated File Read` despite using the `ImageFile.LOAD_TRUNCATED_IMAGES = True` option.

This same result happens whether opening from a file path or loading bytes and opening from there. I tried several methods recommended in previous Github issue submissions.

### What are your OS, Python and Pillow versions?

* OS: MacOS Sonoma 14.0 (23A344)
* Python: 3.9.17
* Pillow: 10.1.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
❯ python
Python 3.9.17 (main, Aug 18 2023, 13:18:41)
[Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import ImageFile, Image
>>> ImageFile.LOAD_TRUNCATED_IMAGES = True
>>> psd_file = "/Users/kp/dev/pil_psd_test/file.psd"
>>> psd = Image.open(fp=psd_file)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/Image.py", line 3289, in open
    im = _open_core(
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/Image.py", line 3270, in _open_core
    im = factory(fp, filename)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/ImageFile.py", line 117, in __init__
    self._open()
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 127, in _open
    self.layers = _layerinfo(_layer_data, size)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 224, in _layerinfo
    length = i32(read(4))
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 168, in read
    return ImageFile._safe_read(fp, size)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/ImageFile.py", line 571, in _safe_read
    raise OSError(msg)
OSError: Truncated File Read

>>> from io import BytesIO
>>> with open(psd_file, 'rb') as fp:
...     image_data = fp.read()
...     bio = BytesIO(image_data)
...     with Image.open(bio) as fo:
...         try:
...             fo.load()
...         except OSError as e:
...             pass
...         img = fo.convert('RGB')
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/Image.py", line 3284, in open
    im = _open_core(fp, filename, prefix, formats)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/Image.py", line 3270, in _open_core
    im = factory(fp, filename)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/ImageFile.py", line 117, in __init__
    self._open()
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 127, in _open
    self.layers = _layerinfo(_layer_data, size)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 224, in _layerinfo
    length = i32(read(4))
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/PsdImagePlugin.py", line 168, in read
    return ImageFile._safe_read(fp, size)
  File "/Users/kp/dev/pil_psd_test/.venv/lib/python3.9/site-packages/PIL/ImageFile.py", line 571, in _safe_read
    raise OSError(msg)
OSError: Truncated File Read
```



Discussion:
Could you attach a copy of a problem file?
Attached 
[file.psd.zip](https://github.com/python-pillow/Pillow/files/13049661/file.psd.zip)


Resolving patch:
diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py
--- a/src/PIL/PsdImagePlugin.py
+++ b/src/PIL/PsdImagePlugin.py
@@ -186,6 +186,9 @@ def read(size):
         ct_types = i16(read(2))
         types = list(range(ct_types))
         if len(types) > 4:
+            fp.seek(len(types) * 6 + 12, io.SEEK_CUR)
+            size = i32(read(4))
+            fp.seek(size, io.SEEK_CUR)
             continue
 
         for _ in types:


Test patch:
diff --git a/Tests/images/five_channels.psd b/Tests/images/five_channels.psd
new file mode 100644
Binary files /dev/null and b/Tests/images/five_channels.psd differ
diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py
--- a/Tests/test_file_psd.py
+++ b/Tests/test_file_psd.py
@@ -111,6 +111,11 @@ def test_rgba():
         assert_image_equal_tofile(im, "Tests/images/imagedraw_square.png")
 
 
+def test_layer_skip():
+    with Image.open("Tests/images/five_channels.psd") as im:
+        assert im.n_frames == 1
+
+
 def test_icc_profile():
     with Image.open(test_file) as im:
         assert "icc_profile" in im.info