blob: ff86ddf9f34d169cd43d17075e3294c8b880c0d2 [file] [log] [blame]
Torsten Curdt70c83202009-01-12 11:09:21 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.commons.compress.archivers.cpio;
20
Sebastian Bazleyfec51a12009-03-31 00:35:56 +000021import java.io.File;
Torsten Curdt70c83202009-01-12 11:09:21 +000022import java.io.IOException;
23import java.io.OutputStream;
Stefan Bodewig90b73bf2013-08-10 17:04:41 +000024import java.nio.ByteBuffer;
Torsten Curdt70c83202009-01-12 11:09:21 +000025import java.util.HashMap;
26
27import org.apache.commons.compress.archivers.ArchiveEntry;
28import org.apache.commons.compress.archivers.ArchiveOutputStream;
Stefan Bodewig90b73bf2013-08-10 17:04:41 +000029import org.apache.commons.compress.archivers.zip.ZipEncoding;
30import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
Sebastian Bazley865686a2009-04-14 23:21:52 +000031import org.apache.commons.compress.utils.ArchiveUtils;
Stefan Bodewig90b73bf2013-08-10 17:04:41 +000032import org.apache.commons.compress.utils.CharsetNames;
Torsten Curdt70c83202009-01-12 11:09:21 +000033
34/**
Stefan Bodewig176337a2009-03-23 09:10:50 +000035 * CPIOArchiveOutputStream is a stream for writing CPIO streams. All formats of
36 * CPIO are supported (old ASCII, old binary, new portable format and the new
37 * portable format with CRC).
Stefan Bodewig45e51c22013-12-22 07:03:43 +000038 *
39 * <p>An entry can be written by creating an instance of CpioArchiveEntry and fill
Stefan Bodewig176337a2009-03-23 09:10:50 +000040 * it with the necessary values and put it into the CPIO stream. Afterwards
41 * write the contents of the file into the CPIO stream. Either close the stream
Stefan Bodewig45e51c22013-12-22 07:03:43 +000042 * by calling finish() or put a next entry into the cpio stream.</p>
43 *
44 * <pre>
Stefan Bodewig176337a2009-03-23 09:10:50 +000045 * CpioArchiveOutputStream out = new CpioArchiveOutputStream(
Stefan Bodewig004124a2009-03-26 10:36:45 +000046 * new FileOutputStream(new File("test.cpio")));
Stefan Bodewig176337a2009-03-23 09:10:50 +000047 * CpioArchiveEntry entry = new CpioArchiveEntry();
Sebastian Bazleyfc425ae2009-03-26 21:23:53 +000048 * entry.setName("testfile");
Stefan Bodewig176337a2009-03-23 09:10:50 +000049 * String contents = &quot;12345&quot;;
50 * entry.setFileSize(contents.length());
Sebastian Bazleyfc425ae2009-03-26 21:23:53 +000051 * entry.setMode(CpioConstants.C_ISREG); // regular file
52 * ... set other attributes, e.g. time, number of links
Stefan Bodewige1640e52009-08-01 20:07:53 +000053 * out.putArchiveEntry(entry);
Stefan Bodewig176337a2009-03-23 09:10:50 +000054 * out.write(testContents.getBytes());
Stefan Bodewig176337a2009-03-23 09:10:50 +000055 * out.close();
Stefan Bodewig45e51c22013-12-22 07:03:43 +000056 * </pre>
57 *
58 * <p>Note: This implementation should be compatible to cpio 2.5</p>
Stefan Bodewig41f4a202009-03-20 15:42:37 +000059 *
Stefan Bodewig45e51c22013-12-22 07:03:43 +000060 * <p>This class uses mutable fields and is not considered threadsafe.</p>
Stefan Bodewig41f4a202009-03-20 15:42:37 +000061 *
Stefan Bodewig45e51c22013-12-22 07:03:43 +000062 * <p>based on code from the jRPM project (jrpm.sourceforge.net)</p>
Torsten Curdt70c83202009-01-12 11:09:21 +000063 */
Stefan Bodewig41f4a202009-03-20 15:42:37 +000064public class CpioArchiveOutputStream extends ArchiveOutputStream implements
65 CpioConstants {
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +000066
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +000067 private CpioArchiveEntry entry;
Torsten Curdt70c83202009-01-12 11:09:21 +000068
69 private boolean closed = false;
70
Christian Grobmeier285ee872009-04-27 17:48:29 +000071 /** indicates if this archive is finished */
Torsten Curdt70c83202009-01-12 11:09:21 +000072 private boolean finished;
73
Sebastian Bazleyb2656822009-03-26 15:58:05 +000074 /**
75 * See {@link CpioArchiveEntry#setFormat(short)} for possible values.
76 */
Sebastian Bazleycf373452009-03-26 15:33:09 +000077 private final short entryFormat;
Torsten Curdt70c83202009-01-12 11:09:21 +000078
Stefan Bodewig3553cda2011-08-06 13:01:16 +000079 private final HashMap<String, CpioArchiveEntry> names =
80 new HashMap<String, CpioArchiveEntry>();
Torsten Curdt70c83202009-01-12 11:09:21 +000081
82 private long crc = 0;
83
84 private long written;
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +000085
Stefan Bodewigc30b5882009-02-10 15:35:35 +000086 private final OutputStream out;
87
Stefan Bodewigb11dca22010-02-18 11:55:21 +000088 private final int blockSize;
89
90 private long nextArtificalDeviceAndInode = 1;
91
Torsten Curdt70c83202009-01-12 11:09:21 +000092 /**
Stefan Bodewig90b73bf2013-08-10 17:04:41 +000093 * The encoding to use for filenames and labels.
94 */
95 private final ZipEncoding encoding;
96
97 /**
98 * Construct the cpio output stream with a specified format, a
99 * blocksize of {@link CpioConstants#BLOCK_SIZE BLOCK_SIZE} and
100 * using ASCII as the file name encoding.
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000101 *
102 * @param out
103 * The cpio stream
104 * @param format
105 * The format of the stream
Torsten Curdt70c83202009-01-12 11:09:21 +0000106 */
107 public CpioArchiveOutputStream(final OutputStream out, final short format) {
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000108 this(out, format, BLOCK_SIZE, CharsetNames.US_ASCII);
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000109 }
110
111 /**
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000112 * Construct the cpio output stream with a specified format using
113 * ASCII as the file name encoding.
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000114 *
115 * @param out
116 * The cpio stream
117 * @param format
118 * The format of the stream
119 * @param blockSize
120 * The block size of the archive.
Sebastian Bazley39c93f42012-03-31 12:30:09 +0000121 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +0000122 * @since 1.1
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000123 */
124 public CpioArchiveOutputStream(final OutputStream out, final short format,
125 final int blockSize) {
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000126 this(out, format, blockSize, CharsetNames.US_ASCII);
127 }
128
129 /**
130 * Construct the cpio output stream with a specified format using
131 * ASCII as the file name encoding.
132 *
133 * @param out
134 * The cpio stream
135 * @param format
136 * The format of the stream
137 * @param blockSize
138 * The block size of the archive.
139 * @param encoding
140 * The encoding of file names to write - use null for
141 * the platform's default.
142 *
143 * @since 1.6
144 */
145 public CpioArchiveOutputStream(final OutputStream out, final short format,
146 final int blockSize, final String encoding) {
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000147 this.out = out;
Sebastian Bazleycf373452009-03-26 15:33:09 +0000148 switch (format) {
149 case FORMAT_NEW:
150 case FORMAT_NEW_CRC:
151 case FORMAT_OLD_ASCII:
152 case FORMAT_OLD_BINARY:
153 break;
154 default:
155 throw new IllegalArgumentException("Unknown format: "+format);
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000156
Sebastian Bazleycf373452009-03-26 15:33:09 +0000157 }
158 this.entryFormat = format;
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000159 this.blockSize = blockSize;
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000160 this.encoding = ZipEncodingHelper.getZipEncoding(encoding);
Torsten Curdt70c83202009-01-12 11:09:21 +0000161 }
162
163 /**
164 * Construct the cpio output stream. The format for this CPIO stream is the
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000165 * "new" format using ASCII encoding for file names
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000166 *
167 * @param out
168 * The cpio stream
Torsten Curdt70c83202009-01-12 11:09:21 +0000169 */
170 public CpioArchiveOutputStream(final OutputStream out) {
171 this(out, FORMAT_NEW);
172 }
173
174 /**
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000175 * Construct the cpio output stream. The format for this CPIO stream is the
176 * "new" format.
177 *
178 * @param out
179 * The cpio stream
180 * @param encoding
181 * The encoding of file names to write - use null for
182 * the platform's default.
183 * @since 1.6
184 */
185 public CpioArchiveOutputStream(final OutputStream out, String encoding) {
186 this(out, FORMAT_NEW, BLOCK_SIZE, encoding);
187 }
188
189 /**
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000190 * Check to make sure that this stream has not been closed
191 *
192 * @throws IOException
193 * if the stream is already closed
194 */
195 private void ensureOpen() throws IOException {
196 if (this.closed) {
197 throw new IOException("Stream closed");
198 }
199 }
200
201 /**
Torsten Curdt70c83202009-01-12 11:09:21 +0000202 * Begins writing a new CPIO file entry and positions the stream to the
203 * start of the entry data. Closes the current entry if still active. The
204 * current time will be used if the entry has no set modification time and
205 * the default header format will be used if no other format is specified in
206 * the entry.
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000207 *
Sebastian Bazleyd92df022009-03-30 17:32:27 +0000208 * @param entry
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000209 * the CPIO cpioEntry to be written
210 * @throws IOException
211 * if an I/O error has occurred or if a CPIO file error has
212 * occurred
Sebastian Bazleyd92df022009-03-30 17:32:27 +0000213 * @throws ClassCastException if entry is not an instance of CpioArchiveEntry
Torsten Curdt70c83202009-01-12 11:09:21 +0000214 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000215 @Override
Sebastian Bazleyd92df022009-03-30 17:32:27 +0000216 public void putArchiveEntry(ArchiveEntry entry) throws IOException {
Christian Grobmeiera45e9cb2009-04-27 17:58:04 +0000217 if(finished) {
218 throw new IOException("Stream has already been finished");
219 }
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000220
Sebastian Bazleyd92df022009-03-30 17:32:27 +0000221 CpioArchiveEntry e = (CpioArchiveEntry) entry;
Torsten Curdt70c83202009-01-12 11:09:21 +0000222 ensureOpen();
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000223 if (this.entry != null) {
Stefan Bodewig004124a2009-03-26 10:36:45 +0000224 closeArchiveEntry(); // close previous entry
Torsten Curdt70c83202009-01-12 11:09:21 +0000225 }
226 if (e.getTime() == -1) {
Stefan Bodewigcc3d4182009-08-01 15:04:22 +0000227 e.setTime(System.currentTimeMillis() / 1000);
Torsten Curdt70c83202009-01-12 11:09:21 +0000228 }
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000229
Sebastian Bazleyb2656822009-03-26 15:58:05 +0000230 final short format = e.getFormat();
Sebastian Bazley09fbdbd2009-03-27 20:47:04 +0000231 if (format != this.entryFormat){
Sebastian Bazleyb2656822009-03-26 15:58:05 +0000232 throw new IOException("Header format: "+format+" does not match existing format: "+this.entryFormat);
Torsten Curdt70c83202009-01-12 11:09:21 +0000233 }
234
235 if (this.names.put(e.getName(), e) != null) {
236 throw new IOException("duplicate entry: " + e.getName());
237 }
238
239 writeHeader(e);
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000240 this.entry = e;
Torsten Curdt70c83202009-01-12 11:09:21 +0000241 this.written = 0;
242 }
243
244 private void writeHeader(final CpioArchiveEntry e) throws IOException {
245 switch (e.getFormat()) {
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000246 case FORMAT_NEW:
Sebastian Bazley865686a2009-04-14 23:21:52 +0000247 out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW));
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000248 count(6);
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000249 writeNewEntry(e);
250 break;
251 case FORMAT_NEW_CRC:
Sebastian Bazley865686a2009-04-14 23:21:52 +0000252 out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW_CRC));
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000253 count(6);
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000254 writeNewEntry(e);
255 break;
256 case FORMAT_OLD_ASCII:
Sebastian Bazley865686a2009-04-14 23:21:52 +0000257 out.write(ArchiveUtils.toAsciiBytes(MAGIC_OLD_ASCII));
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000258 count(6);
Stefan Bodewig3f9bcc62009-02-10 14:20:05 +0000259 writeOldAsciiEntry(e);
260 break;
261 case FORMAT_OLD_BINARY:
262 boolean swapHalfWord = true;
263 writeBinaryLong(MAGIC_OLD_BINARY, 2, swapHalfWord);
264 writeOldBinaryEntry(e, swapHalfWord);
265 break;
Stefan Bodewig8caf1f72013-12-21 06:46:59 +0000266 default:
267 throw new IOException("unknown format " + e.getFormat());
Torsten Curdt70c83202009-01-12 11:09:21 +0000268 }
269 }
270
271 private void writeNewEntry(final CpioArchiveEntry entry) throws IOException {
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000272 long inode = entry.getInode();
273 long devMin = entry.getDeviceMin();
274 if (CPIO_TRAILER.equals(entry.getName())) {
275 inode = devMin = 0;
276 } else {
277 if (inode == 0 && devMin == 0) {
278 inode = nextArtificalDeviceAndInode & 0xFFFFFFFF;
279 devMin = (nextArtificalDeviceAndInode++ >> 32) & 0xFFFFFFFF;
280 } else {
281 nextArtificalDeviceAndInode =
282 Math.max(nextArtificalDeviceAndInode,
283 inode + 0x100000000L * devMin) + 1;
284 }
285 }
286
287 writeAsciiLong(inode, 8, 16);
Torsten Curdt70c83202009-01-12 11:09:21 +0000288 writeAsciiLong(entry.getMode(), 8, 16);
289 writeAsciiLong(entry.getUID(), 8, 16);
290 writeAsciiLong(entry.getGID(), 8, 16);
291 writeAsciiLong(entry.getNumberOfLinks(), 8, 16);
292 writeAsciiLong(entry.getTime(), 8, 16);
293 writeAsciiLong(entry.getSize(), 8, 16);
294 writeAsciiLong(entry.getDeviceMaj(), 8, 16);
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000295 writeAsciiLong(devMin, 8, 16);
Torsten Curdt70c83202009-01-12 11:09:21 +0000296 writeAsciiLong(entry.getRemoteDeviceMaj(), 8, 16);
297 writeAsciiLong(entry.getRemoteDeviceMin(), 8, 16);
298 writeAsciiLong(entry.getName().length() + 1, 8, 16);
299 writeAsciiLong(entry.getChksum(), 8, 16);
300 writeCString(entry.getName());
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000301 pad(entry.getHeaderPadCount());
Torsten Curdt70c83202009-01-12 11:09:21 +0000302 }
303
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000304 private void writeOldAsciiEntry(final CpioArchiveEntry entry)
305 throws IOException {
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000306 long inode = entry.getInode();
307 long device = entry.getDevice();
308 if (CPIO_TRAILER.equals(entry.getName())) {
309 inode = device = 0;
310 } else {
311 if (inode == 0 && device == 0) {
312 inode = nextArtificalDeviceAndInode & 0777777;
313 device = (nextArtificalDeviceAndInode++ >> 18) & 0777777;
314 } else {
315 nextArtificalDeviceAndInode =
316 Math.max(nextArtificalDeviceAndInode,
317 inode + 01000000 * device) + 1;
318 }
319 }
320
321 writeAsciiLong(device, 6, 8);
322 writeAsciiLong(inode, 6, 8);
Torsten Curdt70c83202009-01-12 11:09:21 +0000323 writeAsciiLong(entry.getMode(), 6, 8);
324 writeAsciiLong(entry.getUID(), 6, 8);
325 writeAsciiLong(entry.getGID(), 6, 8);
326 writeAsciiLong(entry.getNumberOfLinks(), 6, 8);
327 writeAsciiLong(entry.getRemoteDevice(), 6, 8);
328 writeAsciiLong(entry.getTime(), 11, 8);
329 writeAsciiLong(entry.getName().length() + 1, 6, 8);
330 writeAsciiLong(entry.getSize(), 11, 8);
331 writeCString(entry.getName());
332 }
333
334 private void writeOldBinaryEntry(final CpioArchiveEntry entry,
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000335 final boolean swapHalfWord) throws IOException {
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000336 long inode = entry.getInode();
337 long device = entry.getDevice();
338 if (CPIO_TRAILER.equals(entry.getName())) {
339 inode = device = 0;
340 } else {
341 if (inode == 0 && device == 0) {
342 inode = nextArtificalDeviceAndInode & 0xFFFF;
343 device = (nextArtificalDeviceAndInode++ >> 16) & 0xFFFF;
344 } else {
345 nextArtificalDeviceAndInode =
346 Math.max(nextArtificalDeviceAndInode,
347 inode + 0x10000 * device) + 1;
348 }
349 }
350
351 writeBinaryLong(device, 2, swapHalfWord);
352 writeBinaryLong(inode, 2, swapHalfWord);
Torsten Curdt70c83202009-01-12 11:09:21 +0000353 writeBinaryLong(entry.getMode(), 2, swapHalfWord);
354 writeBinaryLong(entry.getUID(), 2, swapHalfWord);
355 writeBinaryLong(entry.getGID(), 2, swapHalfWord);
356 writeBinaryLong(entry.getNumberOfLinks(), 2, swapHalfWord);
357 writeBinaryLong(entry.getRemoteDevice(), 2, swapHalfWord);
358 writeBinaryLong(entry.getTime(), 4, swapHalfWord);
359 writeBinaryLong(entry.getName().length() + 1, 2, swapHalfWord);
360 writeBinaryLong(entry.getSize(), 4, swapHalfWord);
361 writeCString(entry.getName());
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000362 pad(entry.getHeaderPadCount());
Torsten Curdt70c83202009-01-12 11:09:21 +0000363 }
364
Stefan Bodewig004124a2009-03-26 10:36:45 +0000365 /*(non-Javadoc)
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000366 *
Stefan Bodewig004124a2009-03-26 10:36:45 +0000367 * @see
368 * org.apache.commons.compress.archivers.ArchiveOutputStream#closeArchiveEntry
369 * ()
Torsten Curdt70c83202009-01-12 11:09:21 +0000370 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000371 @Override
Stefan Bodewig004124a2009-03-26 10:36:45 +0000372 public void closeArchiveEntry() throws IOException {
Christian Grobmeiera45e9cb2009-04-27 17:58:04 +0000373 if(finished) {
374 throw new IOException("Stream has already been finished");
375 }
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000376
Torsten Curdt70c83202009-01-12 11:09:21 +0000377 ensureOpen();
378
Sebastian Bazley38118ec2009-04-27 20:21:48 +0000379 if (entry == null) {
380 throw new IOException("Trying to close non-existent entry");
381 }
382
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000383 if (this.entry.getSize() != this.written) {
Torsten Curdt70c83202009-01-12 11:09:21 +0000384 throw new IOException("invalid entry size (expected "
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000385 + this.entry.getSize() + " but got " + this.written
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000386 + " bytes)");
Torsten Curdt70c83202009-01-12 11:09:21 +0000387 }
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000388 pad(this.entry.getDataPadCount());
Stefan Bodewig58c56fc2011-07-23 12:41:55 +0000389 if (this.entry.getFormat() == FORMAT_NEW_CRC
390 && this.crc != this.entry.getChksum()) {
391 throw new IOException("CRC Error");
Torsten Curdt70c83202009-01-12 11:09:21 +0000392 }
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000393 this.entry = null;
Torsten Curdt70c83202009-01-12 11:09:21 +0000394 this.crc = 0;
395 this.written = 0;
396 }
397
398 /**
399 * Writes an array of bytes to the current CPIO entry data. This method will
400 * block until all the bytes are written.
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000401 *
402 * @param b
403 * the data to be written
404 * @param off
405 * the start offset in the data
406 * @param len
407 * the number of bytes that are written
408 * @throws IOException
409 * if an I/O error has occurred or if a CPIO file error has
410 * occurred
Torsten Curdt70c83202009-01-12 11:09:21 +0000411 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000412 @Override
Sebastian Bazley9d6dbc32009-03-23 21:51:43 +0000413 public void write(final byte[] b, final int off, final int len)
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000414 throws IOException {
Torsten Curdt70c83202009-01-12 11:09:21 +0000415 ensureOpen();
416 if (off < 0 || len < 0 || off > b.length - len) {
417 throw new IndexOutOfBoundsException();
418 } else if (len == 0) {
419 return;
420 }
421
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000422 if (this.entry == null) {
Torsten Curdt70c83202009-01-12 11:09:21 +0000423 throw new IOException("no current CPIO entry");
424 }
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000425 if (this.written + len > this.entry.getSize()) {
Torsten Curdt70c83202009-01-12 11:09:21 +0000426 throw new IOException("attempt to write past end of STORED entry");
427 }
428 out.write(b, off, len);
429 this.written += len;
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000430 if (this.entry.getFormat() == FORMAT_NEW_CRC) {
Torsten Curdt70c83202009-01-12 11:09:21 +0000431 for (int pos = 0; pos < len; pos++) {
432 this.crc += b[pos] & 0xFF;
433 }
434 }
Christian Grobmeierab269432009-04-24 06:30:17 +0000435 count(len);
Torsten Curdt70c83202009-01-12 11:09:21 +0000436 }
437
438 /**
439 * Finishes writing the contents of the CPIO output stream without closing
440 * the underlying stream. Use this method when applying multiple filters in
441 * succession to the same output stream.
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000442 *
443 * @throws IOException
444 * if an I/O exception has occurred or if a CPIO file error has
445 * occurred
Torsten Curdt70c83202009-01-12 11:09:21 +0000446 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000447 @Override
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000448 public void finish() throws IOException {
Torsten Curdt70c83202009-01-12 11:09:21 +0000449 ensureOpen();
Christian Grobmeier545bfa82009-04-27 17:43:58 +0000450 if (finished) {
451 throw new IOException("This archive has already been finished");
Torsten Curdt70c83202009-01-12 11:09:21 +0000452 }
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000453
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000454 if (this.entry != null) {
Sebastian Bazleyeebf2db2009-04-27 19:14:12 +0000455 throw new IOException("This archive contains unclosed entries.");
Torsten Curdt70c83202009-01-12 11:09:21 +0000456 }
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000457 this.entry = new CpioArchiveEntry(this.entryFormat);
458 this.entry.setName(CPIO_TRAILER);
459 this.entry.setNumberOfLinks(1);
460 writeHeader(this.entry);
Stefan Bodewig004124a2009-03-26 10:36:45 +0000461 closeArchiveEntry();
Stefan Bodewig14684242010-02-17 16:00:21 +0000462
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000463 int lengthOfLastBlock = (int) (getBytesWritten() % blockSize);
Stefan Bodewig14684242010-02-17 16:00:21 +0000464 if (lengthOfLastBlock != 0) {
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000465 pad(blockSize - lengthOfLastBlock);
Stefan Bodewig14684242010-02-17 16:00:21 +0000466 }
467
Christian Grobmeier545bfa82009-04-27 17:43:58 +0000468 finished = true;
Torsten Curdt70c83202009-01-12 11:09:21 +0000469 }
470
471 /**
472 * Closes the CPIO output stream as well as the stream being filtered.
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000473 *
474 * @throws IOException
475 * if an I/O error has occurred or if a CPIO file error has
476 * occurred
Torsten Curdt70c83202009-01-12 11:09:21 +0000477 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000478 @Override
Torsten Curdt70c83202009-01-12 11:09:21 +0000479 public void close() throws IOException {
Christian Grobmeier545bfa82009-04-27 17:43:58 +0000480 if(!finished) {
481 finish();
482 }
Stefan Bodewigb11dca22010-02-18 11:55:21 +0000483
Torsten Curdt70c83202009-01-12 11:09:21 +0000484 if (!this.closed) {
Sebastian Bazley15ce51c2009-03-29 02:35:51 +0000485 out.close();
Torsten Curdt70c83202009-01-12 11:09:21 +0000486 this.closed = true;
487 }
488 }
489
Sebastian Bazleyf235f6b2009-03-27 17:51:03 +0000490 private void pad(int count) throws IOException{
491 if (count > 0){
492 byte buff[] = new byte[count];
493 out.write(buff);
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000494 count(count);
Torsten Curdt70c83202009-01-12 11:09:21 +0000495 }
496 }
497
498 private void writeBinaryLong(final long number, final int length,
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000499 final boolean swapHalfWord) throws IOException {
500 byte tmp[] = CpioUtil.long2byteArray(number, length, swapHalfWord);
Torsten Curdt70c83202009-01-12 11:09:21 +0000501 out.write(tmp);
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000502 count(tmp.length);
Torsten Curdt70c83202009-01-12 11:09:21 +0000503 }
504
505 private void writeAsciiLong(final long number, final int length,
Stefan Bodewig41f4a202009-03-20 15:42:37 +0000506 final int radix) throws IOException {
Emmanuel Bourgbb2200b2013-08-07 14:07:10 +0000507 StringBuilder tmp = new StringBuilder();
Torsten Curdt70c83202009-01-12 11:09:21 +0000508 String tmpStr;
509 if (radix == 16) {
510 tmp.append(Long.toHexString(number));
511 } else if (radix == 8) {
512 tmp.append(Long.toOctalString(number));
513 } else {
514 tmp.append(Long.toString(number));
515 }
516
517 if (tmp.length() <= length) {
518 long insertLength = length - tmp.length();
519 for (int pos = 0; pos < insertLength; pos++) {
520 tmp.insert(0, "0");
521 }
522 tmpStr = tmp.toString();
523 } else {
524 tmpStr = tmp.substring(tmp.length() - length);
525 }
Stefan Bodewig231f1df2010-02-19 05:13:31 +0000526 byte[] b = ArchiveUtils.toAsciiBytes(tmpStr);
527 out.write(b);
528 count(b.length);
Torsten Curdt70c83202009-01-12 11:09:21 +0000529 }
530
Christian Grobmeierc69371b2009-04-23 05:53:16 +0000531 /**
532 * Writes an ASCII string to the stream followed by \0
533 * @param str the String to write
534 * @throws IOException if the string couldn't be written
535 */
Torsten Curdt70c83202009-01-12 11:09:21 +0000536 private void writeCString(final String str) throws IOException {
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000537 ByteBuffer buf = encoding.encode(str);
538 final int len = buf.limit() - buf.position();
539 out.write(buf.array(), buf.arrayOffset(), len);
Sebastian Bazley479a3492009-04-15 11:19:16 +0000540 out.write('\0');
Stefan Bodewig90b73bf2013-08-10 17:04:41 +0000541 count(len + 1);
Torsten Curdt70c83202009-01-12 11:09:21 +0000542 }
543
Christian Grobmeierc69371b2009-04-23 05:53:16 +0000544 /**
545 * Creates a new ArchiveEntry. The entryName must be an ASCII encoded string.
546 *
547 * @see org.apache.commons.compress.archivers.ArchiveOutputStream#createArchiveEntry(java.io.File, java.lang.String)
548 */
Stefan Bodewig46628ef2011-08-06 16:30:40 +0000549 @Override
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000550 public ArchiveEntry createArchiveEntry(File inputFile, String entryName)
551 throws IOException {
Christian Grobmeiera45e9cb2009-04-27 17:58:04 +0000552 if(finished) {
553 throw new IOException("Stream has already been finished");
554 }
Sebastian Bazleyfec51a12009-03-31 00:35:56 +0000555 return new CpioArchiveEntry(inputFile, entryName);
556 }
557
Torsten Curdt70c83202009-01-12 11:09:21 +0000558}