TarOutputStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // TarOutputStream.cs
  2. // Copyright (C) 2001 Mike Krueger
  3. //
  4. // This program is free software; you can redistribute it and/or
  5. // modify it under the terms of the GNU General Public License
  6. // as published by the Free Software Foundation; either version 2
  7. // of the License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program; if not, write to the Free Software
  16. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. //
  18. // Linking this library statically or dynamically with other modules is
  19. // making a combined work based on this library. Thus, the terms and
  20. // conditions of the GNU General Public License cover the whole
  21. // combination.
  22. //
  23. // As a special exception, the copyright holders of this library give you
  24. // permission to link this library with independent modules to produce an
  25. // executable, regardless of the license terms of these independent
  26. // modules, and to copy and distribute the resulting executable under
  27. // terms of your choice, provided that you also meet, for each linked
  28. // independent module, the terms and conditions of the license of that
  29. // module. An independent module is a module which is not derived from
  30. // or based on this library. If you modify this library, you may extend
  31. // this exception to your version of the library, but you are not
  32. // obligated to do so. If you do not wish to do so, delete this
  33. // exception statement from your version.
  34. using System;
  35. using System.IO;
  36. using System.Text;
  37. namespace ICSharpCode.SharpZipLib.Tar
  38. {
  39. /// <summary>
  40. /// The TarOutputStream writes a UNIX tar archive as an OutputStream.
  41. /// Methods are provided to put entries, and then write their contents
  42. /// by writing to this stream using write().
  43. /// </summary>
  44. /// public
  45. public class TarOutputStream : Stream
  46. {
  47. protected bool debug;
  48. protected int currSize;
  49. protected int currBytes;
  50. protected byte[] blockBuf;
  51. protected int assemLen;
  52. protected byte[] assemBuf;
  53. protected TarBuffer buffer;
  54. protected Stream outputStream;
  55. /// <summary>
  56. /// I needed to implement the abstract member.
  57. /// </summary>
  58. public override bool CanRead
  59. {
  60. get
  61. {
  62. return outputStream.CanRead;
  63. }
  64. }
  65. /// <summary>
  66. /// I needed to implement the abstract member.
  67. /// </summary>
  68. public override bool CanSeek
  69. {
  70. get
  71. {
  72. return outputStream.CanSeek; // -jr- Should be false?
  73. }
  74. }
  75. /// <summary>
  76. /// I needed to implement the abstract member.
  77. /// </summary>
  78. public override bool CanWrite
  79. {
  80. get
  81. {
  82. return outputStream.CanWrite;
  83. }
  84. }
  85. /// <summary>
  86. /// I needed to implement the abstract member.
  87. /// </summary>
  88. public override long Length
  89. {
  90. get
  91. {
  92. return outputStream.Length;
  93. }
  94. }
  95. /// <summary>
  96. /// I needed to implement the abstract member.
  97. /// </summary>
  98. public override long Position
  99. {
  100. get
  101. {
  102. return outputStream.Position;
  103. }
  104. set
  105. {
  106. outputStream.Position = value;
  107. }
  108. }
  109. /// <summary>
  110. /// I needed to implement the abstract member.
  111. /// </summary>
  112. public override long Seek(long offset, SeekOrigin origin)
  113. {
  114. return outputStream.Seek(offset, origin);
  115. }
  116. /// <summary>
  117. /// I needed to implement the abstract member.
  118. /// </summary>
  119. public override void SetLength(long val)
  120. {
  121. outputStream.SetLength(val);
  122. }
  123. /// <summary>
  124. /// I needed to implement the abstract member.
  125. /// </summary>
  126. public override int ReadByte()
  127. {
  128. return outputStream.ReadByte();
  129. }
  130. /// <summary>
  131. /// I needed to implement the abstract member.
  132. /// </summary>
  133. public override int Read(byte[] b, int off, int len)
  134. {
  135. return outputStream.Read(b, off, len);
  136. }
  137. public override void Flush()
  138. {
  139. outputStream.Flush();
  140. }
  141. public TarOutputStream(Stream outputStream) : this(outputStream, TarBuffer.DefaultBlockFactor)
  142. {
  143. }
  144. public TarOutputStream(Stream outputStream, int blockFactor)
  145. {
  146. this.outputStream = outputStream;
  147. this.buffer = TarBuffer.CreateOutputTarBuffer(outputStream, blockFactor);
  148. this.debug = false;
  149. this.assemLen = 0;
  150. this.assemBuf = new byte[TarBuffer.BlockSize];
  151. this.blockBuf = new byte[TarBuffer.BlockSize];
  152. }
  153. /// <summary>
  154. /// Sets the debugging flag.
  155. /// </summary>
  156. /// <param name = "debugFlag">
  157. /// True to turn on debugging.
  158. /// </param>
  159. public void SetDebug(bool debugFlag)
  160. {
  161. this.debug = debugFlag;
  162. SetBufferDebug(debugFlag);
  163. }
  164. public void SetBufferDebug(bool debug)
  165. {
  166. this.buffer.SetDebug(debug);
  167. }
  168. /// <summary>
  169. /// Ends the TAR archive without closing the underlying OutputStream.
  170. /// The result is that the EOF record of nulls is written.
  171. /// </summary>
  172. public void Finish()
  173. {
  174. this.WriteEOFRecord();
  175. }
  176. /// <summary>
  177. /// Ends the TAR archive and closes the underlying OutputStream.
  178. /// This means that finish() is called followed by calling the
  179. /// TarBuffer's close().
  180. /// </summary>
  181. public override void Close()
  182. {
  183. this.Finish();
  184. this.buffer.Close();
  185. }
  186. /// <summary>
  187. /// Get the record size being used by this stream's TarBuffer.
  188. /// </summary>
  189. /// <returns>
  190. /// The TarBuffer record size.
  191. /// </returns>
  192. public int GetRecordSize()
  193. {
  194. return this.buffer.GetRecordSize();
  195. }
  196. /// <summary>
  197. /// Put an entry on the output stream. This writes the entry's
  198. /// header and positions the output stream for writing
  199. /// the contents of the entry. Once this method is called, the
  200. /// stream is ready for calls to write() to write the entry's
  201. /// contents. Once the contents are written, closeEntry()
  202. /// <B>MUST</B> be called to ensure that all buffered data
  203. /// is completely written to the output stream.
  204. /// </summary>
  205. /// <param name="entry">
  206. /// The TarEntry to be written to the archive.
  207. /// </param>
  208. public void PutNextEntry(TarEntry entry)
  209. {
  210. if (entry.TarHeader.name.Length > TarHeader.NAMELEN)
  211. {
  212. TarHeader longHeader = new TarHeader();
  213. longHeader.typeFlag = TarHeader.LF_GNU_LONGNAME;
  214. longHeader.name.Append("././@LongLink");
  215. longHeader.userId = 0;
  216. longHeader.groupId = 0;
  217. longHeader.groupName.Length = 0;
  218. longHeader.userName.Length = 0;
  219. longHeader.linkName.Length = 0;
  220. longHeader.size = entry.TarHeader.name.Length;
  221. Console.WriteLine("TarOutputStream: PutNext entry Long name found size = " + longHeader.size); // DEBUG
  222. longHeader.WriteHeader(this.blockBuf);
  223. this.buffer.WriteBlock(this.blockBuf); // Add special long filename header block
  224. int nameCharIndex = 0;
  225. while (nameCharIndex < entry.TarHeader.name.Length)
  226. {
  227. TarHeader.GetNameBytes(entry.TarHeader.name, nameCharIndex, this.blockBuf, 0, TarBuffer.BlockSize);
  228. nameCharIndex += TarBuffer.BlockSize;
  229. this.buffer.WriteBlock(this.blockBuf);
  230. }
  231. }
  232. entry.WriteEntryHeader(this.blockBuf);
  233. this.buffer.WriteBlock(this.blockBuf);
  234. this.currBytes = 0;
  235. this.currSize = entry.IsDirectory ? 0 : (int)entry.Size;
  236. }
  237. /// <summary>
  238. /// Close an entry. This method MUST be called for all file
  239. /// entries that contain data. The reason is that we must
  240. /// buffer data written to the stream in order to satisfy
  241. /// the buffer's block based writes. Thus, there may be
  242. /// data fragments still being assembled that must be written
  243. /// to the output stream before this entry is closed and the
  244. /// next entry written.
  245. /// </summary>
  246. public void CloseEntry()
  247. {
  248. if (this.assemLen > 0)
  249. {
  250. for (int i = this.assemLen; i < this.assemBuf.Length; ++i)
  251. {
  252. this.assemBuf[i] = 0;
  253. }
  254. this.buffer.WriteBlock(this.assemBuf);
  255. this.currBytes += this.assemLen;
  256. this.assemLen = 0;
  257. }
  258. if (this.currBytes < this.currSize)
  259. {
  260. throw new IOException("entry closed at '" + this.currBytes + "' before the '" + this.currSize + "' bytes specified in the header were written");
  261. }
  262. }
  263. /// <summary>
  264. /// Writes a byte to the current tar archive entry.
  265. /// This method simply calls Write(byte[], int, int).
  266. /// </summary>
  267. /// <param name="b">
  268. /// The byte written.
  269. /// </param>
  270. public override void WriteByte(byte b)
  271. {
  272. this.Write(new byte[] { b }, 0, 1);
  273. }
  274. /// <summary>
  275. /// Writes bytes to the current tar archive entry. This method
  276. /// is aware of the current entry and will throw an exception if
  277. /// you attempt to write bytes past the length specified for the
  278. /// current entry. The method is also (painfully) aware of the
  279. /// record buffering required by TarBuffer, and manages buffers
  280. /// that are not a multiple of recordsize in length, including
  281. /// assembling records from small buffers.
  282. /// </summary>
  283. /// <param name = "wBuf">
  284. /// The buffer to write to the archive.
  285. /// </param>
  286. /// <param name = "wOffset">
  287. /// The offset in the buffer from which to get bytes.
  288. /// </param>
  289. /// <param name = "numToWrite">
  290. /// The number of bytes to write.
  291. /// </param>
  292. public override void Write(byte[] wBuf, int wOffset, int numToWrite)
  293. {
  294. if ((this.currBytes + numToWrite) > this.currSize)
  295. {
  296. throw new IOException("request to write '" + numToWrite + "' bytes exceeds size in header of '" + this.currSize + "' bytes");
  297. }
  298. //
  299. // We have to deal with assembly!!!
  300. // The programmer can be writing little 32 byte chunks for all
  301. // we know, and we must assemble complete records for writing.
  302. // REVIEW Maybe this should be in TarBuffer? Could that help to
  303. // eliminate some of the buffer copying.
  304. //
  305. if (this.assemLen > 0)
  306. {
  307. if ((this.assemLen + numToWrite ) >= this.blockBuf.Length)
  308. {
  309. int aLen = this.blockBuf.Length - this.assemLen;
  310. Array.Copy(this.assemBuf, 0, this.blockBuf, 0, this.assemLen);
  311. Array.Copy(wBuf, wOffset, this.blockBuf, this.assemLen, aLen);
  312. this.buffer.WriteBlock(this.blockBuf);
  313. this.currBytes += this.blockBuf.Length;
  314. wOffset += aLen;
  315. numToWrite -= aLen;
  316. this.assemLen = 0;
  317. }
  318. else
  319. {// ( (this.assemLen + numToWrite ) < this.blockBuf.length )
  320. Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
  321. wOffset += numToWrite;
  322. this.assemLen += numToWrite;
  323. numToWrite -= numToWrite;
  324. }
  325. }
  326. //
  327. // When we get here we have EITHER:
  328. // o An empty "assemble" buffer.
  329. // o No bytes to write (numToWrite == 0)
  330. //
  331. while (numToWrite > 0)
  332. {
  333. if (numToWrite < this.blockBuf.Length)
  334. {
  335. Array.Copy(wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite);
  336. this.assemLen += numToWrite;
  337. break;
  338. }
  339. this.buffer.WriteBlock(wBuf, wOffset);
  340. int num = this.blockBuf.Length;
  341. this.currBytes += num;
  342. numToWrite -= num;
  343. wOffset += num;
  344. }
  345. }
  346. /// <summary>
  347. /// Write an EOF (end of archive) record to the tar archive.
  348. /// An EOF record consists of a record of all zeros.
  349. /// </summary>
  350. void WriteEOFRecord()
  351. {
  352. for (int i = 0; i < this.blockBuf.Length; ++i)
  353. {
  354. this.blockBuf[i] = 0;
  355. }
  356. this.buffer.WriteBlock(this.blockBuf);
  357. }
  358. }
  359. }
  360. /* The original Java file had this header:
  361. ** Authored by Timothy Gerard Endres
  362. ** <mailto:[email protected]> <http://www.trustice.com>
  363. **
  364. ** This work has been placed into the public domain.
  365. ** You may use this work in any way and for any purpose you wish.
  366. **
  367. ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
  368. ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
  369. ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
  370. ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
  371. ** REDISTRIBUTION OF THIS SOFTWARE.
  372. **
  373. */