2
0

TarEntry.cs 15 KB

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