Merge pull request #20 from zamblauskas/preserve_file_permissions

Read and preserve actual file permissions mode in `TarHeader`.
This commit is contained in:
Kamran 2015-09-06 23:18:07 +01:00
commit 70d0b56fce
4 changed files with 116 additions and 6 deletions

View File

@ -0,0 +1,109 @@
package org.kamranzafar.jtar;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Helps dealing with file permissions.
*/
public class PermissionUtils {
/**
* XXX: When using standard Java permissions, we treat 'owner' and 'group' equally and give no
* permissions for 'others'.
*/
private static enum StandardFilePermission {
EXECUTE(0110), WRITE(0220), READ(0440);
private int mode;
private StandardFilePermission(int mode) {
this.mode = mode;
}
}
private static Map<PosixFilePermission, Integer> posixPermissionToInteger = new HashMap<>();
static {
posixPermissionToInteger.put(PosixFilePermission.OWNER_EXECUTE, 0100);
posixPermissionToInteger.put(PosixFilePermission.OWNER_WRITE, 0200);
posixPermissionToInteger.put(PosixFilePermission.OWNER_READ, 0400);
posixPermissionToInteger.put(PosixFilePermission.GROUP_EXECUTE, 0010);
posixPermissionToInteger.put(PosixFilePermission.GROUP_WRITE, 0020);
posixPermissionToInteger.put(PosixFilePermission.GROUP_READ, 0040);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_EXECUTE, 0001);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_WRITE, 0002);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_READ, 0004);
}
/**
* Get file permissions in octal mode, e.g. 0755.
*
* Note: it uses `java.nio.file.attribute.PosixFilePermission` if OS supports this, otherwise reverts to
* using standard Java file operations, e.g. `java.io.File#canExecute()`. In the first case permissions will
* be precisely as reported by the OS, in the second case 'owner' and 'group' will have equal permissions and
* 'others' will have no permissions, e.g. if file on Windows OS is `read-only` permissions will be `0550`.
*
* @throws NullPointerException if file is null.
* @throws IllegalArgumentException if file does not exist.
*/
public static int permissions(File f) {
if(f == null) {
throw new NullPointerException("File is null.");
}
if(!f.exists()) {
throw new IllegalArgumentException("File " + f + " does not exist.");
}
return isPosix ? posixPermissions(f) : standardPermissions(f);
}
private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
private static int posixPermissions(File f) {
int number = 0;
try {
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(f.toPath());
for (Map.Entry<PosixFilePermission, Integer> entry : posixPermissionToInteger.entrySet()) {
if (permissions.contains(entry.getKey())) {
number += entry.getValue();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return number;
}
private static Set<StandardFilePermission> readStandardPermissions(File f) {
Set<StandardFilePermission> permissions = new HashSet<>();
if(f.canExecute()) {
permissions.add(StandardFilePermission.EXECUTE);
}
if(f.canWrite()) {
permissions.add(StandardFilePermission.WRITE);
}
if(f.canRead()) {
permissions.add(StandardFilePermission.READ);
}
return permissions;
}
private static Integer standardPermissions(File f) {
int number = 0;
Set<StandardFilePermission> permissions = readStandardPermissions(f);
for (StandardFilePermission permission : permissions) {
number += permission.mode;
}
return number;
}
}

View File

@ -170,7 +170,8 @@ public class TarEntry {
* @param entryName
*/
public void extractTarHeader(String entryName) {
header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory());
int permissions = PermissionUtils.permissions(file);
header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory(), permissions);
}
/**

View File

@ -206,12 +206,13 @@ public class TarHeader {
*
* @return
*/
public static TarHeader createHeader(String entryName, long size, long modTime, boolean dir) {
public static TarHeader createHeader(String entryName, long size, long modTime, boolean dir, int permissions) {
String name = entryName;
name = TarUtils.trim(name.replace(File.separatorChar, '/'), '/');
TarHeader header = new TarHeader();
header.linkName = new StringBuffer("");
header.mode = permissions;
if (name.length() > 100) {
header.namePrefix = new StringBuffer(name.substring(0, name.lastIndexOf('/')));
@ -219,16 +220,13 @@ public class TarHeader {
} else {
header.name = new StringBuffer(name);
}
if (dir) {
header.mode = 040755;
header.linkFlag = TarHeader.LF_DIR;
if (header.name.charAt(header.name.length() - 1) != '/') {
header.name.append("/");
}
header.size = 0;
} else {
header.mode = 0100644;
header.linkFlag = TarHeader.LF_NORMAL;
header.size = size;
}

View File

@ -245,13 +245,15 @@ public class JTarTest {
String fileName = "file.txt";
long fileSize = 14523;
long modTime = System.currentTimeMillis() / 1000;
int permissions = 0755;
// Create a header object and check the fields
TarHeader fileHeader = TarHeader.createHeader(fileName, fileSize, modTime, false);
TarHeader fileHeader = TarHeader.createHeader(fileName, fileSize, modTime, false, permissions);
assertEquals(fileName, fileHeader.name.toString());
assertEquals(TarHeader.LF_NORMAL, fileHeader.linkFlag);
assertEquals(fileSize, fileHeader.size);
assertEquals(modTime, fileHeader.modTime);
assertEquals(permissions, fileHeader.mode);
// Create an entry from the header
TarEntry fileEntry = new TarEntry(fileHeader);