001/*
002** Authored by Timothy Gerard Endres
003** <mailto:time@gjt.org>  <http://www.trustice.com>
004** 
005** This work has been placed into the public domain.
006** You may use this work in any way and for any purpose you wish.
007**
008** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
009** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
010** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
011** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
012** REDISTRIBUTION OF THIS SOFTWARE. 
013** 
014*/
015
016package com.ice.tar;
017
018/**
019 * This class encapsulates the Tar Entry Header used in Tar Archives.
020 * The class also holds a number of tar constants, used mostly in headers.
021 *
022 * @author Timothy Gerard Endres, <time@gjt.org>
023 */
024
025public
026class           TarHeader
027extends         Object
028implements      Cloneable
029        {
030        /**
031         * The length of the name field in a header buffer.
032         */
033        public static final int         NAMELEN = 100;
034        /**
035         * The offset of the name field in a header buffer.
036         */
037        public static final int         NAMEOFFSET = 0;
038        /**
039         * The length of the name prefix field in a header buffer.
040         */
041        public static final int         PREFIXLEN = 155;
042        /**
043         * The offset of the name prefix field in a header buffer.
044         */
045        public static final int         PREFIXOFFSET = 345;
046        /**
047         * The length of the mode field in a header buffer.
048         */
049        public static final int         MODELEN = 8;
050        /**
051         * The length of the user id field in a header buffer.
052         */
053        public static final int         UIDLEN = 8;
054        /**
055         * The length of the group id field in a header buffer.
056         */
057        public static final int         GIDLEN = 8;
058        /**
059         * The length of the checksum field in a header buffer.
060         */
061        public static final int         CHKSUMLEN = 8;
062        /**
063         * The length of the size field in a header buffer.
064         */
065        public static final int         SIZELEN = 12;
066        /**
067         * The length of the magic field in a header buffer.
068         */
069        public static final int         MAGICLEN = 8;
070        /**
071         * The length of the modification time field in a header buffer.
072         */
073        public static final int         MODTIMELEN = 12;
074        /**
075         * The length of the user name field in a header buffer.
076         */
077        public static final int         UNAMELEN = 32;
078        /**
079         * The length of the group name field in a header buffer.
080         */
081        public static final int         GNAMELEN = 32;
082        /**
083         * The length of the devices field in a header buffer.
084         */
085        public static final int         DEVLEN = 8;
086
087        /**
088         * LF_ constants represent the "link flag" of an entry, or more commonly,
089         * the "entry type". This is the "old way" of indicating a normal file.
090         */
091        public static final byte        LF_OLDNORM      = 0;
092        /**
093         * Normal file type.
094         */
095        public static final byte        LF_NORMAL       = (byte) '0';
096        /**
097         * Link file type.
098         */
099        public static final byte        LF_LINK         = (byte) '1';
100        /**
101         * Symbolic link file type.
102         */
103        public static final byte        LF_SYMLINK      = (byte) '2';
104        /**
105         * Character device file type.
106         */
107        public static final byte        LF_CHR          = (byte) '3';
108        /**
109         * Block device file type.
110         */
111        public static final byte        LF_BLK          = (byte) '4';
112        /**
113         * Directory file type.
114         */
115        public static final byte        LF_DIR          = (byte) '5';
116        /**
117         * FIFO (pipe) file type.
118         */
119        public static final byte        LF_FIFO         = (byte) '6';
120        /**
121         * Contiguous file type.
122         */
123        public static final byte        LF_CONTIG       = (byte) '7';
124
125        /**
126         * The magic tag representing a POSIX tar archive.
127         */
128        public static final String      TMAGIC          = "ustar";
129
130        /**
131         * The magic tag representing a GNU tar archive.
132         */
133        public static final String      GNU_TMAGIC      = "ustar  ";
134
135        /**
136         * The entry's name.
137         */
138        public StringBuffer             name;
139        /**
140         * The entry's permission mode.
141         */
142        public int                              mode;
143        /**
144         * The entry's user id.
145         */
146        public int                              userId;
147        /**
148         * The entry's group id.
149         */
150        public int                              groupId;
151        /**
152         * The entry's size.
153         */
154        public long                             size;
155        /**
156         * The entry's modification time.
157         */
158        public long                             modTime;
159        /**
160         * The entry's checksum.
161         */
162        public int                              checkSum;
163        /**
164         * The entry's link flag.
165         */
166        public byte                             linkFlag;
167        /**
168         * The entry's link name.
169         */
170        public StringBuffer             linkName;
171        /**
172         * The entry's magic tag.
173         */
174        public StringBuffer             magic;
175        /**
176         * The entry's user name.
177         */
178        public StringBuffer             userName;
179        /**
180         * The entry's group name.
181         */
182        public StringBuffer             groupName;
183        /**
184         * The entry's major device number.
185         */
186        public int                              devMajor;
187        /**
188         * The entry's minor device number.
189         */
190        public int                              devMinor;
191
192
193        public
194        TarHeader()
195                {
196                this.magic = new StringBuffer( TarHeader.TMAGIC );
197
198                this.name = new StringBuffer();
199                this.linkName = new StringBuffer();
200
201                String user =
202                        System.getProperty( "user.name", "" );
203
204                if ( user.length() > 31 )
205                        user = user.substring( 0, 31 );
206
207                this.userId = 0;
208                this.groupId = 0;
209                this.userName = new StringBuffer( user );
210                this.groupName = new StringBuffer( "" );
211                }
212
213        /**
214         * TarHeaders can be cloned.
215         */
216        public Object
217        clone()
218                {
219                TarHeader hdr = null;
220
221                try {
222                        hdr = (TarHeader) super.clone();
223
224                        hdr.name =
225                                (this.name == null ) ? null
226                                        : new StringBuffer( this.name.toString() );
227                        hdr.mode = this.mode;
228                        hdr.userId = this.userId;
229                        hdr.groupId = this.groupId;
230                        hdr.size = this.size;
231                        hdr.modTime = this.modTime;
232                        hdr.checkSum = this.checkSum;
233                        hdr.linkFlag = this.linkFlag;
234                        hdr.linkName =
235                                (this.linkName == null ) ? null
236                                        : new StringBuffer( this.linkName.toString() );
237                        hdr.magic =
238                                (this.magic == null ) ? null
239                                        : new StringBuffer( this.magic.toString() );
240                        hdr.userName =
241                                (this.userName == null ) ? null
242                                        : new StringBuffer( this.userName.toString() );
243                        hdr.groupName =
244                                (this.groupName == null ) ? null
245                                        : new StringBuffer( this.groupName.toString() );
246                        hdr.devMajor = this.devMajor;
247                        hdr.devMinor = this.devMinor;
248                        }
249                catch ( CloneNotSupportedException ex )
250                        {
251                        ex.printStackTrace( System.err );
252                        }
253
254                return hdr;
255                }
256
257        /**
258         * Get the name of this entry.
259         *
260         * @return Teh entry's name.
261         */
262        public String
263        getName()
264                {
265                return this.name.toString();
266                }
267
268        /**
269         * Parse an octal string from a header buffer. This is used for the
270         * file permission mode value.
271         *
272         * @param header The header buffer from which to parse.
273         * @param offset The offset into the buffer from which to parse.
274         * @param length The number of header bytes to parse.
275         * @return The long value of the octal string.
276         */
277        public static long
278        parseOctal( byte[] header, int offset, int length )
279                throws InvalidHeaderException
280                {
281                long result = 0;
282                boolean stillPadding = true;
283
284                int end = offset + length;
285                for ( int i = offset ; i < end ; ++i )
286                        {
287                        if ( header[i] == 0 )
288                                break;
289
290                        if ( header[i] == (byte) ' ' || header[i] == '0' )
291                                {
292                                if ( stillPadding )
293                                        continue;
294
295                                if ( header[i] == (byte) ' ' )
296                                        break;
297                                }
298                        
299                        stillPadding = false;
300
301                        result =
302                                (result << 3)
303                                        + (header[i] - '0');
304                        }
305
306                return result;
307                }
308
309        /**
310         * Parse a file name from a header buffer. This is different from
311         * parseName() in that is recognizes 'ustar' names and will handle
312         * adding on the "prefix" field to the name.
313         *
314         * Contributed by Dmitri Tikhonov <dxt2431@yahoo.com>
315         *
316         * @param header The header buffer from which to parse.
317         * @param offset The offset into the buffer from which to parse.
318         * @param length The number of header bytes to parse.
319         * @return The header's entry name.
320         */
321        public static StringBuffer
322        parseFileName( byte[] header )
323                {
324                StringBuffer result = new StringBuffer( 256 );
325
326                // If header[345] is not equal to zero, then it is the "prefix"
327                // that 'ustar' defines. It must be prepended to the "normal"
328                // name field. We are responsible for the separating '/'.
329                //
330                if ( header[345] != 0 )
331                        {
332                        for ( int i = 345 ; i < 500 && header[i] != 0 ; ++i )
333                                {
334                                result.append( (char)header[i] );
335                                }
336
337                        result.append( "/" );
338                        }
339
340                for ( int i = 0 ; i < 100 && header[i] != 0 ; ++i )
341                        {
342                        result.append( (char)header[i] );
343                        }
344
345                return result;
346                }
347
348        /**
349         * Parse an entry name from a header buffer.
350         *
351         * @param header The header buffer from which to parse.
352         * @param offset The offset into the buffer from which to parse.
353         * @param length The number of header bytes to parse.
354         * @return The header's entry name.
355         */
356        public static StringBuffer
357        parseName( byte[] header, int offset, int length )
358                throws InvalidHeaderException
359                {
360                StringBuffer result = new StringBuffer( length );
361
362                int end = offset + length;
363                for ( int i = offset ; i < end ; ++i )
364                        {
365                        if ( header[i] == 0 )
366                                break;
367                        result.append( (char)header[i] );
368                        }
369
370                return result;
371                }
372
373        /**
374         * This method, like getNameBytes(), is intended to place a name
375         * into a TarHeader's buffer. However, this method is sophisticated
376         * enough to recognize long names (name.length() > NAMELEN). In these
377         * cases, the method will break the name into a prefix and suffix and
378         * place the name in the header in 'ustar' format. It is up to the
379         * TarEntry to manage the "entry header format". This method assumes
380         * the name is valid for the type of archive being generated.
381         *
382         * @param outbuf The buffer containing the entry header to modify.
383         * @param newName The new name to place into the header buffer.
384         * @return The current offset in the tar header (always TarHeader.NAMELEN).
385         * @throws InvalidHeaderException If the name will not fit in the header.
386         */
387        public static int
388        getFileNameBytes( String newName, byte[] outbuf )
389                throws InvalidHeaderException
390                {
391                if ( newName.length() > 100 )
392                        {
393                        // Locate a pathname "break" prior to the maximum name length...
394                        int index = newName.indexOf( "/", newName.length() - 100 );
395                        if ( index == -1 )
396                                throw new InvalidHeaderException
397                                        ( "file name is greater than 100 characters, " + newName );
398
399                        // Get the "suffix subpath" of the name.
400                        String name = newName.substring( index + 1 );
401
402                        // Get the "prefix subpath", or "prefix", of the name.
403                        String prefix = newName.substring( 0, index );
404                        if ( prefix.length() > TarHeader.PREFIXLEN )
405                                throw new InvalidHeaderException
406                                        ( "file prefix is greater than 155 characters" );
407
408                        TarHeader.getNameBytes
409                                ( new StringBuffer( name ), outbuf,
410                                        TarHeader.NAMEOFFSET, TarHeader.NAMELEN );
411
412                        TarHeader.getNameBytes
413                                ( new StringBuffer( prefix ), outbuf,
414                                        TarHeader.PREFIXOFFSET, TarHeader.PREFIXLEN );
415                        }
416                else
417                        {
418                        TarHeader.getNameBytes
419                                ( new StringBuffer( newName ), outbuf,
420                                        TarHeader.NAMEOFFSET, TarHeader.NAMELEN );
421                        }
422
423                // The offset, regardless of the format, is now the end of the
424                // original name field.
425                //
426                return TarHeader.NAMELEN;
427                }
428
429        /**
430         * Move the bytes from the name StringBuffer into the header's buffer.
431         *
432         * @param header The header buffer into which to copy the name.
433         * @param offset The offset into the buffer at which to store.
434         * @param length The number of header bytes to store.
435         * @return The new offset (offset + length).
436         */
437        public static int
438        getNameBytes( StringBuffer name, byte[] buf, int offset, int length )
439                {
440                int i;
441
442                for ( i = 0 ; i < length && i < name.length() ; ++i )
443                        {
444                        buf[ offset + i ] = (byte) name.charAt( i );
445                        }
446
447                for ( ; i < length ; ++i )
448                        {
449                        buf[ offset + i ] = 0;
450                        }
451
452                return offset + length;
453                }
454
455        /**
456         * Parse an octal integer from a header buffer.
457         *
458         * @param header The header buffer from which to parse.
459         * @param offset The offset into the buffer from which to parse.
460         * @param length The number of header bytes to parse.
461         * @return The integer value of the octal bytes.
462         */
463        public static int
464        getOctalBytes( long value, byte[] buf, int offset, int length )
465                {
466                byte[] result = new byte[ length ];
467
468                int idx = length - 1;
469
470                buf[ offset + idx ] = 0;
471                --idx;
472                buf[ offset + idx ] = (byte) ' ';
473                --idx;
474
475                if ( value == 0 )
476                        {
477                        buf[ offset + idx ] = (byte) '0';
478                        --idx;
479                        }
480                else
481                        {
482                        for ( long val = value ; idx >= 0 && val > 0 ; --idx )
483                                {
484                                buf[ offset + idx ] = (byte)
485                                        ( (byte) '0' + (byte) (val & 7) );
486                                val = val >> 3;
487                                }
488                        }
489
490                for ( ; idx >= 0 ; --idx )
491                        {
492                        buf[ offset + idx ] = (byte) ' ';
493                        }
494
495                return offset + length;
496                }
497
498        /**
499         * Parse an octal long integer from a header buffer.
500         *
501         * @param header The header buffer from which to parse.
502         * @param offset The offset into the buffer from which to parse.
503         * @param length The number of header bytes to parse.
504         * @return The long value of the octal bytes.
505         */
506        public static int
507        getLongOctalBytes( long value, byte[] buf, int offset, int length )
508                {
509                byte[] temp = new byte[ length + 1 ];
510                TarHeader.getOctalBytes( value, temp, 0, length + 1 );
511                System.arraycopy( temp, 0, buf, offset, length );
512                return offset + length;
513                }
514
515        /**
516         * Parse the checksum octal integer from a header buffer.
517         *
518         * @param header The header buffer from which to parse.
519         * @param offset The offset into the buffer from which to parse.
520         * @param length The number of header bytes to parse.
521         * @return The integer value of the entry's checksum.
522         */
523        public static int
524        getCheckSumOctalBytes( long value, byte[] buf, int offset, int length )
525                {
526                TarHeader.getOctalBytes( value, buf, offset, length );
527                buf[ offset + length - 1 ] = (byte) ' ';
528                buf[ offset + length - 2 ] = 0;
529                return offset + length;
530                }
531
532        }
533