View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    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         // Strip off drive letters!
    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         // No absolute pathnames
    227         // Windows (and Posix?) paths can start with "//NetworkDrive\",
    228         // so we loop on starting /'s.
    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 }