OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
TarArchiveReader.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.IO;
30 using System.Reflection;
31 using System.Text;
32 using log4net;
33 
34 namespace OpenSim.Framework.Serialization
35 {
39  public class TarArchiveReader
40  {
41  //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 
43  public enum TarEntryType
44  {
45  TYPE_UNKNOWN = 0,
46  TYPE_NORMAL_FILE = 1,
47  TYPE_HARD_LINK = 2,
48  TYPE_SYMBOLIC_LINK = 3,
49  TYPE_CHAR_SPECIAL = 4,
50  TYPE_BLOCK_SPECIAL = 5,
51  TYPE_DIRECTORY = 6,
52  TYPE_FIFO = 7,
53  TYPE_CONTIGUOUS_FILE = 8,
54  }
55 
59  protected BinaryReader m_br;
60 
64  protected static char[] m_nullCharArray = new char[] { '\0' };
68  protected static char[] m_spaceCharArray = new char[] { ' ' };
69 
74  public TarArchiveReader(Stream s)
75  {
76  m_br = new BinaryReader(s);
77  }
78 
84  public byte[] ReadEntry(out string filePath, out TarEntryType entryType)
85  {
86  filePath = String.Empty;
87  entryType = TarEntryType.TYPE_UNKNOWN;
88  TarHeader header = ReadHeader();
89 
90  if (null == header)
91  return null;
92 
93  entryType = header.EntryType;
94  filePath = header.FilePath;
95  return ReadData(header.FileSize);
96  }
97 
102  protected TarHeader ReadHeader()
103  {
104  byte[] header = m_br.ReadBytes(512);
105 
106  // If there are no more bytes in the stream, return null header
107  if (header.Length == 0)
108  return null;
109 
110  // If we've reached the end of the archive we'll be in null block territory, which means
111  // the next byte will be 0
112  if (header[0] == 0)
113  return null;
114 
115  TarHeader tarHeader = new TarHeader();
116 
117  // If we're looking at a GNU tar long link then extract the long name and pull up the next header
118  if (header[156] == (byte)'L')
119  {
120  int longNameLength = ConvertOctalBytesToDecimal(header, 124, 11);
121  tarHeader.FilePath = Encoding.ASCII.GetString(ReadData(longNameLength));
122  //m_log.DebugFormat("[TAR ARCHIVE READER]: Got long file name {0}", tarHeader.FilePath);
123  header = m_br.ReadBytes(512);
124  }
125  else
126  {
127  tarHeader.FilePath = Encoding.ASCII.GetString(header, 0, 100);
128  tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray);
129  //m_log.DebugFormat("[TAR ARCHIVE READER]: Got short file name {0}", tarHeader.FilePath);
130  }
131 
132  tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11);
133 
134  switch (header[156])
135  {
136  case 0:
137  tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
138  break;
139  case (byte)'0':
140  tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE;
141  break;
142  case (byte)'1':
143  tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK;
144  break;
145  case (byte)'2':
146  tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK;
147  break;
148  case (byte)'3':
149  tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL;
150  break;
151  case (byte)'4':
152  tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL;
153  break;
154  case (byte)'5':
155  tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY;
156  break;
157  case (byte)'6':
158  tarHeader.EntryType = TarEntryType.TYPE_FIFO;
159  break;
160  case (byte)'7':
161  tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE;
162  break;
163  }
164 
165  return tarHeader;
166  }
167 
173  protected byte[] ReadData(int fileSize)
174  {
175  byte[] data = m_br.ReadBytes(fileSize);
176 
177  //m_log.DebugFormat("[TAR ARCHIVE READER]: fileSize {0}", fileSize);
178 
179  // Read the rest of the empty padding in the 512 byte block
180  if (fileSize % 512 != 0)
181  {
182  int paddingLeft = 512 - (fileSize % 512);
183 
184  //m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft);
185 
186  m_br.ReadBytes(paddingLeft);
187  }
188 
189  return data;
190  }
191 
192  public void Close()
193  {
194  m_br.Close();
195  }
196 
202  public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count)
203  {
204  // Trim leading white space: ancient tars do that instead
205  // of leading 0s :-( don't ask. really.
206  string oString = Encoding.ASCII.GetString(bytes, startIndex, count).TrimStart(m_spaceCharArray);
207 
208  int d = 0;
209 
210  foreach (char c in oString)
211  {
212  d <<= 3;
213  d |= c - '0';
214  }
215 
216  return d;
217  }
218  }
219 
220  public class TarHeader
221  {
222  public string FilePath;
223  public int FileSize;
224  public TarArchiveReader.TarEntryType EntryType;
225  }
226 }
Temporary code to do the bare minimum required to read a tar archive for our purposes ...
static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count)
Convert octal bytes to a decimal representation
byte[] ReadData(int fileSize)
Read data following a header
BinaryReader m_br
Binary reader for the underlying stream
TarArchiveReader(Stream s)
Generate a tar reader which reads from the given stream.
TarArchiveReader.TarEntryType EntryType
TarHeader ReadHeader()
Read the next 512 byte chunk of data as a tar header.
byte[] ReadEntry(out string filePath, out TarEntryType entryType)
Read the next entry in the tar file.