2
* Licensed to the Apache Software Foundation (ASF) under one
3
* or more contributor license agreements. See the NOTICE file
4
* distributed with this work for additional information
5
* regarding copyright ownership. The ASF licenses this file
6
* to you under the Apache License, Version 2.0 (the
7
* "License"); you may not use this file except in compliance
8
* with the License. You may obtain a copy of the License at
10
* http://www.apache.org/licenses/LICENSE-2.0
12
* Unless required by applicable law or agreed to in writing,
13
* software distributed under the License is distributed on an
14
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
* KIND, either express or implied. See the License for the
16
* specific language governing permissions and limitations
19
package org.apache.commons.compress.archivers.tar;
22
import java.util.Date;
23
import java.util.Locale;
26
* This class represents an entry in a Tar archive. It consists of the entry's
27
* header, as well as the entry's File. Entries can be instantiated in one of
28
* three ways, depending on how they are to be used. <p>
30
* TarEntries that are created from the header bytes read from an archive are
31
* instantiated with the TarEntry( byte[] ) constructor. These entries will be
32
* used when extracting from or listing the contents of an archive. These
33
* entries have their header filled in using the header bytes. They also set the
34
* File to null, since they reference an archive entry not a file. <p>
36
* TarEntries that are created from Files that are to be written into an archive
37
* are instantiated with the TarEntry( File ) constructor. These entries have
38
* their header filled in using the File's information. They also keep a
39
* reference to the File for convenience when writing entries. <p>
41
* Finally, TarEntries can be constructed from nothing but a name. This allows
42
* the programmer to construct the entry by hand, for instance when only an
43
* InputStream is available for writing to the archive, and the header
44
* information is constructed from other information. In this case the header
45
* fields are set to defaults and the File is set to null. <p>
47
* The C structure for a Tar Entry's header is: <pre>
57
* char linkname[NAMSIZ];
59
* char uname[TUNMLEN];
60
* char gname[TGNMLEN];
66
* @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
67
* @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
68
* @author <a href="mailto:peter@apache.org">Peter Donald</a>
69
* @version $Revision: 155439 $ $Date: 2007-01-09 10:24:33 +0100 (Di, 09 Jan 2007) $
71
* @see TarOutputStream
76
* The length of the mode field in a header buffer.
78
private final static int MODELEN = 8;
81
* The length of the user id field in a header buffer.
83
private final static int UIDLEN = 8;
86
* The length of the group id field in a header buffer.
88
private final static int GIDLEN = 8;
91
* The length of the checksum field in a header buffer.
93
private final static int CHKSUMLEN = 8;
96
* The length of the size field in a header buffer.
98
private final static int SIZELEN = 12;
101
* The length of the magic field in a header buffer.
103
private final static int MAGICLEN = 8;
106
* The length of the modification time field in a header buffer.
108
private final static int MODTIMELEN = 12;
111
* The length of the user name field in a header buffer.
113
private final static int UNAMELEN = 32;
116
* The length of the group name field in a header buffer.
118
private final static int GNAMELEN = 32;
121
* The length of the devices field in a header buffer.
123
private final static int DEVLEN = 8;
126
* LF_ constants represent the "link flag" of an entry, or more commonly,
127
* the "entry type". This is the "old way" of indicating a normal file.
129
//private final static byte LF_OLDNORM = 0;
134
private final static byte LF_NORMAL = (byte)'0';
139
//private final static byte LF_LINK = (byte)'1';
142
* Symbolic link file type.
144
//private final static byte LF_SYMLINK = (byte)'2';
147
* Character device file type.
149
//private final static byte LF_CHR = (byte)'3';
152
* Block device file type.
154
//private final static byte LF_BLK = (byte)'4';
157
* Directory file type.
159
private final static byte LF_DIR = (byte)'5';
162
* FIFO (pipe) file type.
164
//private final static byte LF_FIFO = (byte)'6';
167
* Contiguous file type.
169
//private final static byte LF_CONTIG = (byte)'7';
172
* The magic tag representing a POSIX tar archive.
174
private final static String TMAGIC = "ustar";
177
* The magic tag representing a GNU tar archive.
179
//private final static String GNU_TMAGIC = "ustar ";
182
* The name of the GNU tar entry which contains a long name.
184
static String GNU_LONGLINK = "././@LongLink";
187
* Identifies the *next* file on the tape as having a long name.
189
static byte LF_GNUTYPE_LONGNAME = (byte)'L';
194
* The length of the name field in a header buffer.
196
public static final int NAMELEN = 100;
199
* The entry's modification time.
201
private int m_checkSum;
204
* The entry's group name.
206
private int m_devMajor;
209
* The entry's major device number.
211
private int m_devMinor;
214
* The entry's minor device number.
219
* The entry's user id.
221
private int m_groupID;
224
* The entry's user name.
226
private StringBuffer m_groupName;
229
* The entry's checksum.
231
private byte m_linkFlag;
234
* The entry's link flag.
236
private StringBuffer m_linkName;
239
* The entry's link name.
241
private StringBuffer m_magic;
246
private long m_modTime;
253
private StringBuffer m_name;
256
* The entry's group id.
261
* The entry's permission mode.
263
private int m_userID;
266
* The entry's magic tag.
268
private StringBuffer m_userName;
271
* Construct an entry with only a name. This allows the programmer to
272
* construct the entry's header "by hand". File is set to null.
274
* @param name the name of the entry
276
public TarEntry( final String name )
280
final boolean isDir = name.endsWith( "/" );
282
m_name = new StringBuffer( name );
283
m_mode = isDir ? 040755 : 0100644;
284
m_linkFlag = isDir ? TarEntry.LF_DIR : TarEntry.LF_NORMAL;
285
m_modTime = ( new Date() ).getTime() / 1000;
286
m_linkName = new StringBuffer( "" );
287
m_userName = new StringBuffer( "" );
288
m_groupName = new StringBuffer( "" );
292
* Construct an entry with a name an a link flag.
294
* @param name Description of Parameter
295
* @param linkFlag Description of Parameter
297
public TarEntry( final String name, final byte linkFlag )
300
m_linkFlag = linkFlag;
304
* Construct an entry for a file. File is set to file, and the header is
305
* constructed from information from the file.
307
* @param file The file that the entry represents.
309
public TarEntry( final File file )
315
String name = file.getPath();
317
// Strip off drive letters!
318
final String osName =
319
System.getProperty( "os.name" ).toLowerCase( Locale.US );
320
if( -1 != osName.indexOf( "netware" ) )
322
if( name.length() > 2 )
324
final char ch1 = name.charAt( 0 );
325
final char ch2 = name.charAt( 1 );
328
( ( ch1 >= 'a' && ch1 <= 'z' ) ||
329
( ch1 >= 'A' && ch1 <= 'Z' ) ) )
331
name = name.substring( 2 );
335
else if( -1 != osName.indexOf( "netware" ) )
337
final int colon = name.indexOf( ':' );
340
name = name.substring( colon + 1 );
344
name = name.replace( File.separatorChar, '/' );
346
// No absolute pathnames
347
// Windows (and Posix?) paths can start with "\\NetworkDrive\",
348
// so we loop on starting /'s.
349
while( name.startsWith( "/" ) )
351
name = name.substring( 1 );
354
m_linkName = new StringBuffer( "" );
355
m_name = new StringBuffer( name );
357
if( file.isDirectory() )
360
m_linkFlag = TarEntry.LF_DIR;
362
if( m_name.charAt( m_name.length() - 1 ) != '/' )
364
m_name.append( "/" );
370
m_linkFlag = TarEntry.LF_NORMAL;
373
m_size = file.length();
374
m_modTime = file.lastModified() / 1000;
381
* Construct an entry from an archive's header bytes. File is set to null.
383
* @param header The header bytes from a tar archive entry.
385
public TarEntry( final byte[] header )
388
parseTarHeader( header );
392
* Construct an empty entry and prepares the header values.
396
m_magic = new StringBuffer( TarEntry.TMAGIC );
397
m_name = new StringBuffer();
398
m_linkName = new StringBuffer();
400
String user = System.getProperty( "user.name", "" );
401
if( user.length() > 31 )
403
user = user.substring( 0, 31 );
406
m_userName = new StringBuffer( user );
407
m_groupName = new StringBuffer( "" );
411
* Set this entry's group id.
413
* @param groupId This entry's new group id.
415
public void setGroupID( final int groupId )
421
* Set this entry's group id.
423
* @param groupId This entry's new group id.
424
* @deprecated Use setGroupID() instead
425
* @see #setGroupID(int)
427
public void setGroupId( final int groupId )
433
* Set this entry's group name.
435
* @param groupName This entry's new group name.
437
public void setGroupName( final String groupName )
439
m_groupName = new StringBuffer( groupName );
443
* Set this entry's modification time. The parameter passed to this method
446
* @param time This entry's new modification time.
448
public void setModTime( final long time )
450
m_modTime = time / 1000;
454
* Set this entry's modification time.
456
* @param time This entry's new modification time.
458
public void setModTime( final Date time )
460
m_modTime = time.getTime() / 1000;
464
* Set the mode for this entry
466
* @param mode The new Mode value
468
public void setMode( final int mode )
474
* Set this entry's name.
476
* @param name This entry's new name.
478
public void setName( final String name )
480
m_name = new StringBuffer( name );
484
* Set this entry's file size.
486
* @param size This entry's new file size.
488
public void setSize( final long size )
494
* Set this entry's user id.
496
* @param userId This entry's new user id.
498
public void setUserID( final int userId )
504
* Set this entry's user id.
506
* @param userId This entry's new user id.
507
* @deprecated Use setUserID() instead
508
* @see #setUserID(int)
510
public void setUserId( final int userId )
516
* Set this entry's user name.
518
* @param userName This entry's new user name.
520
public void setUserName( final String userName )
522
m_userName = new StringBuffer( userName );
526
* If this entry represents a file, and the file is a directory, return an
527
* array of TarEntries for this entry's children.
529
* @return An array of TarEntry's for this entry's children.
531
public TarEntry[] getDirectoryEntries()
533
if( null == m_file || !m_file.isDirectory() )
535
return new TarEntry[ 0 ];
538
final String[] list = m_file.list();
539
final TarEntry[] result = new TarEntry[ list.length ];
541
for( int i = 0; i < list.length; ++i )
543
result[ i ] = new TarEntry( new File( m_file, list[ i ] ) );
550
* Get this entry's file.
552
* @return This entry's file.
554
public File getFile()
560
* Get this entry's group id.
562
* @return This entry's group id.
563
* @deprecated Use getGroupID() instead
566
public int getGroupId()
572
* Get this entry's group id.
574
* @return This entry's group id.
576
public int getGroupID()
582
* Get this entry's group name.
584
* @return This entry's group name.
586
public String getGroupName()
588
return m_groupName.toString();
592
* Set this entry's modification time.
594
* @return The ModTime value
596
public Date getModTime()
598
return new Date( m_modTime * 1000 );
602
* Get this entry's mode.
604
* @return This entry's mode.
612
* Get this entry's name.
614
* @return This entry's name.
616
public String getName()
618
return m_name.toString();
622
* Get this entry's file size.
624
* @return This entry's file size.
626
public long getSize()
632
* Get this entry's checksum.
634
* @return This entry's checksum.
636
public int getCheckSum()
642
* Get this entry's user id.
644
* @return This entry's user id.
645
* @deprecated Use getUserID() instead
648
public int getUserId()
654
* Get this entry's user id.
656
* @return This entry's user id.
658
public int getUserID()
664
* Get this entry's user name.
666
* @return This entry's user name.
668
public String getUserName()
670
return m_userName.toString();
674
* Determine if the given entry is a descendant of this entry. Descendancy
675
* is determined by the name of the descendant starting with this entry's
678
* @param desc Entry to be checked as a descendent of
679
* @return True if entry is a descendant of
681
public boolean isDescendent( final TarEntry desc )
683
return desc.getName().startsWith( getName() );
687
* Return whether or not this entry represents a directory.
689
* @return True if this entry is a directory.
691
public boolean isDirectory()
695
return m_file.isDirectory();
698
if( m_linkFlag == TarEntry.LF_DIR )
703
if( getName().endsWith( "/" ) )
712
* Indicate if this entry is a GNU long name block
714
* @return true if this is a long name extension provided by GNU tar
716
public boolean isGNULongNameEntry()
718
return m_linkFlag == TarEntry.LF_GNUTYPE_LONGNAME &&
719
m_name.toString().equals( TarEntry.GNU_LONGLINK );
723
* Determine if the two entries are equal. Equality is determined by the
724
* header names being equal.
726
* @param other Entry to be checked for equality.
727
* @return True if the entries are equal.
729
public boolean equals( final TarEntry other )
731
return getName().equals( other.getName() );
735
* Parse an entry's header information from a header buffer.
737
* @param header The tar entry header buffer to get information from.
739
private void parseTarHeader( final byte[] header )
743
m_name = TarUtils.parseName( header, offset, NAMELEN );
745
m_mode = (int)TarUtils.parseOctal( header, offset, TarEntry.MODELEN );
746
offset += TarEntry.MODELEN;
747
m_userID = (int)TarUtils.parseOctal( header, offset, TarEntry.UIDLEN );
748
offset += TarEntry.UIDLEN;
749
m_groupID = (int)TarUtils.parseOctal( header, offset, TarEntry.GIDLEN );
750
offset += TarEntry.GIDLEN;
751
m_size = TarUtils.parseOctal( header, offset, TarEntry.SIZELEN );
752
offset += TarEntry.SIZELEN;
753
m_modTime = TarUtils.parseOctal( header, offset, TarEntry.MODTIMELEN );
754
offset += TarEntry.MODTIMELEN;
755
m_checkSum = (int)TarUtils.parseOctal( header, offset, TarEntry.CHKSUMLEN );
756
offset += TarEntry.CHKSUMLEN;
757
m_linkFlag = header[ offset++ ];
758
m_linkName = TarUtils.parseName( header, offset, NAMELEN );
760
m_magic = TarUtils.parseName( header, offset, TarEntry.MAGICLEN );
761
offset += TarEntry.MAGICLEN;
762
m_userName = TarUtils.parseName( header, offset, TarEntry.UNAMELEN );
763
offset += TarEntry.UNAMELEN;
764
m_groupName = TarUtils.parseName( header, offset, TarEntry.GNAMELEN );
765
offset += TarEntry.GNAMELEN;
766
m_devMajor = (int)TarUtils.parseOctal( header, offset, TarEntry.DEVLEN );
767
offset += TarEntry.DEVLEN;
768
m_devMinor = (int)TarUtils.parseOctal( header, offset, TarEntry.DEVLEN );
772
* Write an entry's header information to a header buffer.
774
* @param buffer The tar entry header buffer to fill in.
776
public void writeEntryHeader( final byte[] buffer )
780
offset = TarUtils.getNameBytes( m_name, buffer, offset, NAMELEN );
781
offset = TarUtils.getOctalBytes( m_mode, buffer, offset, TarEntry.MODELEN );
782
offset = TarUtils.getOctalBytes( m_userID, buffer, offset, TarEntry.UIDLEN );
783
offset = TarUtils.getOctalBytes( m_groupID, buffer, offset, TarEntry.GIDLEN );
784
offset = TarUtils.getLongOctalBytes( m_size, buffer, offset, TarEntry.SIZELEN );
785
offset = TarUtils.getLongOctalBytes( m_modTime, buffer, offset, TarEntry.MODTIMELEN );
787
final int checkSumOffset = offset;
788
for( int i = 0; i < TarEntry.CHKSUMLEN; ++i )
790
buffer[ offset++ ] = (byte)' ';
793
buffer[ offset++ ] = m_linkFlag;
794
offset = TarUtils.getNameBytes( m_linkName, buffer, offset, NAMELEN );
795
offset = TarUtils.getNameBytes( m_magic, buffer, offset, TarEntry.MAGICLEN );
796
offset = TarUtils.getNameBytes( m_userName, buffer, offset, TarEntry.UNAMELEN );
797
offset = TarUtils.getNameBytes( m_groupName, buffer, offset, TarEntry.GNAMELEN );
798
offset = TarUtils.getOctalBytes( m_devMajor, buffer, offset, TarEntry.DEVLEN );
799
offset = TarUtils.getOctalBytes( m_devMinor, buffer, offset, TarEntry.DEVLEN );
801
while( offset < buffer.length )
803
buffer[ offset++ ] = 0;
806
final long checkSum = TarUtils.computeCheckSum( buffer );
807
TarUtils.getCheckSumOctalBytes( checkSum, buffer, checkSumOffset, TarEntry.CHKSUMLEN );