PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/installer/TarEntry.java

#
Java | 729 lines | 356 code | 112 blank | 261 comment | 28 complexity | 2c5b9c2b57b2b7b215598707f9bd28b9 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. ** Authored by Timothy Gerard Endres
  3. ** <mailto:time@gjt.org> <http://www.trustice.com>
  4. **
  5. ** This work has been placed into the public domain.
  6. ** You may use this work in any way and for any purpose you wish.
  7. **
  8. ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
  9. ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
  10. ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
  11. ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
  12. ** REDISTRIBUTION OF THIS SOFTWARE.
  13. **
  14. */
  15. package installer;
  16. import java.io.*;
  17. import java.util.Date;
  18. /**
  19. *
  20. * This class represents an entry in a Tar archive. It consists
  21. * of the entry's header, as well as the entry's File. Entries
  22. * can be instantiated in one of three ways, depending on how
  23. * they are to be used.
  24. * <p>
  25. * TarEntries that are created from the header bytes read from
  26. * an archive are instantiated with the TarEntry( byte[] )
  27. * constructor. These entries will be used when extracting from
  28. * or listing the contents of an archive. These entries have their
  29. * header filled in using the header bytes. They also set the File
  30. * to null, since they reference an archive entry not a file.
  31. * <p>
  32. * TarEntries that are created from Files that are to be written
  33. * into an archive are instantiated with the TarEntry( File )
  34. * constructor. These entries have their header filled in using
  35. * the File's information. They also keep a reference to the File
  36. * for convenience when writing entries.
  37. * <p>
  38. * Finally, TarEntries can be constructed from nothing but a name.
  39. * This allows the programmer to construct the entry by hand, for
  40. * instance when only an InputStream is available for writing to
  41. * the archive, and the header information is constructed from
  42. * other information. In this case the header fields are set to
  43. * defaults and the File is set to null.
  44. *
  45. * <p>
  46. * The C structure for a Tar Entry's header is:
  47. * <pre>
  48. * struct header {
  49. * char name[NAMSIZ];
  50. * char mode[8];
  51. * char uid[8];
  52. * char gid[8];
  53. * char size[12];
  54. * char mtime[12];
  55. * char chksum[8];
  56. * char linkflag;
  57. * char linkname[NAMSIZ];
  58. * char magic[8];
  59. * char uname[TUNMLEN];
  60. * char gname[TGNMLEN];
  61. * char devmajor[8];
  62. * char devminor[8];
  63. * } header;
  64. * </pre>
  65. *
  66. * @see TarHeader
  67. *
  68. */
  69. public
  70. class TarEntry
  71. extends Object
  72. {
  73. /**
  74. * If this entry represents a File, this references it.
  75. */
  76. protected File file;
  77. /**
  78. * This is the entry's header information.
  79. */
  80. protected TarHeader header;
  81. /**
  82. * Construct an entry with only a name. This allows the programmer
  83. * to construct the entry's header "by hand". File is set to null.
  84. */
  85. public
  86. TarEntry( String name )
  87. {
  88. this.initialize();
  89. this.nameTarHeader( this.header, name );
  90. }
  91. /**
  92. * Construct an entry for a file. File is set to file, and the
  93. * header is constructed from information from the file.
  94. *
  95. * @param file The file that the entry represents.
  96. */
  97. public
  98. TarEntry( File file )
  99. throws InvalidHeaderException
  100. {
  101. this.initialize();
  102. this.getFileTarHeader( this.header, file );
  103. }
  104. /**
  105. * Construct an entry from an archive's header bytes. File is set
  106. * to null.
  107. *
  108. * @param headerBuf The header bytes from a tar archive entry.
  109. */
  110. public
  111. TarEntry( byte[] headerBuf )
  112. throws InvalidHeaderException
  113. {
  114. this.initialize();
  115. this.parseTarHeader( this.header, headerBuf );
  116. }
  117. /**
  118. * Initialization code common to all constructors.
  119. */
  120. private void
  121. initialize()
  122. {
  123. this.file = null;
  124. this.header = new TarHeader();
  125. }
  126. /**
  127. * Determine if the two entries are equal. Equality is determined
  128. * by the header names being equal.
  129. *
  130. * @return it Entry to be checked for equality.
  131. * @return True if the entries are equal.
  132. */
  133. public boolean
  134. equals( TarEntry it )
  135. {
  136. return
  137. this.header.name.toString().equals
  138. ( it.header.name.toString() );
  139. }
  140. /**
  141. * Determine if the given entry is a descendant of this entry.
  142. * Descendancy is determined by the name of the descendant
  143. * starting with this entry's name.
  144. *
  145. * @param desc Entry to be checked as a descendent of this.
  146. * @return True if entry is a descendant of this.
  147. */
  148. public boolean
  149. isDescendent( TarEntry desc )
  150. {
  151. return
  152. desc.header.name.toString().startsWith
  153. ( this.header.name.toString() );
  154. }
  155. /**
  156. * Get this entry's header.
  157. *
  158. * @return This entry's TarHeader.
  159. */
  160. public TarHeader
  161. getHeader()
  162. {
  163. return this.header;
  164. }
  165. /**
  166. * Get this entry's name.
  167. *
  168. * @return This entry's name.
  169. */
  170. public String
  171. getName()
  172. {
  173. return this.header.name.toString();
  174. }
  175. /**
  176. * Set this entry's name.
  177. *
  178. * @param name This entry's new name.
  179. */
  180. public void
  181. setName( String name )
  182. {
  183. this.header.name =
  184. new StringBuffer( name );
  185. }
  186. /**
  187. * Get this entry's user id.
  188. *
  189. * @return This entry's user id.
  190. */
  191. public int
  192. getUserId()
  193. {
  194. return this.header.userId;
  195. }
  196. /**
  197. * Set this entry's user id.
  198. *
  199. * @param userId This entry's new user id.
  200. */
  201. public void
  202. setUserId( int userId )
  203. {
  204. this.header.userId = userId;
  205. }
  206. /**
  207. * Get this entry's group id.
  208. *
  209. * @return This entry's group id.
  210. */
  211. public int
  212. getGroupId()
  213. {
  214. return this.header.groupId;
  215. }
  216. /**
  217. * Set this entry's group id.
  218. *
  219. * @param groupId This entry's new group id.
  220. */
  221. public void
  222. setGroupId( int groupId )
  223. {
  224. this.header.groupId = groupId;
  225. }
  226. /**
  227. * Get this entry's user name.
  228. *
  229. * @return This entry's user name.
  230. */
  231. public String
  232. getUserName()
  233. {
  234. return this.header.userName.toString();
  235. }
  236. /**
  237. * Set this entry's user name.
  238. *
  239. * @param userName This entry's new user name.
  240. */
  241. public void
  242. setUserName( String userName )
  243. {
  244. this.header.userName =
  245. new StringBuffer( userName );
  246. }
  247. /**
  248. * Get this entry's group name.
  249. *
  250. * @return This entry's group name.
  251. */
  252. public String
  253. getGroupName()
  254. {
  255. return this.header.groupName.toString();
  256. }
  257. /**
  258. * Set this entry's group name.
  259. *
  260. * @param groupName This entry's new group name.
  261. */
  262. public void
  263. setGroupName( String groupName )
  264. {
  265. this.header.groupName =
  266. new StringBuffer( groupName );
  267. }
  268. /**
  269. * Convenience method to set this entry's group and user ids.
  270. *
  271. * @param userId This entry's new user id.
  272. * @param groupId This entry's new group id.
  273. */
  274. public void
  275. setIds( int userId, int groupId )
  276. {
  277. this.setUserId( userId );
  278. this.setGroupId( groupId );
  279. }
  280. /**
  281. * Convenience method to set this entry's group and user names.
  282. *
  283. * @param userName This entry's new user name.
  284. * @param groupName This entry's new group name.
  285. */
  286. public void
  287. setNames( String userName, String groupName )
  288. {
  289. this.setUserName( userName );
  290. this.setGroupName( groupName );
  291. }
  292. /**
  293. * Set this entry's modification time. The parameter passed
  294. * to this method is in "Java time".
  295. *
  296. * @param time This entry's new modification time.
  297. */
  298. public void
  299. setModTime( long time )
  300. {
  301. this.header.modTime = time / 1000;
  302. }
  303. /**
  304. * Set this entry's modification time.
  305. *
  306. * @param time This entry's new modification time.
  307. */
  308. public void
  309. setModTime( Date time )
  310. {
  311. this.header.modTime = time.getTime() / 1000;
  312. }
  313. /**
  314. * Set this entry's modification time.
  315. *
  316. * @param time This entry's new modification time.
  317. */
  318. public Date
  319. getModTime()
  320. {
  321. return new Date( this.header.modTime * 1000 );
  322. }
  323. /**
  324. * Get this entry's file.
  325. *
  326. * @return This entry's file.
  327. */
  328. public File
  329. getFile()
  330. {
  331. return this.file;
  332. }
  333. /**
  334. * Get this entry's file size.
  335. *
  336. * @return This entry's file size.
  337. */
  338. public long
  339. getSize()
  340. {
  341. return this.header.size;
  342. }
  343. /**
  344. * Set this entry's file size.
  345. *
  346. * @param size This entry's new file size.
  347. */
  348. public void
  349. setSize( long size )
  350. {
  351. this.header.size = size;
  352. }
  353. /**
  354. * Convenience method that will modify an entry's name directly
  355. * in place in an entry header buffer byte array.
  356. *
  357. * @param outbuf The buffer containing the entry header to modify.
  358. * @param newName The new name to place into the header buffer.
  359. */
  360. public void
  361. adjustEntryName( byte[] outbuf, String newName )
  362. {
  363. int offset = 0;
  364. offset = TarHeader.getNameBytes
  365. ( new StringBuffer( newName ),
  366. outbuf, offset, TarHeader.NAMELEN );
  367. }
  368. /**
  369. * Return whether or not this entry represents a directory.
  370. *
  371. * @return True if this entry is a directory.
  372. */
  373. public boolean
  374. isDirectory()
  375. {
  376. if ( this.file != null )
  377. return this.file.isDirectory();
  378. if ( this.header != null )
  379. {
  380. if ( this.header.linkFlag == TarHeader.LF_DIR )
  381. return true;
  382. if ( this.header.name.toString().endsWith( "/" ) )
  383. return true;
  384. }
  385. return false;
  386. }
  387. /**
  388. * Fill in a TarHeader with information from a File.
  389. *
  390. * @param hdr The TarHeader to fill in.
  391. * @param file The file from which to get the header information.
  392. */
  393. public void
  394. getFileTarHeader( TarHeader hdr, File file )
  395. throws InvalidHeaderException
  396. {
  397. this.file = file;
  398. String name = file.getPath();
  399. String osname = System.getProperty( "os.name" );
  400. if ( osname != null )
  401. {
  402. // Strip off drive letters!
  403. // REVIEW Would a better check be "(File.separator == '\')"?
  404. // String Win32Prefix = "Windows";
  405. // String prefix = osname.substring( 0, Win32Prefix.length() );
  406. // if ( prefix.equalsIgnoreCase( Win32Prefix ) )
  407. // if ( File.separatorChar == '\\' )
  408. // Per Patrick Beard:
  409. String Win32Prefix = "windows";
  410. if ( osname.toLowerCase().startsWith( Win32Prefix ) )
  411. {
  412. if ( name.length() > 2 )
  413. {
  414. char ch1 = name.charAt(0);
  415. char ch2 = name.charAt(1);
  416. if ( ch2 == ':'
  417. && ( (ch1 >= 'a' && ch1 <= 'z')
  418. || (ch1 >= 'A' && ch1 <= 'Z') ) )
  419. {
  420. name = name.substring( 2 );
  421. }
  422. }
  423. }
  424. }
  425. name = name.replace( File.separatorChar, '/' );
  426. // No absolute pathnames
  427. // Windows (and Posix?) paths can start with "\\NetworkDrive\",
  428. // so we loop on starting /'s.
  429. for ( ; name.startsWith( "/" ) ; )
  430. name = name.substring( 1 );
  431. hdr.linkName = new StringBuffer( "" );
  432. hdr.name = new StringBuffer( name );
  433. if ( file.isDirectory() )
  434. {
  435. hdr.mode = 040755;
  436. hdr.linkFlag = TarHeader.LF_DIR;
  437. if ( hdr.name.charAt( hdr.name.length() - 1 ) != '/' )
  438. hdr.name.append( "/" );
  439. }
  440. else
  441. {
  442. hdr.mode = 0100644;
  443. hdr.linkFlag = TarHeader.LF_NORMAL;
  444. }
  445. // UNDONE When File lets us get the userName, use it!
  446. hdr.size = file.length();
  447. hdr.modTime = file.lastModified() / 1000;
  448. hdr.checkSum = 0;
  449. hdr.devMajor = 0;
  450. hdr.devMinor = 0;
  451. }
  452. /**
  453. * If this entry represents a file, and the file is a directory, return
  454. * an array of TarEntries for this entry's children.
  455. *
  456. * @return An array of TarEntry's for this entry's children.
  457. */
  458. public TarEntry[]
  459. getDirectoryEntries()
  460. throws InvalidHeaderException
  461. {
  462. if ( this.file == null
  463. || ! this.file.isDirectory() )
  464. {
  465. return new TarEntry[0];
  466. }
  467. String[] list = this.file.list();
  468. TarEntry[] result = new TarEntry[ list.length ];
  469. for ( int i = 0 ; i < list.length ; ++i )
  470. {
  471. result[i] =
  472. new TarEntry
  473. ( new File( this.file, list[i] ) );
  474. }
  475. return result;
  476. }
  477. /**
  478. * Compute the checksum of a tar entry header.
  479. *
  480. * @param buf The tar entry's header buffer.
  481. * @return The computed checksum.
  482. */
  483. public long
  484. computeCheckSum( byte[] buf )
  485. {
  486. long sum = 0;
  487. for ( int i = 0 ; i < buf.length ; ++i )
  488. {
  489. sum += 255 & buf[ i ];
  490. }
  491. return sum;
  492. }
  493. /**
  494. * Write an entry's header information to a header buffer.
  495. *
  496. * @param outbuf The tar entry header buffer to fill in.
  497. */
  498. public void
  499. writeEntryHeader( byte[] outbuf )
  500. {
  501. int offset = 0;
  502. offset = TarHeader.getNameBytes
  503. ( this.header.name, outbuf, offset, TarHeader.NAMELEN );
  504. offset = TarHeader.getOctalBytes
  505. ( this.header.mode, outbuf, offset, TarHeader.MODELEN );
  506. offset = TarHeader.getOctalBytes
  507. ( this.header.userId, outbuf, offset, TarHeader.UIDLEN );
  508. offset = TarHeader.getOctalBytes
  509. ( this.header.groupId, outbuf, offset, TarHeader.GIDLEN );
  510. long size = this.header.size;
  511. offset = TarHeader.getLongOctalBytes
  512. ( size, outbuf, offset, TarHeader.SIZELEN );
  513. offset = TarHeader.getLongOctalBytes
  514. ( this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN );
  515. int csOffset = offset;
  516. for ( int c = 0 ; c < TarHeader.CHKSUMLEN ; ++c )
  517. outbuf[ offset++ ] = (byte) ' ';
  518. outbuf[ offset++ ] = this.header.linkFlag;
  519. offset = TarHeader.getNameBytes
  520. ( this.header.linkName, outbuf, offset, TarHeader.NAMELEN );
  521. offset = TarHeader.getNameBytes
  522. ( this.header.magic, outbuf, offset, TarHeader.MAGICLEN );
  523. offset = TarHeader.getNameBytes
  524. ( this.header.userName, outbuf, offset, TarHeader.UNAMELEN );
  525. offset = TarHeader.getNameBytes
  526. ( this.header.groupName, outbuf, offset, TarHeader.GNAMELEN );
  527. offset = TarHeader.getOctalBytes
  528. ( this.header.devMajor, outbuf, offset, TarHeader.DEVLEN );
  529. offset = TarHeader.getOctalBytes
  530. ( this.header.devMinor, outbuf, offset, TarHeader.DEVLEN );
  531. for ( ; offset < outbuf.length ; )
  532. outbuf[ offset++ ] = 0;
  533. long checkSum = this.computeCheckSum( outbuf );
  534. TarHeader.getCheckSumOctalBytes
  535. ( checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN );
  536. }
  537. /**
  538. * Parse an entry's TarHeader information from a header buffer.
  539. *
  540. * @param hdr The TarHeader to fill in from the buffer information.
  541. * @param header The tar entry header buffer to get information from.
  542. */
  543. public void
  544. parseTarHeader( TarHeader hdr, byte[] header )
  545. throws InvalidHeaderException
  546. {
  547. int offset = 0;
  548. hdr.name =
  549. TarHeader.parseName( header, offset, TarHeader.NAMELEN );
  550. offset += TarHeader.NAMELEN;
  551. hdr.mode = (int)
  552. TarHeader.parseOctal( header, offset, TarHeader.MODELEN );
  553. offset += TarHeader.MODELEN;
  554. hdr.userId = (int)
  555. TarHeader.parseOctal( header, offset, TarHeader.UIDLEN );
  556. offset += TarHeader.UIDLEN;
  557. hdr.groupId = (int)
  558. TarHeader.parseOctal( header, offset, TarHeader.GIDLEN );
  559. offset += TarHeader.GIDLEN;
  560. hdr.size =
  561. TarHeader.parseOctal( header, offset, TarHeader.SIZELEN );
  562. offset += TarHeader.SIZELEN;
  563. hdr.modTime =
  564. TarHeader.parseOctal( header, offset, TarHeader.MODTIMELEN );
  565. offset += TarHeader.MODTIMELEN;
  566. hdr.checkSum = (int)
  567. TarHeader.parseOctal( header, offset, TarHeader.CHKSUMLEN );
  568. offset += TarHeader.CHKSUMLEN;
  569. hdr.linkFlag = header[ offset++ ];
  570. hdr.linkName =
  571. TarHeader.parseName( header, offset, TarHeader.NAMELEN );
  572. offset += TarHeader.NAMELEN;
  573. hdr.magic =
  574. TarHeader.parseName( header, offset, TarHeader.MAGICLEN );
  575. offset += TarHeader.MAGICLEN;
  576. hdr.userName =
  577. TarHeader.parseName( header, offset, TarHeader.UNAMELEN );
  578. offset += TarHeader.UNAMELEN;
  579. hdr.groupName =
  580. TarHeader.parseName( header, offset, TarHeader.GNAMELEN );
  581. offset += TarHeader.GNAMELEN;
  582. hdr.devMajor = (int)
  583. TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
  584. offset += TarHeader.DEVLEN;
  585. hdr.devMinor = (int)
  586. TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
  587. }
  588. /**
  589. * Fill in a TarHeader given only the entry's name.
  590. *
  591. * @param hdr The TarHeader to fill in.
  592. * @param name The tar entry name.
  593. */
  594. public void
  595. nameTarHeader( TarHeader hdr, String name )
  596. {
  597. boolean isDir = name.endsWith( "/" );
  598. hdr.checkSum = 0;
  599. hdr.devMajor = 0;
  600. hdr.devMinor = 0;
  601. hdr.name = new StringBuffer( name );
  602. hdr.mode = isDir ? 040755 : 0100644;
  603. hdr.userId = 0;
  604. hdr.groupId = 0;
  605. hdr.size = 0;
  606. hdr.checkSum = 0;
  607. hdr.modTime =
  608. (new java.util.Date()).getTime() / 1000;
  609. hdr.linkFlag =
  610. isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;
  611. hdr.linkName = new StringBuffer( "" );
  612. hdr.userName = new StringBuffer( "" );
  613. hdr.groupName = new StringBuffer( "" );
  614. hdr.devMajor = 0;
  615. hdr.devMinor = 0;
  616. }
  617. }