-
Notifications
You must be signed in to change notification settings - Fork 0
Add --file_write_strategy
to Bazel
#9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
15ed750
d9e96ed
98a6595
a5facfd
7a13660
62266f3
11665a1
2cd0437
b2ebaa8
652e586
66e45e2
841e9ba
91aa1b0
2e5e1b2
3fc5912
865852e
5a5393e
a2b3d75
d3748b7
d75451f
336e38e
886b309
91dd45f
c3213b5
2b1ab60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -21,12 +21,16 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.annotations.VisibleForTesting; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.base.MoreObjects; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.hash.HashFunction; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.hash.HashingOutputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.io.BaseEncoding; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.io.CountingOutputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.analysis.actions.DeterministicWriter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.util.Fingerprint; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.util.HashCodes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.util.StreamWriter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.vfs.DigestUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.vfs.FileStatus; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.vfs.Path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -36,8 +40,10 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.lib.vfs.XattrProvider; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.devtools.build.skyframe.SkyValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.io.ByteArrayInputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.io.ByteArrayOutputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.io.InputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.io.OutputStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.time.Instant; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.util.Arrays; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.util.Objects; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -67,7 +73,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@Immutable | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@ThreadSafe | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public abstract class FileArtifactValue implements SkyValue, HasDigest { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public abstract class FileArtifactValue implements SkyValue, HasDigest, StreamWriter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* The type of the underlying file system object. If it is a regular file, then it is guaranteed | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* to have a digest. Otherwise it does not have a digest. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -148,11 +154,30 @@ public InputStream getInputStream() { | |||||||||||||||||||||||||||||||||||||||||||||||||||
throw new UnsupportedOperationException(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* Writes the inline file contents to the given {@link OutputStream}. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* @throws UnsupportedOperationException if the file contents are not inline. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public void writeTo(OutputStream out) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
try (var in = getInputStream()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
in.transferTo(out); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** Returns whether the file contents exist remotely. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public boolean isRemote() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* Returns whether the file contents are materialized lazily, for example because they exist | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* remotely. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public final boolean isLazy() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return isRemote() || isInline(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** Returns the location index for remote files. For non-remote files, returns 0. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public int getLocationIndex() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -398,6 +423,20 @@ public static FileArtifactValue createForRemoteFileWithMaterializationData( | |||||||||||||||||||||||||||||||||||||||||||||||||||
digest, size, locationIndex, expirationTime); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
public static FileArtifactValue createForFileWriteActionOutput( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
DeterministicWriter writer, HashFunction hashFunction) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
long size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] digest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
try (CountingOutputStream countingOut = | ||||||||||||||||||||||||||||||||||||||||||||||||||||
new CountingOutputStream(OutputStream.nullOutputStream()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
HashingOutputStream hashingOut = new HashingOutputStream(hashFunction, countingOut)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
writer.writeOutputFile(hashingOut); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
size = countingOut.getCount(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
digest = hashingOut.hash().asBytes(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return new FileWriteOutputArtifactValue(writer, size, digest); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
public static FileArtifactValue createFromExistingWithResolvedPath( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
FileArtifactValue delegate, PathFragment resolvedPath) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return new ResolvedSymlinkArtifactValue(delegate, resolvedPath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1004,6 +1043,105 @@ public boolean wasModifiedSinceDigest(Path path) { | |||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final class FileWriteOutputArtifactValue extends FileArtifactValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
private final DeterministicWriter writer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
private final long size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
private final byte[] digest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@Nullable private FileContentsProxy proxy; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
private FileWriteOutputArtifactValue(DeterministicWriter writer, long size, byte[] digest) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
this.writer = writer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
this.size = size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
this.digest = digest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public FileStateType getType() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return FileStateType.REGULAR_FILE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public byte[] getDigest() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return digest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public long getSize() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public boolean isInline() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public InputStream getInputStream() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO: Avoid materializing the full content in memory by using a variant of | ||||||||||||||||||||||||||||||||||||||||||||||||||||
// Piped{Input,Output}Stream that works well with virtual threads. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
var out = new ByteArrayOutputStream(Math.clamp(getSize(), 0, Integer.MAX_VALUE)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
writeTo(out); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (IOException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
// writer is not expected to throw if out doesn't. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new IllegalStateException(e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return new ByteArrayInputStream(out.toByteArray()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1079
to
+1090
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential Resource Leak in getInputStream MethodThe getInputStream method creates a ByteArrayOutputStream, writes to it, and then returns a ByteArrayInputStream without closing the ByteArrayOutputStream. While ByteArrayOutputStream doesn't use system resources that need explicit closing, this pattern could lead to resource leaks with other stream implementations or if the implementation changes in the future. Additionally, the exception handling assumes the writer won't throw if the output stream doesn't, which may not always be true.
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public void writeTo(OutputStream out) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
writer.writeOutputFile(out); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public long getModifiedTime() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new UnsupportedOperationException(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* Sets the {@link FileContentsProxy} if the output backed by this metadata is materialized | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* later. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public void setContentsProxy(FileContentsProxy proxy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
this.proxy = proxy; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* Returns a non-null {@link FileContentsProxy} if this metadata is backed by a local file, e.g. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
* the file is materialized after action execution. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
@Nullable | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public FileContentsProxy getContentsProxy() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return proxy; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public boolean wasModifiedSinceDigest(Path path) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public boolean equals(Object o) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
if (this == o) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!(o instanceof FileWriteOutputArtifactValue that)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return Objects.equals(writer, that.writer) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
&& size == that.size | ||||||||||||||||||||||||||||||||||||||||||||||||||||
&& Arrays.equals(digest, that.digest); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
public int hashCode() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
return HashCodes.hashObjects(writer, size, Arrays.hashCode(digest)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
/** Metadata for files whose contents are available in memory. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final class InlineFileArtifactValue extends FileArtifactValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
private final byte[] data; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential Memory Exhaustion in Inline File Handling
The code attempts to allocate a ByteArrayOutputStream with a size potentially as large as Integer.MAX_VALUE. While there is a clamp function to limit the size, this could still lead to memory exhaustion if a malicious or corrupted file has a very large reported size. This could be exploited as a denial of service attack by causing the JVM to run out of memory when processing large files.
Standards