Skip to content

Commit 3cf62d8

Browse files
John Didionmr-c
John Didion
andauthored
bug/1480: make writable workdir actually writable (#1481)
Co-authored-by: Michael R. Crusoe <[email protected]>
1 parent fa46c0a commit 3cf62d8

File tree

4 files changed

+111
-13
lines changed

4 files changed

+111
-13
lines changed

cwltool/process.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def stage_files(
308308
os.makedirs(entry.target)
309309
else:
310310
shutil.copytree(entry.resolved, entry.target)
311-
ensure_writable(entry.target)
311+
ensure_writable(entry.target, include_root=True)
312312
elif entry.type == "CreateFile" or entry.type == "CreateWritableFile":
313313
with open(entry.target, "wb") as new:
314314
if secret_store is not None:

cwltool/utils.py

+19-12
Original file line numberDiff line numberDiff line change
@@ -367,23 +367,30 @@ def downloadHttpFile(httpurl):
367367
return str(f.name)
368368

369369

370-
def ensure_writable(path): # type: (str) -> None
370+
def ensure_writable(path: str, include_root: bool = False) -> None:
371+
"""
372+
Ensure that 'path' is writable.
373+
374+
If 'path' is a directory, then all files and directories under 'path' are
375+
made writable, recursively. If 'path' is a file or if 'include_root' is
376+
`True`, then 'path' itself is made writable.
377+
"""
378+
379+
def add_writable_flag(p: str) -> None:
380+
st = os.stat(p)
381+
mode = stat.S_IMODE(st.st_mode)
382+
os.chmod(p, mode | stat.S_IWUSR)
383+
371384
if os.path.isdir(path):
385+
if include_root:
386+
add_writable_flag(path)
372387
for root, dirs, files in os.walk(path):
373388
for name in files:
374-
j = os.path.join(root, name)
375-
st = os.stat(j)
376-
mode = stat.S_IMODE(st.st_mode)
377-
os.chmod(j, mode | stat.S_IWUSR)
389+
add_writable_flag(os.path.join(root, name))
378390
for name in dirs:
379-
j = os.path.join(root, name)
380-
st = os.stat(j)
381-
mode = stat.S_IMODE(st.st_mode)
382-
os.chmod(j, mode | stat.S_IWUSR)
391+
add_writable_flag(os.path.join(root, name))
383392
else:
384-
st = os.stat(path)
385-
mode = stat.S_IMODE(st.st_mode)
386-
os.chmod(path, mode | stat.S_IWUSR)
393+
add_writable_flag(path)
387394

388395

389396
def ensure_non_writable(path): # type: (str) -> None

tests/test_iwdr.py

+48
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pathlib import Path
2+
from stat import S_IWRITE, S_IWGRP, S_IWOTH
23
from typing import Any
34

45
from cwltool.factory import Factory
@@ -80,6 +81,53 @@ def test_iwdr_permutations(tmp_path_factory: Any) -> None:
8081
)
8182

8283

84+
def test_iwdr_permutations_readonly(tmp_path_factory: Any) -> None:
85+
"""Confirm that readonly input files are properly made writable."""
86+
misc = tmp_path_factory.mktemp("misc")
87+
fifth = tmp_path_factory.mktemp("fifth")
88+
fifth_file = fifth / "bar"
89+
fifth_dir = fifth / "foo"
90+
fifth_file.touch()
91+
fifth_dir.mkdir()
92+
sixth = tmp_path_factory.mktemp("sixth")
93+
first = misc / "first"
94+
first.touch()
95+
second = misc / "second"
96+
second.touch()
97+
outdir = str(tmp_path_factory.mktemp("outdir"))
98+
for entry in [first, second, fifth, sixth, fifth_file, fifth_dir]:
99+
mode = entry.stat().st_mode
100+
ro_mask = 0o777 ^ (S_IWRITE | S_IWGRP | S_IWOTH)
101+
entry.chmod(mode & ro_mask)
102+
assert (
103+
main(
104+
[
105+
"--no-container",
106+
"--debug",
107+
"--leave-outputs",
108+
"--outdir",
109+
outdir,
110+
get_data("tests/wf/iwdr_permutations_nocontainer.cwl"),
111+
"--first",
112+
str(first),
113+
"--second",
114+
str(second),
115+
"--fifth",
116+
str(fifth),
117+
"--sixth",
118+
str(sixth),
119+
]
120+
)
121+
== 0
122+
)
123+
for entry in [first, second, fifth, sixth, fifth_file, fifth_dir]:
124+
try:
125+
mode = entry.stat().st_mode
126+
entry.chmod(mode | S_IWRITE)
127+
except PermissionError:
128+
pass
129+
130+
83131
@needs_docker
84132
def test_iwdr_permutations_inplace(tmp_path_factory: Any) -> None:
85133
misc = tmp_path_factory.mktemp("misc")
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env cwl-runner
2+
class: CommandLineTool
3+
cwlVersion: v1.2
4+
requirements:
5+
InitialWorkDirRequirement:
6+
listing:
7+
- entry: $(inputs.first)
8+
entryname: first_writable_file
9+
writable: true
10+
- entry: $(inputs.second)
11+
entryname: second_read_only_file
12+
writable: false
13+
- entry: $(inputs.fifth)
14+
entryname: fifth_writable_directory
15+
writable: true
16+
- entry: $(inputs.sixth)
17+
entryname: sixth_read_only_directory
18+
writable: false
19+
- entry: $(inputs.ninth)
20+
entryname: nineth_writable_directory_literal
21+
writable: true
22+
inputs:
23+
first: File
24+
second: File
25+
fifth: Directory
26+
sixth: Directory
27+
ninth:
28+
type: Directory
29+
default:
30+
class: Directory
31+
basename: foo
32+
listing: []
33+
outputs:
34+
out:
35+
type: Directory
36+
outputBinding:
37+
glob: .
38+
baseCommand: [bash, -c]
39+
arguments:
40+
- |
41+
find .
42+
echo "a" > first_writable_file
43+
touch fifth_writable_directory/c

0 commit comments

Comments
 (0)