1:   // TarInputStream.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:  
35:   using System;
36:   using System.IO;
37:   using System.Text;
38:  
39:   namespace ICSharpCode.SharpZipLib.Tar {
40:       
41:       /// <summary>
42:       /// The TarInputStream reads a UNIX tar archive as an InputStream.
43:       /// methods are provided to position at each successive entry in
44:       /// the archive, and the read each entry as a normal input stream
45:       /// using read().
46:       /// </summary>
47:       public class TarInputStream : Stream
48:       {
49:           protected bool debug;
50:           protected bool hasHitEOF;
51:           
52:           protected int entrySize;
53:           protected int entryOffset;
54:           
55:           protected byte[] readBuf;
56:           
57:           protected TarBuffer buffer;
58:           protected TarEntry  currEntry;
59:           protected IEntryFactory eFactory;
60:           
61:           Stream inputStream;
62:           /// <summary>
63:           /// I needed to implement the abstract member.
64:           /// </summary>
65:           public override bool CanRead {
66:               get {
67:                   return inputStream.CanRead;
68:               }
69:           }
70:           
71:           /// <summary>
72:           /// I needed to implement the abstract member.
73:           /// </summary>
74:           public override bool CanSeek {
75:               get {
76:                   return inputStream.CanSeek;
77:               }
78:           }
79:           
80:           /// <summary>
81:           /// I needed to implement the abstract member.
82:           /// </summary>
83:           public override bool CanWrite {
84:               get {
85:                   return inputStream.CanWrite;
86:               }
87:           }
88:           
89:           /// <summary>
90:           /// I needed to implement the abstract member.
91:           /// </summary>
92:           public override long Length {
93:               get {
94:                   return inputStream.Length;
95:               }
96:           }
97:           
98:           /// <summary>
99:           /// I needed to implement the abstract member.
100:           /// </summary>
101:           public override long Position {
102:               get {
103:                   return inputStream.Position;
104:               }
105:               set {
106:                   inputStream.Position value;
107:               }
108:           }
109:           
110:           /// <summary>
111:           /// Flushes the baseInputStream
112:           /// </summary>
113:           public override void Flush()
114:           {
115:               inputStream.Flush();
116:           }
117:           
118:           /// <summary>
119:           /// I needed to implement the abstract member.
120:           /// </summary>
121:           public override long Seek(long offsetSeekOrigin origin)
122:           {
123:               return inputStream.Seek(offsetorigin);
124:           }
125:           
126:           /// <summary>
127:           /// I needed to implement the abstract member.
128:           /// </summary>
129:           public override void SetLength(long val)
130:           {
131:               inputStream.SetLength(val);
132:           }
133:           
134:           /// <summary>
135:           /// I needed to implement the abstract member.
136:           /// </summary>
137:           public override void Write(byte[] arrayint offsetint count)
138:           {
139:               inputStream.Write(arrayoffsetcount);
140:           }
141:           
142:           /// <summary>
143:           /// I needed to implement the abstract member.
144:           /// </summary>
145:           public override void WriteByte(byte val)
146:           {
147:               inputStream.WriteByte(val);
148:           }
149:               
150:           
151:           public TarInputStream(Stream inputStreamthis(inputStreamTarBuffer.DEFAULT_BLKSIZETarBuffer.DEFAULT_RCDSIZE)
152:           {
153:           }
154:           
155:           public TarInputStream(Stream inputStreamint blockSizethis(inputStreamblockSizeTarBuffer.DEFAULT_RCDSIZE)
156:           {
157:           }
158:           
159:           public TarInputStream(Stream inputStreamint blockSizeint recordSize)
160:           {
161:               this.inputStream inputStream;
162:               this.buffer      TarBuffer.CreateInputTarBuffer(inputStreamblockSizerecordSize);
163:               
164:               this.readBuf null;
165:               this.debug     false;
166:               this.hasHitEOF false;
167:               this.eFactory  null;
168:           }
169:           
170:           public void SetDebug(bool debugF)
171:           {
172:               this.debug debugF;
173:               SetBufferDebug(debugF);
174:           }
175:           public void SetBufferDebug(bool debug)
176:           {
177:               this.buffer.SetDebug(debug);
178:           }
179:           
180:           
181:           
182:           public void SetEntryFactory(IEntryFactory factory)
183:           {
184:               this.eFactory factory;
185:           }
186:           
187:           /// <summary>
188:           /// Closes this stream. Calls the TarBuffer's close() method.
189:           /// The underlying stream is closed by the TarBuffer.
190:           /// </summary>
191:           public override void Close()
192:           {
193:               this.buffer.Close();
194:           }
195:           
196:           /// <summary>
197:           /// Get the record size being used by this stream's TarBuffer.
198:           /// </summary>
199:           /// <returns>
200:           /// TarBuffer record size.
201:           /// </returns>
202:           public int GetRecordSize()
203:           {
204:               return this.buffer.GetRecordSize();
205:           }
206:           
207:           /// <summary>
208:           /// Get the available data that can be read from the current
209:           /// entry in the archive. This does not indicate how much data
210:           /// is left in the entire archive, only in the current entry.
211:           /// This value is determined from the entry's size header field
212:           /// and the amount of data already read from the current entry.
213:           /// </summary>
214:           /// <returns>
215:           /// The number of available bytes for the current entry.
216:           /// </returns>
217:           public int Available {
218:               get {
219:                   return this.entrySize this.entryOffset;
220:               }
221:           }
222:           
223:           /// <summary>
224:           /// Skip bytes in the input buffer. This skips bytes in the
225:           /// current entry's data, not the entire archive, and will
226:           /// stop at the end of the current entry's data if the number
227:           /// to skip extends beyond that point.
228:           /// </summary>
229:           /// <param name="numToSkip">
230:           /// The number of bytes to skip.
231:           /// </param>
232:           public void Skip(int numToSkip)
233:           {
234:               // REVIEW
235:               // This is horribly inefficient, but it ensures that we
236:               // properly skip over bytes via the TarBuffer...
237:               //
238:               byte[] skipBuf new byte[1024];
239:               
240:               for (int num numToSkipnum 0;){
241:                   int numRead this.Read(skipBuf0, (num skipBuf.Length skipBuf.Length : num));
242:                   
243:                   if (numRead == -1) {
244:                       break;
245:                   }
246:                   
247:                   num -= numRead;
248:               }
249:           }
250:           
251:           /// <summary>
252:           /// Since we do not support marking just yet, we return false.
253:           /// </summary>
254:           public bool IsMarkSupported {
255:               get {
256:                   return false;
257:               }
258:           }
259:           
260:           /// <summary>
261:           /// Since we do not support marking just yet, we do nothing.
262:           /// </summary>
263:           /// <param name ="markLimit">
264:           /// The limit to mark.
265:           /// </param>
266:           public void Mark(int markLimit)
267:           {
268:           }
269:           
270:           /// <summary>
271:           /// Since we do not support marking just yet, we do nothing.
272:           /// </summary>
273:           public void Reset()
274:           {
275:           }
276:           
277:           /// <summary>
278:           /// Get the next entry in this tar archive. This will skip
279:           /// over any remaining data in the current entry, if there
280:           /// is one, and place the input stream at the header of the
281:           /// next entry, and read the header and instantiate a new
282:           /// TarEntry from the header bytes and return that entry.
283:           /// If there are no more entries in the archive, null will
284:           /// be returned to indicate that the end of the archive has
285:           /// been reached.
286:           /// </summary>
287:           /// <returns>
288:           /// The next TarEntry in the archive, or null.
289:           /// </returns>
290:           public TarEntry GetNextEntry()
291:           {
292:               if (this.hasHitEOF) {
293:                   return null;
294:               }
295:               
296:               if (this.currEntry != null) {
297:                   int numToSkip this.entrySize this.entryOffset;
298:                   
299:                   if (this.debug) {
300:                       Console.Error.WriteLine("TarInputStream: SKIP currENTRY '" this.currEntry.Name "' SZ " this.entrySize " OFF " this.entryOffset "  skipping " numToSkip " bytes");
301:                   }
302:                   
303:                   if (numToSkip 0) {
304:                       this.Skip(numToSkip);
305:                   }
306:                   
307:                   this.readBuf null;
308:               }
309:               
310:               byte[] headerBuf this.buffer.ReadRecord();
311:               
312:               if (headerBuf == null) {
313:                   if (this.debug) {
314:                       Console.Error.WriteLine("READ NULL RECORD");
315:                   }
316:                   
317:                   this.hasHitEOF true;
318:               else if (this.buffer.IsEOFRecord(headerBuf)) {
319:                   if (this.debug) {
320:                       Console.Error.WriteLine"READ EOF RECORD" );
321:                   }
322:                   
323:                   this.hasHitEOF true;
324:               }
325:               
326:               if (this.hasHitEOF) {
327:                   this.currEntry null;
328:               else {
329:                   try {
330:                       if (this.eFactory == null) {
331:                           this.currEntry new TarEntry(headerBuf);
332:                       else {
333:                           this.currEntry this.eFactory.CreateEntry(headerBuf);
334:                       }
335:                       
336:                       if (!(headerBuf[257] == 'u' && headerBuf[258] == 's' && headerBuf[259] == 't' && headerBuf[260] == 'a' && headerBuf[261] == 'r')) {
337:                           throw new InvalidHeaderException("header magic is not 'ustar', but '" headerBuf[257] + headerBuf[258] + headerBuf[259] + headerBuf[260] + headerBuf[261] + 
338:                                                            "', or (dec) " + ((int)headerBuf[257]) + ", " + ((int)headerBuf[258]) + ", " + ((int)headerBuf[259]) + ", " + ((int)headerBuf[260]) + ", " + ((int)headerBuf[261]));
339:                       }
340:                               
341:                       if (this.debug) {
342:                           Console.Error.WriteLine("TarInputStream: SET CURRENTRY '" this.currEntry.Name "' size = " this.currEntry.Size);
343:                       }
344:               
345:                       this.entryOffset 0;
346:                       
347:                       // REVIEW How do we resolve this discrepancy?!
348:                       this.entrySize = (intthis.currEntry.Size;
349:                   catch (InvalidHeaderException ex) {
350:                       this.entrySize 0;
351:                       this.entryOffset 0;
352:                       this.currEntry null;
353:                       throw new InvalidHeaderException("bad header in block " this.buffer.GetCurrentBlockNum() + " record " this.buffer.GetCurrentRecordNum() + ", " ex.Message);
354:                   }
355:               }
356:               return this.currEntry;
357:           }
358:           
359:           /// <summary>
360:           /// Reads a byte from the current tar archive entry.
361:           /// This method simply calls read(byte[], int, int).
362:           /// </summary>
363:           public override int ReadByte()
364:           {
365:               byte[] oneByteBuffer new byte[1];
366:               int num this.Read(oneByteBuffer01);
367:               if (num <= 0) { // return -1 to indicate that no byte was read.
368:                   return -1;
369:               }
370:               return (int)oneByteBuffer[0];
371:           }
372:           
373:           /// <summary>
374:           /// Reads bytes from the current tar archive entry.
375:           /// 
376:           /// This method is aware of the boundaries of the current
377:           /// entry in the archive and will deal with them as if they
378:           /// entry in the archive and will deal with them as if they
379:           /// </summary>
380:           /// <param name="buf">
381:           /// The buffer into which to place bytes read.
382:           /// </param>
383:           /// <param name="offset">
384:           /// The offset at which to place bytes read.
385:           /// </param>
386:           /// <param name="numToRead">
387:           /// The number of bytes to read.
388:           /// </param>
389:           /// <returns>
390:           /// The number of bytes read, or -1 at EOF.