-
Notifications
You must be signed in to change notification settings - Fork 207
feat(#4983): implement SHA-256 hashing for directories in Cache #4984
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
Changes from all commits
c95815d
9f8a9ae
a46724f
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 | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,11 +6,14 @@ | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||
| import java.io.InputStream; | ||||||||||||||||||||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||||||||||||
| import java.nio.file.Path; | ||||||||||||||||||||||||||||
| import java.security.MessageDigest; | ||||||||||||||||||||||||||||
| import java.security.NoSuchAlgorithmException; | ||||||||||||||||||||||||||||
| import java.util.Base64; | ||||||||||||||||||||||||||||
| import java.util.Comparator; | ||||||||||||||||||||||||||||
| import java.util.stream.Stream; | ||||||||||||||||||||||||||||
| import org.cactoos.Func; | ||||||||||||||||||||||||||||
| import org.cactoos.func.UncheckedFunc; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -78,8 +81,6 @@ public void apply(final Path source, final Path target, final Path tail) { | |||||||||||||||||||||||||||
| "Failed to perform an IO operation with cache", | ||||||||||||||||||||||||||||
| ioexception | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } catch (final NoSuchAlgorithmException exception) { | ||||||||||||||||||||||||||||
| throw new IllegalStateException("SHA-256 hashing algorithm isn't found", exception); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -93,23 +94,80 @@ private Path hash(final Path tail) { | |||||||||||||||||||||||||||
| return full.getParent().resolve(String.format("%s.sha256", full.getFileName().toString())); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Calculate SHA-256 hash of a file or directory. | ||||||||||||||||||||||||||||
| * @param any File or directory path | ||||||||||||||||||||||||||||
| * @return Base64-encoded SHA-256 hash of the file or directory contents | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| private static String sha(final Path any) { | ||||||||||||||||||||||||||||
| final String result; | ||||||||||||||||||||||||||||
| if (Files.isDirectory(any)) { | ||||||||||||||||||||||||||||
| result = Cache.dirSha(any); | ||||||||||||||||||||||||||||
| } else if (Files.isRegularFile(any)) { | ||||||||||||||||||||||||||||
| result = Cache.fileSha(any); | ||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||
| throw new IllegalArgumentException( | ||||||||||||||||||||||||||||
| String.format("Path '%s' is neither a regular file nor a directory", any) | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+102
to
+112
|
||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Calculate SHA-256 hash of a directory by hashing all regular files inside it. | ||||||||||||||||||||||||||||
| * @param dir Directory path. | ||||||||||||||||||||||||||||
| * @return Base64-encoded SHA-256 hash of the directory contents. | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| private static String dirSha(final Path dir) { | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| final MessageDigest digest = MessageDigest.getInstance("SHA-256"); | ||||||||||||||||||||||||||||
| try (Stream<Path> stream = Files.walk(dir)) { | ||||||||||||||||||||||||||||
| stream.filter(Files::isRegularFile) | ||||||||||||||||||||||||||||
| .sorted(Comparator.comparing(Path::toString)) | ||||||||||||||||||||||||||||
| .map(Cache::fileSha) | ||||||||||||||||||||||||||||
| .map(s -> s.getBytes(StandardCharsets.UTF_8)) | ||||||||||||||||||||||||||||
| .forEach(digest::update); | ||||||||||||||||||||||||||||
|
Comment on lines
+127
to
+129
|
||||||||||||||||||||||||||||
| .map(Cache::fileSha) | |
| .map(s -> s.getBytes(StandardCharsets.UTF_8)) | |
| .forEach(digest::update); | |
| .forEach( | |
| path -> { | |
| digest.update( | |
| dir.relativize(path).toString().getBytes(StandardCharsets.UTF_8) | |
| ); | |
| digest.update((byte) 0); | |
| digest.update(fileSha(path).getBytes(StandardCharsets.UTF_8)); | |
| digest.update((byte) 0); | |
| } | |
| ); |
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.
hash(tail)usesthis.base.resolve(tail.normalize()), andPath.resolve(...)will ignorebasewhentailis absolute. This allows callers to make the cache read/write outside the cache root (and similarly,..segments can escape after normalization). Consider validating thattailis relative and that the resolved path stays withinbasebefore using it to construct cache/hash paths.