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 }