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