root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Tar / TarHeader.cs @ 0eea575a
History | View | Annotate | Download (36.5 kB)
1 |
// TarHeader.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 |
|
37 |
/* The tar format and its POSIX successor PAX have a long history which makes for compatability |
38 |
issues when creating and reading files. |
39 |
|
40 |
This is further complicated by a large number of programs with variations on formats |
41 |
One common issue is the handling of names longer than 100 characters. |
42 |
GNU style long names are currently supported. |
43 |
|
44 |
This is the ustar (Posix 1003.1) header. |
45 |
|
46 |
struct header |
47 |
{ |
48 |
char t_name[100]; // 0 Filename |
49 |
char t_mode[8]; // 100 Permissions |
50 |
char t_uid[8]; // 108 Numerical User ID |
51 |
char t_gid[8]; // 116 Numerical Group ID |
52 |
char t_size[12]; // 124 Filesize |
53 |
char t_mtime[12]; // 136 st_mtime |
54 |
char t_chksum[8]; // 148 Checksum |
55 |
char t_typeflag; // 156 Type of File |
56 |
char t_linkname[100]; // 157 Target of Links |
57 |
char t_magic[6]; // 257 "ustar" or other... |
58 |
char t_version[2]; // 263 Version fixed to 00 |
59 |
char t_uname[32]; // 265 User Name |
60 |
char t_gname[32]; // 297 Group Name |
61 |
char t_devmajor[8]; // 329 Major for devices |
62 |
char t_devminor[8]; // 337 Minor for devices |
63 |
char t_prefix[155]; // 345 Prefix for t_name |
64 |
char t_mfill[12]; // 500 Filler up to 512 |
65 |
}; |
66 |
|
67 |
*/ |
68 |
|
69 |
using System; |
70 |
using System.Text; |
71 |
|
72 |
namespace ICSharpCode.SharpZipLib.Silverlight.Tar |
73 |
{ |
74 |
/// <summary> |
75 |
/// This class encapsulates the Tar Entry Header used in Tar Archives. |
76 |
/// The class also holds a number of tar constants, used mostly in headers. |
77 |
/// </summary> |
78 |
public class TarHeader |
79 |
{ |
80 |
#region Constants |
81 |
|
82 |
/// <summary> |
83 |
/// The length of the checksum field in a header buffer. |
84 |
/// </summary> |
85 |
public const int CHKSUMLEN = 8; |
86 |
|
87 |
/// <summary> |
88 |
/// Offset of checksum in a header buffer. |
89 |
/// </summary> |
90 |
public const int CHKSUMOFS = 148; |
91 |
|
92 |
/// <summary> |
93 |
/// The length of the devices field in a header buffer. |
94 |
/// </summary> |
95 |
public const int DEVLEN = 8; |
96 |
|
97 |
/// <summary> |
98 |
/// The length of the group id field in a header buffer. |
99 |
/// </summary> |
100 |
public const int GIDLEN = 8; |
101 |
|
102 |
/// <summary> |
103 |
/// The length of the group name field in a header buffer. |
104 |
/// </summary> |
105 |
public const int GNAMELEN = 32; |
106 |
|
107 |
/// <summary> |
108 |
/// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it |
109 |
/// </summary> |
110 |
public const string GNU_TMAGIC = "ustar "; |
111 |
|
112 |
/// <summary> |
113 |
/// Solaris access control list file type |
114 |
/// </summary> |
115 |
public const byte LF_ACL = (byte) 'A'; |
116 |
|
117 |
// |
118 |
// LF_ constants represent the "type" of an entry |
119 |
// |
120 |
|
121 |
/// <summary> |
122 |
/// Block device file type. |
123 |
/// </summary> |
124 |
public const byte LF_BLK = (byte) '4'; |
125 |
|
126 |
/// <summary> |
127 |
/// Character device file type. |
128 |
/// </summary> |
129 |
public const byte LF_CHR = (byte) '3'; |
130 |
|
131 |
/// <summary> |
132 |
/// Contiguous file type. |
133 |
/// </summary> |
134 |
public const byte LF_CONTIG = (byte) '7'; |
135 |
|
136 |
/// <summary> |
137 |
/// Directory file type. |
138 |
/// </summary> |
139 |
public const byte LF_DIR = (byte) '5'; |
140 |
|
141 |
/// <summary> |
142 |
/// Solaris Extended Attribute File |
143 |
/// </summary> |
144 |
public const byte LF_EXTATTR = (byte) 'E'; |
145 |
|
146 |
/// <summary> |
147 |
/// FIFO (pipe) file type. |
148 |
/// </summary> |
149 |
public const byte LF_FIFO = (byte) '6'; |
150 |
|
151 |
/// <summary> |
152 |
/// Posix.1 2001 global extended header |
153 |
/// </summary> |
154 |
public const byte LF_GHDR = (byte) 'g'; |
155 |
|
156 |
/// <summary> |
157 |
/// GNU dir dump file type |
158 |
/// This is a dir entry that contains the names of files that were in the |
159 |
/// dir at the time the dump was made |
160 |
/// </summary> |
161 |
public const byte LF_GNU_DUMPDIR = (byte) 'D'; |
162 |
|
163 |
/// <summary> |
164 |
/// Identifies the next file on the tape as having a long link name |
165 |
/// </summary> |
166 |
public const byte LF_GNU_LONGLINK = (byte) 'K'; |
167 |
|
168 |
/// <summary> |
169 |
/// Identifies the next file on the tape as having a long name |
170 |
/// </summary> |
171 |
public const byte LF_GNU_LONGNAME = (byte) 'L'; |
172 |
|
173 |
/// <summary> |
174 |
/// Continuation of a file that began on another volume |
175 |
/// </summary> |
176 |
public const byte LF_GNU_MULTIVOL = (byte) 'M'; |
177 |
|
178 |
/// <summary> |
179 |
/// For storing filenames that dont fit in the main header (old GNU) |
180 |
/// </summary> |
181 |
public const byte LF_GNU_NAMES = (byte) 'N'; |
182 |
|
183 |
/// <summary> |
184 |
/// GNU Sparse file |
185 |
/// </summary> |
186 |
public const byte LF_GNU_SPARSE = (byte) 'S'; |
187 |
|
188 |
/// <summary> |
189 |
/// GNU Tape/volume header ignore on extraction |
190 |
/// </summary> |
191 |
public const byte LF_GNU_VOLHDR = (byte) 'V'; |
192 |
|
193 |
/// <summary> |
194 |
/// Link file type. |
195 |
/// </summary> |
196 |
public const byte LF_LINK = (byte) '1'; |
197 |
|
198 |
/// <summary> |
199 |
/// Inode (metadata only) no file content |
200 |
/// </summary> |
201 |
public const byte LF_META = (byte) 'I'; |
202 |
|
203 |
/// <summary> |
204 |
/// Normal file type. |
205 |
/// </summary> |
206 |
public const byte LF_NORMAL = (byte) '0'; |
207 |
|
208 |
/// <summary> |
209 |
/// The "old way" of indicating a normal file. |
210 |
/// </summary> |
211 |
public const byte LF_OLDNORM = 0; |
212 |
|
213 |
/// <summary> |
214 |
/// Symbolic link file type. |
215 |
/// </summary> |
216 |
public const byte LF_SYMLINK = (byte) '2'; |
217 |
|
218 |
/// <summary> |
219 |
/// Posix.1 2001 extended header |
220 |
/// </summary> |
221 |
public const byte LF_XHDR = (byte) 'x'; |
222 |
|
223 |
/// <summary> |
224 |
/// The length of the magic field in a header buffer. |
225 |
/// </summary> |
226 |
public const int MAGICLEN = 6; |
227 |
|
228 |
/// <summary> |
229 |
/// The length of the mode field in a header buffer. |
230 |
/// </summary> |
231 |
public const int MODELEN = 8; |
232 |
|
233 |
/// <summary> |
234 |
/// The length of the modification time field in a header buffer. |
235 |
/// </summary> |
236 |
public const int MODTIMELEN = 12; |
237 |
|
238 |
/// <summary> |
239 |
/// The length of the name field in a header buffer. |
240 |
/// </summary> |
241 |
public const int NAMELEN = 100; |
242 |
|
243 |
/// <summary> |
244 |
/// The length of the size field in a header buffer. |
245 |
/// </summary> |
246 |
public const int SIZELEN = 12; |
247 |
|
248 |
private const long timeConversionFactor = 10000000L; // 1 tick == 100 nanoseconds |
249 |
|
250 |
/// <summary> |
251 |
/// The magic tag representing a POSIX tar archive. (includes trailing NULL) |
252 |
/// </summary> |
253 |
public const string TMAGIC = "ustar "; |
254 |
|
255 |
/// <summary> |
256 |
/// The length of the user id field in a header buffer. |
257 |
/// </summary> |
258 |
public const int UIDLEN = 8; |
259 |
|
260 |
/// <summary> |
261 |
/// The length of the user name field in a header buffer. |
262 |
/// </summary> |
263 |
public const int UNAMELEN = 32; |
264 |
|
265 |
/// <summary> |
266 |
/// The length of the version field in a header buffer. |
267 |
/// </summary> |
268 |
public const int VERSIONLEN = 2; |
269 |
|
270 |
private static readonly DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0); |
271 |
|
272 |
#endregion |
273 |
|
274 |
#region Constructors |
275 |
|
276 |
/// <summary> |
277 |
/// Initialise a default TarHeader instance |
278 |
/// </summary> |
279 |
public TarHeader() |
280 |
{ |
281 |
Magic = TMAGIC; |
282 |
Version = " "; |
283 |
|
284 |
Name = ""; |
285 |
LinkName = ""; |
286 |
|
287 |
UserId = defaultUserId; |
288 |
GroupId = defaultGroupId; |
289 |
UserName = defaultUser; |
290 |
GroupName = defaultGroupName; |
291 |
Size = 0; |
292 |
} |
293 |
|
294 |
#endregion |
295 |
|
296 |
#region Properties |
297 |
|
298 |
/// <summary> |
299 |
/// Get/set the name for this tar entry. |
300 |
/// </summary> |
301 |
/// <exception cref="ArgumentNullException">Thrown when attempting to set the property to null.</exception> |
302 |
public string Name |
303 |
{ |
304 |
get { return name; } |
305 |
set |
306 |
{ |
307 |
if (value == null) |
308 |
{ |
309 |
throw new ArgumentNullException("value"); |
310 |
} |
311 |
name = value; |
312 |
} |
313 |
} |
314 |
|
315 |
/// <summary> |
316 |
/// Get/set the entry's Unix style permission mode. |
317 |
/// </summary> |
318 |
public int Mode |
319 |
{ |
320 |
get { return mode; } |
321 |
set { mode = value; } |
322 |
} |
323 |
|
324 |
|
325 |
/// <summary> |
326 |
/// The entry's user id. |
327 |
/// </summary> |
328 |
/// <remarks> |
329 |
/// This is only directly relevant to unix systems. |
330 |
/// The default is zero. |
331 |
/// </remarks> |
332 |
public int UserId { get; set; } |
333 |
|
334 |
|
335 |
/// <summary> |
336 |
/// Get/set the entry's group id. |
337 |
/// </summary> |
338 |
/// <remarks> |
339 |
/// This is only directly relevant to linux/unix systems. |
340 |
/// The default value is zero. |
341 |
/// </remarks> |
342 |
public int GroupId { get; set; } |
343 |
|
344 |
|
345 |
/// <summary> |
346 |
/// Get/set the entry's size. |
347 |
/// </summary> |
348 |
/// <exception cref="ArgumentOutOfRangeException">Thrown when setting the size to less than zero.</exception> |
349 |
public long Size |
350 |
{ |
351 |
get { return _size; } |
352 |
set |
353 |
{ |
354 |
if (value < 0) |
355 |
{ |
356 |
throw new ArgumentOutOfRangeException("value", "Cannot be less than zero"); |
357 |
} |
358 |
_size = value; |
359 |
} |
360 |
} |
361 |
|
362 |
|
363 |
/// <summary> |
364 |
/// Get/set the entry's modification time. |
365 |
/// </summary> |
366 |
/// <remarks> |
367 |
/// The modification time is only accurate to within a second. |
368 |
/// </remarks> |
369 |
/// <exception cref="ArgumentOutOfRangeException">Thrown when setting the date time to less than 1/1/1970.</exception> |
370 |
public DateTime ModTime |
371 |
{ |
372 |
get { return modTime; } |
373 |
set |
374 |
{ |
375 |
if (value < dateTime1970) |
376 |
{ |
377 |
throw new ArgumentOutOfRangeException("value", "ModTime cannot be before Jan 1st 1970"); |
378 |
} |
379 |
modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second); |
380 |
} |
381 |
} |
382 |
|
383 |
|
384 |
/// <summary> |
385 |
/// Get the entry's checksum. This is only valid/updated after writing or reading an entry. |
386 |
/// </summary> |
387 |
public int Checksum { get; private set; } |
388 |
|
389 |
/// <summary> |
390 |
/// Get value of true if the header checksum is valid, false otherwise. |
391 |
/// </summary> |
392 |
public bool IsChecksumValid { get; private set; } |
393 |
|
394 |
/// <summary> |
395 |
/// Get/set the entry's type flag. |
396 |
/// </summary> |
397 |
public byte TypeFlag { get; set; } |
398 |
|
399 |
/// <summary> |
400 |
/// The entry's link name. |
401 |
/// </summary> |
402 |
/// <exception cref="ArgumentNullException">Thrown when attempting to set LinkName to null.</exception> |
403 |
public string LinkName |
404 |
{ |
405 |
get { return linkName; } |
406 |
set |
407 |
{ |
408 |
if (value == null) |
409 |
{ |
410 |
throw new ArgumentNullException("value"); |
411 |
} |
412 |
linkName = value; |
413 |
} |
414 |
} |
415 |
|
416 |
|
417 |
/// <summary> |
418 |
/// Get/set the entry's magic tag. |
419 |
/// </summary> |
420 |
/// <exception cref="ArgumentNullException">Thrown when attempting to set Magic to null.</exception> |
421 |
public string Magic |
422 |
{ |
423 |
get { return magic; } |
424 |
set |
425 |
{ |
426 |
if (value == null) |
427 |
{ |
428 |
throw new ArgumentNullException("value"); |
429 |
} |
430 |
magic = value; |
431 |
} |
432 |
} |
433 |
|
434 |
|
435 |
/// <summary> |
436 |
/// The entry's version. |
437 |
/// </summary> |
438 |
/// <exception cref="ArgumentNullException">Thrown when attempting to set Version to null.</exception> |
439 |
public string Version |
440 |
{ |
441 |
get { return version; } |
442 |
|
443 |
set |
444 |
{ |
445 |
if (value == null) |
446 |
{ |
447 |
throw new ArgumentNullException("value"); |
448 |
} |
449 |
version = value; |
450 |
} |
451 |
} |
452 |
|
453 |
|
454 |
/// <summary> |
455 |
/// The entry's user name. |
456 |
/// </summary> |
457 |
public string UserName |
458 |
{ |
459 |
get { return userName; } |
460 |
set |
461 |
{ |
462 |
if (value != null) |
463 |
{ |
464 |
userName = value.Substring(0, Math.Min(UNAMELEN, value.Length)); |
465 |
} |
466 |
else |
467 |
{ |
468 |
var currentUser = "Silverlight"; //Environment.UserName; |
469 |
if (currentUser.Length > UNAMELEN) |
470 |
{ |
471 |
currentUser = currentUser.Substring(0, UNAMELEN); |
472 |
} |
473 |
userName = currentUser; |
474 |
} |
475 |
} |
476 |
} |
477 |
|
478 |
/// <summary> |
479 |
/// Get/set the entry's group name. |
480 |
/// </summary> |
481 |
/// <remarks> |
482 |
/// This is only directly relevant to unix systems. |
483 |
/// </remarks> |
484 |
public string GroupName |
485 |
{ |
486 |
get { return groupName; } |
487 |
set |
488 |
{ |
489 |
groupName = value ?? "None"; |
490 |
} |
491 |
} |
492 |
|
493 |
|
494 |
/// <summary> |
495 |
/// Get/set the entry's major device number. |
496 |
/// </summary> |
497 |
public int DevMajor { get; set; } |
498 |
|
499 |
|
500 |
/// <summary> |
501 |
/// Get/set the entry's minor device number. |
502 |
/// </summary> |
503 |
public int DevMinor { get; set; } |
504 |
|
505 |
/// <summary> |
506 |
/// Get the name of this entry. |
507 |
/// </summary> |
508 |
/// <returns>The entry's name.</returns> |
509 |
[Obsolete("Use the Name property instead", true)] |
510 |
public string GetName() |
511 |
{ |
512 |
return name; |
513 |
} |
514 |
|
515 |
#endregion |
516 |
|
517 |
/// <summary> |
518 |
/// Create a new <see cref="TarHeader"/> that is a copy of the current instance. |
519 |
/// </summary> |
520 |
/// <returns>A new <see cref="Object"/> that is a copy of the current instance.</returns> |
521 |
public object Clone() |
522 |
{ |
523 |
return MemberwiseClone(); |
524 |
} |
525 |
|
526 |
/// <summary> |
527 |
/// Parse TarHeader information from a header buffer. |
528 |
/// </summary> |
529 |
/// <param name = "header"> |
530 |
/// The tar entry header buffer to get information from. |
531 |
/// </param> |
532 |
public void ParseBuffer(byte[] header) |
533 |
{ |
534 |
if (header == null) |
535 |
{ |
536 |
throw new ArgumentNullException("header"); |
537 |
} |
538 |
|
539 |
var offset = 0; |
540 |
|
541 |
name = ParseName(header, offset, NAMELEN).ToString(); |
542 |
offset += NAMELEN; |
543 |
|
544 |
mode = (int) ParseOctal(header, offset, MODELEN); |
545 |
offset += MODELEN; |
546 |
|
547 |
UserId = (int) ParseOctal(header, offset, UIDLEN); |
548 |
offset += UIDLEN; |
549 |
|
550 |
GroupId = (int) ParseOctal(header, offset, GIDLEN); |
551 |
offset += GIDLEN; |
552 |
|
553 |
Size = ParseOctal(header, offset, SIZELEN); |
554 |
offset += SIZELEN; |
555 |
|
556 |
ModTime = GetDateTimeFromCTime(ParseOctal(header, offset, MODTIMELEN)); |
557 |
offset += MODTIMELEN; |
558 |
|
559 |
Checksum = (int) ParseOctal(header, offset, CHKSUMLEN); |
560 |
offset += CHKSUMLEN; |
561 |
|
562 |
TypeFlag = header[offset++]; |
563 |
|
564 |
LinkName = ParseName(header, offset, NAMELEN).ToString(); |
565 |
offset += NAMELEN; |
566 |
|
567 |
Magic = ParseName(header, offset, MAGICLEN).ToString(); |
568 |
offset += MAGICLEN; |
569 |
|
570 |
Version = ParseName(header, offset, VERSIONLEN).ToString(); |
571 |
offset += VERSIONLEN; |
572 |
|
573 |
UserName = ParseName(header, offset, UNAMELEN).ToString(); |
574 |
offset += UNAMELEN; |
575 |
|
576 |
GroupName = ParseName(header, offset, GNAMELEN).ToString(); |
577 |
offset += GNAMELEN; |
578 |
|
579 |
DevMajor = (int) ParseOctal(header, offset, DEVLEN); |
580 |
offset += DEVLEN; |
581 |
|
582 |
DevMinor = (int) ParseOctal(header, offset, DEVLEN); |
583 |
|
584 |
// Fields past this point not currently parsed or used... |
585 |
|
586 |
IsChecksumValid = Checksum == MakeCheckSum(header); |
587 |
} |
588 |
|
589 |
/// <summary> |
590 |
/// 'Write' header information to buffer provided, updating the <see cref="Checksum">check sum</see>. |
591 |
/// </summary> |
592 |
/// <param name="outBuffer">output buffer for header information</param> |
593 |
public void WriteHeader(byte[] outBuffer) |
594 |
{ |
595 |
if (outBuffer == null) |
596 |
{ |
597 |
throw new ArgumentNullException("outBuffer"); |
598 |
} |
599 |
|
600 |
var offset = 0; |
601 |
|
602 |
offset = GetNameBytes(Name, outBuffer, offset, NAMELEN); |
603 |
offset = GetOctalBytes(mode, outBuffer, offset, MODELEN); |
604 |
offset = GetOctalBytes(UserId, outBuffer, offset, UIDLEN); |
605 |
offset = GetOctalBytes(GroupId, outBuffer, offset, GIDLEN); |
606 |
|
607 |
var size = Size; |
608 |
|
609 |
offset = GetLongOctalBytes(size, outBuffer, offset, SIZELEN); |
610 |
offset = GetLongOctalBytes(GetCTime(ModTime), outBuffer, offset, MODTIMELEN); |
611 |
|
612 |
var csOffset = offset; |
613 |
for (var c = 0; c < CHKSUMLEN; ++c) |
614 |
{ |
615 |
outBuffer[offset++] = (byte) ' '; |
616 |
} |
617 |
|
618 |
outBuffer[offset++] = TypeFlag; |
619 |
|
620 |
offset = GetNameBytes(LinkName, outBuffer, offset, NAMELEN); |
621 |
offset = GetAsciiBytes(Magic, 0, outBuffer, offset, MAGICLEN); |
622 |
offset = GetNameBytes(Version, outBuffer, offset, VERSIONLEN); |
623 |
offset = GetNameBytes(UserName, outBuffer, offset, UNAMELEN); |
624 |
offset = GetNameBytes(GroupName, outBuffer, offset, GNAMELEN); |
625 |
|
626 |
if (TypeFlag == LF_CHR || TypeFlag == LF_BLK) |
627 |
{ |
628 |
offset = GetOctalBytes(DevMajor, outBuffer, offset, DEVLEN); |
629 |
offset = GetOctalBytes(DevMinor, outBuffer, offset, DEVLEN); |
630 |
} |
631 |
|
632 |
for (; offset < outBuffer.Length;) |
633 |
{ |
634 |
outBuffer[offset++] = 0; |
635 |
} |
636 |
|
637 |
Checksum = ComputeCheckSum(outBuffer); |
638 |
|
639 |
GetCheckSumOctalBytes(Checksum, outBuffer, csOffset, CHKSUMLEN); |
640 |
IsChecksumValid = true; |
641 |
} |
642 |
|
643 |
/// <summary> |
644 |
/// Get a hash code for the current object. |
645 |
/// </summary> |
646 |
/// <returns>A hash code for the current object.</returns> |
647 |
public override int GetHashCode() |
648 |
{ |
649 |
return Name.GetHashCode(); |
650 |
} |
651 |
|
652 |
/// <summary> |
653 |
/// Determines if this instance is equal to the specified object. |
654 |
/// </summary> |
655 |
/// <param name="obj">The object to compare with.</param> |
656 |
/// <returns>true if the objects are equal, false otherwise.</returns> |
657 |
public override bool Equals(object obj) |
658 |
{ |
659 |
var localHeader = obj as TarHeader; |
660 |
|
661 |
if (localHeader != null) |
662 |
{ |
663 |
return name == localHeader.name |
664 |
&& mode == localHeader.mode |
665 |
&& UserId == localHeader.UserId |
666 |
&& GroupId == localHeader.GroupId |
667 |
&& Size == localHeader.Size |
668 |
&& ModTime == localHeader.ModTime |
669 |
&& Checksum == localHeader.Checksum |
670 |
&& TypeFlag == localHeader.TypeFlag |
671 |
&& LinkName == localHeader.LinkName |
672 |
&& Magic == localHeader.Magic |
673 |
&& Version == localHeader.Version |
674 |
&& UserName == localHeader.UserName |
675 |
&& GroupName == localHeader.GroupName |
676 |
&& DevMajor == localHeader.DevMajor |
677 |
&& DevMinor == localHeader.DevMinor; |
678 |
} |
679 |
return false; |
680 |
} |
681 |
|
682 |
/// <summary> |
683 |
/// Set defaults for values used when constructing a TarHeader instance. |
684 |
/// </summary> |
685 |
/// <param name="userId">Value to apply as a default for userId.</param> |
686 |
/// <param name="userName">Value to apply as a default for userName.</param> |
687 |
/// <param name="groupId">Value to apply as a default for groupId.</param> |
688 |
/// <param name="groupName">Value to apply as a default for groupName.</param> |
689 |
internal static void SetValueDefaults(int userId, string userName, int groupId, string groupName) |
690 |
{ |
691 |
defaultUserId = userIdAsSet = userId; |
692 |
defaultUser = userNameAsSet = userName; |
693 |
defaultGroupId = groupIdAsSet = groupId; |
694 |
defaultGroupName = groupNameAsSet = groupName; |
695 |
} |
696 |
|
697 |
internal static void RestoreSetValues() |
698 |
{ |
699 |
defaultUserId = userIdAsSet; |
700 |
defaultUser = userNameAsSet; |
701 |
defaultGroupId = groupIdAsSet; |
702 |
defaultGroupName = groupNameAsSet; |
703 |
} |
704 |
|
705 |
/// <summary> |
706 |
/// Parse an octal string from a header buffer. |
707 |
/// </summary> |
708 |
/// <param name = "header">The header buffer from which to parse.</param> |
709 |
/// <param name = "offset">The offset into the buffer from which to parse.</param> |
710 |
/// <param name = "length">The number of header bytes to parse.</param> |
711 |
/// <returns>The long equivalent of the octal string.</returns> |
712 |
public static long ParseOctal(byte[] header, int offset, int length) |
713 |
{ |
714 |
if (header == null) |
715 |
{ |
716 |
throw new NullReferenceException("header"); |
717 |
} |
718 |
|
719 |
long result = 0; |
720 |
var stillPadding = true; |
721 |
|
722 |
var end = offset + length; |
723 |
for (var i = offset; i < end; ++i) |
724 |
{ |
725 |
if (header[i] == 0) |
726 |
{ |
727 |
break; |
728 |
} |
729 |
|
730 |
if (header[i] == (byte) ' ' || header[i] == '0') |
731 |
{ |
732 |
if (stillPadding) |
733 |
{ |
734 |
continue; |
735 |
} |
736 |
|
737 |
if (header[i] == (byte) ' ') |
738 |
{ |
739 |
break; |
740 |
} |
741 |
} |
742 |
|
743 |
stillPadding = false; |
744 |
|
745 |
result = (result << 3) + (header[i] - '0'); |
746 |
} |
747 |
|
748 |
return result; |
749 |
} |
750 |
|
751 |
/// <summary> |
752 |
/// Parse a name from a header buffer. |
753 |
/// </summary> |
754 |
/// <param name="header"> |
755 |
/// The header buffer from which to parse. |
756 |
/// </param> |
757 |
/// <param name="offset"> |
758 |
/// The offset into the buffer from which to parse. |
759 |
/// </param> |
760 |
/// <param name="length"> |
761 |
/// The number of header bytes to parse. |
762 |
/// </param> |
763 |
/// <returns> |
764 |
/// The name parsed. |
765 |
/// </returns> |
766 |
public static StringBuilder ParseName(byte[] header, int offset, int length) |
767 |
{ |
768 |
if (header == null) |
769 |
{ |
770 |
throw new ArgumentNullException("header"); |
771 |
} |
772 |
|
773 |
if (offset < 0) |
774 |
{ |
775 |
throw new ArgumentOutOfRangeException("offset", "Cannot be less than zero"); |
776 |
} |
777 |
|
778 |
if (length < 0) |
779 |
{ |
780 |
throw new ArgumentOutOfRangeException("length", "Cannot be less than zero"); |
781 |
} |
782 |
|
783 |
if (offset + length > header.Length) |
784 |
{ |
785 |
throw new ArgumentException("Exceeds header size", "length"); |
786 |
} |
787 |
|
788 |
var result = new StringBuilder(length); |
789 |
|
790 |
for (var i = offset; i < offset + length; ++i) |
791 |
{ |
792 |
if (header[i] == 0) |
793 |
{ |
794 |
break; |
795 |
} |
796 |
result.Append((char) header[i]); |
797 |
} |
798 |
|
799 |
return result; |
800 |
} |
801 |
|
802 |
/// <summary> |
803 |
/// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes |
804 |
/// </summary> |
805 |
/// <param name="name">The name to add</param> |
806 |
/// <param name="nameOffset">The offset of the first character</param> |
807 |
/// <param name="buffer">The buffer to add to</param> |
808 |
/// <param name="bufferOffset">The index of the first byte to add</param> |
809 |
/// <param name="length">The number of characters/bytes to add</param> |
810 |
/// <returns>The next free index in the <paramref name="buffer">buffer</paramref></returns> |
811 |
public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length) |
812 |
{ |
813 |
if (name == null) |
814 |
{ |
815 |
throw new ArgumentNullException("name"); |
816 |
} |
817 |
|
818 |
if (buffer == null) |
819 |
{ |
820 |
throw new ArgumentNullException("buffer"); |
821 |
} |
822 |
|
823 |
return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length); |
824 |
} |
825 |
|
826 |
/// <summary> |
827 |
/// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes |
828 |
/// </summary> |
829 |
/// <param name="name">The name to add</param> |
830 |
/// <param name="nameOffset">The offset of the first character</param> |
831 |
/// <param name="buffer">The buffer to add to</param> |
832 |
/// <param name="bufferOffset">The index of the first byte to add</param> |
833 |
/// <param name="length">The number of characters/bytes to add</param> |
834 |
/// <returns>The next free index in the <paramref name="buffer">buffer</paramref></returns> |
835 |
public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length) |
836 |
{ |
837 |
if (name == null) |
838 |
{ |
839 |
throw new ArgumentNullException("name"); |
840 |
} |
841 |
|
842 |
if (buffer == null) |
843 |
{ |
844 |
throw new ArgumentNullException("buffer"); |
845 |
} |
846 |
|
847 |
int i; |
848 |
|
849 |
for (i = 0; i < length - 1 && nameOffset + i < name.Length; ++i) |
850 |
{ |
851 |
buffer[bufferOffset + i] = (byte) name[nameOffset + i]; |
852 |
} |
853 |
|
854 |
for (; i < length; ++i) |
855 |
{ |
856 |
buffer[bufferOffset + i] = 0; |
857 |
} |
858 |
|
859 |
return bufferOffset + length; |
860 |
} |
861 |
|
862 |
/// <summary> |
863 |
/// Add an entry name to the buffer |
864 |
/// </summary> |
865 |
/// <param name="name"> |
866 |
/// The name to add |
867 |
/// </param> |
868 |
/// <param name="buffer"> |
869 |
/// The buffer to add to |
870 |
/// </param> |
871 |
/// <param name="offset"> |
872 |
/// The offset into the buffer from which to start adding |
873 |
/// </param> |
874 |
/// <param name="length"> |
875 |
/// The number of header bytes to add |
876 |
/// </param> |
877 |
/// <returns> |
878 |
/// The index of the next free byte in the buffer |
879 |
/// </returns> |
880 |
public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length) |
881 |
{ |
882 |
if (name == null) |
883 |
{ |
884 |
throw new ArgumentNullException("name"); |
885 |
} |
886 |
|
887 |
if (buffer == null) |
888 |
{ |
889 |
throw new ArgumentNullException("buffer"); |
890 |
} |
891 |
|
892 |
return GetNameBytes(name.ToString(), 0, buffer, offset, length); |
893 |
} |
894 |
|
895 |
/// <summary> |
896 |
/// Add an entry name to the buffer |
897 |
/// </summary> |
898 |
/// <param name="name">The name to add</param> |
899 |
/// <param name="buffer">The buffer to add to</param> |
900 |
/// <param name="offset">The offset into the buffer from which to start adding</param> |
901 |
/// <param name="length">The number of header bytes to add</param> |
902 |
/// <returns>The index of the next free byte in the buffer</returns> |
903 |
public static int GetNameBytes(string name, byte[] buffer, int offset, int length) |
904 |
{ |
905 |
if (name == null) |
906 |
{ |
907 |
throw new ArgumentNullException("name"); |
908 |
} |
909 |
|
910 |
if (buffer == null) |
911 |
{ |
912 |
throw new ArgumentNullException("buffer"); |
913 |
} |
914 |
|
915 |
return GetNameBytes(name, 0, buffer, offset, length); |
916 |
} |
917 |
|
918 |
/// <summary> |
919 |
/// Add a string to a buffer as a collection of ascii bytes. |
920 |
/// </summary> |
921 |
/// <param name="toAdd">The string to add</param> |
922 |
/// <param name="nameOffset">The offset of the first character to add.</param> |
923 |
/// <param name="buffer">The buffer to add to.</param> |
924 |
/// <param name="bufferOffset">The offset to start adding at.</param> |
925 |
/// <param name="length">The number of ascii characters to add.</param> |
926 |
/// <returns>The next free index in the buffer.</returns> |
927 |
public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length) |
928 |
{ |
929 |
if (toAdd == null) |
930 |
{ |
931 |
throw new ArgumentNullException("toAdd"); |
932 |
} |
933 |
|
934 |
if (buffer == null) |
935 |
{ |
936 |
throw new ArgumentNullException("buffer"); |
937 |
} |
938 |
|
939 |
for (var i = 0; i < length && nameOffset + i < toAdd.Length; ++i) |
940 |
{ |
941 |
buffer[bufferOffset + i] = (byte) toAdd[nameOffset + i]; |
942 |
} |
943 |
return bufferOffset + length; |
944 |
} |
945 |
|
946 |
/// <summary> |
947 |
/// Put an octal representation of a value into a buffer |
948 |
/// </summary> |
949 |
/// <param name = "value"> |
950 |
/// the value to be converted to octal |
951 |
/// </param> |
952 |
/// <param name = "buffer"> |
953 |
/// buffer to store the octal string |
954 |
/// </param> |
955 |
/// <param name = "offset"> |
956 |
/// The offset into the buffer where the value starts |
957 |
/// </param> |
958 |
/// <param name = "length"> |
959 |
/// The length of the octal string to create |
960 |
/// </param> |
961 |
/// <returns> |
962 |
/// The offset of the character next byte after the octal string |
963 |
/// </returns> |
964 |
public static int GetOctalBytes(long value, byte[] buffer, int offset, int length) |
965 |
{ |
966 |
if (buffer == null) |
967 |
{ |
968 |
throw new ArgumentNullException("buffer"); |
969 |
} |
970 |
|
971 |
var localIndex = length - 1; |
972 |
|
973 |
// Either a space or null is valid here. We use NULL as per GNUTar |
974 |
buffer[offset + localIndex] = 0; |
975 |
--localIndex; |
976 |
|
977 |
if (value > 0) |
978 |
{ |
979 |
for (var v = value; (localIndex >= 0) && (v > 0); --localIndex) |
980 |
{ |
981 |
buffer[offset + localIndex] = (byte) ((byte) '0' + (byte) (v & 7)); |
982 |
v >>= 3; |
983 |
} |
984 |
} |
985 |
|
986 |
for (; localIndex >= 0; --localIndex) |
987 |
{ |
988 |
buffer[offset + localIndex] = (byte) '0'; |
989 |
} |
990 |
|
991 |
return offset + length; |
992 |
} |
993 |
|
994 |
/// <summary> |
995 |
/// Put an octal representation of a value into a buffer |
996 |
/// </summary> |
997 |
/// <param name = "value">Value to be convert to octal</param> |
998 |
/// <param name = "buffer">The buffer to update</param> |
999 |
/// <param name = "offset">The offset into the buffer to store the value</param> |
1000 |
/// <param name = "length">The length of the octal string</param> |
1001 |
/// <returns>Index of next byte</returns> |
1002 |
public static int GetLongOctalBytes(long value, byte[] buffer, int offset, int length) |
1003 |
{ |
1004 |
return GetOctalBytes(value, buffer, offset, length); |
1005 |
} |
1006 |
|
1007 |
/// <summary> |
1008 |
/// Add the checksum integer to header buffer. |
1009 |
/// </summary> |
1010 |
/// <param name = "value"></param> |
1011 |
/// <param name = "buffer">The header buffer to set the checksum for</param> |
1012 |
/// <param name = "offset">The offset into the buffer for the checksum</param> |
1013 |
/// <param name = "length">The number of header bytes to update. |
1014 |
/// It's formatted differently from the other fields: it has 6 digits, a |
1015 |
/// null, then a space -- rather than digits, a space, then a null. |
1016 |
/// The final space is already there, from checksumming |
1017 |
/// </param> |
1018 |
/// <returns>The modified buffer offset</returns> |
1019 |
private static int GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length) |
1020 |
{ |
1021 |
GetOctalBytes(value, buffer, offset, length - 1); |
1022 |
return offset + length; |
1023 |
} |
1024 |
|
1025 |
/// <summary> |
1026 |
/// Compute the checksum for a tar entry header. |
1027 |
/// The checksum field must be all spaces prior to this happening |
1028 |
/// </summary> |
1029 |
/// <param name = "buffer">The tar entry's header buffer.</param> |
1030 |
/// <returns>The computed checksum.</returns> |
1031 |
private static int ComputeCheckSum(byte[] buffer) |
1032 |
{ |
1033 |
var sum = 0; |
1034 |
for (var i = 0; i < buffer.Length; ++i) |
1035 |
{ |
1036 |
sum += buffer[i]; |
1037 |
} |
1038 |
return sum; |
1039 |
} |
1040 |
|
1041 |
/// <summary> |
1042 |
/// Make a checksum for a tar entry ignoring the checksum contents. |
1043 |
/// </summary> |
1044 |
/// <param name = "buffer">The tar entry's header buffer.</param> |
1045 |
/// <returns>The checksum for the buffer</returns> |
1046 |
private static int MakeCheckSum(byte[] buffer) |
1047 |
{ |
1048 |
var sum = 0; |
1049 |
for (var i = 0; i < CHKSUMOFS; ++i) |
1050 |
{ |
1051 |
sum += buffer[i]; |
1052 |
} |
1053 |
|
1054 |
for (var i = 0; i < CHKSUMLEN; ++i) |
1055 |
{ |
1056 |
sum += (byte) ' '; |
1057 |
} |
1058 |
|
1059 |
for (var i = CHKSUMOFS + CHKSUMLEN; i < buffer.Length; ++i) |
1060 |
{ |
1061 |
sum += buffer[i]; |
1062 |
} |
1063 |
return sum; |
1064 |
} |
1065 |
|
1066 |
private static int GetCTime(DateTime dateTime) |
1067 |
{ |
1068 |
return unchecked((int) ((dateTime.Ticks - dateTime1970.Ticks)/timeConversionFactor)); |
1069 |
} |
1070 |
|
1071 |
private static DateTime GetDateTimeFromCTime(long ticks) |
1072 |
{ |
1073 |
DateTime result; |
1074 |
|
1075 |
try |
1076 |
{ |
1077 |
result = new DateTime(dateTime1970.Ticks + ticks*timeConversionFactor); |
1078 |
} |
1079 |
catch (ArgumentOutOfRangeException) |
1080 |
{ |
1081 |
result = dateTime1970; |
1082 |
} |
1083 |
return result; |
1084 |
} |
1085 |
|
1086 |
#region Instance Fields |
1087 |
|
1088 |
private string groupName; |
1089 |
private string linkName; |
1090 |
private string magic; |
1091 |
private int mode; |
1092 |
private DateTime modTime; |
1093 |
private string name; |
1094 |
private long _size; |
1095 |
private string userName; |
1096 |
private string version; |
1097 |
|
1098 |
#endregion |
1099 |
|
1100 |
#region Class Fields |
1101 |
|
1102 |
// Values used during recursive operations. |
1103 |
internal static int defaultGroupId; |
1104 |
internal static string defaultGroupName = "None"; |
1105 |
internal static string defaultUser; |
1106 |
internal static int defaultUserId; |
1107 |
internal static int groupIdAsSet; |
1108 |
internal static string groupNameAsSet = "None"; |
1109 |
internal static int userIdAsSet; |
1110 |
internal static string userNameAsSet; |
1111 |
|
1112 |
#endregion |
1113 |
} |
1114 |
} |
1115 |
|
1116 |
/* The original Java file had this header: |
1117 |
* |
1118 |
** Authored by Timothy Gerard Endres |
1119 |
** <mailto:time@gjt.org> <http://www.trustice.com> |
1120 |
** |
1121 |
** This work has been placed into the public domain. |
1122 |
** You may use this work in any way and for any purpose you wish. |
1123 |
** |
1124 |
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, |
1125 |
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR |
1126 |
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY |
1127 |
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR |
1128 |
** REDISTRIBUTION OF THIS SOFTWARE. |
1129 |
** |
1130 |
*/ |