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