    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         * Parsing stops when a NUL is found, or a trailing space,
    039         * or the buffer length is reached.
    040         *
    041         * Behaviour with non-octal input is currently undefined.
    042         *
    043         * @param buffer The buffer from which to parse.
    044         * @param offset The offset into the buffer from which to parse.
    045         * @param length The maximum number of bytes to parse.
    046         * @return The long value of the octal string.
    047         */
    048        public static long parseOctal(byte[] buffer, final int offset, final int length) {
    049            long    result = 0;
    050            boolean stillPadding = true;
    051            int     end = offset + length;
    052
    053            for (int i = offset; i < end; ++i) {
    054                final byte currentByte = buffer[i];
    055                if (currentByte == 0) { // Found trailing null
    056                    break;
    057                }
    058
    059                // Ignore leading spaces ('0' can be ignored anyway)
    060                if (currentByte == (byte) ' ' || currentByte == '0') {
    061                    if (stillPadding) {
    062                        continue;
    063                    }
    064
    065                    if (currentByte == (byte) ' ') { // Found trailing space
    066                        break;
    067                    }
    068                }
    069
    070                stillPadding = false;
    071                // CheckStyle:MagicNumber OFF
    072                if (currentByte < '0' || currentByte > '7'){
    073                    throw new IllegalArgumentException(
    074                            "Invalid octal digit at position "+i+" in '"+new String(buffer, offset, length)+"'");
    075                }
    076                result = (result << 3) + (currentByte - '0');// TODO needs to reject invalid bytes
    077                // CheckStyle:MagicNumber ON
    078            }
    079
    080            return result;
    081        }
    082
    083        /**
    084         * Parse an entry name from a buffer.
    085         * Parsing stops when a NUL is found
    086         * or the buffer length is reached.
    087         *
    088         * @param buffer The buffer from which to parse.
    089         * @param offset The offset into the buffer from which to parse.
    090         * @param length The maximum number of bytes to parse.
    091         * @return The entry name.
    092         */
    093        public static String parseName(byte[] buffer, final int offset, final int length) {
    094            StringBuffer result = new StringBuffer(length);
    095            int          end = offset + length;
    096
    097            for (int i = offset; i < end; ++i) {
    098                if (buffer[i] == 0) { // Trailing null
    099                    break;
    100                }
    101
    102                result.append((char) buffer[i]);
    103            }
    104
    105            return result.toString();
    106        }
    107
    108        /**
    109         * Copy a name (StringBuffer) into a buffer.
    110         * Copies characters from the name into the buffer
    111         * starting at the specified offset.
    112         * If the buffer is longer than the name, the buffer
    113         * is filled with trailing NULs.
    114         * If the name is longer than the buffer,
    115         * the output is truncated.
    116         *
    117         * @param name The header name from which to copy the characters.
    118         * @param buf The buffer where the name is to be stored.
    119         * @param offset The starting offset into the buffer
    120         * @param length The maximum number of header bytes to copy.
    121         * @return The updated offset, i.e. offset + length
    122         */
    123        public static int formatNameBytes(String name, byte[] buf, final int offset, final int length) {
    124            int i;
    125
    126            // copy until end of input or output is reached.
    127            for (i = 0; i < length && i < name.length(); ++i) {
    128                buf[offset + i] = (byte) name.charAt(i);
    129            }
    130
    131            // Pad any remaining output bytes with NUL
    132            for (; i < length; ++i) {
    133                buf[offset + i] = 0;
    134            }
    135
    136            return offset + length;
    137        }
    138
    139        /**
    140         * Fill buffer with unsigned octal number, padded with leading zeroes.
    141         *
    142         * @param value number to convert to octal - treated as unsigned
    143         * @param buffer destination buffer
    144         * @param offset starting offset in buffer
    145         * @param length length of buffer to fill
    146         * @throws IllegalArgumentException if the value will not fit in the buffer
    147         */
    148        public static void formatUnsignedOctalString(final long value, byte[] buffer,
    149                final int offset, final int length) {
    150            int remaining = length;
    151            remaining--;
    152            if (value == 0) {
    153                buffer[offset + remaining--] = (byte) '0';
    154            } else {
    155                long val = value;
    156                for (; remaining >= 0 && val != 0; --remaining) {
    157                    // CheckStyle:MagicNumber OFF
    158                    buffer[offset + remaining] = (byte) ((byte) '0' + (byte) (val & 7));
    159                    val = val >>> 3;
    160                    // CheckStyle:MagicNumber ON
    161                }
    162                if (val != 0){
    163                    throw new IllegalArgumentException
    164                    (value+"="+Long.toOctalString(value)+ " will not fit in octal number buffer of length "+length);
    165                }
    166            }
    167
    168            for (; remaining >= 0; --remaining) { // leading zeros
    169                buffer[offset + remaining] = (byte) '0';
    170            }
    171        }
    172
    173        /**
    174         * Write an octal integer into a buffer.
    175         *
    176         * Uses {@link #formatUnsignedOctalString} to format
    177         * the value as an octal string with leading zeros.
    178         * The converted number is followed by space and NUL
    179         *
    180         * @param value The value to write
    181         * @param buf The buffer to receive the output
    182         * @param offset The starting offset into the buffer
    183         * @param length The size of the output buffer
    184         * @return The updated offset, i.e offset+length
    185         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    186         */
    187        public static int formatOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    188
    189            int idx=length-2; // For space and trailing null
    190            formatUnsignedOctalString(value, buf, offset, idx);
    191
    192            buf[offset + idx++] = (byte) ' '; // Trailing space
    193            buf[offset + idx]   = 0; // Trailing null
    194
    195            return offset + length;
    196        }
    197
    198        /**
    199         * Write an octal long integer into a buffer.
    200         *
    201         * Uses {@link #formatUnsignedOctalString} to format
    202         * the value as an octal string with leading zeros.
    203         * The converted number is followed by a space.
    204         *
    205         * @param value The value to write as octal
    206         * @param buf The destinationbuffer.
    207         * @param offset The starting offset into the buffer.
    208         * @param length The length of the buffer
    209         * @return The updated offset
    210         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    211         */
    212        public static int formatLongOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    213
    214            int idx=length-1; // For space
    215
    216            formatUnsignedOctalString(value, buf, offset, idx);
    217            buf[offset + idx] = (byte) ' '; // Trailing space
    218
    219            return offset + length;
    220        }
    221
    222        /**
    223         * Writes an octal value 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 NUL and then space.
    228         *
    229         * @param value The value to convert
    230         * @param buf The destination buffer
    231         * @param offset The starting offset into the buffer.
    232         * @param length The size of the buffer.
    233         * @return The updated value of offset, i.e. offset+length
    234         * @throws IllegalArgumentException if the value (and trailer) will not fit in the buffer
    235         */
    236        public static int formatCheckSumOctalBytes(final long value, byte[] buf, final int offset, final int length) {
    237
    238            int idx=length-2; // for NUL and space
    239            formatUnsignedOctalString(value, buf, offset, idx);
    240
    241            buf[offset + idx++]   = 0; // Trailing null
    242            buf[offset + idx]     = (byte) ' '; // Trailing space
    243
    244            return offset + length;
    245        }
    246
    247        /**
    248         * Compute the checksum of a tar entry header.
    249         *
    250         * @param buf The tar entry's header buffer.
    251         * @return The computed checksum.
    252         */
    253        public static long computeCheckSum(final byte[] buf) {
    254            long sum = 0;
    255
    256            for (int i = 0; i < buf.length; ++i) {
    257                sum += BYTE_MASK & buf[i];
    258            }
    259
    260            return sum;
    261        }
    262    }