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    }