@@ -70,6 +70,20 @@ public abstract class AbstractActionInputPrefetcher implements ActionInputPrefet
7070 protected final Path execRoot ;
7171 protected final ImmutableList <Pattern > patternsToDownload ;
7272
73+ private static class Context {
74+ private final Set <Path > nonWritableDirs = Sets .newConcurrentHashSet ();
75+
76+ public void addNonWritableDir (Path dir ) {
77+ nonWritableDirs .add (dir );
78+ }
79+
80+ public void finalizeContext () throws IOException {
81+ for (Path path : nonWritableDirs ) {
82+ path .setWritable (false );
83+ }
84+ }
85+ }
86+
7387 /** Priority for the staging task. */
7488 protected enum Priority {
7589 /**
@@ -176,27 +190,37 @@ protected ListenableFuture<Void> prefetchFiles(
176190 files .add (input );
177191 }
178192
193+ Context context = new Context ();
194+
179195 Flowable <TransferResult > treeDownloads =
180196 Flowable .fromIterable (trees .entrySet ())
181197 .flatMapSingle (
182198 entry ->
183199 toTransferResult (
184200 prefetchInputTreeOrSymlink (
185- metadataProvider , entry .getKey (), entry .getValue (), priority )));
201+ context ,
202+ metadataProvider ,
203+ entry .getKey (),
204+ entry .getValue (),
205+ priority )));
186206
187207 Flowable <TransferResult > fileDownloads =
188208 Flowable .fromIterable (files )
189209 .flatMapSingle (
190210 input ->
191211 toTransferResult (
192- prefetchInputFileOrSymlink (metadataProvider , input , priority )));
212+ prefetchInputFileOrSymlink (context , metadataProvider , input , priority )));
193213
194214 Flowable <TransferResult > transfers = Flowable .merge (treeDownloads , fileDownloads );
195- Completable prefetch = mergeBulkTransfer (transfers ).onErrorResumeNext (this ::onErrorResumeNext );
215+ Completable prefetch =
216+ Completable .using (
217+ () -> context , ctx -> mergeBulkTransfer (transfers ), Context ::finalizeContext )
218+ .onErrorResumeNext (this ::onErrorResumeNext );
196219 return toListenableFuture (prefetch );
197220 }
198221
199222 private Completable prefetchInputTreeOrSymlink (
223+ Context context ,
200224 MetadataProvider provider ,
201225 SpecialArtifact tree ,
202226 List <TreeFileArtifact > treeFiles ,
@@ -216,7 +240,7 @@ private Completable prefetchInputTreeOrSymlink(
216240 PathFragment prefetchExecPath = treeMetadata .getMaterializationExecPath ().orElse (execPath );
217241
218242 Completable prefetch =
219- prefetchInputTree (provider , prefetchExecPath , treeFiles , treeMetadata , priority );
243+ prefetchInputTree (context , provider , prefetchExecPath , treeFiles , treeMetadata , priority );
220244
221245 // If prefetching to a different path, plant a symlink into it.
222246 if (!prefetchExecPath .equals (execPath )) {
@@ -249,6 +273,7 @@ private boolean shouldDownloadAnyTreeFiles(
249273 }
250274
251275 private Completable prefetchInputTree (
276+ Context context ,
252277 MetadataProvider provider ,
253278 PathFragment execPath ,
254279 List <TreeFileArtifact > treeFiles ,
@@ -303,7 +328,7 @@ private Completable prefetchInputTree(
303328 }
304329 }
305330 checkState (dir .equals (path ));
306- finalizeDownload (tempPath , path );
331+ finalizeDownload (context , tempPath , path );
307332 }
308333
309334 for (Path dir : dirs ) {
@@ -337,7 +362,8 @@ private Completable prefetchInputTree(
337362 }
338363
339364 private Completable prefetchInputFileOrSymlink (
340- MetadataProvider metadataProvider , ActionInput input , Priority priority ) throws IOException {
365+ Context context , MetadataProvider metadataProvider , ActionInput input , Priority priority )
366+ throws IOException {
341367 if (input instanceof VirtualActionInput ) {
342368 prefetchVirtualActionInput ((VirtualActionInput ) input );
343369 return Completable .complete ();
@@ -353,7 +379,7 @@ private Completable prefetchInputFileOrSymlink(
353379 PathFragment prefetchExecPath = metadata .getMaterializationExecPath ().orElse (execPath );
354380
355381 Completable prefetch =
356- downloadFileNoCheckRx (execRoot .getRelative (prefetchExecPath ), metadata , priority );
382+ downloadFileNoCheckRx (context , execRoot .getRelative (prefetchExecPath ), metadata , priority );
357383
358384 // If prefetching to a different path, plant a symlink into it.
359385 if (!prefetchExecPath .equals (execPath )) {
@@ -371,15 +397,16 @@ private Completable prefetchInputFileOrSymlink(
371397 * <p>The file will be written into a temporary file and moved to the final destination after the
372398 * download finished.
373399 */
374- private Completable downloadFileRx (Path path , FileArtifactValue metadata , Priority priority ) {
400+ private Completable downloadFileRx (
401+ Context context , Path path , FileArtifactValue metadata , Priority priority ) {
375402 if (!canDownloadFile (path , metadata )) {
376403 return Completable .complete ();
377404 }
378- return downloadFileNoCheckRx (path , metadata , priority );
405+ return downloadFileNoCheckRx (context , path , metadata , priority );
379406 }
380407
381408 private Completable downloadFileNoCheckRx (
382- Path path , FileArtifactValue metadata , Priority priority ) {
409+ Context context , Path path , FileArtifactValue metadata , Priority priority ) {
383410 if (path .isSymbolicLink ()) {
384411 try {
385412 path = path .getRelative (path .readSymbolicLink ());
@@ -402,7 +429,7 @@ private Completable downloadFileNoCheckRx(
402429 directExecutor ())
403430 .doOnComplete (
404431 () -> {
405- finalizeDownload (tempPath , finalPath );
432+ finalizeDownload (context , tempPath , finalPath );
406433 completed .set (true );
407434 }),
408435 tempPath -> {
@@ -439,11 +466,24 @@ public void downloadFile(Path path, FileArtifactValue metadata)
439466
440467 protected ListenableFuture <Void > downloadFileAsync (
441468 PathFragment path , FileArtifactValue metadata , Priority priority ) {
469+ Context context = new Context ();
442470 return toListenableFuture (
443- downloadFileRx (execRoot .getFileSystem ().getPath (path ), metadata , priority ));
471+ Completable .using (
472+ () -> context ,
473+ ctx ->
474+ downloadFileRx (context , execRoot .getFileSystem ().getPath (path ), metadata , priority ),
475+ Context ::finalizeContext ));
444476 }
445477
446- private void finalizeDownload (Path tmpPath , Path path ) throws IOException {
478+ private void finalizeDownload (Context context , Path tmpPath , Path path ) throws IOException {
479+ Path parentDir = path .getParentDirectory ();
480+ // In case the parent directory of the destination is not writable, temporarily change it to
481+ // writable. b/254844173.
482+ if (parentDir != null && !parentDir .isWritable ()) {
483+ context .addNonWritableDir (parentDir );
484+ parentDir .setWritable (true );
485+ }
486+
447487 // The permission of output file is changed to 0555 after action execution. We manually change
448488 // the permission here for the downloaded file to keep this behaviour consistent.
449489 tmpPath .chmod (0555 );
0 commit comments