File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\org\apache\tools\tar\TarOutputStream.java.html

1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 /*
20 * This package is based on the work done by Timothy Gerard Endres
21 * (time@ice.com) to whom the Ant project is very grateful for his great code.
22 */
23
24 package hudson.org.apache.tools.tar;
25
26 import org.apache.tools.tar.TarBuffer;
27 import org.apache.tools.tar.TarConstants;
28 import org.apache.tools.tar.TarEntry;
29
30 import java.io.FilterOutputStream;
31 import java.io.OutputStream;
32 import java.io.IOException;
33
34 /**
35 * The TarOutputStream writes a UNIX tar archive as an OutputStream.
36 * Methods are provided to put entries, and then write their contents
37 * by writing to this stream using write().
38 *
39 */
40 public class TarOutputStream extends FilterOutputStream {
41 /** Fail if a long file name is required in the archive. */
42 public static final int LONGFILE_ERROR = 0;
43
44 /** Long paths will be truncated in the archive. */
45 public static final int LONGFILE_TRUNCATE = 1;
46
47 /** GNU tar extensions are used to store long file names in the archive. */
48 public static final int LONGFILE_GNU = 2;
49
50 // CheckStyle:VisibilityModifier OFF - bc
51 protected boolean debug;
52 protected long currSize;
53 protected String currName;
54 protected long currBytes;
55 protected byte[] oneBuf;
56 protected byte[] recordBuf;
57 protected int assemLen;
58 protected byte[] assemBuf;
59 protected TarBuffer buffer;
60 protected int longFileMode = LONGFILE_ERROR;
61 // CheckStyle:VisibilityModifier ON
62
63 private boolean closed = false;
64
65 /**
66 * Constructor for TarInputStream.
67 * @param os the output stream to use
68 */
69 public TarOutputStream(OutputStream os) {
70 this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
71 }
72
73 /**
74 * Constructor for TarInputStream.
75 * @param os the output stream to use
76 * @param blockSize the block size to use
77 */
78 public TarOutputStream(OutputStream os, int blockSize) {
79 this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE);
80 }
81
82 /**
83 * Constructor for TarInputStream.
84 * @param os the output stream to use
85 * @param blockSize the block size to use
86 * @param recordSize the record size to use
87 */
88 public TarOutputStream(OutputStream os, int blockSize, int recordSize) {
89 super(os);
90
91 this.buffer = new TarBuffer(os, blockSize, recordSize);
92 this.debug = false;
93 this.assemLen = 0;
94 this.assemBuf = new byte[recordSize];
95 this.recordBuf = new byte[recordSize];
96 this.oneBuf = new byte[1];
97 }
98
99 /**
100 * Set the long file mode.
101 * This can be LONGFILE_ERROR(0), LONGFILE_TRUNCATE(1) or LONGFILE_GNU(2).
102 * This specifies the treatment of long file names (names >= TarConstants.NAMELEN).
103 * Default is LONGFILE_ERROR.
104 * @param longFileMode the mode to use
105 */
106 public void setLongFileMode(int longFileMode) {
107 this.longFileMode = longFileMode;
108 }
109
110
111 /**
112 * Sets the debugging flag.
113 *
114 * @param debugF True to turn on debugging.
115 */
116 public void setDebug(boolean debugF) {
117 this.debug = debugF;
118 }
119
120 /**
121 * Sets the debugging flag in this stream's TarBuffer.
122 *
123 * @param debug True to turn on debugging.
124 */
125 public void setBufferDebug(boolean debug) {
126 this.buffer.setDebug(debug);
127 }
128
129 /**
130 * Ends the TAR archive without closing the underlying OutputStream.
131 * The result is that the two EOF records of nulls are written.
132 * @throws IOException on error
133 */
134 public void finish() throws IOException {
135 // See Bugzilla 28776 for a discussion on this
136 // http://issues.apache.org/bugzilla/show_bug.cgi?id=28776
137 this.writeEOFRecord();
138 this.writeEOFRecord();
139 }
140
141 /**
142 * Ends the TAR archive and closes the underlying OutputStream.
143 * This means that finish() is called followed by calling the
144 * TarBuffer's close().
145 * @throws IOException on error
146 */
147 public void close() throws IOException {
148 if (!closed) {
149 this.finish();
150 this.buffer.close();
151 out.close();
152 closed = true;
153 }
154 }
155
156 /**
157 * Get the record size being used by this stream's TarBuffer.
158 *
159 * @return The TarBuffer record size.
160 */
161 public int getRecordSize() {
162 return this.buffer.getRecordSize();
163 }
164
165 /**
166 * Put an entry on the output stream. This writes the entry's
167 * header record and positions the output stream for writing
168 * the contents of the entry. Once this method is called, the
169 * stream is ready for calls to write() to write the entry's
170 * contents. Once the contents are written, closeEntry()
171 * <B>MUST</B> be called to ensure that all buffered data
172 * is completely written to the output stream.
173 *
174 * @param entry The TarEntry to be written to the archive.
175 * @throws IOException on error
176 */
177 public void putNextEntry(TarEntry entry) throws IOException {
178 if (entry.getName().length() >= TarConstants.NAMELEN) {
179
180 if (longFileMode == LONGFILE_GNU) {
181 // create a TarEntry for the LongLink, the contents
182 // of which are the entry's name
183 TarEntry longLinkEntry = new TarEntry(TarConstants.GNU_LONGLINK,
184 TarConstants.LF_GNUTYPE_LONGNAME);
185
186 byte[] name = entry.getName().getBytes("UTF-8");
187 longLinkEntry.setSize(name.length + 1);
188 putNextEntry(longLinkEntry);
189 write(name);
190 write(0);
191 closeEntry();
192 } else if (longFileMode != LONGFILE_TRUNCATE) {
193 throw new RuntimeException("file name '" + entry.getName()
194 + "' is too long ( > "
195 + TarConstants.NAMELEN + " bytes)");
196 }
197 }
198
199 entry.writeEntryHeader(this.recordBuf);
200 this.buffer.writeRecord(this.recordBuf);
201
202 this.currBytes = 0;
203
204 if (entry.isDirectory()) {
205 this.currSize = 0;
206 } else {
207 this.currSize = entry.getSize();
208 }
209 currName = entry.getName();
210 }
211
212 /**
213 * Close an entry. This method MUST be called for all file
214 * entries that contain data. The reason is that we must
215 * buffer data written to the stream in order to satisfy
216 * the buffer's record based writes. Thus, there may be
217 * data fragments still being assembled that must be written
218 * to the output stream before this entry is closed and the
219 * next entry written.
220 * @throws IOException on error
221 */
222 public void closeEntry() throws IOException {
223 if (this.assemLen > 0) {
224 for (int i = this.assemLen; i < this.assemBuf.length; ++i) {
225 this.assemBuf[i] = 0;
226 }
227
228 this.buffer.writeRecord(this.assemBuf);
229
230 this.currBytes += this.assemLen;
231 this.assemLen = 0;
232 }
233
234 if (this.currBytes < this.currSize) {
235 throw new IOException("entry '" + currName + "' closed at '"
236 + this.currBytes
237 + "' before the '" + this.currSize
238 + "' bytes specified in the header were written");
239 }
240 }
241
242 /**
243 * Writes a byte to the current tar archive entry.
244 *
245 * This method simply calls read( byte[], int, int ).
246 *
247 * @param b The byte written.
248 * @throws IOException on error
249 */
250 public void write(int b) throws IOException {
251 this.oneBuf[0] = (byte) b;
252
253 this.write(this.oneBuf, 0, 1);
254 }
255
256 /**
257 * Writes bytes to the current tar archive entry.
258 *
259 * This method simply calls write( byte[], int, int ).
260 *
261 * @param wBuf The buffer to write to the archive.
262 * @throws IOException on error
263 */
264 public void write(byte[] wBuf) throws IOException {
265 this.write(wBuf, 0, wBuf.length);
266 }
267
268 /**
269 * Writes bytes to the current tar archive entry. This method
270 * is aware of the current entry and will throw an exception if
271 * you attempt to write bytes past the length specified for the
272 * current entry. The method is also (painfully) aware of the
273 * record buffering required by TarBuffer, and manages buffers
274 * that are not a multiple of recordsize in length, including
275 * assembling records from small buffers.
276 *
277 * @param wBuf The buffer to write to the archive.
278 * @param wOffset The offset in the buffer from which to get bytes.
279 * @param numToWrite The number of bytes to write.
280 * @throws IOException on error
281 */
282 public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
283 if ((this.currBytes + numToWrite) > this.currSize) {
284 throw new IOException("request to write '" + numToWrite
285 + "' bytes exceeds size in header of '"
286 + this.currSize + "' bytes for entry '"
287 + currName + "'");
288
289 //
290 // We have to deal with assembly!!!
291 // The programmer can be writing little 32 byte chunks for all
292 // we know, and we must assemble complete records for writing.
293 // REVIEW Maybe this should be in TarBuffer? Could that help to
294 // eliminate some of the buffer copying.
295 //
296 }
297
298 if (this.assemLen > 0) {
299 if ((this.assemLen + numToWrite) >= this.recordBuf.length) {
300 int aLen = this.recordBuf.length - this.assemLen;
301
302 System.arraycopy(this.assemBuf, 0, this.recordBuf, 0,
303 this.assemLen);
304 System.arraycopy(wBuf, wOffset, this.recordBuf,
305 this.assemLen, aLen);
306 this.buffer.writeRecord(this.recordBuf);
307
308 this.currBytes += this.recordBuf.length;
309 wOffset += aLen;
310 numToWrite -= aLen;
311 this.assemLen = 0;
312 } else {
313 System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
314 numToWrite);
315
316 wOffset += numToWrite;
317 this.assemLen += numToWrite;
318 numToWrite = 0;
319 }
320 }
321
322 //
323 // When we get here we have EITHER:
324 // o An empty "assemble" buffer.
325 // o No bytes to write (numToWrite == 0)
326 //
327 while (numToWrite > 0) {
328 if (numToWrite < this.recordBuf.length) {
329 System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
330 numToWrite);
331
332 this.assemLen += numToWrite;
333
334 break;
335 }
336
337 this.buffer.writeRecord(wBuf, wOffset);
338
339 int num = this.recordBuf.length;
340
341 this.currBytes += num;
342 numToWrite -= num;
343 wOffset += num;
344 }
345 }
346
347 /**
348 * Write an EOF (end of archive) record to the tar archive.
349 * An EOF record consists of a record of all zeros.
350 */
351 private void writeEOFRecord() throws IOException {
352 for (int i = 0; i < this.recordBuf.length; ++i) {
353 this.recordBuf[i] = 0;
354 }
355
356 this.buffer.writeRecord(this.recordBuf);
357 }
358 }
359
360
361