package er.woinstaller.archiver;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import er.woinstaller.io.BoundedInputStream;
import er.woinstaller.io.FileUtilities;
import er.woinstaller.ui.IWOInstallerProgressMonitor;
import er.woinstaller.ui.NullProgressMonitor;
public class CPIO {
public static final int S_IFDIR = 16384;
public static final int S_IFREG = 32768;
public static final int S_IFLNK = 40960;
private File _cpioFile;
InputStream paxStream;
long fileLength = 0;
public CPIO(File cpioFile) throws FileNotFoundException {
this(new BufferedInputStream(new FileInputStream(cpioFile)));
_cpioFile = cpioFile;
fileLength = _cpioFile.length();
}
public CPIO(InputStream input) {
paxStream = input;
}
public void setLength(long length) {
this.fileLength = length;
}
@SuppressWarnings("unused")
public void extractTo(File destinationFolder, boolean symbolicLinksSupported, IWOInstallerProgressMonitor progressMonitor) throws IOException, InterruptedException {
progressMonitor.beginTask("Extracting WebObjects ...", fileLength);
long amount = 0;
List<Link> links = new LinkedList<Link>();
try {
byte[] sixBuffer = new byte[6];
byte[] elevenBuffer = new byte[11];
boolean done = false;
do {
String magic = readString(paxStream, sixBuffer);
if (!"070707".equals(magic)) {
throw new IOException("Expected magic '070707' but got '" + magic + "' (next = " + readString(paxStream, new byte[50]) + ").");
}
else {
String dev = readString(paxStream, sixBuffer);
String ino = readString(paxStream, sixBuffer);
String modeStr = readString(paxStream, sixBuffer);
String uid = readString(paxStream, sixBuffer);
String gid = readString(paxStream, sixBuffer);
String nlink = readString(paxStream, sixBuffer);
String rdev = readString(paxStream, sixBuffer);
String mtime = readString(paxStream, elevenBuffer);
String nameSizeStr = readString(paxStream, sixBuffer);
String fileSizeStr = readString(paxStream, elevenBuffer);
int nameSize = Integer.parseInt(nameSizeStr, 8);
String name = readString(paxStream, new byte[nameSize]);
int fileSize = Integer.parseInt(fileSizeStr, 8);
if ("TRAILER!!!".equals(name)) {
done = true;
}
else {
File destinationFile = toFile(destinationFolder, name);
int mode = Integer.parseInt(modeStr, 8);
if ((mode & S_IFDIR) == S_IFDIR) {
if (".".equals(name)) {
// skip
}
else if (destinationFile.exists()) {
throw new IOException("The directory '" + destinationFile + "' already exists.");
}
else if (!destinationFile.mkdirs()) {
throw new IOException("Failed to create directory '" + destinationFile + "'.");
}
skipFully(paxStream, fileSize);
}
else if ((mode & S_IFLNK) == S_IFLNK) {
String realName = readString(paxStream, new byte[fileSize]);
File realFile = new File(realName);
if (!symbolicLinksSupported) {
realFile = toFile(destinationFile.getParentFile(), realName);
}
links.add(new Link(realFile, destinationFile));
}
else if ((mode & S_IFREG) == S_IFREG) {
if (destinationFile.exists()) {
throw new IOException("The file '" + destinationFile + "' already exists.");
}
InputStream is = new BoundedInputStream(paxStream, 0, fileSize);
FileOutputStream fos = new FileOutputStream(destinationFile);
FileUtilities.writeInputStreamToOutputStream(is, fos, fileSize, new NullProgressMonitor());
}
else {
throw new IOException("Unknown mode " + modeStr + " for " + name + ".");
}
int relativeAmount = 70 + nameSize + fileSize;
amount += relativeAmount;
progressMonitor.worked(amount);
}
}
if (progressMonitor.isCanceled()) {
throw new IOException("Operation canceled.");
}
} while (!done);
}
finally {
// System.out.println(amount + ":" + fileLength);
paxStream.close();
}
progressMonitor.done();
progressMonitor.beginTask("Linking WebObjects ...", links.size());
Collections.sort(links, new LinkNameLengthComparator());
int linkNum = 0;
for (Link link : links) {
link.create(symbolicLinksSupported);
progressMonitor.worked(linkNum++);
}
}
protected File toFile(File workingDir, String path) {
String localPath = path.replaceFirst("^\\./", "");
localPath = localPath.replace("/", File.separator);
File file = new File(localPath);
if (!file.isAbsolute()) {
file = new File(workingDir, localPath);
}
return file;
}
protected String readString(InputStream is, byte[] b) throws IOException {
readFully(is, b);
int length;
for (length = b.length - 1; length >= 0 && b[length] == 0; length--) {
// skip
}
return new String(b, 0, length + 1);
}
protected byte[] readFully(InputStream is, byte[] b) throws IOException {
return readFully(is, b, 0, b.length);
}
protected byte[] readFully(InputStream is, byte[] b, int offset, int length) throws IOException {
int totalAmountRead = 0;
while (totalAmountRead < length) {
int amountRead = is.read(b, offset + totalAmountRead, length - totalAmountRead);
if (amountRead == -1) {
throw new IOException("Stream ended before " + length + " bytes (read " + totalAmountRead + ")");
}
totalAmountRead += amountRead;
}
return b;
}
protected void skipFully(InputStream inputStream, long skip) throws IOException {
long toSkip = skip;
while (toSkip > 0) {
toSkip -= inputStream.skip(toSkip);
}
}
protected static class Link {
private File _realFile;
private File _linkFile;
public Link(File realFile, File linkFile) {
_realFile = realFile;
_linkFile = linkFile;
}
public Link(String realName, String linkName) {
_realFile = new File(realName);
_linkFile = new File(linkName);
}
public File getRealFile() {
return _realFile;
}
public File getLinkFile() {
return _linkFile;
}
public void create(boolean symbolicLinksSupported) throws IOException, InterruptedException {
if (symbolicLinksSupported) {
Process p = Runtime.getRuntime().exec(new String[] { "/bin/ln", "-s", _realFile.getPath(), _linkFile.getCanonicalPath() });
int retval = p.waitFor();
if (retval != 0) {
throw new IOException("Failed to create link from " + _realFile + " to " + _linkFile);
}
}
else {
try {
copyFileToFile(_realFile, _linkFile);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
protected void copyFileToFile(File source, File destination) throws IOException {
if (!source.exists()) {
throw new IOException("The file '" + source + "' does not exist (tried to link to '" + destination + "').");
}
if (destination.exists()) {
throw new IOException("The file '" + destination + "' already exists.");
}
if (source.isDirectory()) {
if (!destination.mkdirs()) {
throw new IOException("Failed to create the directory '" + destination + "'.");
}
for (File child : source.listFiles()) {
copyFileToFile(child, new File(destination, child.getName()));
}
}
else {
FileInputStream fis = new FileInputStream(source);
FileUtilities.writeInputStreamToFile(fis, destination, (int) source.length(), new NullProgressMonitor());
}
}
}
protected static class LinkNameLengthComparator implements Comparator<Link>, Serializable {
public int compare(Link s1, Link s2) {
int length1 = s1.getRealFile().toString().length();
int length2 = s2.getRealFile().toString().length();
int comparison;
if (length1 > length2) {
comparison = 1;
}
else if (length1 < length2) {
comparison = -1;
}
else {
comparison = 0;
}
return comparison;
}
}
}