Skip to content

Commit 1ee93f7

Browse files
smallzhongfengsteveloughran
authored andcommitted
HADOOP-18145. Fileutil's unzip method causes unzipped files to lose their original permissions (#4036)
Contributed by jingxiong zhong Change-Id: Ie252e609798719dc658364f9bae48b34dc72a79c
1 parent 4f85c9a commit 1ee93f7

File tree

2 files changed

+127
-29
lines changed

2 files changed

+127
-29
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileUtil.java

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@
3636
import java.nio.charset.CharsetEncoder;
3737
import java.nio.charset.StandardCharsets;
3838
import java.nio.file.AccessDeniedException;
39+
import java.nio.file.attribute.PosixFilePermission;
3940
import java.nio.file.FileSystems;
4041
import java.nio.file.Files;
4142
import java.nio.file.Paths;
4243
import java.util.ArrayList;
4344
import java.util.Enumeration;
45+
import java.util.EnumSet;
4446
import java.util.List;
4547
import java.util.Map;
4648
import java.util.Objects;
49+
import java.util.Set;
4750
import java.util.concurrent.ExecutionException;
4851
import java.util.concurrent.ExecutorService;
4952
import java.util.concurrent.Executors;
@@ -52,13 +55,14 @@
5255
import java.util.jar.JarOutputStream;
5356
import java.util.jar.Manifest;
5457
import java.util.zip.GZIPInputStream;
55-
import java.util.zip.ZipEntry;
56-
import java.util.zip.ZipFile;
57-
import java.util.zip.ZipInputStream;
5858

5959
import org.apache.commons.collections.map.CaseInsensitiveMap;
6060
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
6161
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
62+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
63+
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
64+
import org.apache.commons.compress.archivers.zip.ZipFile;
65+
import org.apache.commons.io.FileUtils;
6266
import org.apache.hadoop.classification.InterfaceAudience;
6367
import org.apache.hadoop.classification.InterfaceStability;
6468
import org.apache.hadoop.conf.Configuration;
@@ -622,12 +626,12 @@ public static long getDU(File dir) {
622626
*/
623627
public static void unZip(InputStream inputStream, File toDir)
624628
throws IOException {
625-
try (ZipInputStream zip = new ZipInputStream(inputStream)) {
629+
try (ZipArchiveInputStream zip = new ZipArchiveInputStream(inputStream)) {
626630
int numOfFailedLastModifiedSet = 0;
627631
String targetDirPath = toDir.getCanonicalPath() + File.separator;
628-
for(ZipEntry entry = zip.getNextEntry();
632+
for(ZipArchiveEntry entry = zip.getNextZipEntry();
629633
entry != null;
630-
entry = zip.getNextEntry()) {
634+
entry = zip.getNextZipEntry()) {
631635
if (!entry.isDirectory()) {
632636
File file = new File(toDir, entry.getName());
633637
if (!file.getCanonicalPath().startsWith(targetDirPath)) {
@@ -646,6 +650,9 @@ public static void unZip(InputStream inputStream, File toDir)
646650
if (!file.setLastModified(entry.getTime())) {
647651
numOfFailedLastModifiedSet++;
648652
}
653+
if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
654+
Files.setPosixFilePermissions(file.toPath(), permissionsFromMode(entry.getUnixMode()));
655+
}
649656
}
650657
}
651658
if (numOfFailedLastModifiedSet > 0) {
@@ -655,6 +662,49 @@ public static void unZip(InputStream inputStream, File toDir)
655662
}
656663
}
657664

665+
/**
666+
* The permission operation of this method only involves users, user groups, and others.
667+
* If SUID is set, only executable permissions are reserved.
668+
* @param mode Permissions are represented by numerical values
669+
* @return The original permissions for files are stored in collections
670+
*/
671+
private static Set<PosixFilePermission> permissionsFromMode(int mode) {
672+
EnumSet<PosixFilePermission> permissions =
673+
EnumSet.noneOf(PosixFilePermission.class);
674+
addPermissions(permissions, mode, PosixFilePermission.OTHERS_READ,
675+
PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE);
676+
addPermissions(permissions, mode >> 3, PosixFilePermission.GROUP_READ,
677+
PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE);
678+
addPermissions(permissions, mode >> 6, PosixFilePermission.OWNER_READ,
679+
PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE);
680+
return permissions;
681+
}
682+
683+
/**
684+
* Assign the original permissions to the file
685+
* @param permissions The original permissions for files are stored in collections
686+
* @param mode Use a value of type int to indicate permissions
687+
* @param r Read permission
688+
* @param w Write permission
689+
* @param x Execute permission
690+
*/
691+
private static void addPermissions(
692+
Set<PosixFilePermission> permissions,
693+
int mode,
694+
PosixFilePermission r,
695+
PosixFilePermission w,
696+
PosixFilePermission x) {
697+
if ((mode & 1L) == 1L) {
698+
permissions.add(x);
699+
}
700+
if ((mode & 2L) == 2L) {
701+
permissions.add(w);
702+
}
703+
if ((mode & 4L) == 4L) {
704+
permissions.add(r);
705+
}
706+
}
707+
658708
/**
659709
* Given a File input it will unzip it in the unzip directory.
660710
* passed as the second parameter
@@ -663,14 +713,14 @@ public static void unZip(InputStream inputStream, File toDir)
663713
* @throws IOException An I/O exception has occurred
664714
*/
665715
public static void unZip(File inFile, File unzipDir) throws IOException {
666-
Enumeration<? extends ZipEntry> entries;
716+
Enumeration<? extends ZipArchiveEntry> entries;
667717
ZipFile zipFile = new ZipFile(inFile);
668718

669719
try {
670-
entries = zipFile.entries();
720+
entries = zipFile.getEntries();
671721
String targetDirPath = unzipDir.getCanonicalPath() + File.separator;
672722
while (entries.hasMoreElements()) {
673-
ZipEntry entry = entries.nextElement();
723+
ZipArchiveEntry entry = entries.nextElement();
674724
if (!entry.isDirectory()) {
675725
InputStream in = zipFile.getInputStream(entry);
676726
try {
@@ -695,6 +745,9 @@ public static void unZip(File inFile, File unzipDir) throws IOException {
695745
} finally {
696746
out.close();
697747
}
748+
if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
749+
Files.setPosixFilePermissions(file.toPath(), permissionsFromMode(entry.getUnixMode()));
750+
}
698751
} finally {
699752
in.close();
700753
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileUtil.java

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@
5353
import java.util.jar.Attributes;
5454
import java.util.jar.JarFile;
5555
import java.util.jar.Manifest;
56-
import java.util.zip.ZipEntry;
57-
import java.util.zip.ZipOutputStream;
5856

5957
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
6058
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
59+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
60+
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
6161
import org.apache.commons.io.FileUtils;
6262
import org.apache.hadoop.conf.Configuration;
6363
import org.apache.hadoop.test.GenericTestUtils;
@@ -744,26 +744,71 @@ public void testCreateLocalTempFile() throws IOException {
744744
public void testUnZip() throws Exception {
745745
// make sa simple zip
746746
final File simpleZip = new File(del, FILE);
747-
OutputStream os = new FileOutputStream(simpleZip);
748-
ZipOutputStream tos = new ZipOutputStream(os);
749-
try {
750-
ZipEntry ze = new ZipEntry("foo");
751-
byte[] data = "some-content".getBytes("UTF-8");
752-
ze.setSize(data.length);
753-
tos.putNextEntry(ze);
754-
tos.write(data);
755-
tos.closeEntry();
747+
try (OutputStream os = new FileOutputStream(simpleZip);
748+
ZipArchiveOutputStream tos = new ZipArchiveOutputStream(os)) {
749+
List<ZipArchiveEntry> ZipArchiveList = new ArrayList<>(7);
750+
int count = 0;
751+
// create 7 files to verify permissions
752+
for (int i = 0; i < 7; i++) {
753+
ZipArchiveList.add(new ZipArchiveEntry("foo_" + i));
754+
ZipArchiveEntry archiveEntry = ZipArchiveList.get(i);
755+
archiveEntry.setUnixMode(count += 0100);
756+
byte[] data = "some-content".getBytes("UTF-8");
757+
archiveEntry.setSize(data.length);
758+
tos.putArchiveEntry(archiveEntry);
759+
tos.write(data);
760+
}
761+
tos.closeArchiveEntry();
756762
tos.flush();
757763
tos.finish();
758-
} finally {
759-
tos.close();
760764
}
761-
765+
762766
// successfully unzip it into an existing dir:
763767
FileUtil.unZip(simpleZip, tmp);
768+
File foo0 = new File(tmp, "foo_0");
769+
File foo1 = new File(tmp, "foo_1");
770+
File foo2 = new File(tmp, "foo_2");
771+
File foo3 = new File(tmp, "foo_3");
772+
File foo4 = new File(tmp, "foo_4");
773+
File foo5 = new File(tmp, "foo_5");
774+
File foo6 = new File(tmp, "foo_6");
764775
// check result:
765-
Verify.exists(new File(tmp, "foo"));
766-
assertEquals(12, new File(tmp, "foo").length());
776+
assertTrue(foo0.exists());
777+
assertTrue(foo1.exists());
778+
assertTrue(foo2.exists());
779+
assertTrue(foo3.exists());
780+
assertTrue(foo4.exists());
781+
assertTrue(foo5.exists());
782+
assertTrue(foo6.exists());
783+
assertEquals(12, foo0.length());
784+
// tests whether file foo_0 has executable permissions
785+
assertTrue("file lacks execute permissions", foo0.canExecute());
786+
assertFalse("file has write permissions", foo0.canWrite());
787+
assertFalse("file has read permissions", foo0.canRead());
788+
// tests whether file foo_1 has writable permissions
789+
assertFalse("file has execute permissions", foo1.canExecute());
790+
assertTrue("file lacks write permissions", foo1.canWrite());
791+
assertFalse("file has read permissions", foo1.canRead());
792+
// tests whether file foo_2 has executable and writable permissions
793+
assertTrue("file lacks execute permissions", foo2.canExecute());
794+
assertTrue("file lacks write permissions", foo2.canWrite());
795+
assertFalse("file has read permissions", foo2.canRead());
796+
// tests whether file foo_3 has readable permissions
797+
assertFalse("file has execute permissions", foo3.canExecute());
798+
assertFalse("file has write permissions", foo3.canWrite());
799+
assertTrue("file lacks read permissions", foo3.canRead());
800+
// tests whether file foo_4 has readable and executable permissions
801+
assertTrue("file lacks execute permissions", foo4.canExecute());
802+
assertFalse("file has write permissions", foo4.canWrite());
803+
assertTrue("file lacks read permissions", foo4.canRead());
804+
// tests whether file foo_5 has readable and writable permissions
805+
assertFalse("file has execute permissions", foo5.canExecute());
806+
assertTrue("file lacks write permissions", foo5.canWrite());
807+
assertTrue("file lacks read permissions", foo5.canRead());
808+
// tests whether file foo_6 has readable, writable and executable permissions
809+
assertTrue("file lacks execute permissions", foo6.canExecute());
810+
assertTrue("file lacks write permissions", foo6.canWrite());
811+
assertTrue("file lacks read permissions", foo6.canRead());
767812

768813
final File regularFile =
769814
Verify.createNewFile(new File(tmp, "QuickBrownFoxJumpsOverTheLazyDog"));
@@ -775,14 +820,14 @@ public void testUnZip2() throws IOException {
775820
// make a simple zip
776821
final File simpleZip = new File(del, FILE);
777822
OutputStream os = new FileOutputStream(simpleZip);
778-
try (ZipOutputStream tos = new ZipOutputStream(os)) {
823+
try (ZipArchiveOutputStream tos = new ZipArchiveOutputStream(os)) {
779824
// Add an entry that contains invalid filename
780-
ZipEntry ze = new ZipEntry("../foo");
825+
ZipArchiveEntry ze = new ZipArchiveEntry("../foo");
781826
byte[] data = "some-content".getBytes(StandardCharsets.UTF_8);
782827
ze.setSize(data.length);
783-
tos.putNextEntry(ze);
828+
tos.putArchiveEntry(ze);
784829
tos.write(data);
785-
tos.closeEntry();
830+
tos.closeArchiveEntry();
786831
tos.flush();
787832
tos.finish();
788833
}

0 commit comments

Comments
 (0)