@@ -70,6 +70,20 @@ public abstract class AbstractActionInputPrefetcher implements ActionInputPrefet
70
70
protected final Path execRoot ;
71
71
protected final ImmutableList <Pattern > patternsToDownload ;
72
72
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
+
73
87
/** Priority for the staging task. */
74
88
protected enum Priority {
75
89
/**
@@ -176,27 +190,37 @@ protected ListenableFuture<Void> prefetchFiles(
176
190
files .add (input );
177
191
}
178
192
193
+ Context context = new Context ();
194
+
179
195
Flowable <TransferResult > treeDownloads =
180
196
Flowable .fromIterable (trees .entrySet ())
181
197
.flatMapSingle (
182
198
entry ->
183
199
toTransferResult (
184
200
prefetchInputTreeOrSymlink (
185
- metadataProvider , entry .getKey (), entry .getValue (), priority )));
201
+ context ,
202
+ metadataProvider ,
203
+ entry .getKey (),
204
+ entry .getValue (),
205
+ priority )));
186
206
187
207
Flowable <TransferResult > fileDownloads =
188
208
Flowable .fromIterable (files )
189
209
.flatMapSingle (
190
210
input ->
191
211
toTransferResult (
192
- prefetchInputFileOrSymlink (metadataProvider , input , priority )));
212
+ prefetchInputFileOrSymlink (context , metadataProvider , input , priority )));
193
213
194
214
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 );
196
219
return toListenableFuture (prefetch );
197
220
}
198
221
199
222
private Completable prefetchInputTreeOrSymlink (
223
+ Context context ,
200
224
MetadataProvider provider ,
201
225
SpecialArtifact tree ,
202
226
List <TreeFileArtifact > treeFiles ,
@@ -216,7 +240,7 @@ private Completable prefetchInputTreeOrSymlink(
216
240
PathFragment prefetchExecPath = treeMetadata .getMaterializationExecPath ().orElse (execPath );
217
241
218
242
Completable prefetch =
219
- prefetchInputTree (provider , prefetchExecPath , treeFiles , treeMetadata , priority );
243
+ prefetchInputTree (context , provider , prefetchExecPath , treeFiles , treeMetadata , priority );
220
244
221
245
// If prefetching to a different path, plant a symlink into it.
222
246
if (!prefetchExecPath .equals (execPath )) {
@@ -249,6 +273,7 @@ private boolean shouldDownloadAnyTreeFiles(
249
273
}
250
274
251
275
private Completable prefetchInputTree (
276
+ Context context ,
252
277
MetadataProvider provider ,
253
278
PathFragment execPath ,
254
279
List <TreeFileArtifact > treeFiles ,
@@ -303,7 +328,7 @@ private Completable prefetchInputTree(
303
328
}
304
329
}
305
330
checkState (dir .equals (path ));
306
- finalizeDownload (tempPath , path );
331
+ finalizeDownload (context , tempPath , path );
307
332
}
308
333
309
334
for (Path dir : dirs ) {
@@ -337,7 +362,8 @@ private Completable prefetchInputTree(
337
362
}
338
363
339
364
private Completable prefetchInputFileOrSymlink (
340
- MetadataProvider metadataProvider , ActionInput input , Priority priority ) throws IOException {
365
+ Context context , MetadataProvider metadataProvider , ActionInput input , Priority priority )
366
+ throws IOException {
341
367
if (input instanceof VirtualActionInput ) {
342
368
prefetchVirtualActionInput ((VirtualActionInput ) input );
343
369
return Completable .complete ();
@@ -353,7 +379,7 @@ private Completable prefetchInputFileOrSymlink(
353
379
PathFragment prefetchExecPath = metadata .getMaterializationExecPath ().orElse (execPath );
354
380
355
381
Completable prefetch =
356
- downloadFileNoCheckRx (execRoot .getRelative (prefetchExecPath ), metadata , priority );
382
+ downloadFileNoCheckRx (context , execRoot .getRelative (prefetchExecPath ), metadata , priority );
357
383
358
384
// If prefetching to a different path, plant a symlink into it.
359
385
if (!prefetchExecPath .equals (execPath )) {
@@ -371,15 +397,16 @@ private Completable prefetchInputFileOrSymlink(
371
397
* <p>The file will be written into a temporary file and moved to the final destination after the
372
398
* download finished.
373
399
*/
374
- private Completable downloadFileRx (Path path , FileArtifactValue metadata , Priority priority ) {
400
+ private Completable downloadFileRx (
401
+ Context context , Path path , FileArtifactValue metadata , Priority priority ) {
375
402
if (!canDownloadFile (path , metadata )) {
376
403
return Completable .complete ();
377
404
}
378
- return downloadFileNoCheckRx (path , metadata , priority );
405
+ return downloadFileNoCheckRx (context , path , metadata , priority );
379
406
}
380
407
381
408
private Completable downloadFileNoCheckRx (
382
- Path path , FileArtifactValue metadata , Priority priority ) {
409
+ Context context , Path path , FileArtifactValue metadata , Priority priority ) {
383
410
if (path .isSymbolicLink ()) {
384
411
try {
385
412
path = path .getRelative (path .readSymbolicLink ());
@@ -402,7 +429,7 @@ private Completable downloadFileNoCheckRx(
402
429
directExecutor ())
403
430
.doOnComplete (
404
431
() -> {
405
- finalizeDownload (tempPath , finalPath );
432
+ finalizeDownload (context , tempPath , finalPath );
406
433
completed .set (true );
407
434
}),
408
435
tempPath -> {
@@ -439,11 +466,24 @@ public void downloadFile(Path path, FileArtifactValue metadata)
439
466
440
467
protected ListenableFuture <Void > downloadFileAsync (
441
468
PathFragment path , FileArtifactValue metadata , Priority priority ) {
469
+ Context context = new Context ();
442
470
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 ));
444
476
}
445
477
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
+
447
487
// The permission of output file is changed to 0555 after action execution. We manually change
448
488
// the permission here for the downloaded file to keep this behaviour consistent.
449
489
tmpPath .chmod (0555 );
0 commit comments