[1.11.x] Fixed #28170 -- Fixed file_move_safe() crash when moving files to a CIFS mount.
Backport of 789c290150
from master
This commit is contained in:
parent
877d7b71ae
commit
7250393f31
|
@ -5,6 +5,7 @@ Move a file in the safest way possible::
|
||||||
>>> file_move_safe("/tmp/old_file", "/tmp/new_file")
|
>>> file_move_safe("/tmp/old_file", "/tmp/new_file")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import errno
|
||||||
import os
|
import os
|
||||||
from shutil import copystat
|
from shutil import copystat
|
||||||
|
|
||||||
|
@ -67,7 +68,15 @@ def file_move_safe(old_file_name, new_file_name, chunk_size=1024 * 64, allow_ove
|
||||||
finally:
|
finally:
|
||||||
locks.unlock(fd)
|
locks.unlock(fd)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
|
try:
|
||||||
copystat(old_file_name, new_file_name)
|
copystat(old_file_name, new_file_name)
|
||||||
|
except OSError as e:
|
||||||
|
# Certain filesystems (e.g. CIFS) fail to copy the file's metadata if
|
||||||
|
# the type of the destination filesystem isn't the same as the source
|
||||||
|
# filesystem; ignore that.
|
||||||
|
if getattr(e, 'errno', 0) != errno.EPERM:
|
||||||
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(old_file_name)
|
os.remove(old_file_name)
|
||||||
|
|
|
@ -54,3 +54,6 @@ Bugfixes
|
||||||
|
|
||||||
* Made date-based generic views return a 404 rather than crash when given an
|
* Made date-based generic views return a 404 rather than crash when given an
|
||||||
out of range date (:ticket:`28209`).
|
out of range date (:ticket:`28209`).
|
||||||
|
|
||||||
|
* Fixed a regression where ``file_move_safe()`` crashed when moving files to a
|
||||||
|
CIFS mount (:ticket:`28170`).
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import errno
|
||||||
import gzip
|
import gzip
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
@ -317,6 +318,31 @@ class FileMoveSafeTests(unittest.TestCase):
|
||||||
os.close(handle_a)
|
os.close(handle_a)
|
||||||
os.close(handle_b)
|
os.close(handle_b)
|
||||||
|
|
||||||
|
def test_file_move_copystat_cifs(self):
|
||||||
|
"""
|
||||||
|
file_move_safe() ignores a copystat() EPERM PermissionError. This
|
||||||
|
happens when the destination filesystem is CIFS, for example.
|
||||||
|
"""
|
||||||
|
copystat_EACCES_error = OSError(errno.EACCES, 'msg')
|
||||||
|
copystat_EPERM_error = OSError(errno.EPERM, 'msg')
|
||||||
|
handle_a, self.file_a = tempfile.mkstemp()
|
||||||
|
handle_b, self.file_b = tempfile.mkstemp()
|
||||||
|
try:
|
||||||
|
# This exception is required to reach the copystat() call in
|
||||||
|
# file_safe_move().
|
||||||
|
with mock.patch('django.core.files.move.os.rename', side_effect=OSError()):
|
||||||
|
# An error besides EPERM isn't ignored.
|
||||||
|
with mock.patch('django.core.files.move.copystat', side_effect=copystat_EACCES_error):
|
||||||
|
with self.assertRaises(OSError) as e:
|
||||||
|
file_move_safe(self.file_a, self.file_b, allow_overwrite=True)
|
||||||
|
self.assertEqual(e.exception.errno, errno.EACCES)
|
||||||
|
# EPERM is ignored.
|
||||||
|
with mock.patch('django.core.files.move.copystat', side_effect=copystat_EPERM_error):
|
||||||
|
self.assertIsNone(file_move_safe(self.file_a, self.file_b, allow_overwrite=True))
|
||||||
|
finally:
|
||||||
|
os.close(handle_a)
|
||||||
|
os.close(handle_b)
|
||||||
|
|
||||||
|
|
||||||
class SpooledTempTests(unittest.TestCase):
|
class SpooledTempTests(unittest.TestCase):
|
||||||
def test_in_memory_spooled_temp(self):
|
def test_in_memory_spooled_temp(self):
|
||||||
|
|
Loading…
Reference in New Issue