001: /*******************************************************************************
002: * Copyright (c) 2004, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.wizards.datatransfer;
011:
012: import java.io.FilterOutputStream;
013: import java.io.IOException;
014: import java.io.OutputStream;
015:
016: /**
017: * Output stream for writing ustar archive files (tar) compatible
018: * with the specification in IEEE Std 1003.1-2001.
019: *
020: * @since 3.1
021: */
022: public class TarOutputStream extends FilterOutputStream {
023:
024: private int byteswritten = 0;
025: private int datapos = 0;
026: private long cursize = 0;
027:
028: /**
029: * Creates a new tar output stream.
030: *
031: * @param out the stream to write to
032: */
033: public TarOutputStream(OutputStream out) {
034: super (out);
035: }
036:
037: /**
038: * Close the output stream and write any necessary padding.
039: */
040: public void close() throws IOException {
041: // Spec says to write 1024 bytes of zeros at the end.
042: byte[] zeros = new byte[1024];
043: cursize = 1024;
044: write(zeros, 0, 1024);
045:
046: // Default block size for tar files is 10240, so we have to
047: // pad the end of the file to be a multiple of this size.
048: if ((byteswritten % 10240) != 0) {
049: int length = 10240 - (byteswritten % 10240);
050: cursize = length;
051: zeros = new byte[length];
052: write(zeros, 0, length);
053: }
054: super .close();
055: }
056:
057: /**
058: * Close the current entry in the tar file. Must be called
059: * after each entry is completed.
060: *
061: * @throws IOException
062: */
063: public void closeEntry() throws IOException {
064: byte[] data = new byte[512];
065: int len = 512 - datapos;
066: if (len > 0 && datapos > 0) {
067: cursize = len;
068: write(data, 0, len);
069: }
070: }
071:
072: /**
073: * The checksum of a tar file header is simply the sum of the bytes in
074: * the header.
075: *
076: * @param header
077: * @return checksum
078: */
079: private long headerChecksum(byte[] header) {
080: long sum = 0;
081: for (int i = 0; i < 512; i++) {
082: sum += header[i] & 0xff;
083: }
084: return sum;
085: }
086:
087: /**
088: * Adds an entry for a new file in the tar archive.
089: *
090: * @param e TarEntry describing the file
091: * @throws IOException
092: */
093: public void putNextEntry(TarEntry e) throws IOException {
094: byte[] header = new byte[512];
095: String filename = e.getName();
096: String prefix = null;
097: int pos, i;
098:
099: /* Split filename into name and prefix if necessary. */
100: byte[] filenameBytes = filename.getBytes("UTF8"); //$NON-NLS-1$
101: if (filenameBytes.length > 99) {
102: int seppos = filename.lastIndexOf('/');
103: if (seppos == -1) {
104: throw new IOException("filename too long"); //$NON-NLS-1$
105: }
106: prefix = filename.substring(0, seppos);
107: filename = filename.substring(seppos + 1);
108: filenameBytes = filename.getBytes("UTF8"); //$NON-NLS-1$
109: if (filenameBytes.length > 99) {
110: throw new IOException("filename too long"); //$NON-NLS-1$
111: }
112: }
113:
114: /* Filename. */
115: pos = 0;
116: System.arraycopy(filenameBytes, 0, header, 0,
117: filenameBytes.length);
118: pos += 100;
119:
120: /* File mode. */
121: StringBuffer mode = new StringBuffer(Long.toOctalString(e
122: .getMode()));
123: while (mode.length() < 7) {
124: mode.insert(0, '0');
125: }
126: for (i = 0; i < 7; i++) {
127: header[pos + i] = (byte) mode.charAt(i);
128: }
129: pos += 8;
130:
131: /* UID. */
132: header[pos] = '0';
133: pos += 8;
134:
135: /* GID. */
136: header[pos] = '0';
137: pos += 8;
138:
139: /* Length of the file. */
140: String length = Long.toOctalString(e.getSize());
141: for (i = 0; i < length.length(); i++) {
142: header[pos + i] = (byte) length.charAt(i);
143: }
144: pos += 12;
145:
146: /* mtime */
147: String mtime = Long.toOctalString(e.getTime());
148: for (i = 0; i < mtime.length(); i++) {
149: header[pos + i] = (byte) mtime.charAt(i);
150: }
151: pos += 12;
152:
153: /* "Blank" out the checksum. */
154: for (i = 0; i < 8; i++) {
155: header[pos + i] = ' ';
156: }
157: pos += 8;
158:
159: /* Link flag. */
160: header[pos] = (byte) e.getFileType();
161: pos += 1;
162:
163: /* Link destination. */
164: pos += 100;
165:
166: /* Add ustar header. */
167: String ustar = "ustar 00"; //$NON-NLS-1$
168: for (i = 0; i < ustar.length(); i++) {
169: header[pos + i] = (byte) ustar.charAt(i);
170: }
171: header[pos + 5] = 0;
172: pos += 8;
173:
174: /* Username. */
175: String uname = "nobody"; //$NON-NLS-1$
176: for (i = 0; i < uname.length(); i++) {
177: header[pos + i] = (byte) uname.charAt(i);
178: }
179: pos += 32;
180:
181: /* Group name. */
182: String gname = "nobody"; //$NON-NLS-1$
183: for (i = 0; i < gname.length(); i++) {
184: header[pos + i] = (byte) gname.charAt(i);
185: }
186: pos += 32;
187:
188: /* Device major. */
189: pos += 8;
190:
191: /* Device minor. */
192: pos += 8;
193:
194: /* File prefix. */
195: if (prefix != null) {
196: byte[] prefixBytes = prefix.getBytes("UTF8"); //$NON-NLS-1$
197: if (prefixBytes.length > 155) {
198: throw new IOException("prefix too large"); //$NON-NLS-1$
199: }
200: System.arraycopy(prefixBytes, 0, header, pos,
201: prefixBytes.length);
202: }
203:
204: long sum = headerChecksum(header);
205: pos = 100 + 8 + 8 + 8 + 12 + 12;
206: String sumval = Long.toOctalString(sum);
207: for (i = 0; i < sumval.length(); i++) {
208: header[pos + i] = (byte) sumval.charAt(i);
209: }
210:
211: cursize = 512;
212: write(header, 0, 512);
213:
214: cursize = e.getSize();
215: }
216:
217: /**
218: * Writes data for the current file into the archive.
219: */
220: public void write(byte[] b, int off, int len) throws IOException {
221: super .write(b, off, len);
222: datapos = (datapos + len) % 512;
223: byteswritten += len;
224: cursize -= len;
225: if (cursize < 0) {
226: throw new IOException(
227: "too much data written for current file"); //$NON-NLS-1$
228: }
229: }
230: }
|