Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 1 | /* |
| 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 | */ |
| 19 | package org.apache.commons.compress.archivers.cpio; |
| 20 | |
Sebastian Bazley | fec51a1 | 2009-03-31 00:35:56 +0000 | [diff] [blame] | 21 | import java.io.File; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 22 | import java.io.IOException; |
| 23 | import java.io.OutputStream; |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 24 | import java.nio.ByteBuffer; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 25 | import java.util.HashMap; |
| 26 | |
| 27 | import org.apache.commons.compress.archivers.ArchiveEntry; |
| 28 | import org.apache.commons.compress.archivers.ArchiveOutputStream; |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 29 | import org.apache.commons.compress.archivers.zip.ZipEncoding; |
| 30 | import org.apache.commons.compress.archivers.zip.ZipEncodingHelper; |
Sebastian Bazley | 865686a | 2009-04-14 23:21:52 +0000 | [diff] [blame] | 31 | import org.apache.commons.compress.utils.ArchiveUtils; |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 32 | import org.apache.commons.compress.utils.CharsetNames; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 33 | |
| 34 | /** |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 35 | * 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 Bodewig | 45e51c2 | 2013-12-22 07:03:43 +0000 | [diff] [blame^] | 38 | * |
| 39 | * <p>An entry can be written by creating an instance of CpioArchiveEntry and fill |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 40 | * 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 Bodewig | 45e51c2 | 2013-12-22 07:03:43 +0000 | [diff] [blame^] | 42 | * by calling finish() or put a next entry into the cpio stream.</p> |
| 43 | * |
| 44 | * <pre> |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 45 | * CpioArchiveOutputStream out = new CpioArchiveOutputStream( |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 46 | * new FileOutputStream(new File("test.cpio"))); |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 47 | * CpioArchiveEntry entry = new CpioArchiveEntry(); |
Sebastian Bazley | fc425ae | 2009-03-26 21:23:53 +0000 | [diff] [blame] | 48 | * entry.setName("testfile"); |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 49 | * String contents = "12345"; |
| 50 | * entry.setFileSize(contents.length()); |
Sebastian Bazley | fc425ae | 2009-03-26 21:23:53 +0000 | [diff] [blame] | 51 | * entry.setMode(CpioConstants.C_ISREG); // regular file |
| 52 | * ... set other attributes, e.g. time, number of links |
Stefan Bodewig | e1640e5 | 2009-08-01 20:07:53 +0000 | [diff] [blame] | 53 | * out.putArchiveEntry(entry); |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 54 | * out.write(testContents.getBytes()); |
Stefan Bodewig | 176337a | 2009-03-23 09:10:50 +0000 | [diff] [blame] | 55 | * out.close(); |
Stefan Bodewig | 45e51c2 | 2013-12-22 07:03:43 +0000 | [diff] [blame^] | 56 | * </pre> |
| 57 | * |
| 58 | * <p>Note: This implementation should be compatible to cpio 2.5</p> |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 59 | * |
Stefan Bodewig | 45e51c2 | 2013-12-22 07:03:43 +0000 | [diff] [blame^] | 60 | * <p>This class uses mutable fields and is not considered threadsafe.</p> |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 61 | * |
Stefan Bodewig | 45e51c2 | 2013-12-22 07:03:43 +0000 | [diff] [blame^] | 62 | * <p>based on code from the jRPM project (jrpm.sourceforge.net)</p> |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 63 | */ |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 64 | public class CpioArchiveOutputStream extends ArchiveOutputStream implements |
| 65 | CpioConstants { |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 66 | |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 67 | private CpioArchiveEntry entry; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 68 | |
| 69 | private boolean closed = false; |
| 70 | |
Christian Grobmeier | 285ee87 | 2009-04-27 17:48:29 +0000 | [diff] [blame] | 71 | /** indicates if this archive is finished */ |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 72 | private boolean finished; |
| 73 | |
Sebastian Bazley | b265682 | 2009-03-26 15:58:05 +0000 | [diff] [blame] | 74 | /** |
| 75 | * See {@link CpioArchiveEntry#setFormat(short)} for possible values. |
| 76 | */ |
Sebastian Bazley | cf37345 | 2009-03-26 15:33:09 +0000 | [diff] [blame] | 77 | private final short entryFormat; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 78 | |
Stefan Bodewig | 3553cda | 2011-08-06 13:01:16 +0000 | [diff] [blame] | 79 | private final HashMap<String, CpioArchiveEntry> names = |
| 80 | new HashMap<String, CpioArchiveEntry>(); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 81 | |
| 82 | private long crc = 0; |
| 83 | |
| 84 | private long written; |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 85 | |
Stefan Bodewig | c30b588 | 2009-02-10 15:35:35 +0000 | [diff] [blame] | 86 | private final OutputStream out; |
| 87 | |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 88 | private final int blockSize; |
| 89 | |
| 90 | private long nextArtificalDeviceAndInode = 1; |
| 91 | |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 92 | /** |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 93 | * 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 101 | * |
| 102 | * @param out |
| 103 | * The cpio stream |
| 104 | * @param format |
| 105 | * The format of the stream |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 106 | */ |
| 107 | public CpioArchiveOutputStream(final OutputStream out, final short format) { |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 108 | this(out, format, BLOCK_SIZE, CharsetNames.US_ASCII); |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | /** |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 112 | * Construct the cpio output stream with a specified format using |
| 113 | * ASCII as the file name encoding. |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 114 | * |
| 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 Bazley | 39c93f4 | 2012-03-31 12:30:09 +0000 | [diff] [blame] | 121 | * |
Gary D. Gregory | 2bd0dd4 | 2012-04-01 13:02:39 +0000 | [diff] [blame] | 122 | * @since 1.1 |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 123 | */ |
| 124 | public CpioArchiveOutputStream(final OutputStream out, final short format, |
| 125 | final int blockSize) { |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 126 | 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 Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 147 | this.out = out; |
Sebastian Bazley | cf37345 | 2009-03-26 15:33:09 +0000 | [diff] [blame] | 148 | 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 Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 156 | |
Sebastian Bazley | cf37345 | 2009-03-26 15:33:09 +0000 | [diff] [blame] | 157 | } |
| 158 | this.entryFormat = format; |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 159 | this.blockSize = blockSize; |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 160 | this.encoding = ZipEncodingHelper.getZipEncoding(encoding); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Construct the cpio output stream. The format for this CPIO stream is the |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 165 | * "new" format using ASCII encoding for file names |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 166 | * |
| 167 | * @param out |
| 168 | * The cpio stream |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 169 | */ |
| 170 | public CpioArchiveOutputStream(final OutputStream out) { |
| 171 | this(out, FORMAT_NEW); |
| 172 | } |
| 173 | |
| 174 | /** |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 175 | * 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 190 | * 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 202 | * 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 207 | * |
Sebastian Bazley | d92df02 | 2009-03-30 17:32:27 +0000 | [diff] [blame] | 208 | * @param entry |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 209 | * 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 Bazley | d92df02 | 2009-03-30 17:32:27 +0000 | [diff] [blame] | 213 | * @throws ClassCastException if entry is not an instance of CpioArchiveEntry |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 214 | */ |
Stefan Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 215 | @Override |
Sebastian Bazley | d92df02 | 2009-03-30 17:32:27 +0000 | [diff] [blame] | 216 | public void putArchiveEntry(ArchiveEntry entry) throws IOException { |
Christian Grobmeier | a45e9cb | 2009-04-27 17:58:04 +0000 | [diff] [blame] | 217 | if(finished) { |
| 218 | throw new IOException("Stream has already been finished"); |
| 219 | } |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 220 | |
Sebastian Bazley | d92df02 | 2009-03-30 17:32:27 +0000 | [diff] [blame] | 221 | CpioArchiveEntry e = (CpioArchiveEntry) entry; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 222 | ensureOpen(); |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 223 | if (this.entry != null) { |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 224 | closeArchiveEntry(); // close previous entry |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 225 | } |
| 226 | if (e.getTime() == -1) { |
Stefan Bodewig | cc3d418 | 2009-08-01 15:04:22 +0000 | [diff] [blame] | 227 | e.setTime(System.currentTimeMillis() / 1000); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 228 | } |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 229 | |
Sebastian Bazley | b265682 | 2009-03-26 15:58:05 +0000 | [diff] [blame] | 230 | final short format = e.getFormat(); |
Sebastian Bazley | 09fbdbd | 2009-03-27 20:47:04 +0000 | [diff] [blame] | 231 | if (format != this.entryFormat){ |
Sebastian Bazley | b265682 | 2009-03-26 15:58:05 +0000 | [diff] [blame] | 232 | throw new IOException("Header format: "+format+" does not match existing format: "+this.entryFormat); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 233 | } |
| 234 | |
| 235 | if (this.names.put(e.getName(), e) != null) { |
| 236 | throw new IOException("duplicate entry: " + e.getName()); |
| 237 | } |
| 238 | |
| 239 | writeHeader(e); |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 240 | this.entry = e; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 241 | this.written = 0; |
| 242 | } |
| 243 | |
| 244 | private void writeHeader(final CpioArchiveEntry e) throws IOException { |
| 245 | switch (e.getFormat()) { |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 246 | case FORMAT_NEW: |
Sebastian Bazley | 865686a | 2009-04-14 23:21:52 +0000 | [diff] [blame] | 247 | out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW)); |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 248 | count(6); |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 249 | writeNewEntry(e); |
| 250 | break; |
| 251 | case FORMAT_NEW_CRC: |
Sebastian Bazley | 865686a | 2009-04-14 23:21:52 +0000 | [diff] [blame] | 252 | out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW_CRC)); |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 253 | count(6); |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 254 | writeNewEntry(e); |
| 255 | break; |
| 256 | case FORMAT_OLD_ASCII: |
Sebastian Bazley | 865686a | 2009-04-14 23:21:52 +0000 | [diff] [blame] | 257 | out.write(ArchiveUtils.toAsciiBytes(MAGIC_OLD_ASCII)); |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 258 | count(6); |
Stefan Bodewig | 3f9bcc6 | 2009-02-10 14:20:05 +0000 | [diff] [blame] | 259 | 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 Bodewig | 8caf1f7 | 2013-12-21 06:46:59 +0000 | [diff] [blame] | 266 | default: |
| 267 | throw new IOException("unknown format " + e.getFormat()); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 268 | } |
| 269 | } |
| 270 | |
| 271 | private void writeNewEntry(final CpioArchiveEntry entry) throws IOException { |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 272 | 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 288 | 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 Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 295 | writeAsciiLong(devMin, 8, 16); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 296 | 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 Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 301 | pad(entry.getHeaderPadCount()); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 302 | } |
| 303 | |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 304 | private void writeOldAsciiEntry(final CpioArchiveEntry entry) |
| 305 | throws IOException { |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 306 | 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 323 | 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 335 | final boolean swapHalfWord) throws IOException { |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 336 | 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 353 | 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 Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 362 | pad(entry.getHeaderPadCount()); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 363 | } |
| 364 | |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 365 | /*(non-Javadoc) |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 366 | * |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 367 | * @see |
| 368 | * org.apache.commons.compress.archivers.ArchiveOutputStream#closeArchiveEntry |
| 369 | * () |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 370 | */ |
Stefan Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 371 | @Override |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 372 | public void closeArchiveEntry() throws IOException { |
Christian Grobmeier | a45e9cb | 2009-04-27 17:58:04 +0000 | [diff] [blame] | 373 | if(finished) { |
| 374 | throw new IOException("Stream has already been finished"); |
| 375 | } |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 376 | |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 377 | ensureOpen(); |
| 378 | |
Sebastian Bazley | 38118ec | 2009-04-27 20:21:48 +0000 | [diff] [blame] | 379 | if (entry == null) { |
| 380 | throw new IOException("Trying to close non-existent entry"); |
| 381 | } |
| 382 | |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 383 | if (this.entry.getSize() != this.written) { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 384 | throw new IOException("invalid entry size (expected " |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 385 | + this.entry.getSize() + " but got " + this.written |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 386 | + " bytes)"); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 387 | } |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 388 | pad(this.entry.getDataPadCount()); |
Stefan Bodewig | 58c56fc | 2011-07-23 12:41:55 +0000 | [diff] [blame] | 389 | if (this.entry.getFormat() == FORMAT_NEW_CRC |
| 390 | && this.crc != this.entry.getChksum()) { |
| 391 | throw new IOException("CRC Error"); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 392 | } |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 393 | this.entry = null; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 394 | 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 401 | * |
| 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 411 | */ |
Stefan Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 412 | @Override |
Sebastian Bazley | 9d6dbc3 | 2009-03-23 21:51:43 +0000 | [diff] [blame] | 413 | public void write(final byte[] b, final int off, final int len) |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 414 | throws IOException { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 415 | 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 Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 422 | if (this.entry == null) { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 423 | throw new IOException("no current CPIO entry"); |
| 424 | } |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 425 | if (this.written + len > this.entry.getSize()) { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 426 | throw new IOException("attempt to write past end of STORED entry"); |
| 427 | } |
| 428 | out.write(b, off, len); |
| 429 | this.written += len; |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 430 | if (this.entry.getFormat() == FORMAT_NEW_CRC) { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 431 | for (int pos = 0; pos < len; pos++) { |
| 432 | this.crc += b[pos] & 0xFF; |
| 433 | } |
| 434 | } |
Christian Grobmeier | ab26943 | 2009-04-24 06:30:17 +0000 | [diff] [blame] | 435 | count(len); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 436 | } |
| 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 Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 442 | * |
| 443 | * @throws IOException |
| 444 | * if an I/O exception has occurred or if a CPIO file error has |
| 445 | * occurred |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 446 | */ |
Stefan Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 447 | @Override |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 448 | public void finish() throws IOException { |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 449 | ensureOpen(); |
Christian Grobmeier | 545bfa8 | 2009-04-27 17:43:58 +0000 | [diff] [blame] | 450 | if (finished) { |
| 451 | throw new IOException("This archive has already been finished"); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 452 | } |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 453 | |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 454 | if (this.entry != null) { |
Sebastian Bazley | eebf2db | 2009-04-27 19:14:12 +0000 | [diff] [blame] | 455 | throw new IOException("This archive contains unclosed entries."); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 456 | } |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 457 | this.entry = new CpioArchiveEntry(this.entryFormat); |
| 458 | this.entry.setName(CPIO_TRAILER); |
| 459 | this.entry.setNumberOfLinks(1); |
| 460 | writeHeader(this.entry); |
Stefan Bodewig | 004124a | 2009-03-26 10:36:45 +0000 | [diff] [blame] | 461 | closeArchiveEntry(); |
Stefan Bodewig | 1468424 | 2010-02-17 16:00:21 +0000 | [diff] [blame] | 462 | |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 463 | int lengthOfLastBlock = (int) (getBytesWritten() % blockSize); |
Stefan Bodewig | 1468424 | 2010-02-17 16:00:21 +0000 | [diff] [blame] | 464 | if (lengthOfLastBlock != 0) { |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 465 | pad(blockSize - lengthOfLastBlock); |
Stefan Bodewig | 1468424 | 2010-02-17 16:00:21 +0000 | [diff] [blame] | 466 | } |
| 467 | |
Christian Grobmeier | 545bfa8 | 2009-04-27 17:43:58 +0000 | [diff] [blame] | 468 | finished = true; |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 469 | } |
| 470 | |
| 471 | /** |
| 472 | * Closes the CPIO output stream as well as the stream being filtered. |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 473 | * |
| 474 | * @throws IOException |
| 475 | * if an I/O error has occurred or if a CPIO file error has |
| 476 | * occurred |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 477 | */ |
Stefan Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 478 | @Override |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 479 | public void close() throws IOException { |
Christian Grobmeier | 545bfa8 | 2009-04-27 17:43:58 +0000 | [diff] [blame] | 480 | if(!finished) { |
| 481 | finish(); |
| 482 | } |
Stefan Bodewig | b11dca2 | 2010-02-18 11:55:21 +0000 | [diff] [blame] | 483 | |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 484 | if (!this.closed) { |
Sebastian Bazley | 15ce51c | 2009-03-29 02:35:51 +0000 | [diff] [blame] | 485 | out.close(); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 486 | this.closed = true; |
| 487 | } |
| 488 | } |
| 489 | |
Sebastian Bazley | f235f6b | 2009-03-27 17:51:03 +0000 | [diff] [blame] | 490 | private void pad(int count) throws IOException{ |
| 491 | if (count > 0){ |
| 492 | byte buff[] = new byte[count]; |
| 493 | out.write(buff); |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 494 | count(count); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 495 | } |
| 496 | } |
| 497 | |
| 498 | private void writeBinaryLong(final long number, final int length, |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 499 | final boolean swapHalfWord) throws IOException { |
| 500 | byte tmp[] = CpioUtil.long2byteArray(number, length, swapHalfWord); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 501 | out.write(tmp); |
Stefan Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 502 | count(tmp.length); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 503 | } |
| 504 | |
| 505 | private void writeAsciiLong(final long number, final int length, |
Stefan Bodewig | 41f4a20 | 2009-03-20 15:42:37 +0000 | [diff] [blame] | 506 | final int radix) throws IOException { |
Emmanuel Bourg | bb2200b | 2013-08-07 14:07:10 +0000 | [diff] [blame] | 507 | StringBuilder tmp = new StringBuilder(); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 508 | 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 Bodewig | 231f1df | 2010-02-19 05:13:31 +0000 | [diff] [blame] | 526 | byte[] b = ArchiveUtils.toAsciiBytes(tmpStr); |
| 527 | out.write(b); |
| 528 | count(b.length); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 529 | } |
| 530 | |
Christian Grobmeier | c69371b | 2009-04-23 05:53:16 +0000 | [diff] [blame] | 531 | /** |
| 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 Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 536 | private void writeCString(final String str) throws IOException { |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 537 | ByteBuffer buf = encoding.encode(str); |
| 538 | final int len = buf.limit() - buf.position(); |
| 539 | out.write(buf.array(), buf.arrayOffset(), len); |
Sebastian Bazley | 479a349 | 2009-04-15 11:19:16 +0000 | [diff] [blame] | 540 | out.write('\0'); |
Stefan Bodewig | 90b73bf | 2013-08-10 17:04:41 +0000 | [diff] [blame] | 541 | count(len + 1); |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 542 | } |
| 543 | |
Christian Grobmeier | c69371b | 2009-04-23 05:53:16 +0000 | [diff] [blame] | 544 | /** |
| 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 Bodewig | 46628ef | 2011-08-06 16:30:40 +0000 | [diff] [blame] | 549 | @Override |
Sebastian Bazley | fec51a1 | 2009-03-31 00:35:56 +0000 | [diff] [blame] | 550 | public ArchiveEntry createArchiveEntry(File inputFile, String entryName) |
| 551 | throws IOException { |
Christian Grobmeier | a45e9cb | 2009-04-27 17:58:04 +0000 | [diff] [blame] | 552 | if(finished) { |
| 553 | throw new IOException("Stream has already been finished"); |
| 554 | } |
Sebastian Bazley | fec51a1 | 2009-03-31 00:35:56 +0000 | [diff] [blame] | 555 | return new CpioArchiveEntry(inputFile, entryName); |
| 556 | } |
| 557 | |
Torsten Curdt | 70c8320 | 2009-01-12 11:09:21 +0000 | [diff] [blame] | 558 | } |