1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.provider.tar;
18
19 import java.io.File;
20 import java.util.Date;
21 import java.util.Locale;
22
23 /***
24 * This class represents an entry in a Tar archive. It consists of the entry's
25 * header, as well as the entry's File. Entries can be instantiated in one of
26 * three ways, depending on how they are to be used. <p>
27 *
28 * TarEntries that are created from the header bytes read from an archive are
29 * instantiated with the TarEntry( byte[] ) constructor. These entries will be
30 * used when extracting from or listing the contents of an archive. These
31 * entries have their header filled in using the header bytes. They also set the
32 * File to null, since they reference an archive entry not a file. <p>
33 *
34 * TarEntries that are created from Files that are to be written into an archive
35 * are instantiated with the TarEntry( File ) constructor. These entries have
36 * their header filled in using the File's information. They also keep a
37 * reference to the File for convenience when writing entries. <p>
38 *
39 * Finally, TarEntries can be constructed from nothing but a name. This allows
40 * the programmer to construct the entry by hand, for instance when only an
41 * InputStream is available for writing to the archive, and the header
42 * information is constructed from other information. In this case the header
43 * fields are set to defaults and the File is set to null. <p>
44 *
45 * The C structure for a Tar Entry's header is: <pre>
46 * struct header {
47 * char name[NAMSIZ];
48 * char mode[8];
49 * char uid[8];
50 * char gid[8];
51 * char size[12];
52 * char mtime[12];
53 * char chksum[8];
54 * char linkflag;
55 * char linkname[NAMSIZ];
56 * char magic[8];
57 * char uname[TUNMLEN];
58 * char gname[TGNMLEN];
59 * char devmajor[8];
60 * char devminor[8];
61 * } header;
62 * </pre>
63 *
64 * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
65 * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
66 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
67 * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
68 * @see TarInputStream
69 * @see TarOutputStream
70 */
71 class TarEntry
72 {
73 /***
74 * The length of the name field in a header buffer.
75 */
76 public static final int NAMELEN = 100;
77
78 /***
79 * The entry's modification time.
80 */
81 private int m_checkSum;
82
83 /***
84 * The entry's group name.
85 */
86 private int m_devMajor;
87
88 /***
89 * The entry's major device number.
90 */
91 private int m_devMinor;
92
93 /***
94 * The entry's minor device number.
95 */
96 private File m_file;
97
98 /***
99 * The entry's user id.
100 */
101 private int m_groupID;
102
103 /***
104 * The entry's user name.
105 */
106 private StringBuffer m_groupName;
107
108 /***
109 * The entry's checksum.
110 */
111 private byte m_linkFlag;
112
113 /***
114 * The entry's link flag.
115 */
116 private StringBuffer m_linkName;
117
118 /***
119 * The entry's link name.
120 */
121 private StringBuffer m_magic;
122
123 /***
124 * The entry's size.
125 */
126 private long m_modTime;
127
128 /***
129 * The entry's name.
130 */
131 private int m_mode;
132
133 private StringBuffer m_name;
134
135 /***
136 * The entry's group id.
137 */
138 private long m_size;
139
140 /***
141 * The entry's permission mode.
142 */
143 private int m_userID;
144
145 /***
146 * The entry's magic tag.
147 */
148 private StringBuffer m_userName;
149
150 /***
151 * Construct an entry with only a name. This allows the programmer to
152 * construct the entry's header "by hand". File is set to null.
153 *
154 * @param name the name of the entry
155 */
156 TarEntry( final String name )
157 {
158 this();
159
160 final boolean isDir = name.endsWith( "/" );
161
162 m_name = new StringBuffer( name );
163 m_mode = isDir ? 040755 : 0100644;
164 m_linkFlag = isDir ? TarConstants.LF_DIR : TarConstants.LF_NORMAL;
165 m_modTime = ( new Date() ).getTime() / 1000;
166 m_linkName = new StringBuffer( "" );
167 m_userName = new StringBuffer( "" );
168 m_groupName = new StringBuffer( "" );
169 }
170
171 /***
172 * Construct an entry with a name an a link flag.
173 *
174 * @param name Description of Parameter
175 * @param linkFlag Description of Parameter
176 */
177 TarEntry( final String name, final byte linkFlag )
178 {
179 this( name );
180 m_linkFlag = linkFlag;
181 }
182
183 /***
184 * Construct an entry for a file. File is set to file, and the header is
185 * constructed from information from the file.
186 *
187 * @param file The file that the entry represents.
188 */
189 TarEntry( final File file )
190 {
191 this();
192
193 m_file = file;
194
195 String name = file.getPath();
196
197
198 final String osName =
199 System.getProperty( "os.name" ).toLowerCase( Locale.US );
200 if( -1 != osName.indexOf( "netware" ) )
201 {
202 if( name.length() > 2 )
203 {
204 final char ch1 = name.charAt( 0 );
205 final char ch2 = name.charAt( 1 );
206
207 if( ch2 == ':' &&
208 ( ( ch1 >= 'a' && ch1 <= 'z' ) ||
209 ( ch1 >= 'A' && ch1 <= 'Z' ) ) )
210 {
211 name = name.substring( 2 );
212 }
213 }
214 }
215 else if( -1 != osName.indexOf( "netware" ) )
216 {
217 final int colon = name.indexOf( ':' );
218 if( colon != -1 )
219 {
220 name = name.substring( colon + 1 );
221 }
222 }
223
224 name = name.replace( File.separatorChar, '/' );
225
226
227
228
229 while( name.startsWith( "/" ) )
230 {
231 name = name.substring( 1 );
232 }
233
234 m_linkName = new StringBuffer( "" );
235 m_name = new StringBuffer( name );
236
237 if( file.isDirectory() )
238 {
239 m_mode = 040755;
240 m_linkFlag = TarConstants.LF_DIR;
241
242 if( m_name.charAt( m_name.length() - 1 ) != '/' )
243 {
244 m_name.append( "/" );
245 }
246 }
247 else
248 {
249 m_mode = 0100644;
250 m_linkFlag = TarConstants.LF_NORMAL;
251 }
252
253 m_size = file.length();
254 m_modTime = file.lastModified() / 1000;
255 m_checkSum = 0;
256 m_devMajor = 0;
257 m_devMinor = 0;
258 }
259
260 /***
261 * Construct an entry from an archive's header bytes. File is set to null.
262 *
263 * @param header The header bytes from a tar archive entry.
264 */
265 TarEntry( final byte[] header )
266 {
267 this();
268 parseTarHeader( header );
269 }
270
271 /***
272 * Construct an empty entry and prepares the header values.
273 */
274 private TarEntry()
275 {
276 m_magic = new StringBuffer( TarConstants.TMAGIC );
277 m_name = new StringBuffer();
278 m_linkName = new StringBuffer();
279
280 String user = System.getProperty( "user.name", "" );
281 if( user.length() > 31 )
282 {
283 user = user.substring( 0, 31 );
284 }
285
286 m_userName = new StringBuffer( user );
287 m_groupName = new StringBuffer( "" );
288 }
289
290 /***
291 * Set this entry's group id.
292 *
293 * @param groupId This entry's new group id.
294 */
295 public void setGroupID( final int groupId )
296 {
297 m_groupID = groupId;
298 }
299
300 /***
301 * Set this entry's group id.
302 *
303 * @param groupId This entry's new group id.
304 * @deprecated Use setGroupID() instead
305 * @see #setGroupID(int)
306 */
307 public void setGroupId( final int groupId )
308 {
309 m_groupID = groupId;
310 }
311
312 /***
313 * Set this entry's group name.
314 *
315 * @param groupName This entry's new group name.
316 */
317 public void setGroupName( final String groupName )
318 {
319 m_groupName = new StringBuffer( groupName );
320 }
321
322 /***
323 * Set this entry's modification time. The parameter passed to this method
324 * is in "Java time".
325 *
326 * @param time This entry's new modification time.
327 */
328 public void setModTime( final long time )
329 {
330 m_modTime = time / 1000;
331 }
332
333 /***
334 * Set this entry's modification time.
335 *
336 * @param time This entry's new modification time.
337 */
338 public void setModTime( final Date time )
339 {
340 m_modTime = time.getTime() / 1000;
341 }
342
343 /***
344 * Set the mode for this entry
345 *
346 * @param mode The new Mode value
347 */
348 public void setMode( final int mode )
349 {
350 m_mode = mode;
351 }
352
353 /***
354 * Set this entry's name.
355 *
356 * @param name This entry's new name.
357 */
358 public void setName( final String name )
359 {
360 m_name = new StringBuffer( name );
361 }
362
363 /***
364 * Set this entry's file size.
365 *
366 * @param size This entry's new file size.
367 */
368 public void setSize( final long size )
369 {
370 m_size = size;
371 }
372
373 /***
374 * Set this entry's user id.
375 *
376 * @param userId This entry's new user id.
377 */
378 public void setUserID( final int userId )
379 {
380 m_userID = userId;
381 }
382
383 /***
384 * Set this entry's user id.
385 *
386 * @param userId This entry's new user id.
387 * @deprecated Use setUserID() instead
388 * @see #setUserID(int)
389 */
390 public void setUserId( final int userId )
391 {
392 m_userID = userId;
393 }
394
395 /***
396 * Set this entry's user name.
397 *
398 * @param userName This entry's new user name.
399 */
400 public void setUserName( final String userName )
401 {
402 m_userName = new StringBuffer( userName );
403 }
404
405 /***
406 * If this entry represents a file, and the file is a directory, return an
407 * array of TarEntries for this entry's children.
408 *
409 * @return An array of TarEntry's for this entry's children.
410 */
411 public TarEntry[] getDirectoryEntries()
412 {
413 if( null == m_file || !m_file.isDirectory() )
414 {
415 return new TarEntry[ 0 ];
416 }
417
418 final String[] list = m_file.list();
419 final TarEntry[] result = new TarEntry[ list.length ];
420
421 for( int i = 0; i < list.length; ++i )
422 {
423 result[ i ] = new TarEntry( new File( m_file, list[ i ] ) );
424 }
425
426 return result;
427 }
428
429 /***
430 * Get this entry's file.
431 *
432 * @return This entry's file.
433 */
434 public File getFile()
435 {
436 return m_file;
437 }
438
439 /***
440 * Get this entry's group id.
441 *
442 * @return This entry's group id.
443 * @deprecated Use getGroupID() instead
444 * @see #getGroupID()
445 */
446 public int getGroupId()
447 {
448 return m_groupID;
449 }
450
451 /***
452 * Get this entry's group id.
453 *
454 * @return This entry's group id.
455 */
456 public int getGroupID()
457 {
458 return m_groupID;
459 }
460
461 /***
462 * Get this entry's group name.
463 *
464 * @return This entry's group name.
465 */
466 public String getGroupName()
467 {
468 return m_groupName.toString();
469 }
470
471 /***
472 * Set this entry's modification time.
473 *
474 * @return The ModTime value
475 */
476 public Date getModTime()
477 {
478 return new Date( m_modTime * 1000 );
479 }
480
481 /***
482 * Get this entry's mode.
483 *
484 * @return This entry's mode.
485 */
486 public int getMode()
487 {
488 return m_mode;
489 }
490
491 /***
492 * Get this entry's name.
493 *
494 * @return This entry's name.
495 */
496 public String getName()
497 {
498 return m_name.toString();
499 }
500
501 /***
502 * Get this entry's file size.
503 *
504 * @return This entry's file size.
505 */
506 public long getSize()
507 {
508 return m_size;
509 }
510
511 /***
512 * Get this entry's checksum.
513 *
514 * @return This entry's checksum.
515 */
516 public int getCheckSum()
517 {
518 return m_checkSum;
519 }
520
521 /***
522 * Get this entry's user id.
523 *
524 * @return This entry's user id.
525 * @deprecated Use getUserID() instead
526 * @see #getUserID()
527 */
528 public int getUserId()
529 {
530 return m_userID;
531 }
532
533 /***
534 * Get this entry's user id.
535 *
536 * @return This entry's user id.
537 */
538 public int getUserID()
539 {
540 return m_userID;
541 }
542
543 /***
544 * Get this entry's user name.
545 *
546 * @return This entry's user name.
547 */
548 public String getUserName()
549 {
550 return m_userName.toString();
551 }
552
553 /***
554 * Determine if the given entry is a descendant of this entry. Descendancy
555 * is determined by the name of the descendant starting with this entry's
556 * name.
557 *
558 * @param desc Entry to be checked as a descendent of
559 * @return True if entry is a descendant of
560 */
561 public boolean isDescendent( final TarEntry desc )
562 {
563 return desc.getName().startsWith( getName() );
564 }
565
566 /***
567 * Return whether or not this entry represents a directory.
568 *
569 * @return True if this entry is a directory.
570 */
571 public boolean isDirectory()
572 {
573 if( m_file != null )
574 {
575 return m_file.isDirectory();
576 }
577
578 if( m_linkFlag == TarConstants.LF_DIR )
579 {
580 return true;
581 }
582
583 if( getName().endsWith( "/" ) )
584 {
585 return true;
586 }
587
588 return false;
589 }
590
591 /***
592 * Indicate if this entry is a GNU long name block
593 *
594 * @return true if this is a long name extension provided by GNU tar
595 */
596 public boolean isGNULongNameEntry()
597 {
598 return m_linkFlag == TarConstants.LF_GNUTYPE_LONGNAME &&
599 m_name.toString().equals( TarConstants.GNU_LONGLINK );
600 }
601
602 /***
603 * Determine if the two entries are equal. Equality is determined by the
604 * header names being equal.
605 *
606 * @param other Entry to be checked for equality.
607 * @return True if the entries are equal.
608 */
609 public boolean equals( final TarEntry other )
610 {
611 return getName().equals( other.getName() );
612 }
613
614 /***
615 * Parse an entry's header information from a header buffer.
616 *
617 * @param header The tar entry header buffer to get information from.
618 */
619 private void parseTarHeader( final byte[] header )
620 {
621 int offset = 0;
622
623 m_name = TarUtils.parseName( header, offset, NAMELEN );
624 offset += NAMELEN;
625 m_mode = (int)TarUtils.parseOctal( header, offset, TarConstants.MODELEN );
626 offset += TarConstants.MODELEN;
627 m_userID = (int)TarUtils.parseOctal( header, offset, TarConstants.UIDLEN );
628 offset += TarConstants.UIDLEN;
629 m_groupID = (int)TarUtils.parseOctal( header, offset, TarConstants.GIDLEN );
630 offset += TarConstants.GIDLEN;
631 m_size = TarUtils.parseOctal( header, offset, TarConstants.SIZELEN );
632 offset += TarConstants.SIZELEN;
633 m_modTime = TarUtils.parseOctal( header, offset, TarConstants.MODTIMELEN );
634 offset += TarConstants.MODTIMELEN;
635 m_checkSum = (int)TarUtils.parseOctal( header, offset, TarConstants.CHKSUMLEN );
636 offset += TarConstants.CHKSUMLEN;
637 m_linkFlag = header[ offset++ ];
638 m_linkName = TarUtils.parseName( header, offset, NAMELEN );
639 offset += NAMELEN;
640 m_magic = TarUtils.parseName( header, offset, TarConstants.MAGICLEN );
641 offset += TarConstants.MAGICLEN;
642 m_userName = TarUtils.parseName( header, offset, TarConstants.UNAMELEN );
643 offset += TarConstants.UNAMELEN;
644 m_groupName = TarUtils.parseName( header, offset, TarConstants.GNAMELEN );
645 offset += TarConstants.GNAMELEN;
646 m_devMajor = (int)TarUtils.parseOctal( header, offset, TarConstants.DEVLEN );
647 offset += TarConstants.DEVLEN;
648 m_devMinor = (int)TarUtils.parseOctal( header, offset, TarConstants.DEVLEN );
649 }
650
651 /***
652 * Write an entry's header information to a header buffer.
653 *
654 * @param buffer The tar entry header buffer to fill in.
655 */
656 public void writeEntryHeader( final byte[] buffer )
657 {
658 int offset = 0;
659
660 offset = TarUtils.getNameBytes( m_name, buffer, offset, NAMELEN );
661 offset = TarUtils.getOctalBytes( m_mode, buffer, offset, TarConstants.MODELEN );
662 offset = TarUtils.getOctalBytes( m_userID, buffer, offset, TarConstants.UIDLEN );
663 offset = TarUtils.getOctalBytes( m_groupID, buffer, offset, TarConstants.GIDLEN );
664 offset = TarUtils.getLongOctalBytes( m_size, buffer, offset, TarConstants.SIZELEN );
665 offset = TarUtils.getLongOctalBytes( m_modTime, buffer, offset, TarConstants.MODTIMELEN );
666
667 final int checkSumOffset = offset;
668 for( int i = 0; i < TarConstants.CHKSUMLEN; ++i )
669 {
670 buffer[ offset++ ] = (byte)' ';
671 }
672
673 buffer[ offset++ ] = m_linkFlag;
674 offset = TarUtils.getNameBytes( m_linkName, buffer, offset, NAMELEN );
675 offset = TarUtils.getNameBytes( m_magic, buffer, offset, TarConstants.MAGICLEN );
676 offset = TarUtils.getNameBytes( m_userName, buffer, offset, TarConstants.UNAMELEN );
677 offset = TarUtils.getNameBytes( m_groupName, buffer, offset, TarConstants.GNAMELEN );
678 offset = TarUtils.getOctalBytes( m_devMajor, buffer, offset, TarConstants.DEVLEN );
679 offset = TarUtils.getOctalBytes( m_devMinor, buffer, offset, TarConstants.DEVLEN );
680
681 while( offset < buffer.length )
682 {
683 buffer[ offset++ ] = 0;
684 }
685
686 final long checkSum = TarUtils.computeCheckSum( buffer );
687 TarUtils.getCheckSumOctalBytes( checkSum, buffer, checkSumOffset, TarConstants.CHKSUMLEN );
688 }
689 }