Skip to content

Commit f61e5da

Browse files
author
Robert Evans
committed
STORM-3052: Allow for blobs to be unzipped/untarred
1 parent cfe1124 commit f61e5da

File tree

2 files changed

+127
-134
lines changed

2 files changed

+127
-134
lines changed

storm-core/src/jvm/org/apache/storm/localizer/Localizer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ private LocalizedResource downloadBlob(Map conf, String key, File localFile,
543543
out.close();
544544
in.close();
545545
if (uncompress) {
546-
Utils.unpack(new File(downloadFile), new File(localFileWithVersion));
546+
Utils.unpack(new File(downloadFile), new File(localFileWithVersion), (boolean)OR(_conf.get(Config.DISABLE_SYMLINKS), false));
547547
LOG.debug("uncompressed " + downloadFile + " to: " + localFileWithVersion);
548548
}
549549

storm-core/src/jvm/org/apache/storm/utils/Utils.java

Lines changed: 126 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -861,52 +861,9 @@ public static long secureRandomLong() {
861861
* @param jarFile the .jar file to unpack
862862
* @param toDir the destination directory into which to unpack the jar
863863
*/
864-
public static void unJar(File jarFile, File toDir)
865-
throws IOException {
866-
JarFile jar = new JarFile(jarFile);
867-
try {
868-
Enumeration<JarEntry> entries = jar.entries();
869-
while (entries.hasMoreElements()) {
870-
final JarEntry entry = entries.nextElement();
871-
if (!entry.isDirectory()) {
872-
InputStream in = jar.getInputStream(entry);
873-
try {
874-
File file = new File(toDir, entry.getName());
875-
ensureDirectory(file.getParentFile());
876-
OutputStream out = new FileOutputStream(file);
877-
try {
878-
copyBytes(in, out, 8192);
879-
} finally {
880-
out.close();
881-
}
882-
} finally {
883-
in.close();
884-
}
885-
}
886-
}
887-
} finally {
888-
jar.close();
889-
}
890-
}
891-
892-
/**
893-
* Copies from one stream to another.
894-
*
895-
* @param in InputStream to read from
896-
* @param out OutputStream to write to
897-
* @param buffSize the size of the buffer
898-
*/
899-
public static void copyBytes(InputStream in, OutputStream out, int buffSize)
900-
throws IOException {
901-
PrintStream ps = out instanceof PrintStream ? (PrintStream)out : null;
902-
byte buf[] = new byte[buffSize];
903-
int bytesRead = in.read(buf);
904-
while (bytesRead >= 0) {
905-
out.write(buf, 0, bytesRead);
906-
if ((ps != null) && ps.checkError()) {
907-
throw new IOException("Unable to write to output stream.");
908-
}
909-
bytesRead = in.read(buf);
864+
public static void unJar(File jarFile, File toDir) throws IOException {
865+
try (JarFile jar = new JarFile(jarFile)) {
866+
extractZipFile(jar, toDir, null);
910867
}
911868
}
912869

@@ -930,20 +887,17 @@ private static void ensureDirectory(File dir) throws IOException {
930887
*
931888
* @param inFile The tar file as input.
932889
* @param untarDir The untar directory where to untar the tar file.
890+
* @param symlinksDisabled true if symlinks should be disabled, else false.
933891
* @throws IOException
934892
*/
935-
public static void unTar(File inFile, File untarDir) throws IOException {
936-
if (!untarDir.mkdirs()) {
937-
if (!untarDir.isDirectory()) {
938-
throw new IOException("Mkdirs failed to create " + untarDir);
939-
}
940-
}
893+
public static void unTar(File inFile, File untarDir, boolean symlinksDisabled) throws IOException {
894+
ensureDirectory(untarDir);
941895

942896
boolean gzipped = inFile.toString().endsWith("gz");
943-
if (isOnWindows()) {
897+
if (Utils.isOnWindows() || symlinksDisabled) {
944898
// Tar is not native to Windows. Use simple Java based implementation for
945899
// tests and simple tar archives
946-
unTarUsingJava(inFile, untarDir, gzipped);
900+
unTarUsingJava(inFile, untarDir, gzipped, symlinksDisabled);
947901
} else {
948902
// spawn tar utility to untar archive for full fledged unix behavior such
949903
// as resolving symlinks in tar archives
@@ -980,7 +934,9 @@ private static void unTarUsingTar(File inFile, File untarDir,
980934
}
981935

982936
private static void unTarUsingJava(File inFile, File untarDir,
983-
boolean gzipped) throws IOException {
937+
boolean gzipped, boolean symlinksDisabled) throws IOException {
938+
final String base = untarDir.getCanonicalPath();
939+
LOG.trace("java untar {} to {}", inFile, base);
984940
InputStream inputStream = null;
985941
try {
986942
if (gzipped) {
@@ -991,7 +947,7 @@ private static void unTarUsingJava(File inFile, File untarDir,
991947
}
992948
try (TarArchiveInputStream tis = new TarArchiveInputStream(inputStream)) {
993949
for (TarArchiveEntry entry = tis.getNextTarEntry(); entry != null; ) {
994-
unpackEntries(tis, entry, untarDir);
950+
unpackEntries(tis, entry, untarDir, base, symlinksDisabled);
995951
entry = tis.getNextTarEntry();
996952
}
997953
}
@@ -1003,35 +959,82 @@ private static void unTarUsingJava(File inFile, File untarDir,
1003959
}
1004960

1005961
private static void unpackEntries(TarArchiveInputStream tis,
1006-
TarArchiveEntry entry, File outputDir) throws IOException {
962+
TarArchiveEntry entry, File outputDir, final String base,
963+
boolean symlinksDisabled) throws IOException {
964+
File target = new File(outputDir, entry.getName());
965+
String found = target.getCanonicalPath();
966+
if (!found.startsWith(base)) {
967+
LOG.error("Invalid location {} is outside of {}", found, base);
968+
return;
969+
}
1007970
if (entry.isDirectory()) {
1008-
File subDir = new File(outputDir, entry.getName());
1009-
if (!subDir.mkdirs() && !subDir.isDirectory()) {
1010-
throw new IOException("Mkdirs failed to create tar internal dir "
1011-
+ outputDir);
1012-
}
971+
LOG.trace("Extracting dir {}", target);
972+
ensureDirectory(target);
1013973
for (TarArchiveEntry e : entry.getDirectoryEntries()) {
1014-
unpackEntries(tis, e, subDir);
974+
unpackEntries(tis, e, target, base, symlinksDisabled);
1015975
}
1016-
return;
976+
} else if (entry.isSymbolicLink()) {
977+
if (symlinksDisabled) {
978+
LOG.info("Symlinks disabled skipping {}", target);
979+
} else {
980+
Path src = target.toPath();
981+
Path dest = Paths.get(entry.getLinkName());
982+
LOG.trace("Extracting sym link {} to {}", target, dest);
983+
// Create symbolic link relative to tar parent dir
984+
Files.createSymbolicLink(src, dest);
985+
}
986+
} else if (entry.isFile()) {
987+
LOG.trace("Extracting file {}", target);
988+
ensureDirectory(target.getParentFile());
989+
try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(target))) {
990+
IOUtils.copy(tis, outputStream);
991+
}
992+
} else {
993+
LOG.error("{} is not a currently supported tar entry type.", entry);
1017994
}
1018-
File outputFile = new File(outputDir, entry.getName());
1019-
if (!outputFile.getParentFile().exists()) {
1020-
if (!outputFile.getParentFile().mkdirs()) {
1021-
throw new IOException("Mkdirs failed to create tar internal dir "
1022-
+ outputDir);
995+
996+
Path p = target.toPath();
997+
if (Files.exists(p)) {
998+
try {
999+
//We created it so lets chmod it properly
1000+
int mode = entry.getMode();
1001+
Files.setPosixFilePermissions(p, parsePerms(mode));
1002+
} catch (UnsupportedOperationException e) {
1003+
//Ignored the file system we are on does not support this, so don't do it.
10231004
}
10241005
}
1025-
int count;
1026-
byte data[] = new byte[2048];
1027-
BufferedOutputStream outputStream = new BufferedOutputStream(
1028-
new FileOutputStream(outputFile));
1006+
}
10291007

1030-
while ((count = tis.read(data)) != -1) {
1031-
outputStream.write(data, 0, count);
1008+
private static Set<PosixFilePermission> parsePerms(int mode) {
1009+
Set<PosixFilePermission> ret = new HashSet<>();
1010+
if ((mode & 0001) > 0) {
1011+
ret.add(PosixFilePermission.OTHERS_EXECUTE);
1012+
}
1013+
if ((mode & 0002) > 0) {
1014+
ret.add(PosixFilePermission.OTHERS_WRITE);
1015+
}
1016+
if ((mode & 0004) > 0) {
1017+
ret.add(PosixFilePermission.OTHERS_READ);
1018+
}
1019+
if ((mode & 0010) > 0) {
1020+
ret.add(PosixFilePermission.GROUP_EXECUTE);
10321021
}
1033-
outputStream.flush();
1034-
outputStream.close();
1022+
if ((mode & 0020) > 0) {
1023+
ret.add(PosixFilePermission.GROUP_WRITE);
1024+
}
1025+
if ((mode & 0040) > 0) {
1026+
ret.add(PosixFilePermission.GROUP_READ);
1027+
}
1028+
if ((mode & 0100) > 0) {
1029+
ret.add(PosixFilePermission.OWNER_EXECUTE);
1030+
}
1031+
if ((mode & 0200) > 0) {
1032+
ret.add(PosixFilePermission.OWNER_WRITE);
1033+
}
1034+
if ((mode & 0400) > 0) {
1035+
ret.add(PosixFilePermission.OWNER_READ);
1036+
}
1037+
return ret;
10351038
}
10361039

10371040
public static boolean isOnWindows() {
@@ -1045,16 +1048,21 @@ public static boolean isAbsolutePath(String path) {
10451048
return Paths.get(path).isAbsolute();
10461049
}
10471050

1048-
public static void unpack(File localrsrc, File dst) throws IOException {
1051+
public static void unpack(File localrsrc, File dst, boolean symLinksDisabled) throws IOException {
10491052
String lowerDst = localrsrc.getName().toLowerCase();
1050-
if (lowerDst.endsWith(".jar")) {
1053+
if (lowerDst.endsWith(".jar") ||
1054+
lowerDst.endsWith("_jar")) {
10511055
unJar(localrsrc, dst);
1052-
} else if (lowerDst.endsWith(".zip")) {
1056+
} else if (lowerDst.endsWith(".zip") ||
1057+
lowerDst.endsWith("_zip")) {
10531058
unZip(localrsrc, dst);
10541059
} else if (lowerDst.endsWith(".tar.gz") ||
1055-
lowerDst.endsWith(".tgz") ||
1056-
lowerDst.endsWith(".tar")) {
1057-
unTar(localrsrc, dst);
1060+
lowerDst.endsWith("_tar_gz") ||
1061+
lowerDst.endsWith(".tgz") ||
1062+
lowerDst.endsWith("_tgz") ||
1063+
lowerDst.endsWith(".tar") ||
1064+
lowerDst.endsWith("_tar")) {
1065+
unTar(localrsrc, dst, symLinksDisabled);
10581066
} else {
10591067
LOG.warn("Cannot unpack " + localrsrc);
10601068
if (!localrsrc.renameTo(dst)) {
@@ -1067,6 +1075,35 @@ public static void unpack(File localrsrc, File dst) throws IOException {
10671075
}
10681076
}
10691077

1078+
private static void extractZipFile(ZipFile zipFile, File toDir, String prefix) throws IOException {
1079+
ensureDirectory(toDir);
1080+
final String base = toDir.getCanonicalPath();
1081+
1082+
Enumeration<? extends ZipEntry> entries = zipFile.entries();
1083+
while (entries.hasMoreElements()) {
1084+
ZipEntry entry = entries.nextElement();
1085+
if (!entry.isDirectory()) {
1086+
if (prefix != null && !entry.getName().startsWith(prefix)) {
1087+
//No need to extract it, it is not what we are looking for.
1088+
continue;
1089+
}
1090+
File file = new File(toDir, entry.getName());
1091+
String found = file.getCanonicalPath();
1092+
if (!found.startsWith(base)) {
1093+
LOG.error("Invalid location {} is outside of {}", found, base);
1094+
continue;
1095+
}
1096+
1097+
try (InputStream in = zipFile.getInputStream(entry)) {
1098+
ensureDirectory(file.getParentFile());
1099+
try (OutputStream out = new FileOutputStream(file)) {
1100+
IOUtils.copy(in, out);
1101+
}
1102+
}
1103+
}
1104+
}
1105+
}
1106+
10701107
public static boolean canUserReadBlob(ReadableBlobMeta meta, String user) {
10711108
SettableBlobMeta settable = meta.get_settable();
10721109
for (AccessControl acl : settable.get_acl()) {
@@ -1397,45 +1434,12 @@ public static void validateTopologyBlobStoreMap(Map<String, ?> stormConf, Set<St
13971434
* Given a File input it will unzip the file in a the unzip directory
13981435
* passed as the second parameter
13991436
* @param inFile The zip file as input
1400-
* @param unzipDir The unzip directory where to unzip the zip file.
1437+
* @param toDir The unzip directory where to unzip the zip file.
14011438
* @throws IOException
14021439
*/
1403-
public static void unZip(File inFile, File unzipDir) throws IOException {
1404-
Enumeration<? extends ZipEntry> entries;
1405-
ZipFile zipFile = new ZipFile(inFile);
1406-
1407-
try {
1408-
entries = zipFile.entries();
1409-
while (entries.hasMoreElements()) {
1410-
ZipEntry entry = entries.nextElement();
1411-
if (!entry.isDirectory()) {
1412-
InputStream in = zipFile.getInputStream(entry);
1413-
try {
1414-
File file = new File(unzipDir, entry.getName());
1415-
if (!file.getParentFile().mkdirs()) {
1416-
if (!file.getParentFile().isDirectory()) {
1417-
throw new IOException("Mkdirs failed to create " +
1418-
file.getParentFile().toString());
1419-
}
1420-
}
1421-
OutputStream out = new FileOutputStream(file);
1422-
try {
1423-
byte[] buffer = new byte[8192];
1424-
int i;
1425-
while ((i = in.read(buffer)) != -1) {
1426-
out.write(buffer, 0, i);
1427-
}
1428-
} finally {
1429-
out.close();
1430-
}
1431-
} finally {
1432-
in.close();
1433-
}
1434-
}
1435-
}
1436-
} finally {
1437-
zipFile.close();
1438-
}
1440+
public static void unZip(File inFile, File toDir) throws IOException {
1441+
try (ZipFile zipFile = new ZipFile(inFile)) {
1442+
extractZipFile(zipFile, toDir, null); }
14391443
}
14401444

14411445
/**
@@ -1905,21 +1909,10 @@ public static int execCommand(String... command) throws ExecuteException, IOExce
19051909
public static void extractDirFromJar(String jarpath, String dir, File destdir) {
19061910
_instance.extractDirFromJarImpl(jarpath, dir, destdir);
19071911
}
1908-
1912+
19091913
public void extractDirFromJarImpl(String jarpath, String dir, File destdir) {
19101914
try (JarFile jarFile = new JarFile(jarpath)) {
1911-
Enumeration<JarEntry> jarEnums = jarFile.entries();
1912-
while (jarEnums.hasMoreElements()) {
1913-
JarEntry entry = jarEnums.nextElement();
1914-
if (!entry.isDirectory() && entry.getName().startsWith(dir)) {
1915-
File aFile = new File(destdir, entry.getName());
1916-
aFile.getParentFile().mkdirs();
1917-
try (FileOutputStream out = new FileOutputStream(aFile);
1918-
InputStream in = jarFile.getInputStream(entry)) {
1919-
IOUtils.copy(in, out);
1920-
}
1921-
}
1922-
}
1915+
extractZipFile(jarFile, destdir, dir);
19231916
} catch (IOException e) {
19241917
LOG.info("Could not extract {} from {}", dir, jarpath);
19251918
}

0 commit comments

Comments
 (0)