    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         * @return The boolean value of the bytes.
    117         * @throws IllegalArgumentException if an invalid byte is detected.
    118         */
    119        public static boolean parseBoolean(final byte[] buffer, final int offset) {
    120            return buffer[offset] == 1;
    121        }
    122
    123        // Helper method to generate the exception message
    124        private static String exceptionMessage(byte[] buffer, final int offset,
    125                final int length, int current, final byte currentByte) {
    126            String string = new String(buffer, offset, length);
    127            string=string.replaceAll("\0", "{NUL}"); // Replace NULs to allow string to be printed
    128            final String s = "Invalid byte "+currentByte+" at offset "+(current-offset)+" in '"+string+"' len="+length;
    129            return s;
    130        }
    131
    132        /**
    133         * Parse an entry name from a buffer.
    134         * Parsing stops when a NUL is found
    135         * or the buffer length is reached.
    136         *
    137         * @param buffer The buffer from which to parse.
    138         * @param offset The offset into the buffer from which to parse.
    139         * @param length The maximum number of bytes to parse.
    140         * @return The entry name.
    141         */
    142        public static String parseName(byte[] buffer, final int offset, final int length) {
    143            StringBuffer result = new StringBuffer(length);
    144            int          end = offset + length;
    145
    146            for (int i = offset; i < end; ++i) {
    147                byte b = buffer[i];
    148                if (b == 0) { // Trailing null
    149                    break;
    150                }
    151                result.append((char) (b & 0xFF)); // Allow for sign-extension
    152            }
    153
    154            return result.toString();
    155        }
    156
    157        /**
    158         * Copy a name (StringBuffer) into a buffer.
    159         * Copies characters from the name into the buffer
    160         * starting at the specified offset.
    161         * If the buffer is longer than the name, the buffer
    162         * is filled with trailing NULs.
    163         * If the name is longer than the buffer,
    164         * the output is truncated.
    165         *
    166         * @param name The header name from which to copy the characters.
    167         * @param buf The buffer where the name is to be stored.
    168         * @param offset The starting offset into the buffer
    169         * @param length The maximum number of header bytes to copy.
    170         * @return The updated offset, i.e. offset + length
    171         */
    172        public static int formatNameBytes(String name, byte[] buf, final int offset, final int length) {
    173            int i;
    174
    175            // copy until end of input or output is reached.
    176            for (i = 0; i < length && i < name.length(); ++i) {
    177                buf[offset + i] = (byte) name.charAt(i);
    178            }
    179
    180            // Pad any remaining output bytes with NUL
    181            for (; i < length; ++i) {
    182                buf[offset + i] = 0;
    183            }
    184
    185            return offset + length;
    186        }
    187
    188        /**
    189         * Fill buffer with unsigned octal number, padded with leading zeroes.
    190         *
    191         * @param value number to convert to octal - treated as unsigned
    192         * @param buffer destination buffer
    193         * @param offset starting offset in buffer
    194         * @param length length of buffer to fill
    195         * @throws IllegalArgumentException if the value will not fit in the buffer
    196         */
    197        public static void formatUnsignedOctalString(final long value, byte[] buffer,
    198                final int offset, final int length) {
    199            int remaining = length;
    200            remaining--;
    201            if (value == 0) {
    202                buffer[offset + remaining--] = (byte) '0';
    203            } else {
    204                long val = value;
    205                for (; remaining >= 0 && val != 0; --remaining) {
    206                    // CheckStyle:MagicNumber OFF
    207                    buffer[offset + remaining] = (byte) ((byte) '0' + (byte) (val & 7));
    208                    val = val >>> 3;
    209                    // CheckStyle:MagicNumber ON
    210                }
    211                if (val != 0){
    212                    throw new IllegalArgumentException
    213                    (value+"="+Long.toOctalString(value)+ " will not fit in octal number buffer of length "+length);
    214                }
    215            }
    216
    217            for (; remaining >= 0; --remaining) { // leading zeros
    218                buffer[offset + remaining] = (byte) '0';
    219            }
    220        }
    221
    222        /**
    223         * Write an octal integer into a buffer.
    224         *
    225         * Uses {@link #formatUnsignedOctalString} to format
    226         * the value as an octal string with leading zeros.
    227         * The converted number is followed by space and NUL
    228         *
    229         * @param value The value to write
    230         * @param buf The buffer to receive the output
    231         * @param offset The starting offset into the buffer
    232         * @param length The size of the output buffer
    233         * @return The updated offset, i.e offset+length
    234         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    235         */
    236        public static int formatOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    237
    238            int idx=length-2; // For space and trailing null
    239            formatUnsignedOctalString(value, buf, offset, idx);
    240
    241            buf[offset + idx++] = (byte) ' '; // Trailing space
    242            buf[offset + idx]   = 0; // Trailing null
    243
    244            return offset + length;
    245        }
    246
    247        /**
    248         * Write an octal long integer into a buffer.
    249         *
    250         * Uses {@link #formatUnsignedOctalString} to format
    251         * the value as an octal string with leading zeros.
    252         * The converted number is followed by a space.
    253         *
    254         * @param value The value to write as octal
    255         * @param buf The destinationbuffer.
    256         * @param offset The starting offset into the buffer.
    257         * @param length The length of the buffer
    258         * @return The updated offset
    259         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    260         */
    261        public static int formatLongOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    262
    263            int idx=length-1; // For space
    264
    265            formatUnsignedOctalString(value, buf, offset, idx);
    266            buf[offset + idx] = (byte) ' '; // Trailing space
    267
    268            return offset + length;
    269        }
    270
    271        /**
    272         * Writes an octal value into a buffer.
    273         *
    274         * Uses {@link #formatUnsignedOctalString} to format
    275         * the value as an octal string with leading zeros.
    276         * The converted number is followed by NUL and then space.
    277         *
    278         * @param value The value to convert
    279         * @param buf The destination buffer
    280         * @param offset The starting offset into the buffer.
    281         * @param length The size of the buffer.
    282         * @return The updated value of offset, i.e. offset+length
    283         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    284         */
    285        public static int formatCheckSumOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    286
    287            int idx=length-2; // for NUL and space
    288            formatUnsignedOctalString(value, buf, offset, idx);
    289
    290            buf[offset + idx++]   = 0; // Trailing null
    291            buf[offset + idx]     = (byte) ' '; // Trailing space
    292
    293            return offset + length;
    294        }
    295
    296        /**
    297         * Compute the checksum of a tar entry header.
    298         *
    299         * @param buf The tar entry's header buffer.
    300         * @return The computed checksum.
    301         */
    302        public static long computeCheckSum(final byte[] buf) {
    303            long sum = 0;
    304
    305            for (int i = 0; i < buf.length; ++i) {
    306                sum += BYTE_MASK & buf[i];
    307            }
    308
    309            return sum;
    310        }
    311    }