Skip to content

Commit 372ef69

Browse files
Bugfix/1827 invalid cross device link on localFS commit (#1829)
Co-authored-by: Martin Durant <[email protected]>
1 parent 0d5d47e commit 372ef69

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

fsspec/implementations/local.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,22 @@ def __getstate__(self):
430430
def commit(self):
431431
if self.autocommit:
432432
raise RuntimeError("Can only commit if not already set to autocommit")
433-
shutil.move(self.temp, self.path)
434433
try:
435-
mask = 0o666
436-
os.chmod(self.path, mask & ~get_umask(mask))
437-
except RuntimeError:
438-
pass
434+
shutil.move(self.temp, self.path)
435+
except PermissionError as e:
436+
# shutil.move raises PermissionError if os.rename
437+
# and the default copy2 fallback with shutil.copystats fail.
438+
# The file should be there nonetheless, but without copied permissions.
439+
# If it doesn't exist, there was no permission to create the file.
440+
if not os.path.exists(self.path):
441+
raise e
442+
else:
443+
# If PermissionError is not raised, permissions can be set.
444+
try:
445+
mask = 0o666
446+
os.chmod(self.path, mask & ~get_umask(mask))
447+
except RuntimeError:
448+
pass
439449

440450
def discard(self):
441451
if self.autocommit:

fsspec/implementations/tests/test_local.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import bz2
2+
import errno
23
import gzip
34
import os
45
import os.path
@@ -562,6 +563,25 @@ def test_multiple_filesystems_use_umask_cache(tmpdir):
562563
assert get_umask.cache_info().hits == 1
563564

564565

566+
def test_transaction_cross_device_but_mock_temp_dir_on_wrong_device(tmpdir):
567+
# If the temporary file for a transaction is not on the correct device,
568+
# os.rename in shutil.move will raise EXDEV and lookup('chmod') will raise
569+
# a PermissionError.
570+
fs = LocalFileSystem()
571+
with (
572+
patch(
573+
"os.rename",
574+
side_effect=OSError(errno.EXDEV, "Invalid cross-device link"),
575+
),
576+
patch(
577+
"os.chmod",
578+
side_effect=PermissionError("Operation not permitted"),
579+
),
580+
):
581+
with fs.transaction, fs.open(tmpdir + "/afile", "wb") as f:
582+
f.write(b"data")
583+
584+
565585
def test_make_path_posix():
566586
cwd = os.getcwd()
567587
if WIN:

0 commit comments

Comments
 (0)