Skip to content

Commit e98b920

Browse files
smallzhongfengHarshitGupta11
authored andcommitted
HADOOP-18145. Fileutil's unzip method causes unzipped files to lose their original permissions (apache#4036)
Contributed by jingxiong zhong
1 parent 992eda5 commit e98b920

File tree

2 files changed

+126
-29
lines changed

2 files changed

+126
-29
lines changed

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

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,18 @@
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.LinkOption;
4243
import java.nio.file.Paths;
4344
import java.util.ArrayList;
4445
import java.util.Enumeration;
46+
import java.util.EnumSet;
4547
import java.util.List;
4648
import java.util.Map;
4749
import java.util.Objects;
50+
import java.util.Set;
4851
import java.util.concurrent.ExecutionException;
4952
import java.util.concurrent.ExecutorService;
5053
import java.util.concurrent.Executors;
@@ -53,13 +56,13 @@
5356
import java.util.jar.JarOutputStream;
5457
import java.util.jar.Manifest;
5558
import java.util.zip.GZIPInputStream;
56-
import java.util.zip.ZipEntry;
57-
import java.util.zip.ZipFile;
58-
import java.util.zip.ZipInputStream;
5959

6060
import org.apache.commons.collections.map.CaseInsensitiveMap;
6161
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
6262
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
63+
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
64+
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
65+
import org.apache.commons.compress.archivers.zip.ZipFile;
6366
import org.apache.commons.io.FileUtils;
6467
import org.apache.hadoop.classification.InterfaceAudience;
6568
import org.apache.hadoop.classification.InterfaceStability;
@@ -644,12 +647,12 @@ public static long getDU(File dir) {
644647
*/
645648
public static void unZip(InputStream inputStream, File toDir)
646649
throws IOException {
647-
try (ZipInputStream zip = new ZipInputStream(inputStream)) {
650+
try (ZipArchiveInputStream zip = new ZipArchiveInputStream(inputStream)) {
648651
int numOfFailedLastModifiedSet = 0;
649652
String targetDirPath = toDir.getCanonicalPath() + File.separator;
650-
for(ZipEntry entry = zip.getNextEntry();
653+
for(ZipArchiveEntry entry = zip.getNextZipEntry();
651654
entry != null;
652-
entry = zip.getNextEntry()) {
655+
entry = zip.getNextZipEntry()) {
653656
if (!entry.isDirectory()) {
654657
File file = new File(toDir, entry.getName());
655658
if (!file.getCanonicalPath().startsWith(targetDirPath)) {
@@ -668,6 +671,9 @@ public static void unZip(InputStream inputStream, File toDir)
668671
if (!file.setLastModified(entry.getTime())) {
669672
numOfFailedLastModifiedSet++;
670673
}
674+
if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
675+
Files.setPosixFilePermissions(file.toPath(), permissionsFromMode(entry.getUnixMode()));
676+
}
671677
}
672678
}
673679
if (numOfFailedLastModifiedSet > 0) {
@@ -677,6 +683,49 @@ public static void unZip(InputStream inputStream, File toDir)
677683
}
678684
}
679685

686+
/**
687+
* The permission operation of this method only involves users, user groups, and others.
688+
* If SUID is set, only executable permissions are reserved.
689+
* @param mode Permissions are represented by numerical values
690+
* @return The original permissions for files are stored in collections
691+
*/
692+
private static Set<PosixFilePermission> permissionsFromMode(int mode) {
693+
EnumSet<PosixFilePermission> permissions =
694+
EnumSet.noneOf(PosixFilePermission.class);
695+
addPermissions(permissions, mode, PosixFilePermission.OTHERS_READ,
696+
PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE);
697+
addPermissions(permissions, mode >> 3, PosixFilePermission.GROUP_READ,
698+
PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE);
699+
addPermissions(permissions, mode >> 6, PosixFilePermission.OWNER_READ,
700+
PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE);
701+
return permissions;
702+
}
703+
704+
/**
705+
* Assign the original permissions to the file
706+
* @param permissions The original permissions for files are stored in collections
707+
* @param mode Use a value of type int to indicate permissions
708+
* @param r Read permission
709+
* @param w Write permission
710+
* @param x Execute permission
711+
*/
712+
private static void addPermissions(
713+
Set<PosixFilePermission> permissions,
714+
int mode,
715+
PosixFilePermission r,
716+
PosixFilePermission w,
717+
PosixFilePermission x) {
718+
if ((mode & 1L) == 1L) {
719+
permissions.add(x);
720+
}
721+
if ((mode & 2L) == 2L) {
722+
permissions.add(w);
723+
}
724+
if ((mode & 4L) == 4L) {
725+
permissions.add(r);
726+
}
727+
}
728+
680729
/**
681730
* Given a File input it will unzip it in the unzip directory.
682731
* passed as the second parameter
@@ -685,14 +734,14 @@ public static void unZip(InputStream inputStream, File toDir)
685734
* @throws IOException An I/O exception has occurred
686735
*/
687736
public static void unZip(File inFile, File unzipDir) throws IOException {
688-
Enumeration<? extends ZipEntry> entries;
737+
Enumeration<? extends ZipArchiveEntry> entries;
689738
ZipFile zipFile = new ZipFile(inFile);
690739

691740
try {
692-
entries = zipFile.entries();
741+
entries = zipFile.getEntries();
693742
String targetDirPath = unzipDir.getCanonicalPath() + File.separator;
694743
while (entries.hasMoreElements()) {
695-
ZipEntry entry = entries.nextElement();
744+
ZipArchiveEntry entry = entries.nextElement();
696745
if (!entry.isDirectory()) {
697746
InputStream in = zipFile.getInputStream(entry);
698747
try {
@@ -717,6 +766,9 @@ public static void unZip(File inFile, File unzipDir) throws IOException {
717766
} finally {
718767
out.close();
719768
}
769+
if (entry.getPlatform() == ZipArchiveEntry.PLATFORM_UNIX) {
770+
Files.setPosixFilePermissions(file.toPath(), permissionsFromMode(entry.getUnixMode()));
771+
}
720772
} finally {
721773
in.close();
722774
}

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;
@@ -773,26 +773,71 @@ public void testCreateLocalTempFile() throws IOException {
773773
public void testUnZip() throws Exception {
774774
// make sa simple zip
775775
final File simpleZip = new File(del, FILE);
776-
OutputStream os = new FileOutputStream(simpleZip);
777-
ZipOutputStream tos = new ZipOutputStream(os);
778-
try {
779-
ZipEntry ze = new ZipEntry("foo");
780-
byte[] data = "some-content".getBytes("UTF-8");
781-
ze.setSize(data.length);
782-
tos.putNextEntry(ze);
783-
tos.write(data);
784-
tos.closeEntry();
776+
try (OutputStream os = new FileOutputStream(simpleZip);
777+
ZipArchiveOutputStream tos = new ZipArchiveOutputStream(os)) {
778+
List<ZipArchiveEntry> ZipArchiveList = new ArrayList<>(7);
779+
int count = 0;
780+
// create 7 files to verify permissions
781+
for (int i = 0; i < 7; i++) {
782+
ZipArchiveList.add(new ZipArchiveEntry("foo_" + i));
783+
ZipArchiveEntry archiveEntry = ZipArchiveList.get(i);
784+
archiveEntry.setUnixMode(count += 0100);
785+
byte[] data = "some-content".getBytes("UTF-8");
786+
archiveEntry.setSize(data.length);
787+
tos.putArchiveEntry(archiveEntry);
788+
tos.write(data);
789+
}
790+
tos.closeArchiveEntry();
785791
tos.flush();
786792
tos.finish();
787-
} finally {
788-
tos.close();
789793
}
790-
794+
791795
// successfully unzip it into an existing dir:
792796
FileUtil.unZip(simpleZip, tmp);
797+
File foo0 = new File(tmp, "foo_0");
798+
File foo1 = new File(tmp, "foo_1");
799+
File foo2 = new File(tmp, "foo_2");
800+
File foo3 = new File(tmp, "foo_3");
801+
File foo4 = new File(tmp, "foo_4");
802+
File foo5 = new File(tmp, "foo_5");
803+
File foo6 = new File(tmp, "foo_6");
793804
// check result:
794-
Verify.exists(new File(tmp, "foo"));
795-
assertEquals(12, new File(tmp, "foo").length());
805+
assertTrue(foo0.exists());
806+
assertTrue(foo1.exists());
807+
assertTrue(foo2.exists());
808+
assertTrue(foo3.exists());
809+
assertTrue(foo4.exists());
810+
assertTrue(foo5.exists());
811+
assertTrue(foo6.exists());
812+
assertEquals(12, foo0.length());
813+
// tests whether file foo_0 has executable permissions
814+
assertTrue("file lacks execute permissions", foo0.canExecute());
815+
assertFalse("file has write permissions", foo0.canWrite());
816+
assertFalse("file has read permissions", foo0.canRead());
817+
// tests whether file foo_1 has writable permissions
818+
assertFalse("file has execute permissions", foo1.canExecute());
819+
assertTrue("file lacks write permissions", foo1.canWrite());
820+
assertFalse("file has read permissions", foo1.canRead());
821+
// tests whether file foo_2 has executable and writable permissions
822+
assertTrue("file lacks execute permissions", foo2.canExecute());
823+
assertTrue("file lacks write permissions", foo2.canWrite());
824+
assertFalse("file has read permissions", foo2.canRead());
825+
// tests whether file foo_3 has readable permissions
826+
assertFalse("file has execute permissions", foo3.canExecute());
827+
assertFalse("file has write permissions", foo3.canWrite());
828+
assertTrue("file lacks read permissions", foo3.canRead());
829+
// tests whether file foo_4 has readable and executable permissions
830+
assertTrue("file lacks execute permissions", foo4.canExecute());
831+
assertFalse("file has write permissions", foo4.canWrite());
832+
assertTrue("file lacks read permissions", foo4.canRead());
833+
// tests whether file foo_5 has readable and writable permissions
834+
assertFalse("file has execute permissions", foo5.canExecute());
835+
assertTrue("file lacks write permissions", foo5.canWrite());
836+
assertTrue("file lacks read permissions", foo5.canRead());
837+
// tests whether file foo_6 has readable, writable and executable permissions
838+
assertTrue("file lacks execute permissions", foo6.canExecute());
839+
assertTrue("file lacks write permissions", foo6.canWrite());
840+
assertTrue("file lacks read permissions", foo6.canRead());
796841

797842
final File regularFile =
798843
Verify.createNewFile(new File(tmp, "QuickBrownFoxJumpsOverTheLazyDog"));
@@ -804,14 +849,14 @@ public void testUnZip2() throws IOException {
804849
// make a simple zip
805850
final File simpleZip = new File(del, FILE);
806851
OutputStream os = new FileOutputStream(simpleZip);
807-
try (ZipOutputStream tos = new ZipOutputStream(os)) {
852+
try (ZipArchiveOutputStream tos = new ZipArchiveOutputStream(os)) {
808853
// Add an entry that contains invalid filename
809-
ZipEntry ze = new ZipEntry("../foo");
854+
ZipArchiveEntry ze = new ZipArchiveEntry("../foo");
810855
byte[] data = "some-content".getBytes(StandardCharsets.UTF_8);
811856
ze.setSize(data.length);
812-
tos.putNextEntry(ze);
857+
tos.putArchiveEntry(ze);
813858
tos.write(data);
814-
tos.closeEntry();
859+
tos.closeArchiveEntry();
815860
tos.flush();
816861
tos.finish();
817862
}

0 commit comments

Comments
 (0)