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 }