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 }