Ticket #16108: django-file-storage-remove-race.patch

File django-file-storage-remove-race.patch, 3.9 KB (added by Aymeric Augustin, 13 years ago)
  • tests/regressiontests/file_storage/tests.py

     
    304304        """
    305305        File storage should be robust against directory creation race conditions.
    306306        """
     307        real_makedirs = os.makedirs
     308
    307309        # Monkey-patch os.makedirs, to simulate a normal call, a raced call,
    308310        # and an error.
    309311        def fake_makedirs(path):
    310312            if path == os.path.join(self.temp_dir, 'normal'):
    311                 os.mkdir(path)
     313                real_makedirs(path)
    312314            elif path == os.path.join(self.temp_dir, 'raced'):
    313                 os.mkdir(path)
     315                real_makedirs(path)
    314316                raise OSError(errno.EEXIST, 'simulated EEXIST')
    315317            elif path == os.path.join(self.temp_dir, 'error'):
    316318                raise OSError(errno.EACCES, 'simulated EACCES')
    317319            else:
    318320                self.fail('unexpected argument %r' % path)
    319321
    320         real_makedirs = os.makedirs
    321322        try:
    322323            os.makedirs = fake_makedirs
    323324
     
    333334
    334335            # Check that OSErrors aside from EEXIST are still raised.
    335336            self.assertRaises(OSError,
    336                 lambda: self.storage.save('error/test.file',
    337                     ContentFile('not saved')))
     337                self.storage.save, 'error/test.file', ContentFile('not saved'))
    338338        finally:
    339339            os.makedirs = real_makedirs
    340340
     341    def test_remove_race_handling(self):
     342        """
     343        File storage should be robust against file removal race conditions.
     344        """
     345        real_remove = os.remove
     346
     347        # Monkey-patch os.remove, to simulate a normal call, a raced call,
     348        # and an error.
     349        def fake_remove(path):
     350            if path == os.path.join(self.temp_dir, 'normal.file'):
     351                real_remove(path)
     352            elif path == os.path.join(self.temp_dir, 'raced.file'):
     353                real_remove(path)
     354                raise OSError(errno.ENOENT, 'simulated ENOENT')
     355            elif path == os.path.join(self.temp_dir, 'error.file'):
     356                raise OSError(errno.EACCES, 'simulated EACCES')
     357            else:
     358                self.fail('unexpected argument %r' % path)
     359
     360        try:
     361            os.remove = fake_remove
     362
     363            self.storage.save('normal.file', ContentFile('delete normally'))
     364            self.storage.delete('normal.file')
     365            self.assertFalse(self.storage.exists('normal.file'))
     366
     367            self.storage.save('raced.file', ContentFile('delete with race'))
     368            self.storage.delete('raced.file')
     369            self.assertFalse(self.storage.exists('normal.file'))
     370
     371            # Check that OSErrors aside from ENOENT are still raised.
     372            self.storage.save('error.file', ContentFile('delete with error'))
     373            self.assertRaises(OSError, self.storage.delete, 'error.file')
     374        finally:
     375            os.remove = real_remove
     376
     377
    341378class CustomStorage(FileSystemStorage):
    342379    def get_available_name(self, name):
    343380        """
  • django/core/files/storage.py

     
    219219    def delete(self, name):
    220220        name = self.path(name)
    221221        # If the file exists, delete it from the filesystem.
     222        # Note that there is a race between os.path.exists and os.remove:
     223        # if os.remove fails with ENOENT, the file was removed
     224        # concurrently, and we can continue normally.
    222225        if os.path.exists(name):
    223             os.remove(name)
     226            try:
     227                os.remove(name)
     228            except OSError, e:
     229                if e.errno != errno.ENOENT:
     230                    raise
    224231
    225232    def exists(self, name):
    226233        return os.path.exists(self.path(name))
Back to Top