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 }