Commit e2efc896 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

Fixed #22680 -- I/O operation on closed file.

This patch is two-fold; first it ensure that Django does close everything in
request.FILES at the end of the request and secondly the storage system should
no longer close any files during save, it's up to the caller to handle that --
or let Django close the files at the end of the request.
parent a1c6cd6a
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -211,7 +211,6 @@ class FileSystemStorage(Storage):
                # This file has a file path that we can move.
                if hasattr(content, 'temporary_file_path'):
                    file_move_safe(content.temporary_file_path(), full_path)
                    content.close()

                # This is a normal uploadedfile that we can stream.
                else:
@@ -230,7 +229,6 @@ class FileSystemStorage(Storage):
                                _file = os.fdopen(fd, mode)
                            _file.write(chunk)
                    finally:
                        content.close()
                        locks.unlock(fd)
                        if _file is not None:
                            _file.close()
+0 −3
Original line number Diff line number Diff line
@@ -96,9 +96,6 @@ class InMemoryUploadedFile(UploadedFile):
    def open(self, mode=None):
        self.file.seek(0)

    def close(self):
        pass

    def chunks(self, chunk_size=None):
        self.file.seek(0)
        yield self.read()
+2 −0
Original line number Diff line number Diff line
@@ -219,6 +219,8 @@ class BaseHandler(object):
            signals.got_request_exception.send(sender=self.__class__, request=request)
            response = self.handle_uncaught_exception(request, resolver, sys.exc_info())

        response._closable_objects.append(request)

        return response

    def handle_uncaught_exception(self, request, resolver, exc_info):
+10 −0
Original line number Diff line number Diff line
@@ -228,6 +228,7 @@ class MultiPartParser(object):
                                    break

                    except SkipFile:
                        self._close_files()
                        # Just use up the rest of this file...
                        exhaust(field_stream)
                    else:
@@ -237,6 +238,7 @@ class MultiPartParser(object):
                    # If this is neither a FIELD or a FILE, just exhaust the stream.
                    exhaust(stream)
        except StopUpload as e:
            self._close_files()
            if not e.connection_reset:
                exhaust(self._input_data)
        else:
@@ -268,6 +270,14 @@ class MultiPartParser(object):
        """Cleanup filename from Internet Explorer full paths."""
        return filename and filename[filename.rfind("\\") + 1:].strip()

    def _close_files(self):
        # Free up all file handles.
        # FIXME: this currently assumes that upload handlers store the file as 'file'
        # We should document that... (Maybe add handler.free_file to complement new_file)
        for handler in self._upload_handlers:
            if hasattr(handler, 'file'):
                handler.file.close()


class LazyStream(six.Iterator):
    """
+6 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import os
import re
import sys
from io import BytesIO
from itertools import chain
from pprint import pformat

from django.conf import settings
@@ -256,6 +257,11 @@ class HttpRequest(object):
        else:
            self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()

    def close(self):
        if hasattr(self, '_files'):
            for f in chain.from_iterable(l[1] for l in self._files.lists()):
                f.close()

    # File-like and iterator interface.
    #
    # Expects self._stream to be set to an appropriate source of bytes by
Loading