IoUtils.java revision 2bad9bff258de6275bd3847e5e9f3169b0a93c61
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package libcore.io; 18 19import java.io.Closeable; 20import java.io.File; 21import java.io.FileDescriptor; 22import java.io.FileNotFoundException; 23import java.io.IOException; 24import java.io.RandomAccessFile; 25import java.net.BindException; 26import java.net.ConnectException; 27import java.net.Inet4Address; 28import java.net.Inet6Address; 29import java.net.InetAddress; 30import java.net.InetSocketAddress; 31import java.net.NetworkInterface; 32import java.net.Socket; 33import java.net.SocketAddress; 34import java.net.SocketException; 35import java.net.SocketOptions; 36import java.net.SocketTimeoutException; 37import java.net.UnknownHostException; 38import java.nio.charset.Charsets; 39import java.util.Arrays; 40import libcore.util.MutableInt; 41import static libcore.io.OsConstants.*; 42 43// TODO: kill this! 44import org.apache.harmony.luni.platform.Platform; 45 46public final class IoUtils { 47 private IoUtils() { 48 } 49 50 /** 51 * Implements java.io/java.net "available" semantics. 52 */ 53 public static int available(FileDescriptor fd) throws IOException { 54 try { 55 MutableInt available = new MutableInt(0); 56 int rc = Libcore.os.ioctlInt(fd, FIONREAD, available); 57 if (available.value < 0) { 58 // If the fd refers to a regular file, the result is the difference between 59 // the file size and the file position. This may be negative if the position 60 // is past the end of the file. If the fd refers to a special file masquerading 61 // as a regular file, the result may be negative because the special file 62 // may appear to have zero size and yet a previous read call may have 63 // read some amount of data and caused the file position to be advanced. 64 available.value = 0; 65 } 66 return available.value; 67 } catch (ErrnoException errnoException) { 68 if (errnoException.errno == ENOTTY) { 69 // The fd is unwilling to opine about its read buffer. 70 return 0; 71 } 72 throw errnoException.rethrowAsIOException(); 73 } 74 } 75 76 /** 77 * java.io only throws FileNotFoundException when opening files, regardless of what actually 78 * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening 79 * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also 80 * have an Android-specific hack to alter the default permissions. 81 */ 82 public static FileDescriptor open(String path, int flags) throws FileNotFoundException { 83 FileDescriptor fd = null; 84 try { 85 // On Android, we don't want default permissions to allow global access. 86 int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600; 87 fd = Libcore.os.open(path, flags, mode); 88 if (fd.valid()) { 89 // Posix open(2) fails with EISDIR only if you ask for write permission. 90 // Java disallows reading directories too. 91 boolean isDirectory = false; 92 if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) { 93 throw new ErrnoException("open", EISDIR); 94 } 95 } 96 return fd; 97 } catch (ErrnoException errnoException) { 98 try { 99 if (fd != null) { 100 close(fd); 101 } 102 } catch (IOException ignored) { 103 } 104 FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage()); 105 ex.initCause(errnoException); 106 throw ex; 107 } 108 } 109 110 /** 111 * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional 112 * Unix practice where you'd read until you got 0 bytes (and any future read would return -1). 113 */ 114 public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException { 115 Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount); 116 if (byteCount == 0) { 117 return 0; 118 } 119 try { 120 int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount); 121 if (readCount == 0) { 122 return -1; 123 } 124 return readCount; 125 } catch (ErrnoException errnoException) { 126 if (errnoException.errno == EAGAIN) { 127 // We return 0 rather than throw if we try to read from an empty non-blocking pipe. 128 return 0; 129 } 130 throw errnoException.rethrowAsIOException(); 131 } 132 } 133 134 /** 135 * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike 136 * Unix it never just writes as many bytes as happens to be convenient.) 137 */ 138 public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException { 139 Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount); 140 if (byteCount == 0) { 141 return; 142 } 143 try { 144 while (byteCount > 0) { 145 int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount); 146 byteCount -= bytesWritten; 147 byteOffset += bytesWritten; 148 } 149 } catch (ErrnoException errnoException) { 150 throw errnoException.rethrowAsIOException(); 151 } 152 } 153 154 public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException { 155 if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) { 156 // Linux won't let you bind a link-local address without a scope id. Find one. 157 NetworkInterface nif = NetworkInterface.getByInetAddress(address); 158 if (nif == null) { 159 throw new SocketException("Can't bind to a link-local address without a scope id: " + address); 160 } 161 try { 162 address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex()); 163 } catch (UnknownHostException ex) { 164 throw new AssertionError(ex); // Can't happen. 165 } 166 } 167 try { 168 Libcore.os.bind(fd, address, port); 169 } catch (ErrnoException errnoException) { 170 throw new BindException(errnoException.getMessage(), errnoException); 171 } 172 } 173 174 /** 175 * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null 176 * or invalid. 177 */ 178 public static void close(FileDescriptor fd) throws IOException { 179 try { 180 if (fd != null && fd.valid()) { 181 Libcore.os.close(fd); 182 } 183 } catch (ErrnoException errnoException) { 184 throw errnoException.rethrowAsIOException(); 185 } 186 } 187 188 /** 189 * Closes 'closeable', ignoring any exceptions. Does nothing if 'closeable' is null. 190 */ 191 public static void closeQuietly(Closeable closeable) { 192 if (closeable != null) { 193 try { 194 closeable.close(); 195 } catch (IOException ignored) { 196 } 197 } 198 } 199 200 /** 201 * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid. 202 */ 203 public static void closeQuietly(FileDescriptor fd) { 204 try { 205 IoUtils.close(fd); 206 } catch (IOException ignored) { 207 } 208 } 209 210 /** 211 * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null. 212 */ 213 public static void closeQuietly(Socket socket) { 214 if (socket != null) { 215 try { 216 socket.close(); 217 } catch (Exception ignored) { 218 } 219 } 220 } 221 222 /** 223 * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout 224 * means this method won't throw SocketTimeoutException. 225 */ 226 public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException { 227 try { 228 return IoUtils.connect(fd, inetAddress, port, 0); 229 } catch (SocketTimeoutException ex) { 230 throw new AssertionError(ex); // Can't happen for a connect without a timeout. 231 } 232 } 233 234 /** 235 * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'. 236 * Use timeoutMs == 0 for a blocking connect with no timeout. 237 */ 238 public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException { 239 try { 240 return connectErrno(fd, inetAddress, port, timeoutMs); 241 } catch (ErrnoException errnoException) { 242 throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException); 243 } catch (SocketException ex) { 244 throw ex; // We don't want to doubly wrap these. 245 } catch (SocketTimeoutException ex) { 246 throw ex; // We don't want to doubly wrap these. 247 } catch (IOException ex) { 248 throw new SocketException(ex); 249 } 250 } 251 252 private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException { 253 // With no timeout, just call connect(2) directly. 254 if (timeoutMs == 0) { 255 Libcore.os.connect(fd, inetAddress, port); 256 return true; 257 } 258 259 // With a timeout, we set the socket to non-blocking, connect(2), and then loop 260 // using poll(2) to decide whether we're connected, whether we should keep waiting, 261 // or whether we've seen a permanent failure and should give up. 262 long finishTimeMs = System.currentTimeMillis() + timeoutMs; 263 IoUtils.setBlocking(fd, false); 264 try { 265 try { 266 Libcore.os.connect(fd, inetAddress, port); 267 return true; // We connected immediately. 268 } catch (ErrnoException errnoException) { 269 if (errnoException.errno != EINPROGRESS) { 270 throw errnoException; 271 } 272 // EINPROGRESS means we should keep trying... 273 } 274 int remainingTimeoutMs; 275 do { 276 remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis()); 277 if (remainingTimeoutMs <= 0) { 278 throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null)); 279 } 280 } while (!IoUtils.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs)); 281 return true; // Or we'd have thrown. 282 } finally { 283 IoUtils.setBlocking(fd, true); 284 } 285 } 286 287 private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) { 288 String detail = "failed to connect to " + inetAddress + " (port " + port + ")"; 289 if (timeoutMs > 0) { 290 detail += " after " + timeoutMs + "ms"; 291 } 292 if (cause != null) { 293 detail += ": " + cause.getMessage(); 294 } 295 return detail; 296 } 297 298 public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException { 299 ErrnoException cause = null; 300 try { 301 StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() }; 302 pollFds[0].fd = fd; 303 pollFds[0].events = (short) POLLOUT; 304 int rc = Libcore.os.poll(pollFds, remainingTimeoutMs); 305 if (rc == 0) { 306 return false; // Timeout. 307 } 308 int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR); 309 if (connectError == 0) { 310 return true; // Success! 311 } 312 throw new ErrnoException("isConnected", connectError); // The connect(2) failed. 313 } catch (ErrnoException errnoException) { 314 if (errnoException.errno == EINTR) { 315 return false; // Punt and ask the caller to try again. 316 } else { 317 cause = errnoException; 318 } 319 } 320 // TODO: is it really helpful/necessary to throw so many different exceptions? 321 String detail = connectDetail(inetAddress, port, timeoutMs, cause); 322 if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED || 323 cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE || 324 cause.errno == ENETUNREACH) { 325 throw new ConnectException(detail, cause); 326 } else if (cause.errno == EACCES) { 327 throw new SecurityException(detail, cause); 328 } else if (cause.errno == ETIMEDOUT) { 329 throw new SocketTimeoutException(detail, cause); 330 } 331 throw new SocketException(detail, cause); 332 } 333 334 /** 335 * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'. 336 */ 337 public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException { 338 try { 339 int flags = Libcore.os.fcntlVoid(fd, F_GETFL); 340 if (!blocking) { 341 flags |= O_NONBLOCK; 342 } else { 343 flags &= ~O_NONBLOCK; 344 } 345 Libcore.os.fcntlLong(fd, F_SETFL, flags); 346 } catch (ErrnoException errnoException) { 347 throw errnoException.rethrowAsIOException(); 348 } 349 } 350 351 public static FileDescriptor socket(boolean stream) throws SocketException { 352 FileDescriptor fd; 353 try { 354 fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0); 355 356 // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults 357 // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which 358 // would be correct for the *unicast* hop limit). 359 // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to 360 // have been applied as a result of that discussion. If that bug is ever fixed, we can 361 // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets. 362 // (IPv4 is already correct.) 363 if (!stream) { 364 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1); 365 } 366 367 return fd; 368 } catch (ErrnoException errnoException) { 369 throw errnoException.rethrowAsSocketException(); 370 } 371 } 372 373 // Socket options used by java.net but not exposed in SocketOptions. 374 public static final int JAVA_MCAST_JOIN_GROUP = 19; 375 public static final int JAVA_MCAST_LEAVE_GROUP = 20; 376 public static final int JAVA_IP_MULTICAST_TTL = 17; 377 378 /** 379 * java.net has its own socket options similar to the underlying Unix ones. We paper over the 380 * differences here. 381 */ 382 public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException { 383 try { 384 return getSocketOptionErrno(fd, option); 385 } catch (ErrnoException errnoException) { 386 throw errnoException.rethrowAsSocketException(); 387 } 388 } 389 390 private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException { 391 switch (option) { 392 case SocketOptions.IP_MULTICAST_IF: 393 // This is IPv4-only. 394 return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF); 395 case SocketOptions.IP_MULTICAST_IF2: 396 // This is IPv6-only. 397 return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF); 398 case SocketOptions.IP_MULTICAST_LOOP: 399 // Since setting this from java.net always sets IPv4 and IPv6 to the same value, 400 // it doesn't matter which we return. 401 return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP)); 402 case IoUtils.JAVA_IP_MULTICAST_TTL: 403 // Since setting this from java.net always sets IPv4 and IPv6 to the same value, 404 // it doesn't matter which we return. 405 return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS); 406 case SocketOptions.IP_TOS: 407 // Since setting this from java.net always sets IPv4 and IPv6 to the same value, 408 // it doesn't matter which we return. 409 return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS); 410 case SocketOptions.SO_BROADCAST: 411 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST)); 412 case SocketOptions.SO_KEEPALIVE: 413 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE)); 414 case SocketOptions.SO_LINGER: 415 StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER); 416 if (!linger.isOn()) { 417 return false; 418 } 419 return linger.l_linger; 420 case SocketOptions.SO_OOBINLINE: 421 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE)); 422 case SocketOptions.SO_RCVBUF: 423 return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF); 424 case SocketOptions.SO_REUSEADDR: 425 return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR)); 426 case SocketOptions.SO_SNDBUF: 427 return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF); 428 case SocketOptions.SO_TIMEOUT: 429 return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis(); 430 case SocketOptions.TCP_NODELAY: 431 return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY)); 432 default: 433 throw new SocketException("Unknown socket option: " + option); 434 } 435 } 436 437 private static boolean booleanFromInt(int i) { 438 return (i != 0); 439 } 440 441 private static int booleanToInt(boolean b) { 442 return b ? 1 : 0; 443 } 444 445 /** 446 * java.net has its own socket options similar to the underlying Unix ones. We paper over the 447 * differences here. 448 */ 449 public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException { 450 try { 451 setSocketOptionErrno(fd, option, value); 452 } catch (ErrnoException errnoException) { 453 throw errnoException.rethrowAsSocketException(); 454 } 455 } 456 457 private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException { 458 switch (option) { 459 case SocketOptions.IP_MULTICAST_IF: 460 throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android"); 461 case SocketOptions.IP_MULTICAST_IF2: 462 // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int. 463 Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value); 464 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value); 465 return; 466 case SocketOptions.IP_MULTICAST_LOOP: 467 // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte. 468 Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value)); 469 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value)); 470 return; 471 case IoUtils.JAVA_IP_MULTICAST_TTL: 472 // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int, 473 // IPv4 multicast TTL uses a byte. 474 Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value); 475 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value); 476 return; 477 case SocketOptions.IP_TOS: 478 Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value); 479 Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value); 480 return; 481 case SocketOptions.SO_BROADCAST: 482 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value)); 483 return; 484 case SocketOptions.SO_KEEPALIVE: 485 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value)); 486 return; 487 case SocketOptions.SO_LINGER: 488 boolean on = false; 489 int seconds = 0; 490 if (value instanceof Integer) { 491 on = true; 492 seconds = Math.min((Integer) value, 65535); 493 } 494 StructLinger linger = new StructLinger(booleanToInt(on), seconds); 495 Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger); 496 return; 497 case SocketOptions.SO_OOBINLINE: 498 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value)); 499 return; 500 case SocketOptions.SO_RCVBUF: 501 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value); 502 return; 503 case SocketOptions.SO_REUSEADDR: 504 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value)); 505 return; 506 case SocketOptions.SO_SNDBUF: 507 Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value); 508 return; 509 case SocketOptions.SO_TIMEOUT: 510 int millis = (Integer) value; 511 StructTimeval tv = StructTimeval.fromMillis(millis); 512 Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv); 513 return; 514 case SocketOptions.TCP_NODELAY: 515 Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value)); 516 return; 517 case IoUtils.JAVA_MCAST_JOIN_GROUP: 518 case IoUtils.JAVA_MCAST_LEAVE_GROUP: 519 StructGroupReq groupReq = (StructGroupReq) value; 520 int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6; 521 int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP; 522 Libcore.os.setsockoptGroupReq(fd, level, op, groupReq); 523 return; 524 default: 525 throw new SocketException("Unknown socket option: " + option); 526 } 527 } 528 529 public static InetAddress getSocketLocalAddress(FileDescriptor fd) { 530 SocketAddress sa = Libcore.os.getsockname(fd); 531 InetSocketAddress isa = (InetSocketAddress) sa; 532 return isa.getAddress(); 533 } 534 535 public static int getSocketLocalPort(FileDescriptor fd) { 536 SocketAddress sa = Libcore.os.getsockname(fd); 537 InetSocketAddress isa = (InetSocketAddress) sa; 538 return isa.getPort(); 539 } 540 541 /** 542 * Returns the contents of 'path' as a byte array. 543 */ 544 public static byte[] readFileAsByteArray(String path) throws IOException { 545 return readFileAsBytes(path).toByteArray(); 546 } 547 548 /** 549 * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8. 550 */ 551 public static String readFileAsString(String path) throws IOException { 552 return readFileAsBytes(path).toString(Charsets.UTF_8); 553 } 554 555 private static UnsafeByteSequence readFileAsBytes(String path) throws IOException { 556 RandomAccessFile f = null; 557 try { 558 f = new RandomAccessFile(path, "r"); 559 UnsafeByteSequence bytes = new UnsafeByteSequence((int) f.length()); 560 byte[] buffer = new byte[8192]; 561 while (true) { 562 int byteCount = f.read(buffer); 563 if (byteCount == -1) { 564 return bytes; 565 } 566 bytes.write(buffer, 0, byteCount); 567 } 568 } finally { 569 IoUtils.closeQuietly(f); 570 } 571 } 572 573 /** 574 * Recursively delete everything in {@code dir}. 575 */ 576 // TODO: this should specify paths as Strings rather than as Files 577 public static void deleteContents(File dir) throws IOException { 578 File[] files = dir.listFiles(); 579 if (files == null) { 580 throw new IllegalArgumentException("not a directory: " + dir); 581 } 582 for (File file : files) { 583 if (file.isDirectory()) { 584 deleteContents(file); 585 } 586 if (!file.delete()) { 587 throw new IOException("failed to delete file: " + file); 588 } 589 } 590 } 591} 592