Skip to content

Commit 0fc6b52

Browse files
author
Robert Evans
committed
STORM-3052: Allow for blobs to be unzipped/untarred
1 parent 3c7c054 commit 0fc6b52

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

@@ -928,20 +885,17 @@ private static void ensureDirectory(File dir) throws IOException {
928885
*
929886
* @param inFile The tar file as input.
930887
* @param untarDir The untar directory where to untar the tar file.
888+
* @param symlinksDisabled true if symlinks should be disabled, else false.
931889
* @throws IOException
932890
*/
933-
public static void unTar(File inFile, File untarDir) throws IOException {
934-
if (!untarDir.mkdirs()) {
935-
if (!untarDir.isDirectory()) {
936-
throw new IOException("Mkdirs failed to create " + untarDir);
937-
}
938-
}
891+
public static void unTar(File inFile, File untarDir, boolean symlinksDisabled) throws IOException {
892+
ensureDirectory(untarDir);
939893

940894
boolean gzipped = inFile.toString().endsWith("gz");
941-
if (isOnWindows()) {
895+
if (Utils.isOnWindows() || symlinksDisabled) {
942896
// Tar is not native to Windows. Use simple Java based implementation for
943897
// tests and simple tar archives
944-
unTarUsingJava(inFile, untarDir, gzipped);
898+
unTarUsingJava(inFile, untarDir, gzipped, symlinksDisabled);
945899
} else {
946900
// spawn tar utility to untar archive for full fledged unix behavior such
947901
// as resolving symlinks in tar archives
@@ -978,7 +932,9 @@ private static void unTarUsingTar(File inFile, File untarDir,
978932
}
979933

980934
private static void unTarUsingJava(File inFile, File untarDir,
981-
boolean gzipped) throws IOException {
935+
boolean gzipped, boolean symlinksDisabled) throws IOException {
936+
final String base = untarDir.getCanonicalPath();
937+
LOG.trace("java untar {} to {}", inFile, base);
982938
InputStream inputStream = null;
983939
try {
984940
if (gzipped) {
@@ -989,7 +945,7 @@ private static void unTarUsingJava(File inFile, File untarDir,
989945
}
990946
try (TarArchiveInputStream tis = new TarArchiveInputStream(inputStream)) {
991947
for (TarArchiveEntry entry = tis.getNextTarEntry(); entry != null; ) {
992-
unpackEntries(tis, entry, untarDir);
948+
unpackEntries(tis, entry, untarDir, base, symlinksDisabled);
993949
entry = tis.getNextTarEntry();
994950
}
995951
}
@@ -1001,35 +957,82 @@ private static void unTarUsingJava(File inFile, File untarDir,
1001957
}
1002958

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

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

10351038
public static boolean isOnWindows() {
@@ -1043,16 +1046,21 @@ public static boolean isAbsolutePath(String path) {
10431046
return Paths.get(path).isAbsolute();
10441047
}
10451048

1046-
public static void unpack(File localrsrc, File dst) throws IOException {
1049+
public static void unpack(File localrsrc, File dst, boolean symLinksDisabled) throws IOException {
10471050
String lowerDst = localrsrc.getName().toLowerCase();
1048-
if (lowerDst.endsWith(".jar")) {
1051+
if (lowerDst.endsWith(".jar") ||
1052+
lowerDst.endsWith("_jar")) {
10491053
unJar(localrsrc, dst);
1050-
} else if (lowerDst.endsWith(".zip")) {
1054+
} else if (lowerDst.endsWith(".zip") ||
1055+
lowerDst.endsWith("_zip")) {
10511056
unZip(localrsrc, dst);
10521057
} else if (lowerDst.endsWith(".tar.gz") ||
1053-
lowerDst.endsWith(".tgz") ||
1054-
lowerDst.endsWith(".tar")) {
1055-
unTar(localrsrc, dst);
1058+
lowerDst.endsWith("_tar_gz") ||
1059+
lowerDst.endsWith(".tgz") ||
1060+
lowerDst.endsWith("_tgz") ||
1061+
lowerDst.endsWith(".tar") ||
1062+
lowerDst.endsWith("_tar")) {
1063+
unTar(localrsrc, dst, symLinksDisabled);
10561064
} else {
10571065
LOG.warn("Cannot unpack " + localrsrc);
10581066
if (!localrsrc.renameTo(dst)) {
@@ -1065,6 +1073,35 @@ public static void unpack(File localrsrc, File dst) throws IOException {
10651073
}
10661074
}
10671075

1076+
private static void extractZipFile(ZipFile zipFile, File toDir, String prefix) throws IOException {
1077+
ensureDirectory(toDir);
1078+
final String base = toDir.getCanonicalPath();
1079+
1080+
Enumeration<? extends ZipEntry> entries = zipFile.entries();
1081+
while (entries.hasMoreElements()) {
1082+
ZipEntry entry = entries.nextElement();
1083+
if (!entry.isDirectory()) {
1084+
if (prefix != null && !entry.getName().startsWith(prefix)) {
1085+
//No need to extract it, it is not what we are looking for.
1086+
continue;
1087+
}
1088+
File file = new File(toDir, entry.getName());
1089+
String found = file.getCanonicalPath();
1090+
if (!found.startsWith(base)) {
1091+
LOG.error("Invalid location {} is outside of {}", found, base);
1092+
continue;
1093+
}
1094+
1095+
try (InputStream in = zipFile.getInputStream(entry)) {
1096+
ensureDirectory(file.getParentFile());
1097+
try (OutputStream out = new FileOutputStream(file)) {
1098+
IOUtils.copy(in, out);
1099+
}
1100+
}
1101+
}
1102+
}
1103+
}
1104+
10681105
public static boolean canUserReadBlob(ReadableBlobMeta meta, String user) {
10691106
SettableBlobMeta settable = meta.get_settable();
10701107
for (AccessControl acl : settable.get_acl()) {
@@ -1374,45 +1411,12 @@ public static void handleUncaughtException(Throwable t) {
13741411
* Given a File input it will unzip the file in a the unzip directory
13751412
* passed as the second parameter
13761413
* @param inFile The zip file as input
1377-
* @param unzipDir The unzip directory where to unzip the zip file.
1414+
* @param toDir The unzip directory where to unzip the zip file.
13781415
* @throws IOException
13791416
*/
1380-
public static void unZip(File inFile, File unzipDir) throws IOException {
1381-
Enumeration<? extends ZipEntry> entries;
1382-
ZipFile zipFile = new ZipFile(inFile);
1383-
1384-
try {
1385-
entries = zipFile.entries();
1386-
while (entries.hasMoreElements()) {
1387-
ZipEntry entry = entries.nextElement();
1388-
if (!entry.isDirectory()) {
1389-
InputStream in = zipFile.getInputStream(entry);
1390-
try {
1391-
File file = new File(unzipDir, entry.getName());
1392-
if (!file.getParentFile().mkdirs()) {
1393-
if (!file.getParentFile().isDirectory()) {
1394-
throw new IOException("Mkdirs failed to create " +
1395-
file.getParentFile().toString());
1396-
}
1397-
}
1398-
OutputStream out = new FileOutputStream(file);
1399-
try {
1400-
byte[] buffer = new byte[8192];
1401-
int i;
1402-
while ((i = in.read(buffer)) != -1) {
1403-
out.write(buffer, 0, i);
1404-
}
1405-
} finally {
1406-
out.close();
1407-
}
1408-
} finally {
1409-
in.close();
1410-
}
1411-
}
1412-
}
1413-
} finally {
1414-
zipFile.close();
1415-
}
1417+
public static void unZip(File inFile, File toDir) throws IOException {
1418+
try (ZipFile zipFile = new ZipFile(inFile)) {
1419+
extractZipFile(zipFile, toDir, null); }
14161420
}
14171421

14181422
/**
@@ -1882,21 +1886,10 @@ public static int execCommand(String... command) throws ExecuteException, IOExce
18821886
public static void extractDirFromJar(String jarpath, String dir, File destdir) {
18831887
_instance.extractDirFromJarImpl(jarpath, dir, destdir);
18841888
}
1885-
1889+
18861890
public void extractDirFromJarImpl(String jarpath, String dir, File destdir) {
18871891
try (JarFile jarFile = new JarFile(jarpath)) {
1888-
Enumeration<JarEntry> jarEnums = jarFile.entries();
1889-
while (jarEnums.hasMoreElements()) {
1890-
JarEntry entry = jarEnums.nextElement();
1891-
if (!entry.isDirectory() && entry.getName().startsWith(dir)) {
1892-
File aFile = new File(destdir, entry.getName());
1893-
aFile.getParentFile().mkdirs();
1894-
try (FileOutputStream out = new FileOutputStream(aFile);
1895-
InputStream in = jarFile.getInputStream(entry)) {
1896-
IOUtils.copy(in, out);
1897-
}
1898-
}
1899-
}
1892+
extractZipFile(jarFile, destdir, dir);
19001893
} catch (IOException e) {
19011894
LOG.info("Could not extract {} from {}", dir, jarpath);
19021895
}

0 commit comments

Comments
 (0)