    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one
    003     * or more contributor license agreements.  See the NOTICE file
    004     * distributed with this work for additional information
    005     * regarding copyright ownership.  The ASF licenses this file
    006     * to you under the Apache License, Version 2.0 (the
    007     * "License"); you may not use this file except in compliance
    008     * with the License.  You may obtain a copy of the License at
    009     *
    010     * http://www.apache.org/licenses/LICENSE-2.0
    011     *
    012     * Unless required by applicable law or agreed to in writing,
    013     * software distributed under the License is distributed on an
    014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015     * KIND, either express or implied.  See the License for the
    016     * specific language governing permissions and limitations
    017     * under the License.
    018     */
    019    package org.apache.commons.compress.archivers.tar;
    020
    021    /**
    022     * This class provides static utility methods to work with byte streams.
    023     *
    024     * @Immutable
    025     */
    026    // CheckStyle:HideUtilityClassConstructorCheck OFF (bc)
    027    public class TarUtils {
    028
    029        private static final int BYTE_MASK = 255;
    030
    031        /** Private constructor to prevent instantiation of this utility class. */
    032        private TarUtils(){
    033        }
    034
    035        /**
    036         * Parse an octal string from a buffer.
    037         * Leading spaces are ignored.
    038         * The buffer must contain a trailing space or NUL,
    039         * and may contain an additional trailing space or NUL.
    040         *
    041         * The input buffer is allowed to contain all NULs,
    042         * in which case the method returns 0L
    043         * (this allows for missing fields).
    044         *
    045         * @param buffer The buffer from which to parse.
    046         * @param offset The offset into the buffer from which to parse.
    047         * @param length The maximum number of bytes to parse - must be at least 2 bytes.
    048         * @return The long value of the octal string.
    049         * @throws IllegalArgumentException if the trailing space/NUL is missing or if a invalid byte is detected.
    050         */
    051        public static long parseOctal(final byte[] buffer, final int offset, final int length) {
    052            long    result = 0;
    053            int     end = offset + length;
    054            int     start = offset;
    055
    056            if (length < 2){
    057                throw new IllegalArgumentException("Length "+length+" must be at least 2");
    058            }
    059
    060            boolean allNUL = true;
    061            for (int i = start; i < end; i++){
    062                if (buffer[i] != 0){
    063                    allNUL = false;
    064                    break;
    065                }
    066            }
    067            if (allNUL) {
    068                return 0L;
    069            }
    070
    071            // Skip leading spaces
    072            while (start < end){
    073                if (buffer[start] == ' '){
    074                    start++;
    075                } else {
    076                    break;
    077                }
    078            }
    079
    080            // Must have trailing NUL or space
    081            byte trailer;
    082            trailer = buffer[end-1];
    083            if (trailer == 0 || trailer == ' '){
    084                end--;
    085            } else {
    086                throw new IllegalArgumentException(
    087                        exceptionMessage(buffer, offset, length, end-1, trailer));
    088            }
    089            // May have additional NUL or space
    090            trailer = buffer[end-1];
    091            if (trailer == 0 || trailer == ' '){
    092                end--;
    093            }
    094
    095            for ( ;start < end; start++) {
    096                final byte currentByte = buffer[start];
    097                // CheckStyle:MagicNumber OFF
    098                if (currentByte < '0' || currentByte > '7'){
    099                    throw new IllegalArgumentException(
    100                            exceptionMessage(buffer, offset, length, start, currentByte));
    101                }
    102                result = (result << 3) + (currentByte - '0'); // convert from ASCII
    103                // CheckStyle:MagicNumber ON
    104            }
    105
    106            return result;
    107        }
    108
    109        /**
    110         * Parse a boolean byte from a buffer.
    111         * Leading spaces and NUL are ignored.
    112         * The buffer may contain trailing spaces or NULs.
    113         *
    114         * @param buffer The buffer from which to parse.
    115         * @param offset The offset into the buffer from which to parse.
    116         * @param length The maximum number of bytes to parse - must be at least 1 byte.
    117         * @return The boolean value of the bytes.
    118         * @throws IllegalArgumentException if an invalid byte is detected.
    119         */
    120        public static boolean parseBoolean(final byte[] buffer, final int offset) {
    121            return (buffer[offset] == 1);
    122        }
    123
    124        // Helper method to generate the exception message
    125        private static String exceptionMessage(byte[] buffer, final int offset,
    126                final int length, int current, final byte currentByte) {
    127            String string = new String(buffer, offset, length);
    128            string=string.replaceAll("\0", "{NUL}"); // Replace NULs to allow string to be printed
    129            final String s = "Invalid byte "+currentByte+" at offset "+(current-offset)+" in '"+string+"' len="+length;
    130            return s;
    131        }
    132
    133        /**
    134         * Parse an entry name from a buffer.
    135         * Parsing stops when a NUL is found
    136         * or the buffer length is reached.
    137         *
    138         * @param buffer The buffer from which to parse.
    139         * @param offset The offset into the buffer from which to parse.
    140         * @param length The maximum number of bytes to parse.
    141         * @return The entry name.
    142         */
    143        public static String parseName(byte[] buffer, final int offset, final int length) {
    144            StringBuffer result = new StringBuffer(length);
    145            int          end = offset + length;
    146
    147            for (int i = offset; i < end; ++i) {
    148                byte b = buffer[i];
    149                if (b == 0) { // Trailing null
    150                    break;
    151                }
    152                result.append((char) (b & 0xFF)); // Allow for sign-extension
    153            }
    154
    155            return result.toString();
    156        }
    157
    158        /**
    159         * Copy a name (StringBuffer) into a buffer.
    160         * Copies characters from the name into the buffer
    161         * starting at the specified offset.
    162         * If the buffer is longer than the name, the buffer
    163         * is filled with trailing NULs.
    164         * If the name is longer than the buffer,
    165         * the output is truncated.
    166         *
    167         * @param name The header name from which to copy the characters.
    168         * @param buf The buffer where the name is to be stored.
    169         * @param offset The starting offset into the buffer
    170         * @param length The maximum number of header bytes to copy.
    171         * @return The updated offset, i.e. offset + length
    172         */
    173        public static int formatNameBytes(String name, byte[] buf, final int offset, final int length) {
    174            int i;
    175
    176            // copy until end of input or output is reached.
    177            for (i = 0; i < length && i < name.length(); ++i) {
    178                buf[offset + i] = (byte) name.charAt(i);
    179            }
    180
    181            // Pad any remaining output bytes with NUL
    182            for (; i < length; ++i) {
    183                buf[offset + i] = 0;
    184            }
    185
    186            return offset + length;
    187        }
    188
    189        /**
    190         * Fill buffer with unsigned octal number, padded with leading zeroes.
    191         *
    192         * @param value number to convert to octal - treated as unsigned
    193         * @param buffer destination buffer
    194         * @param offset starting offset in buffer
    195         * @param length length of buffer to fill
    196         * @throws IllegalArgumentException if the value will not fit in the buffer
    197         */
    198        public static void formatUnsignedOctalString(final long value, byte[] buffer,
    199                final int offset, final int length) {
    200            int remaining = length;
    201            remaining--;
    202            if (value == 0) {
    203                buffer[offset + remaining--] = (byte) '0';
    204            } else {
    205                long val = value;
    206                for (; remaining >= 0 && val != 0; --remaining) {
    207                    // CheckStyle:MagicNumber OFF
    208                    buffer[offset + remaining] = (byte) ((byte) '0' + (byte) (val & 7));
    209                    val = val >>> 3;
    210                    // CheckStyle:MagicNumber ON
    211                }
    212                if (val != 0){
    213                    throw new IllegalArgumentException
    214                    (value+"="+Long.toOctalString(value)+ " will not fit in octal number buffer of length "+length);
    215                }
    216            }
    217
    218            for (; remaining >= 0; --remaining) { // leading zeros
    219                buffer[offset + remaining] = (byte) '0';
    220            }
    221        }
    222
    223        /**
    224         * Write an octal integer into a buffer.
    225         *
    226         * Uses {@link #formatUnsignedOctalString} to format
    227         * the value as an octal string with leading zeros.
    228         * The converted number is followed by space and NUL
    229         *
    230         * @param value The value to write
    231         * @param buf The buffer to receive the output
    232         * @param offset The starting offset into the buffer
    233         * @param length The size of the output buffer
    234         * @return The updated offset, i.e offset+length
    235         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    236         */
    237        public static int formatOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    238
    239            int idx=length-2; // For space and trailing null
    240            formatUnsignedOctalString(value, buf, offset, idx);
    241
    242            buf[offset + idx++] = (byte) ' '; // Trailing space
    243            buf[offset + idx]   = 0; // Trailing null
    244
    245            return offset + length;
    246        }
    247
    248        /**
    249         * Write an octal long integer into a buffer.
    250         *
    251         * Uses {@link #formatUnsignedOctalString} to format
    252         * the value as an octal string with leading zeros.
    253         * The converted number is followed by a space.
    254         *
    255         * @param value The value to write as octal
    256         * @param buf The destinationbuffer.
    257         * @param offset The starting offset into the buffer.
    258         * @param length The length of the buffer
    259         * @return The updated offset
    260         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    261         */
    262        public static int formatLongOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    263
    264            int idx=length-1; // For space
    265
    266            formatUnsignedOctalString(value, buf, offset, idx);
    267            buf[offset + idx] = (byte) ' '; // Trailing space
    268
    269            return offset + length;
    270        }
    271
    272        /**
    273         * Writes an octal value into a buffer.
    274         *
    275         * Uses {@link #formatUnsignedOctalString} to format
    276         * the value as an octal string with leading zeros.
    277         * The converted number is followed by NUL and then space.
    278         *
    279         * @param value The value to convert
    280         * @param buf The destination buffer
    281         * @param offset The starting offset into the buffer.
    282         * @param length The size of the buffer.
    283         * @return The updated value of offset, i.e. offset+length
    284         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    285         */
    286        public static int formatCheckSumOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    287
    288            int idx=length-2; // for NUL and space
    289            formatUnsignedOctalString(value, buf, offset, idx);
    290
    291            buf[offset + idx++]   = 0; // Trailing null
    292            buf[offset + idx]     = (byte) ' '; // Trailing space
    293
    294            return offset + length;
    295        }
    296
    297        /**
    298         * Compute the checksum of a tar entry header.
    299         *
    300         * @param buf The tar entry's header buffer.
    301         * @return The computed checksum.
    302         */
    303        public static long computeCheckSum(final byte[] buf) {
    304            long sum = 0;
    305
    306            for (int i = 0; i < buf.length; ++i) {
    307                sum += BYTE_MASK & buf[i];
    308            }
    309
    310            return sum;
    311        }
    312    }