Formating

This commit is contained in:
topjohnwu 2019-04-01 23:47:22 -04:00
parent 4662a4286b
commit 219e8b25f1
8 changed files with 1264 additions and 1270 deletions

View File

@ -15,95 +15,95 @@ import java.util.Set;
*/ */
public class PermissionUtils { public class PermissionUtils {
/** /**
* XXX: When using standard Java permissions, we treat 'owner' and 'group' equally and give no * XXX: When using standard Java permissions, we treat 'owner' and 'group' equally and give no
* permissions for 'others'. * permissions for 'others'.
*/ */
private static enum StandardFilePermission { private static enum StandardFilePermission {
EXECUTE(0110), WRITE(0220), READ(0440); EXECUTE(0110), WRITE(0220), READ(0440);
private int mode; private int mode;
private StandardFilePermission(int mode) { private StandardFilePermission(int mode) {
this.mode = mode; this.mode = mode;
} }
} }
private static Map<PosixFilePermission, Integer> posixPermissionToInteger = new HashMap<>(); private static Map<PosixFilePermission, Integer> posixPermissionToInteger = new HashMap<>();
static { static {
posixPermissionToInteger.put(PosixFilePermission.OWNER_EXECUTE, 0100); posixPermissionToInteger.put(PosixFilePermission.OWNER_EXECUTE, 0100);
posixPermissionToInteger.put(PosixFilePermission.OWNER_WRITE, 0200); posixPermissionToInteger.put(PosixFilePermission.OWNER_WRITE, 0200);
posixPermissionToInteger.put(PosixFilePermission.OWNER_READ, 0400); posixPermissionToInteger.put(PosixFilePermission.OWNER_READ, 0400);
posixPermissionToInteger.put(PosixFilePermission.GROUP_EXECUTE, 0010); posixPermissionToInteger.put(PosixFilePermission.GROUP_EXECUTE, 0010);
posixPermissionToInteger.put(PosixFilePermission.GROUP_WRITE, 0020); posixPermissionToInteger.put(PosixFilePermission.GROUP_WRITE, 0020);
posixPermissionToInteger.put(PosixFilePermission.GROUP_READ, 0040); posixPermissionToInteger.put(PosixFilePermission.GROUP_READ, 0040);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_EXECUTE, 0001); posixPermissionToInteger.put(PosixFilePermission.OTHERS_EXECUTE, 0001);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_WRITE, 0002); posixPermissionToInteger.put(PosixFilePermission.OTHERS_WRITE, 0002);
posixPermissionToInteger.put(PosixFilePermission.OTHERS_READ, 0004); posixPermissionToInteger.put(PosixFilePermission.OTHERS_READ, 0004);
} }
/** /**
* Get file permissions in octal mode, e.g. 0755. * Get file permissions in octal mode, e.g. 0755.
* *
* Note: it uses `java.nio.file.attribute.PosixFilePermission` if OS supports this, otherwise reverts to * 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 * 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 * 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`. * '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 NullPointerException if file is null.
* @throws IllegalArgumentException if file does not exist. * @throws IllegalArgumentException if file does not exist.
*/ */
public static int permissions(File f) { public static int permissions(File f) {
if(f == null) { if(f == null) {
throw new NullPointerException("File is null."); throw new NullPointerException("File is null.");
} }
if(!f.exists()) { if(!f.exists()) {
throw new IllegalArgumentException("File " + f + " does not exist."); throw new IllegalArgumentException("File " + f + " does not exist.");
} }
return isPosix ? posixPermissions(f) : standardPermissions(f); return isPosix ? posixPermissions(f) : standardPermissions(f);
} }
private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix"); private static final boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
private static int posixPermissions(File f) { private static int posixPermissions(File f) {
int number = 0; int number = 0;
try { try {
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(f.toPath()); Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(f.toPath());
for (Map.Entry<PosixFilePermission, Integer> entry : posixPermissionToInteger.entrySet()) { for (Map.Entry<PosixFilePermission, Integer> entry : posixPermissionToInteger.entrySet()) {
if (permissions.contains(entry.getKey())) { if (permissions.contains(entry.getKey())) {
number += entry.getValue(); number += entry.getValue();
} }
} }
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return number; return number;
} }
private static Set<StandardFilePermission> readStandardPermissions(File f) { private static Set<StandardFilePermission> readStandardPermissions(File f) {
Set<StandardFilePermission> permissions = new HashSet<>(); Set<StandardFilePermission> permissions = new HashSet<>();
if(f.canExecute()) { if(f.canExecute()) {
permissions.add(StandardFilePermission.EXECUTE); permissions.add(StandardFilePermission.EXECUTE);
} }
if(f.canWrite()) { if(f.canWrite()) {
permissions.add(StandardFilePermission.WRITE); permissions.add(StandardFilePermission.WRITE);
} }
if(f.canRead()) { if(f.canRead()) {
permissions.add(StandardFilePermission.READ); permissions.add(StandardFilePermission.READ);
} }
return permissions; return permissions;
} }
private static Integer standardPermissions(File f) { private static Integer standardPermissions(File f) {
int number = 0; int number = 0;
Set<StandardFilePermission> permissions = readStandardPermissions(f); Set<StandardFilePermission> permissions = readStandardPermissions(f);
for (StandardFilePermission permission : permissions) { for (StandardFilePermission permission : permissions) {
number += permission.mode; number += permission.mode;
} }
return number; return number;
} }
} }

View File

@ -25,256 +25,256 @@ import java.util.Date;
* *
*/ */
public class TarEntry { public class TarEntry {
protected File file; protected File file;
protected TarHeader header; protected TarHeader header;
private TarEntry() { private TarEntry() {
this.file = null; this.file = null;
header = new TarHeader(); header = new TarHeader();
} }
public TarEntry(File file, String entryName) { public TarEntry(File file, String entryName) {
this(); this();
this.file = file; this.file = file;
this.extractTarHeader(entryName); this.extractTarHeader(entryName);
} }
public TarEntry(byte[] headerBuf) { public TarEntry(byte[] headerBuf) {
this(); this();
this.parseTarHeader(headerBuf); this.parseTarHeader(headerBuf);
} }
/** /**
* Constructor to create an entry from an existing TarHeader object. * Constructor to create an entry from an existing TarHeader object.
* *
* This method is useful to add new entries programmatically (e.g. for * This method is useful to add new entries programmatically (e.g. for
* adding files or directories that do not exist in the file system). * adding files or directories that do not exist in the file system).
*/ */
public TarEntry(TarHeader header) { public TarEntry(TarHeader header) {
this.file = null; this.file = null;
this.header = header; this.header = header;
} }
@Override @Override
public boolean equals(Object it) { public boolean equals(Object it) {
if (!(it instanceof TarEntry)) { if (!(it instanceof TarEntry)) {
return false; return false;
} }
return header.name.toString().equals( return header.name.toString().equals(
((TarEntry) it).header.name.toString()); ((TarEntry) it).header.name.toString());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return header.name.hashCode(); return header.name.hashCode();
} }
public boolean isDescendent(TarEntry desc) { public boolean isDescendent(TarEntry desc) {
return desc.header.name.toString().startsWith(header.name.toString()); return desc.header.name.toString().startsWith(header.name.toString());
} }
public TarHeader getHeader() { public TarHeader getHeader() {
return header; return header;
} }
public String getName() { public String getName() {
String name = header.name.toString(); String name = header.name.toString();
if (header.namePrefix != null && !header.namePrefix.toString().equals("")) { if (header.namePrefix != null && !header.namePrefix.toString().equals("")) {
name = header.namePrefix.toString() + "/" + name; name = header.namePrefix.toString() + "/" + name;
} }
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
header.name = new StringBuffer(name); header.name = new StringBuffer(name);
} }
public int getUserId() { public int getUserId() {
return header.userId; return header.userId;
} }
public void setUserId(int userId) { public void setUserId(int userId) {
header.userId = userId; header.userId = userId;
} }
public int getGroupId() { public int getGroupId() {
return header.groupId; return header.groupId;
} }
public void setGroupId(int groupId) { public void setGroupId(int groupId) {
header.groupId = groupId; header.groupId = groupId;
} }
public String getUserName() { public String getUserName() {
return header.userName.toString(); return header.userName.toString();
} }
public void setUserName(String userName) { public void setUserName(String userName) {
header.userName = new StringBuffer(userName); header.userName = new StringBuffer(userName);
} }
public String getGroupName() { public String getGroupName() {
return header.groupName.toString(); return header.groupName.toString();
} }
public void setGroupName(String groupName) { public void setGroupName(String groupName) {
header.groupName = new StringBuffer(groupName); header.groupName = new StringBuffer(groupName);
} }
public void setIds(int userId, int groupId) { public void setIds(int userId, int groupId) {
this.setUserId(userId); this.setUserId(userId);
this.setGroupId(groupId); this.setGroupId(groupId);
} }
public void setModTime(long time) { public void setModTime(long time) {
header.modTime = time / 1000; header.modTime = time / 1000;
} }
public void setModTime(Date time) { public void setModTime(Date time) {
header.modTime = time.getTime() / 1000; header.modTime = time.getTime() / 1000;
} }
public Date getModTime() { public Date getModTime() {
return new Date(header.modTime * 1000); return new Date(header.modTime * 1000);
} }
public File getFile() { public File getFile() {
return this.file; return this.file;
} }
public long getSize() { public long getSize() {
return header.size; return header.size;
} }
public void setSize(long size) { public void setSize(long size) {
header.size = size; header.size = size;
} }
/** /**
* Checks if the org.kamrazafar.jtar entry is a directory * Checks if the org.kamrazafar.jtar entry is a directory
*/ */
public boolean isDirectory() { public boolean isDirectory() {
if (this.file != null) if (this.file != null)
return this.file.isDirectory(); return this.file.isDirectory();
if (header != null) { if (header != null) {
if (header.linkFlag == TarHeader.LF_DIR) if (header.linkFlag == TarHeader.LF_DIR)
return true; return true;
if (header.name.toString().endsWith("/")) if (header.name.toString().endsWith("/"))
return true; return true;
} }
return false; return false;
} }
/** /**
* Extract header from File * Extract header from File
*/ */
public void extractTarHeader(String entryName) { public void extractTarHeader(String entryName) {
int permissions = PermissionUtils.permissions(file); int permissions = PermissionUtils.permissions(file);
header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory(), permissions); header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory(), permissions);
} }
/** /**
* Calculate checksum * Calculate checksum
*/ */
public long computeCheckSum(byte[] buf) { public long computeCheckSum(byte[] buf) {
long sum = 0; long sum = 0;
for (int i = 0; i < buf.length; ++i) { for (int i = 0; i < buf.length; ++i) {
sum += 255 & buf[i]; sum += 255 & buf[i];
} }
return sum; return sum;
} }
/** /**
* Writes the header to the byte buffer * Writes the header to the byte buffer
*/ */
public void writeEntryHeader(byte[] outbuf) { public void writeEntryHeader(byte[] outbuf) {
int offset = 0; int offset = 0;
offset = TarHeader.getNameBytes(header.name, outbuf, offset, TarHeader.NAMELEN); offset = TarHeader.getNameBytes(header.name, outbuf, offset, TarHeader.NAMELEN);
offset = Octal.getOctalBytes(header.mode, outbuf, offset, TarHeader.MODELEN); offset = Octal.getOctalBytes(header.mode, outbuf, offset, TarHeader.MODELEN);
offset = Octal.getOctalBytes(header.userId, outbuf, offset, TarHeader.UIDLEN); offset = Octal.getOctalBytes(header.userId, outbuf, offset, TarHeader.UIDLEN);
offset = Octal.getOctalBytes(header.groupId, outbuf, offset, TarHeader.GIDLEN); offset = Octal.getOctalBytes(header.groupId, outbuf, offset, TarHeader.GIDLEN);
long size = header.size; long size = header.size;
offset = Octal.getLongOctalBytes(size, outbuf, offset, TarHeader.SIZELEN); offset = Octal.getLongOctalBytes(size, outbuf, offset, TarHeader.SIZELEN);
offset = Octal.getLongOctalBytes(header.modTime, outbuf, offset, TarHeader.MODTIMELEN); offset = Octal.getLongOctalBytes(header.modTime, outbuf, offset, TarHeader.MODTIMELEN);
int csOffset = offset; int csOffset = offset;
for (int c = 0; c < TarHeader.CHKSUMLEN; ++c) for (int c = 0; c < TarHeader.CHKSUMLEN; ++c)
outbuf[offset++] = (byte) ' '; outbuf[offset++] = (byte) ' ';
outbuf[offset++] = header.linkFlag; outbuf[offset++] = header.linkFlag;
offset = TarHeader.getNameBytes(header.linkName, outbuf, offset, TarHeader.NAMELEN); offset = TarHeader.getNameBytes(header.linkName, outbuf, offset, TarHeader.NAMELEN);
offset = TarHeader.getNameBytes(header.magic, outbuf, offset, TarHeader.USTAR_MAGICLEN); offset = TarHeader.getNameBytes(header.magic, outbuf, offset, TarHeader.USTAR_MAGICLEN);
offset = TarHeader.getNameBytes(header.userName, outbuf, offset, TarHeader.USTAR_USER_NAMELEN); offset = TarHeader.getNameBytes(header.userName, outbuf, offset, TarHeader.USTAR_USER_NAMELEN);
offset = TarHeader.getNameBytes(header.groupName, outbuf, offset, TarHeader.USTAR_GROUP_NAMELEN); offset = TarHeader.getNameBytes(header.groupName, outbuf, offset, TarHeader.USTAR_GROUP_NAMELEN);
offset = Octal.getOctalBytes(header.devMajor, outbuf, offset, TarHeader.USTAR_DEVLEN); offset = Octal.getOctalBytes(header.devMajor, outbuf, offset, TarHeader.USTAR_DEVLEN);
offset = Octal.getOctalBytes(header.devMinor, outbuf, offset, TarHeader.USTAR_DEVLEN); offset = Octal.getOctalBytes(header.devMinor, outbuf, offset, TarHeader.USTAR_DEVLEN);
offset = TarHeader.getNameBytes(header.namePrefix, outbuf, offset, TarHeader.USTAR_FILENAME_PREFIX); offset = TarHeader.getNameBytes(header.namePrefix, outbuf, offset, TarHeader.USTAR_FILENAME_PREFIX);
for (; offset < outbuf.length;) for (; offset < outbuf.length;)
outbuf[offset++] = 0; outbuf[offset++] = 0;
long checkSum = this.computeCheckSum(outbuf); long checkSum = this.computeCheckSum(outbuf);
Octal.getCheckSumOctalBytes(checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN); Octal.getCheckSumOctalBytes(checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN);
} }
/** /**
* Parses the tar header to the byte buffer * Parses the tar header to the byte buffer
*/ */
public void parseTarHeader(byte[] bh) { public void parseTarHeader(byte[] bh) {
int offset = 0; int offset = 0;
header.name = TarHeader.parseName(bh, offset, TarHeader.NAMELEN); header.name = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
offset += TarHeader.NAMELEN; offset += TarHeader.NAMELEN;
header.mode = (int) Octal.parseOctal(bh, offset, TarHeader.MODELEN); header.mode = (int) Octal.parseOctal(bh, offset, TarHeader.MODELEN);
offset += TarHeader.MODELEN; offset += TarHeader.MODELEN;
header.userId = (int) Octal.parseOctal(bh, offset, TarHeader.UIDLEN); header.userId = (int) Octal.parseOctal(bh, offset, TarHeader.UIDLEN);
offset += TarHeader.UIDLEN; offset += TarHeader.UIDLEN;
header.groupId = (int) Octal.parseOctal(bh, offset, TarHeader.GIDLEN); header.groupId = (int) Octal.parseOctal(bh, offset, TarHeader.GIDLEN);
offset += TarHeader.GIDLEN; offset += TarHeader.GIDLEN;
header.size = Octal.parseOctal(bh, offset, TarHeader.SIZELEN); header.size = Octal.parseOctal(bh, offset, TarHeader.SIZELEN);
offset += TarHeader.SIZELEN; offset += TarHeader.SIZELEN;
header.modTime = Octal.parseOctal(bh, offset, TarHeader.MODTIMELEN); header.modTime = Octal.parseOctal(bh, offset, TarHeader.MODTIMELEN);
offset += TarHeader.MODTIMELEN; offset += TarHeader.MODTIMELEN;
header.checkSum = (int) Octal.parseOctal(bh, offset, TarHeader.CHKSUMLEN); header.checkSum = (int) Octal.parseOctal(bh, offset, TarHeader.CHKSUMLEN);
offset += TarHeader.CHKSUMLEN; offset += TarHeader.CHKSUMLEN;
header.linkFlag = bh[offset++]; header.linkFlag = bh[offset++];
header.linkName = TarHeader.parseName(bh, offset, TarHeader.NAMELEN); header.linkName = TarHeader.parseName(bh, offset, TarHeader.NAMELEN);
offset += TarHeader.NAMELEN; offset += TarHeader.NAMELEN;
header.magic = TarHeader.parseName(bh, offset, TarHeader.USTAR_MAGICLEN); header.magic = TarHeader.parseName(bh, offset, TarHeader.USTAR_MAGICLEN);
offset += TarHeader.USTAR_MAGICLEN; offset += TarHeader.USTAR_MAGICLEN;
header.userName = TarHeader.parseName(bh, offset, TarHeader.USTAR_USER_NAMELEN); header.userName = TarHeader.parseName(bh, offset, TarHeader.USTAR_USER_NAMELEN);
offset += TarHeader.USTAR_USER_NAMELEN; offset += TarHeader.USTAR_USER_NAMELEN;
header.groupName = TarHeader.parseName(bh, offset, TarHeader.USTAR_GROUP_NAMELEN); header.groupName = TarHeader.parseName(bh, offset, TarHeader.USTAR_GROUP_NAMELEN);
offset += TarHeader.USTAR_GROUP_NAMELEN; offset += TarHeader.USTAR_GROUP_NAMELEN;
header.devMajor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN); header.devMajor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN);
offset += TarHeader.USTAR_DEVLEN; offset += TarHeader.USTAR_DEVLEN;
header.devMinor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN); header.devMinor = (int) Octal.parseOctal(bh, offset, TarHeader.USTAR_DEVLEN);
offset += TarHeader.USTAR_DEVLEN; offset += TarHeader.USTAR_DEVLEN;
header.namePrefix = TarHeader.parseName(bh, offset, TarHeader.USTAR_FILENAME_PREFIX); header.namePrefix = TarHeader.parseName(bh, offset, TarHeader.USTAR_FILENAME_PREFIX);
} }
} }

View File

@ -69,169 +69,169 @@ import java.io.File;
public class TarHeader { public class TarHeader {
/* /*
* Header * Header
*/ */
public static final int NAMELEN = 100; public static final int NAMELEN = 100;
public static final int MODELEN = 8; public static final int MODELEN = 8;
public static final int UIDLEN = 8; public static final int UIDLEN = 8;
public static final int GIDLEN = 8; public static final int GIDLEN = 8;
public static final int SIZELEN = 12; public static final int SIZELEN = 12;
public static final int MODTIMELEN = 12; public static final int MODTIMELEN = 12;
public static final int CHKSUMLEN = 8; public static final int CHKSUMLEN = 8;
public static final byte LF_OLDNORM = 0; public static final byte LF_OLDNORM = 0;
/* /*
* File Types * File Types
*/ */
public static final byte LF_NORMAL = (byte) '0'; public static final byte LF_NORMAL = (byte) '0';
public static final byte LF_LINK = (byte) '1'; public static final byte LF_LINK = (byte) '1';
public static final byte LF_SYMLINK = (byte) '2'; public static final byte LF_SYMLINK = (byte) '2';
public static final byte LF_CHR = (byte) '3'; public static final byte LF_CHR = (byte) '3';
public static final byte LF_BLK = (byte) '4'; public static final byte LF_BLK = (byte) '4';
public static final byte LF_DIR = (byte) '5'; public static final byte LF_DIR = (byte) '5';
public static final byte LF_FIFO = (byte) '6'; public static final byte LF_FIFO = (byte) '6';
public static final byte LF_CONTIG = (byte) '7'; public static final byte LF_CONTIG = (byte) '7';
/* /*
* Ustar header * Ustar header
*/ */
public static final String USTAR_MAGIC = "ustar"; // POSIX public static final String USTAR_MAGIC = "ustar"; // POSIX
public static final int USTAR_MAGICLEN = 8; public static final int USTAR_MAGICLEN = 8;
public static final int USTAR_USER_NAMELEN = 32; public static final int USTAR_USER_NAMELEN = 32;
public static final int USTAR_GROUP_NAMELEN = 32; public static final int USTAR_GROUP_NAMELEN = 32;
public static final int USTAR_DEVLEN = 8; public static final int USTAR_DEVLEN = 8;
public static final int USTAR_FILENAME_PREFIX = 155; public static final int USTAR_FILENAME_PREFIX = 155;
// Header values // Header values
public StringBuffer name; public StringBuffer name;
public int mode; public int mode;
public int userId; public int userId;
public int groupId; public int groupId;
public long size; public long size;
public long modTime; public long modTime;
public int checkSum; public int checkSum;
public byte linkFlag; public byte linkFlag;
public StringBuffer linkName; public StringBuffer linkName;
public StringBuffer magic; // ustar indicator and version public StringBuffer magic; // ustar indicator and version
public StringBuffer userName; public StringBuffer userName;
public StringBuffer groupName; public StringBuffer groupName;
public int devMajor; public int devMajor;
public int devMinor; public int devMinor;
public StringBuffer namePrefix; public StringBuffer namePrefix;
public TarHeader() { public TarHeader() {
this.magic = new StringBuffer(TarHeader.USTAR_MAGIC); this.magic = new StringBuffer(TarHeader.USTAR_MAGIC);
this.name = new StringBuffer(); this.name = new StringBuffer();
this.linkName = new StringBuffer(); this.linkName = new StringBuffer();
String user = System.getProperty("user.name", ""); String user = System.getProperty("user.name", "");
if (user.length() > 31) if (user.length() > 31)
user = user.substring(0, 31); user = user.substring(0, 31);
this.userId = 0; this.userId = 0;
this.groupId = 0; this.groupId = 0;
this.userName = new StringBuffer(user); this.userName = new StringBuffer(user);
this.groupName = new StringBuffer(""); this.groupName = new StringBuffer("");
this.namePrefix = new StringBuffer(); this.namePrefix = new StringBuffer();
} }
/** /**
* Parse an entry name from a header buffer. * Parse an entry name from a header buffer.
* *
* @param header * @param header
* The header buffer from which to parse. * The header buffer from which to parse.
* @param offset * @param offset
* The offset into the buffer from which to parse. * The offset into the buffer from which to parse.
* @param length * @param length
* The number of header bytes to parse. * The number of header bytes to parse.
* @return The header's entry name. * @return The header's entry name.
*/ */
public static StringBuffer parseName(byte[] header, int offset, int length) { public static StringBuffer parseName(byte[] header, int offset, int length) {
StringBuffer result = new StringBuffer(length); StringBuffer result = new StringBuffer(length);
int end = offset + length; int end = offset + length;
for (int i = offset; i < end; ++i) { for (int i = offset; i < end; ++i) {
if (header[i] == 0) if (header[i] == 0)
break; break;
result.append((char) header[i]); result.append((char) header[i]);
} }
return result; return result;
} }
/** /**
* Determine the number of bytes in an entry name. * Determine the number of bytes in an entry name.
* *
* @param name * @param name
* The header buffer from which to parse. * The header buffer from which to parse.
* @param offset * @param offset
* The offset into the buffer from which to parse. * The offset into the buffer from which to parse.
* @param length * @param length
* The number of header bytes to parse. * The number of header bytes to parse.
* @return The number of bytes in a header's entry name. * @return The number of bytes in a header's entry name.
*/ */
public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) { public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) {
int i; int i;
for (i = 0; i < length && i < name.length(); ++i) { for (i = 0; i < length && i < name.length(); ++i) {
buf[offset + i] = (byte) name.charAt(i); buf[offset + i] = (byte) name.charAt(i);
} }
for (; i < length; ++i) { for (; i < length; ++i) {
buf[offset + i] = 0; buf[offset + i] = 0;
} }
return offset + length; return offset + length;
} }
/** /**
* Creates a new header for a file/directory entry. * Creates a new header for a file/directory entry.
* *
* *
* @param entryName * @param entryName
* File name * File name
* @param size * @param size
* File size in bytes * File size in bytes
* @param modTime * @param modTime
* Last modification time in numeric Unix time format * Last modification time in numeric Unix time format
* @param dir * @param dir
* Is directory * Is directory
*/ */
public static TarHeader createHeader(String entryName, long size, long modTime, boolean dir, int permissions) { public static TarHeader createHeader(String entryName, long size, long modTime, boolean dir, int permissions) {
String name = entryName; String name = entryName;
name = TarUtils.trim(name.replace(File.separatorChar, '/'), '/'); name = TarUtils.trim(name.replace(File.separatorChar, '/'), '/');
TarHeader header = new TarHeader(); TarHeader header = new TarHeader();
header.linkName = new StringBuffer(""); header.linkName = new StringBuffer("");
header.mode = permissions; header.mode = permissions;
if (name.length() > 100) { if (name.length() > 100) {
header.namePrefix = new StringBuffer(name.substring(0, name.lastIndexOf('/'))); header.namePrefix = new StringBuffer(name.substring(0, name.lastIndexOf('/')));
header.name = new StringBuffer(name.substring(name.lastIndexOf('/') + 1)); header.name = new StringBuffer(name.substring(name.lastIndexOf('/') + 1));
} else { } else {
header.name = new StringBuffer(name); header.name = new StringBuffer(name);
} }
if (dir) { if (dir) {
header.linkFlag = TarHeader.LF_DIR; header.linkFlag = TarHeader.LF_DIR;
if (header.name.charAt(header.name.length() - 1) != '/') { if (header.name.charAt(header.name.length() - 1) != '/') {
header.name.append("/"); header.name.append("/");
} }
header.size = 0; header.size = 0;
} else { } else {
header.linkFlag = TarHeader.LF_NORMAL; header.linkFlag = TarHeader.LF_NORMAL;
header.size = size; header.size = size;
} }
header.modTime = modTime; header.modTime = modTime;
header.checkSum = 0; header.checkSum = 0;
header.devMajor = 0; header.devMajor = 0;
header.devMinor = 0; header.devMinor = 0;
return header; return header;
} }
} }

View File

@ -27,223 +27,223 @@ import java.io.InputStream;
*/ */
public class TarInputStream extends FilterInputStream { public class TarInputStream extends FilterInputStream {
private static final int SKIP_BUFFER_SIZE = 2048; private static final int SKIP_BUFFER_SIZE = 2048;
private TarEntry currentEntry; private TarEntry currentEntry;
private long currentFileSize; private long currentFileSize;
private long bytesRead; private long bytesRead;
private boolean defaultSkip = false; private boolean defaultSkip = false;
public TarInputStream(InputStream in) { public TarInputStream(InputStream in) {
super(in); super(in);
currentFileSize = 0; currentFileSize = 0;
bytesRead = 0; bytesRead = 0;
} }
@Override @Override
public boolean markSupported() { public boolean markSupported() {
return false; return false;
} }
/** /**
* Not supported * Not supported
* *
*/ */
@Override @Override
public synchronized void mark(int readlimit) { public synchronized void mark(int readlimit) {
} }
/** /**
* Not supported * Not supported
* *
*/ */
@Override @Override
public synchronized void reset() throws IOException { public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported"); throw new IOException("mark/reset not supported");
} }
/** /**
* Read a byte * Read a byte
* *
* @see java.io.FilterInputStream#read() * @see java.io.FilterInputStream#read()
*/ */
@Override @Override
public int read() throws IOException { public int read() throws IOException {
byte[] buf = new byte[1]; byte[] buf = new byte[1];
int res = this.read(buf, 0, 1); int res = this.read(buf, 0, 1);
if (res != -1) { if (res != -1) {
return 0xFF & buf[0]; return 0xFF & buf[0];
} }
return res; return res;
} }
/** /**
* Checks if the bytes being read exceed the entry size and adjusts the byte * Checks if the bytes being read exceed the entry size and adjusts the byte
* array length. Updates the byte counters * array length. Updates the byte counters
* *
* *
* @see java.io.FilterInputStream#read(byte[], int, int) * @see java.io.FilterInputStream#read(byte[], int, int)
*/ */
@Override @Override
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
if (currentEntry != null) { if (currentEntry != null) {
if (currentFileSize == currentEntry.getSize()) { if (currentFileSize == currentEntry.getSize()) {
return -1; return -1;
} else if ((currentEntry.getSize() - currentFileSize) < len) { } else if ((currentEntry.getSize() - currentFileSize) < len) {
len = (int) (currentEntry.getSize() - currentFileSize); len = (int) (currentEntry.getSize() - currentFileSize);
} }
} }
int br = super.read(b, off, len); int br = super.read(b, off, len);
if (br != -1) { if (br != -1) {
if (currentEntry != null) { if (currentEntry != null) {
currentFileSize += br; currentFileSize += br;
} }
bytesRead += br; bytesRead += br;
} }
return br; return br;
} }
/** /**
* Returns the next entry in the tar file * Returns the next entry in the tar file
* *
* @return TarEntry * @return TarEntry
* @throws IOException * @throws IOException
*/ */
public TarEntry getNextEntry() throws IOException { public TarEntry getNextEntry() throws IOException {
closeCurrentEntry(); closeCurrentEntry();
byte[] header = new byte[TarConstants.HEADER_BLOCK]; byte[] header = new byte[TarConstants.HEADER_BLOCK];
byte[] theader = new byte[TarConstants.HEADER_BLOCK]; byte[] theader = new byte[TarConstants.HEADER_BLOCK];
int tr = 0; int tr = 0;
// Read full header // Read full header
while (tr < TarConstants.HEADER_BLOCK) { while (tr < TarConstants.HEADER_BLOCK) {
int res = read(theader, 0, TarConstants.HEADER_BLOCK - tr); int res = read(theader, 0, TarConstants.HEADER_BLOCK - tr);
if (res < 0) { if (res < 0) {
break; break;
} }
System.arraycopy(theader, 0, header, tr, res); System.arraycopy(theader, 0, header, tr, res);
tr += res; tr += res;
} }
// Check if record is null // Check if record is null
boolean eof = true; boolean eof = true;
for (byte b : header) { for (byte b : header) {
if (b != 0) { if (b != 0) {
eof = false; eof = false;
break; break;
} }
} }
if (!eof) { if (!eof) {
currentEntry = new TarEntry(header); currentEntry = new TarEntry(header);
} }
return currentEntry; return currentEntry;
} }
/** /**
* Returns the current offset (in bytes) from the beginning of the stream. * Returns the current offset (in bytes) from the beginning of the stream.
* This can be used to find out at which point in a tar file an entry's content begins, for instance. * This can be used to find out at which point in a tar file an entry's content begins, for instance.
*/ */
public long getCurrentOffset() { public long getCurrentOffset() {
return bytesRead; return bytesRead;
} }
/** /**
* Closes the current tar entry * Closes the current tar entry
* *
* @throws IOException * @throws IOException
*/ */
protected void closeCurrentEntry() throws IOException { protected void closeCurrentEntry() throws IOException {
if (currentEntry != null) { if (currentEntry != null) {
if (currentEntry.getSize() > currentFileSize) { if (currentEntry.getSize() > currentFileSize) {
// Not fully read, skip rest of the bytes // Not fully read, skip rest of the bytes
long bs = 0; long bs = 0;
while (bs < currentEntry.getSize() - currentFileSize) { while (bs < currentEntry.getSize() - currentFileSize) {
long res = skip(currentEntry.getSize() - currentFileSize - bs); long res = skip(currentEntry.getSize() - currentFileSize - bs);
if (res == 0 && currentEntry.getSize() - currentFileSize > 0) { if (res == 0 && currentEntry.getSize() - currentFileSize > 0) {
// I suspect file corruption // I suspect file corruption
throw new IOException("Possible tar file corruption"); throw new IOException("Possible tar file corruption");
} }
bs += res; bs += res;
} }
} }
currentEntry = null; currentEntry = null;
currentFileSize = 0L; currentFileSize = 0L;
skipPad(); skipPad();
} }
} }
/** /**
* Skips the pad at the end of each tar entry file content * Skips the pad at the end of each tar entry file content
* *
* @throws IOException * @throws IOException
*/ */
protected void skipPad() throws IOException { protected void skipPad() throws IOException {
if (bytesRead > 0) { if (bytesRead > 0) {
int extra = (int) (bytesRead % TarConstants.DATA_BLOCK); int extra = (int) (bytesRead % TarConstants.DATA_BLOCK);
if (extra > 0) { if (extra > 0) {
long bs = 0; long bs = 0;
while (bs < TarConstants.DATA_BLOCK - extra) { while (bs < TarConstants.DATA_BLOCK - extra) {
long res = skip(TarConstants.DATA_BLOCK - extra - bs); long res = skip(TarConstants.DATA_BLOCK - extra - bs);
bs += res; bs += res;
} }
} }
} }
} }
/** /**
* Skips 'n' bytes on the InputStream<br> * Skips 'n' bytes on the InputStream<br>
* Overrides default implementation of skip * Overrides default implementation of skip
* *
*/ */
@Override @Override
public long skip(long n) throws IOException { public long skip(long n) throws IOException {
if (defaultSkip) { if (defaultSkip) {
// use skip method of parent stream // use skip method of parent stream
// may not work if skip not implemented by parent // may not work if skip not implemented by parent
long bs = super.skip(n); long bs = super.skip(n);
bytesRead += bs; bytesRead += bs;
return bs; return bs;
} }
if (n <= 0) { if (n <= 0) {
return 0; return 0;
} }
long left = n; long left = n;
byte[] sBuff = new byte[SKIP_BUFFER_SIZE]; byte[] sBuff = new byte[SKIP_BUFFER_SIZE];
while (left > 0) { while (left > 0) {
int res = read(sBuff, 0, (int) (left < SKIP_BUFFER_SIZE ? left : SKIP_BUFFER_SIZE)); int res = read(sBuff, 0, (int) (left < SKIP_BUFFER_SIZE ? left : SKIP_BUFFER_SIZE));
if (res < 0) { if (res < 0) {
break; break;
} }
left -= res; left -= res;
} }
return n - left; return n - left;
} }
public boolean isDefaultSkip() { public boolean isDefaultSkip() {
return defaultSkip; return defaultSkip;
} }
public void setDefaultSkip(boolean defaultSkip) { public void setDefaultSkip(boolean defaultSkip) {
this.defaultSkip = defaultSkip; this.defaultSkip = defaultSkip;
} }
} }

View File

@ -17,20 +17,14 @@
package org.kamranzafar.jtar; package org.kamranzafar.jtar;
import java.io.BufferedOutputStream; import java.io.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/** /**
* @author Kamran Zafar * @author Kamran Zafar
* *
*/ */
public class TarOutputStream extends OutputStream { public class TarOutputStream extends OutputStream {
private final OutputStream out; private final OutputStream out;
private long bytesWritten; private long bytesWritten;
private long currentFileSize; private long currentFileSize;
private TarEntry currentEntry; private TarEntry currentEntry;
@ -41,24 +35,24 @@ public class TarOutputStream extends OutputStream {
currentFileSize = 0; currentFileSize = 0;
} }
public TarOutputStream(final File fout) throws FileNotFoundException { public TarOutputStream(final File fout) throws FileNotFoundException {
this.out = new BufferedOutputStream(new FileOutputStream(fout)); this.out = new BufferedOutputStream(new FileOutputStream(fout));
bytesWritten = 0; bytesWritten = 0;
currentFileSize = 0; currentFileSize = 0;
} }
/** /**
* Opens a file for writing. * Opens a file for writing.
*/ */
public TarOutputStream(final File fout, final boolean append) throws IOException { public TarOutputStream(final File fout, final boolean append) throws IOException {
@SuppressWarnings("resource") @SuppressWarnings("resource")
RandomAccessFile raf = new RandomAccessFile(fout, "rw"); RandomAccessFile raf = new RandomAccessFile(fout, "rw");
final long fileSize = fout.length(); final long fileSize = fout.length();
if (append && fileSize > TarConstants.EOF_BLOCK) { if (append && fileSize > TarConstants.EOF_BLOCK) {
raf.seek(fileSize - TarConstants.EOF_BLOCK); raf.seek(fileSize - TarConstants.EOF_BLOCK);
} }
out = new BufferedOutputStream(new FileOutputStream(raf.getFD())); out = new BufferedOutputStream(new FileOutputStream(raf.getFD()));
} }
/** /**
* Appends the EOF record and closes the stream * Appends the EOF record and closes the stream

View File

@ -24,70 +24,70 @@ import java.io.File;
* *
*/ */
public class TarUtils { public class TarUtils {
/** /**
* Determines the tar file size of the given folder/file path * Determines the tar file size of the given folder/file path
*/ */
public static long calculateTarSize(File path) { public static long calculateTarSize(File path) {
return tarSize(path) + TarConstants.EOF_BLOCK; return tarSize(path) + TarConstants.EOF_BLOCK;
} }
private static long tarSize(File dir) { private static long tarSize(File dir) {
long size = 0; long size = 0;
if (dir.isFile()) { if (dir.isFile()) {
return entrySize(dir.length()); return entrySize(dir.length());
} else { } else {
File[] subFiles = dir.listFiles(); File[] subFiles = dir.listFiles();
if (subFiles != null && subFiles.length > 0) { if (subFiles != null && subFiles.length > 0) {
for (File file : subFiles) { for (File file : subFiles) {
if (file.isFile()) { if (file.isFile()) {
size += entrySize(file.length()); size += entrySize(file.length());
} else { } else {
size += tarSize(file); size += tarSize(file);
} }
} }
} else { } else {
// Empty folder header // Empty folder header
return TarConstants.HEADER_BLOCK; return TarConstants.HEADER_BLOCK;
} }
} }
return size; return size;
} }
private static long entrySize(long fileSize) { private static long entrySize(long fileSize) {
long size = 0; long size = 0;
size += TarConstants.HEADER_BLOCK; // Header size += TarConstants.HEADER_BLOCK; // Header
size += fileSize; // File size size += fileSize; // File size
long extra = size % TarConstants.DATA_BLOCK; long extra = size % TarConstants.DATA_BLOCK;
if (extra > 0) { if (extra > 0) {
size += (TarConstants.DATA_BLOCK - extra); // pad size += (TarConstants.DATA_BLOCK - extra); // pad
} }
return size; return size;
} }
public static String trim(String s, char c) { public static String trim(String s, char c) {
StringBuffer tmp = new StringBuffer(s); StringBuffer tmp = new StringBuffer(s);
for (int i = 0; i < tmp.length(); i++) { for (int i = 0; i < tmp.length(); i++) {
if (tmp.charAt(i) != c) { if (tmp.charAt(i) != c) {
break; break;
} else { } else {
tmp.deleteCharAt(i); tmp.deleteCharAt(i);
} }
} }
for (int i = tmp.length() - 1; i >= 0; i--) { for (int i = tmp.length() - 1; i >= 0; i--) {
if (tmp.charAt(i) != c) { if (tmp.charAt(i) != c) {
break; break;
} else { } else {
tmp.deleteCharAt(i); tmp.deleteCharAt(i);
} }
} }
return tmp.toString(); return tmp.toString();
} }
} }