root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Tar / TarEntry.cs @ 0eea575a
History | View | Annotate | Download (17.5 kB)
1 |
// TarEntry.cs |
---|---|
2 |
// |
3 |
// Copyright (C) 2001 Mike Krueger |
4 |
// |
5 |
// This program is free software; you can redistribute it and/or |
6 |
// modify it under the terms of the GNU General Public License |
7 |
// as published by the Free Software Foundation; either version 2 |
8 |
// of the License, or (at your option) any later version. |
9 |
// |
10 |
// This program is distributed in the hope that it will be useful, |
11 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
// GNU General Public License for more details. |
14 |
// |
15 |
// You should have received a copy of the GNU General Public License |
16 |
// along with this program; if not, write to the Free Software |
17 |
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
18 |
// |
19 |
// Linking this library statically or dynamically with other modules is |
20 |
// making a combined work based on this library. Thus, the terms and |
21 |
// conditions of the GNU General Public License cover the whole |
22 |
// combination. |
23 |
// |
24 |
// As a special exception, the copyright holders of this library give you |
25 |
// permission to link this library with independent modules to produce an |
26 |
// executable, regardless of the license terms of these independent |
27 |
// modules, and to copy and distribute the resulting executable under |
28 |
// terms of your choice, provided that you also meet, for each linked |
29 |
// independent module, the terms and conditions of the license of that |
30 |
// module. An independent module is a module which is not derived from |
31 |
// or based on this library. If you modify this library, you may extend |
32 |
// this exception to your version of the library, but you are not |
33 |
// obligated to do so. If you do not wish to do so, delete this |
34 |
// exception statement from your version. |
35 |
|
36 |
using System; |
37 |
using System.IO; |
38 |
using System.Linq; |
39 |
|
40 |
namespace ICSharpCode.SharpZipLib.Silverlight.Tar |
41 |
{ |
42 |
/// <summary> |
43 |
/// This class represents an entry in a Tar archive. It consists |
44 |
/// of the entry's _header, as well as the entry's File. Entries |
45 |
/// can be instantiated in one of three ways, depending on how |
46 |
/// they are to be used. |
47 |
/// <p> |
48 |
/// TarEntries that are created from the _header bytes read from |
49 |
/// an archive are instantiated with the TarEntry( byte[] ) |
50 |
/// constructor. These entries will be used when extracting from |
51 |
/// or listing the contents of an archive. These entries have their |
52 |
/// _header filled in using the _header bytes. They also set the File |
53 |
/// to null, since they reference an archive entry not a _file.</p> |
54 |
/// <p> |
55 |
/// TarEntries that are created from files that are to be written |
56 |
/// into an archive are instantiated with the CreateEntryFromFile(string) |
57 |
/// pseudo constructor. These entries have their _header filled in using |
58 |
/// the File's information. They also keep a reference to the File |
59 |
/// for convenience when writing entries.</p> |
60 |
/// <p> |
61 |
/// Finally, TarEntries can be constructed from nothing but a name. |
62 |
/// This allows the programmer to construct the entry by hand, for |
63 |
/// instance when only an InputStream is available for writing to |
64 |
/// the archive, and the _header information is constructed from |
65 |
/// other information. In this case the _header fields are set to |
66 |
/// defaults and the File is set to null.</p> |
67 |
/// <see cref="TarHeader"/> |
68 |
/// </summary> |
69 |
public class TarEntry |
70 |
{ |
71 |
#region Constructors |
72 |
|
73 |
/// <summary> |
74 |
/// Initialise a default instance of <see cref="TarEntry"/>. |
75 |
/// </summary> |
76 |
private TarEntry() |
77 |
{ |
78 |
_header = new TarHeader(); |
79 |
} |
80 |
|
81 |
/// <summary> |
82 |
/// Construct an entry from an archive's _header bytes. File is set |
83 |
/// to null. |
84 |
/// </summary> |
85 |
/// <param name = "headerBuffer"> |
86 |
/// The _header bytes from a tar archive entry. |
87 |
/// </param> |
88 |
public TarEntry(byte[] headerBuffer) |
89 |
{ |
90 |
_header = new TarHeader(); |
91 |
_header.ParseBuffer(headerBuffer); |
92 |
} |
93 |
|
94 |
/// <summary> |
95 |
/// Construct a TarEntry using the <paramref name="header">_header</paramref> provided |
96 |
/// </summary> |
97 |
/// <param name="header">Header details for entry</param> |
98 |
public TarEntry(TarHeader header) |
99 |
{ |
100 |
if (header == null) |
101 |
{ |
102 |
throw new ArgumentNullException("header"); |
103 |
} |
104 |
|
105 |
_header = (TarHeader) header.Clone(); |
106 |
} |
107 |
|
108 |
#endregion |
109 |
|
110 |
/// <summary> |
111 |
/// Get this entry's _header. |
112 |
/// </summary> |
113 |
/// <returns> |
114 |
/// This entry's TarHeader. |
115 |
/// </returns> |
116 |
public TarHeader TarHeader |
117 |
{ |
118 |
get { return _header; } |
119 |
} |
120 |
|
121 |
/// <summary> |
122 |
/// Get/Set this entry's name. |
123 |
/// </summary> |
124 |
public string Name |
125 |
{ |
126 |
get { return _header.Name; } |
127 |
set { _header.Name = value; } |
128 |
} |
129 |
|
130 |
/// <summary> |
131 |
/// Get/set this entry's user id. |
132 |
/// </summary> |
133 |
public int UserId |
134 |
{ |
135 |
get { return _header.UserId; } |
136 |
set { _header.UserId = value; } |
137 |
} |
138 |
|
139 |
/// <summary> |
140 |
/// Get/set this entry's group id. |
141 |
/// </summary> |
142 |
public int GroupId |
143 |
{ |
144 |
get { return _header.GroupId; } |
145 |
set { _header.GroupId = value; } |
146 |
} |
147 |
|
148 |
/// <summary> |
149 |
/// Get/set this entry's user name. |
150 |
/// </summary> |
151 |
public string UserName |
152 |
{ |
153 |
get { return _header.UserName; } |
154 |
set { _header.UserName = value; } |
155 |
} |
156 |
|
157 |
/// <summary> |
158 |
/// Get/set this entry's group name. |
159 |
/// </summary> |
160 |
public string GroupName |
161 |
{ |
162 |
get { return _header.GroupName; } |
163 |
set { _header.GroupName = value; } |
164 |
} |
165 |
|
166 |
/// <summary> |
167 |
/// Get/Set the modification time for this entry |
168 |
/// </summary> |
169 |
public DateTime ModTime |
170 |
{ |
171 |
get { return _header.ModTime; } |
172 |
set { _header.ModTime = value; } |
173 |
} |
174 |
|
175 |
/// <summary> |
176 |
/// Get this entry's _file. |
177 |
/// </summary> |
178 |
/// <returns> |
179 |
/// This entry's _file. |
180 |
/// </returns> |
181 |
public string File |
182 |
{ |
183 |
get { return _file; } |
184 |
} |
185 |
|
186 |
/// <summary> |
187 |
/// Get/set this entry's recorded _file size. |
188 |
/// </summary> |
189 |
public long Size |
190 |
{ |
191 |
get { return _header.Size; } |
192 |
set { _header.Size = value; } |
193 |
} |
194 |
|
195 |
/// <summary> |
196 |
/// Return true if this entry represents a directory, false otherwise |
197 |
/// </summary> |
198 |
/// <returns> |
199 |
/// True if this entry is a directory. |
200 |
/// </returns> |
201 |
public bool IsDirectory |
202 |
{ |
203 |
get |
204 |
{ |
205 |
if (_file != null) |
206 |
{ |
207 |
return Directory.Exists(_file); |
208 |
} |
209 |
|
210 |
if (_header != null) |
211 |
{ |
212 |
if ((_header.TypeFlag == TarHeader.LF_DIR) || Name.EndsWith("/")) |
213 |
{ |
214 |
return true; |
215 |
} |
216 |
} |
217 |
return false; |
218 |
} |
219 |
} |
220 |
|
221 |
/// <summary> |
222 |
/// Clone this tar entry. |
223 |
/// </summary> |
224 |
/// <returns>Returns a clone of this entry.</returns> |
225 |
public object Clone() |
226 |
{ |
227 |
var entry = new TarEntry{_file = _file, _header = ((TarHeader) _header.Clone()), Name = Name}; |
228 |
return entry; |
229 |
} |
230 |
|
231 |
/// <summary> |
232 |
/// Construct an entry with only a <paramref name="name">name</paramref>. |
233 |
/// This allows the programmer to construct the entry's _header "by hand". |
234 |
/// </summary> |
235 |
/// <param name="name">The name to use for the entry</param> |
236 |
/// <returns>Returns the newly created <see cref="TarEntry"/></returns> |
237 |
public static TarEntry CreateTarEntry(string name) |
238 |
{ |
239 |
var entry = new TarEntry(); |
240 |
NameTarHeader(entry._header, name); |
241 |
return entry; |
242 |
} |
243 |
|
244 |
/// <summary> |
245 |
/// Construct an entry for a _file. File is set to _file, and the |
246 |
/// _header is constructed from information from the _file. |
247 |
/// </summary> |
248 |
/// <param name = "fileName">The _file name that the entry represents.</param> |
249 |
/// <returns>Returns the newly created <see cref="TarEntry"/></returns> |
250 |
public static TarEntry CreateEntryFromFile(string fileName) |
251 |
{ |
252 |
var entry = new TarEntry(); |
253 |
entry.GetFileTarHeader(entry._header, fileName); |
254 |
return entry; |
255 |
} |
256 |
|
257 |
/// <summary> |
258 |
/// Determine if the two entries are equal. Equality is determined |
259 |
/// by the _header names being equal. |
260 |
/// </summary> |
261 |
/// <param name="obj">The <see cref="object"/> to compare with the current Object.</param> |
262 |
/// <returns> |
263 |
/// True if the entries are equal; false if not. |
264 |
/// </returns> |
265 |
public override bool Equals(object obj) |
266 |
{ |
267 |
var localEntry = obj as TarEntry; |
268 |
|
269 |
if (localEntry != null) |
270 |
{ |
271 |
return Name.Equals(localEntry.Name); |
272 |
} |
273 |
return false; |
274 |
} |
275 |
|
276 |
/// <summary> |
277 |
/// Derive a Hash value for the current <see cref="object"/> |
278 |
/// </summary> |
279 |
/// <returns>A Hash code for the current <see cref="object"/></returns> |
280 |
public override int GetHashCode() |
281 |
{ |
282 |
return Name.GetHashCode(); |
283 |
} |
284 |
|
285 |
/// <summary> |
286 |
/// Determine if the given entry is a descendant of this entry. |
287 |
/// Descendancy is determined by the name of the descendant |
288 |
/// starting with this entry's name. |
289 |
/// </summary> |
290 |
/// <param name = "toTest"> |
291 |
/// Entry to be checked as a descendent of this. |
292 |
/// </param> |
293 |
/// <returns> |
294 |
/// True if entry is a descendant of this. |
295 |
/// </returns> |
296 |
public bool IsDescendent(TarEntry toTest) |
297 |
{ |
298 |
if (toTest == null) |
299 |
{ |
300 |
throw new ArgumentNullException("toTest"); |
301 |
} |
302 |
|
303 |
return toTest.Name.StartsWith(Name); |
304 |
} |
305 |
|
306 |
/// <summary> |
307 |
/// Convenience method to set this entry's group and user ids. |
308 |
/// </summary> |
309 |
/// <param name="userId"> |
310 |
/// This entry's new user id. |
311 |
/// </param> |
312 |
/// <param name="groupId"> |
313 |
/// This entry's new group id. |
314 |
/// </param> |
315 |
public void SetIds(int userId, int groupId) |
316 |
{ |
317 |
UserId = userId; |
318 |
GroupId = groupId; |
319 |
} |
320 |
|
321 |
/// <summary> |
322 |
/// Convenience method to set this entry's group and user names. |
323 |
/// </summary> |
324 |
/// <param name="userName"> |
325 |
/// This entry's new user name. |
326 |
/// </param> |
327 |
/// <param name="groupName"> |
328 |
/// This entry's new group name. |
329 |
/// </param> |
330 |
public void SetNames(string userName, string groupName) |
331 |
{ |
332 |
UserName = userName; |
333 |
GroupName = groupName; |
334 |
} |
335 |
|
336 |
/// <summary> |
337 |
/// Fill in a TarHeader with information from a File. |
338 |
/// </summary> |
339 |
/// <param name="header"> |
340 |
/// The TarHeader to fill in. |
341 |
/// </param> |
342 |
/// <param name="file"> |
343 |
/// The _file from which to get the _header information. |
344 |
/// </param> |
345 |
public void GetFileTarHeader(TarHeader header, string file) |
346 |
{ |
347 |
if (header == null) |
348 |
{ |
349 |
throw new ArgumentNullException("header"); |
350 |
} |
351 |
|
352 |
if (file == null) |
353 |
{ |
354 |
throw new ArgumentNullException("file"); |
355 |
} |
356 |
|
357 |
_file = file; |
358 |
|
359 |
// bugfix from torhovl from #D forum: |
360 |
var name = file; |
361 |
|
362 |
// 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory |
363 |
if (name.IndexOf(Environment.CurrentDirectory) == 0) |
364 |
{ |
365 |
name = name.Substring(Environment.CurrentDirectory.Length); |
366 |
} |
367 |
/* |
368 |
if (Path.DirectorySeparatorChar == '\\') |
369 |
{ |
370 |
// check if the OS is Windows |
371 |
// Strip off drive letters! |
372 |
if (name.Length > 2) |
373 |
{ |
374 |
char ch1 = name[0]; |
375 |
char ch2 = name[1]; |
376 |
|
377 |
if (ch2 == ':' && Char.IsLetter(ch1)) |
378 |
{ |
379 |
name = name.Substring(2); |
380 |
} |
381 |
} |
382 |
} |
383 |
*/ |
384 |
|
385 |
name = name.Replace(Path.DirectorySeparatorChar, '/'); |
386 |
|
387 |
// No absolute pathnames |
388 |
// Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\", |
389 |
// so we loop on starting /'s. |
390 |
while (name.StartsWith("/")) |
391 |
{ |
392 |
name = name.Substring(1); |
393 |
} |
394 |
|
395 |
header.LinkName = String.Empty; |
396 |
header.Name = name; |
397 |
|
398 |
if (Directory.Exists(file)) |
399 |
{ |
400 |
header.Mode = 1003; // Magic number for security access for a UNIX filesystem |
401 |
header.TypeFlag = TarHeader.LF_DIR; |
402 |
if ((header.Name.Length == 0) || header.Name[header.Name.Length - 1] != '/') |
403 |
{ |
404 |
header.Name = header.Name + "/"; |
405 |
} |
406 |
|
407 |
header.Size = 0; |
408 |
} |
409 |
else |
410 |
{ |
411 |
header.Mode = 33216; // Magic number for security access for a UNIX filesystem |
412 |
header.TypeFlag = TarHeader.LF_NORMAL; |
413 |
header.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; |
414 |
} |
415 |
|
416 |
header.ModTime = |
417 |
System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); |
418 |
header.DevMajor = 0; |
419 |
header.DevMinor = 0; |
420 |
} |
421 |
|
422 |
/// <summary> |
423 |
/// Get entries for all files present in this entries directory. |
424 |
/// If this entry doesnt represent a directory zero entries are returned. |
425 |
/// </summary> |
426 |
/// <returns> |
427 |
/// An array of TarEntry's for this entry's children. |
428 |
/// </returns> |
429 |
public TarEntry[] GetDirectoryEntries() |
430 |
{ |
431 |
if ((_file == null) || !Directory.Exists(_file)) |
432 |
{ |
433 |
return new TarEntry[0]; |
434 |
} |
435 |
|
436 |
#if !SL4 |
437 |
var list = Directory.GetFileSystemEntries(_file); |
438 |
#else |
439 |
var list = Directory.EnumerateFileSystemEntries(_file).ToArray(); |
440 |
#endif |
441 |
var result = new TarEntry[list.Length]; |
442 |
|
443 |
for (var i = 0; i < list.Length; ++i) |
444 |
{ |
445 |
result[i] = CreateEntryFromFile(list[i]); |
446 |
} |
447 |
|
448 |
return result; |
449 |
} |
450 |
|
451 |
/// <summary> |
452 |
/// Write an entry's _header information to a _header buffer. |
453 |
/// </summary> |
454 |
/// <param name = "outBuffer"> |
455 |
/// The tar entry _header buffer to fill in. |
456 |
/// </param> |
457 |
public void WriteEntryHeader(byte[] outBuffer) |
458 |
{ |
459 |
_header.WriteHeader(outBuffer); |
460 |
} |
461 |
|
462 |
/// <summary> |
463 |
/// Convenience method that will modify an entry's name directly |
464 |
/// in place in an entry _header buffer byte array. |
465 |
/// </summary> |
466 |
/// <param name="buffer"> |
467 |
/// The buffer containing the entry _header to modify. |
468 |
/// </param> |
469 |
/// <param name="newName"> |
470 |
/// The new name to place into the _header buffer. |
471 |
/// </param> |
472 |
public static void AdjustEntryName(byte[] buffer, string newName) |
473 |
{ |
474 |
var offset = 0; |
475 |
TarHeader.GetNameBytes(newName, buffer, offset, TarHeader.NAMELEN); |
476 |
} |
477 |
|
478 |
/// <summary> |
479 |
/// Fill in a TarHeader given only the entry's name. |
480 |
/// </summary> |
481 |
/// <param name="header"> |
482 |
/// The TarHeader to fill in. |
483 |
/// </param> |
484 |
/// <param name="name"> |
485 |
/// The tar entry name. |
486 |
/// </param> |
487 |
public static void NameTarHeader(TarHeader header, string name) |
488 |
{ |
489 |
if (header == null) |
490 |
{ |
491 |
throw new ArgumentNullException("header"); |
492 |
} |
493 |
|
494 |
if (name == null) |
495 |
{ |
496 |
throw new ArgumentNullException("name"); |
497 |
} |
498 |
|
499 |
var isDir = name.EndsWith("/"); |
500 |
|
501 |
header.Name = name; |
502 |
header.Mode = isDir ? 1003 : 33216; |
503 |
header.UserId = 0; |
504 |
header.GroupId = 0; |
505 |
header.Size = 0; |
506 |
|
507 |
header.ModTime = DateTime.UtcNow; |
508 |
|
509 |
header.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; |
510 |
|
511 |
header.LinkName = String.Empty; |
512 |
header.UserName = String.Empty; |
513 |
header.GroupName = String.Empty; |
514 |
|
515 |
header.DevMajor = 0; |
516 |
header.DevMinor = 0; |
517 |
} |
518 |
|
519 |
#region Instance Fields |
520 |
|
521 |
/// <summary> |
522 |
/// The name of the _file this entry represents or null if the entry is not based on a _file. |
523 |
/// </summary> |
524 |
private string _file; |
525 |
|
526 |
/// <summary> |
527 |
/// The entry's _header information. |
528 |
/// </summary> |
529 |
private TarHeader _header; |
530 |
|
531 |
#endregion |
532 |
} |
533 |
} |
534 |
|
535 |
/* The original Java file had this header: |
536 |
* |
537 |
** Authored by Timothy Gerard Endres |
538 |
** <mailto:time@gjt.org> <http://www.trustice.com> |
539 |
** |
540 |
** This work has been placed into the public domain. |
541 |
** You may use this work in any way and for any purpose you wish. |
542 |
** |
543 |
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, |
544 |
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR |
545 |
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY |
546 |
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR |
547 |
** REDISTRIBUTION OF THIS SOFTWARE. |
548 |
** |
549 |
*/ |