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 }