You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

TarInputStream.java 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. /*
  55. * This package is based on the work done by Timothy Gerard Endres
  56. * (time@ice.com) to whom the Ant project is very grateful for his great code.
  57. */
  58. package org.apache.tools.tar;
  59. import java.io.FilterInputStream;
  60. import java.io.IOException;
  61. import java.io.InputStream;
  62. import java.io.OutputStream;
  63. /**
  64. * The TarInputStream reads a UNIX tar archive as an InputStream.
  65. * methods are provided to position at each successive entry in
  66. * the archive, and the read each entry as a normal input stream
  67. * using read().
  68. *
  69. * @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
  70. * @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
  71. */
  72. public class TarInputStream extends FilterInputStream {
  73. protected boolean debug;
  74. protected boolean hasHitEOF;
  75. protected int entrySize;
  76. protected int entryOffset;
  77. protected byte[] oneBuf;
  78. protected byte[] readBuf;
  79. protected TarBuffer buffer;
  80. protected TarEntry currEntry;
  81. public TarInputStream(InputStream is) {
  82. this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
  83. }
  84. public TarInputStream(InputStream is, int blockSize) {
  85. this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
  86. }
  87. public TarInputStream(InputStream is, int blockSize, int recordSize) {
  88. super(is);
  89. this.buffer = new TarBuffer(is, blockSize, recordSize);
  90. this.readBuf = null;
  91. this.oneBuf = new byte[1];
  92. this.debug = false;
  93. this.hasHitEOF = false;
  94. }
  95. /**
  96. * Sets the debugging flag.
  97. *
  98. * @param debugF True to turn on debugging.
  99. */
  100. public void setDebug(boolean debug) {
  101. this.debug = debug;
  102. this.buffer.setDebug(debug);
  103. }
  104. /**
  105. * Closes this stream. Calls the TarBuffer's close() method.
  106. */
  107. public void close() throws IOException {
  108. this.buffer.close();
  109. }
  110. /**
  111. * Get the record size being used by this stream's TarBuffer.
  112. *
  113. * @return The TarBuffer record size.
  114. */
  115. public int getRecordSize() {
  116. return this.buffer.getRecordSize();
  117. }
  118. /**
  119. * Get the available data that can be read from the current
  120. * entry in the archive. This does not indicate how much data
  121. * is left in the entire archive, only in the current entry.
  122. * This value is determined from the entry's size header field
  123. * and the amount of data already read from the current entry.
  124. *
  125. *
  126. * @return The number of available bytes for the current entry.
  127. */
  128. public int available() throws IOException {
  129. return this.entrySize - this.entryOffset;
  130. }
  131. /**
  132. * Skip bytes in the input buffer. This skips bytes in the
  133. * current entry's data, not the entire archive, and will
  134. * stop at the end of the current entry's data if the number
  135. * to skip extends beyond that point.
  136. *
  137. * @param numToSkip The number of bytes to skip.
  138. */
  139. public long skip(long numToSkip) throws IOException {
  140. // REVIEW
  141. // This is horribly inefficient, but it ensures that we
  142. // properly skip over bytes via the TarBuffer...
  143. //
  144. byte[] skipBuf = new byte[8 * 1024];
  145. long skip = numToSkip;
  146. while (skip > 0) {
  147. int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip);
  148. int numRead = this.read(skipBuf, 0, realSkip);
  149. if (numRead == -1) {
  150. break;
  151. }
  152. skip -= numRead;
  153. }
  154. return (numToSkip - skip);
  155. }
  156. /**
  157. * Since we do not support marking just yet, we return false.
  158. *
  159. * @return False.
  160. */
  161. public boolean markSupported() {
  162. return false;
  163. }
  164. /**
  165. * Since we do not support marking just yet, we do nothing.
  166. *
  167. * @param markLimit The limit to mark.
  168. */
  169. public void mark(int markLimit) {
  170. }
  171. /**
  172. * Since we do not support marking just yet, we do nothing.
  173. */
  174. public void reset() {
  175. }
  176. /**
  177. * Get the next entry in this tar archive. This will skip
  178. * over any remaining data in the current entry, if there
  179. * is one, and place the input stream at the header of the
  180. * next entry, and read the header and instantiate a new
  181. * TarEntry from the header bytes and return that entry.
  182. * If there are no more entries in the archive, null will
  183. * be returned to indicate that the end of the archive has
  184. * been reached.
  185. *
  186. * @return The next TarEntry in the archive, or null.
  187. */
  188. public TarEntry getNextEntry() throws IOException {
  189. if (this.hasHitEOF) {
  190. return null;
  191. }
  192. if (this.currEntry != null) {
  193. int numToSkip = this.entrySize - this.entryOffset;
  194. if (this.debug) {
  195. System.err.println("TarInputStream: SKIP currENTRY '"
  196. + this.currEntry.getName() + "' SZ "
  197. + this.entrySize + " OFF "
  198. + this.entryOffset + " skipping "
  199. + numToSkip + " bytes");
  200. }
  201. if (numToSkip > 0) {
  202. this.skip(numToSkip);
  203. }
  204. this.readBuf = null;
  205. }
  206. byte[] headerBuf = this.buffer.readRecord();
  207. if (headerBuf == null) {
  208. if (this.debug) {
  209. System.err.println("READ NULL RECORD");
  210. }
  211. this.hasHitEOF = true;
  212. } else if (this.buffer.isEOFRecord(headerBuf)) {
  213. if (this.debug) {
  214. System.err.println("READ EOF RECORD");
  215. }
  216. this.hasHitEOF = true;
  217. }
  218. if (this.hasHitEOF) {
  219. this.currEntry = null;
  220. } else {
  221. this.currEntry = new TarEntry(headerBuf);
  222. if (!(headerBuf[257] == 'u' && headerBuf[258] == 's'
  223. && headerBuf[259] == 't' && headerBuf[260] == 'a'
  224. && headerBuf[261] == 'r')) {
  225. this.entrySize = 0;
  226. this.entryOffset = 0;
  227. this.currEntry = null;
  228. throw new IOException("bad header in block "
  229. + this.buffer.getCurrentBlockNum()
  230. + " record "
  231. + this.buffer.getCurrentRecordNum()
  232. + ", " +
  233. "header magic is not 'ustar', but '"
  234. + headerBuf[257]
  235. + headerBuf[258]
  236. + headerBuf[259]
  237. + headerBuf[260]
  238. + headerBuf[261]
  239. + "', or (dec) "
  240. + ((int) headerBuf[257])
  241. + ", "
  242. + ((int) headerBuf[258])
  243. + ", "
  244. + ((int) headerBuf[259])
  245. + ", "
  246. + ((int) headerBuf[260])
  247. + ", "
  248. + ((int) headerBuf[261]));
  249. }
  250. if (this.debug) {
  251. System.err.println("TarInputStream: SET CURRENTRY '"
  252. + this.currEntry.getName()
  253. + "' size = "
  254. + this.currEntry.getSize());
  255. }
  256. this.entryOffset = 0;
  257. // REVIEW How do we resolve this discrepancy?!
  258. this.entrySize = (int) this.currEntry.getSize();
  259. }
  260. if (this.currEntry != null && this.currEntry.isGNULongNameEntry()) {
  261. // read in the name
  262. StringBuffer longName = new StringBuffer();
  263. byte[] buffer = new byte[256];
  264. int length = 0;
  265. while ((length = read(buffer)) >= 0) {
  266. longName.append(new String(buffer, 0, length));
  267. }
  268. getNextEntry();
  269. this.currEntry.setName(longName.toString());
  270. }
  271. return this.currEntry;
  272. }
  273. /**
  274. * Reads a byte from the current tar archive entry.
  275. *
  276. * This method simply calls read( byte[], int, int ).
  277. *
  278. * @return The byte read, or -1 at EOF.
  279. */
  280. public int read() throws IOException {
  281. int num = this.read(this.oneBuf, 0, 1);
  282. if (num == -1) {
  283. return num;
  284. } else {
  285. return (int) this.oneBuf[0];
  286. }
  287. }
  288. /**
  289. * Reads bytes from the current tar archive entry.
  290. *
  291. * This method simply calls read( byte[], int, int ).
  292. *
  293. * @param buf The buffer into which to place bytes read.
  294. * @return The number of bytes read, or -1 at EOF.
  295. */
  296. public int read(byte[] buf) throws IOException {
  297. return this.read(buf, 0, buf.length);
  298. }
  299. /**
  300. * Reads bytes from the current tar archive entry.
  301. *
  302. * This method is aware of the boundaries of the current
  303. * entry in the archive and will deal with them as if they
  304. * were this stream's start and EOF.
  305. *
  306. * @param buf The buffer into which to place bytes read.
  307. * @param offset The offset at which to place bytes read.
  308. * @param numToRead The number of bytes to read.
  309. * @return The number of bytes read, or -1 at EOF.
  310. */
  311. public int read(byte[] buf, int offset, int numToRead) throws IOException {
  312. int totalRead = 0;
  313. if (this.entryOffset >= this.entrySize) {
  314. return -1;
  315. }
  316. if ((numToRead + this.entryOffset) > this.entrySize) {
  317. numToRead = (this.entrySize - this.entryOffset);
  318. }
  319. if (this.readBuf != null) {
  320. int sz = (numToRead > this.readBuf.length) ? this.readBuf.length
  321. : numToRead;
  322. System.arraycopy(this.readBuf, 0, buf, offset, sz);
  323. if (sz >= this.readBuf.length) {
  324. this.readBuf = null;
  325. } else {
  326. int newLen = this.readBuf.length - sz;
  327. byte[] newBuf = new byte[newLen];
  328. System.arraycopy(this.readBuf, sz, newBuf, 0, newLen);
  329. this.readBuf = newBuf;
  330. }
  331. totalRead += sz;
  332. numToRead -= sz;
  333. offset += sz;
  334. }
  335. while (numToRead > 0) {
  336. byte[] rec = this.buffer.readRecord();
  337. if (rec == null) {
  338. // Unexpected EOF!
  339. throw new IOException("unexpected EOF with " + numToRead
  340. + " bytes unread");
  341. }
  342. int sz = numToRead;
  343. int recLen = rec.length;
  344. if (recLen > sz) {
  345. System.arraycopy(rec, 0, buf, offset, sz);
  346. this.readBuf = new byte[recLen - sz];
  347. System.arraycopy(rec, sz, this.readBuf, 0, recLen - sz);
  348. } else {
  349. sz = recLen;
  350. System.arraycopy(rec, 0, buf, offset, recLen);
  351. }
  352. totalRead += sz;
  353. numToRead -= sz;
  354. offset += sz;
  355. }
  356. this.entryOffset += totalRead;
  357. return totalRead;
  358. }
  359. /**
  360. * Copies the contents of the current tar archive entry directly into
  361. * an output stream.
  362. *
  363. * @param out The OutputStream into which to write the entry's data.
  364. */
  365. public void copyEntryContents(OutputStream out) throws IOException {
  366. byte[] buf = new byte[32 * 1024];
  367. while (true) {
  368. int numRead = this.read(buf, 0, buf.length);
  369. if (numRead == -1) {
  370. break;
  371. }
  372. out.write(buf, 0, numRead);
  373. }
  374. }
  375. }