blob: 2f58146ace1c46b5fdec9dae3ca24a1a0abc3e2e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.wizards.datatransfer;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Output stream for writing ustar archive files (tar) compatible
* with the specification in IEEE Std 1003.1-2001.
*
* @since 3.1
*/
public class TarOutputStream extends FilterOutputStream {
private int byteswritten = 0;
private int datapos = 0;
private long cursize = 0;
/**
* Creates a new tar output stream.
*
* @param out the stream to write to
*/
public TarOutputStream(OutputStream out) {
super(out);
}
/**
* Close the output stream and write any necessary padding.
*/
public void close() throws IOException {
// Spec says to write 1024 bytes of zeros at the end.
byte[] zeros = new byte[1024];
cursize = 1024;
write(zeros, 0, 1024);
// Default block size for tar files is 10240, so we have to
// pad the end of the file to be a multiple of this size.
if((byteswritten % 10240) != 0) {
int length = 10240 - (byteswritten % 10240);
cursize = length;
zeros = new byte[length];
write(zeros, 0, length);
}
super.close();
}
/**
* Close the current entry in the tar file. Must be called
* after each entry is completed.
*
* @throws IOException
*/
public void closeEntry() throws IOException {
byte[] data = new byte[512];
int len = 512 - datapos;
if(len > 0 && datapos > 0) {
cursize = len;
write(data, 0, len);
}
}
/**
* The checksum of a tar file header is simply the sum of the bytes in
* the header.
*
* @param header
* @return checksum
*/
private long headerChecksum(byte[] header) {
long sum = 0;
for(int i = 0; i < 512; i++) {
sum += header[i] & 0xff;
}
return sum;
}
/**
* Adds an entry for a new file in the tar archive.
*
* @param e TarEntry describing the file
* @throws IOException
*/
public void putNextEntry(TarEntry e) throws IOException {
byte[] header = new byte[512];
String filename = e.getName();
String prefix = null;
int pos, i;
/* Split filename into name and prefix if necessary. */
byte[] filenameBytes = filename.getBytes("UTF8"); //$NON-NLS-1$
if (filenameBytes.length > 99) {
int seppos = filename.lastIndexOf('/');
if(seppos == -1) throw new IOException("filename too long"); //$NON-NLS-1$
prefix = filename.substring(0, seppos);
filename = filename.substring(seppos + 1);
filenameBytes = filename.getBytes("UTF8"); //$NON-NLS-1$
if (filenameBytes.length > 99)
throw new IOException("filename too long"); //$NON-NLS-1$
}
/* Filename. */
pos = 0;
System.arraycopy(filenameBytes, 0, header, 0, filenameBytes.length);
pos += 100;
/* File mode. */
StringBuffer mode = new StringBuffer(Long.toOctalString(e.getMode()));
while(mode.length() < 7) mode.insert(0,'0');
for(i = 0; i < 7; i++) {
header[pos + i] = (byte) mode.charAt(i);
}
pos += 8;
/* UID. */
header[pos] = '0';
pos += 8;
/* GID. */
header[pos] = '0';
pos += 8;
/* Length of the file. */
String length = Long.toOctalString(e.getSize());
for(i = 0; i < length.length(); i++) {
header[pos + i] = (byte) length.charAt(i);
}
pos += 12;
/* mtime */
String mtime = Long.toOctalString(e.getTime());
for(i = 0; i < mtime.length(); i++) {
header[pos + i] = (byte) mtime.charAt(i);
}
pos += 12;
/* "Blank" out the checksum. */
for(i = 0; i < 8; i++) {
header[pos + i] = ' ';
}
pos += 8;
/* Link flag. */
header[pos] = (byte) e.getFileType();
pos += 1;
/* Link destination. */
pos += 100;
/* Add ustar header. */
String ustar = "ustar 00"; //$NON-NLS-1$
for(i = 0; i < ustar.length(); i++) {
header[pos + i] = (byte) ustar.charAt(i);
}
header[pos + 5] = 0;
pos += 8;
/* Username. */
String uname = "nobody"; //$NON-NLS-1$
for(i = 0; i < uname.length(); i++) {
header[pos + i] = (byte) uname.charAt(i);
}
pos += 32;
/* Group name. */
String gname = "nobody"; //$NON-NLS-1$
for(i = 0; i < gname.length(); i++) {
header[pos + i] = (byte) gname.charAt(i);
}
pos += 32;
/* Device major. */
pos += 8;
/* Device minor. */
pos += 8;
/* File prefix. */
if(prefix != null) {
byte[] prefixBytes = prefix.getBytes("UTF8"); //$NON-NLS-1$
if (prefixBytes.length > 155)
throw new IOException("prefix too large"); //$NON-NLS-1$
System.arraycopy(prefixBytes, 0, header, pos, prefixBytes.length);
}
long sum = headerChecksum(header);
pos = 100 + 8 + 8 + 8 + 12 + 12;
String sumval = Long.toOctalString(sum);
for(i = 0; i < sumval.length(); i++) {
header[pos + i] = (byte) sumval.charAt(i);
}
cursize = 512;
write(header, 0, 512);
cursize = e.getSize();
}
/**
* Writes data for the current file into the archive.
*/
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
datapos = (datapos + len) % 512;
byteswritten += len;
cursize -= len;
if(cursize < 0) {
throw new IOException("too much data written for current file"); //$NON-NLS-1$
}
}
}