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 }