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 }