| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.tomcat.util.http.fileupload; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| |
| |
| /** |
| * General file manipulation utilities. |
| * <p> |
| * Facilities are provided in the following areas: |
| * <ul> |
| * <li>writing to a file |
| * <li>reading from a file |
| * <li>make a directory including parent directories |
| * <li>copying files and directories |
| * <li>deleting files and directories |
| * <li>converting to and from a URL |
| * <li>listing files and directories by filter and extension |
| * <li>comparing file content |
| * <li>file last changed date |
| * <li>calculating a checksum |
| * </ul> |
| * <p> |
| * Note that a specific charset should be specified whenever possible. |
| * Relying on the platform default means that the code is Locale-dependent. |
| * Only use the default if the files are known to always use the platform default. |
| * <p> |
| * Origin of code: Excalibur, Alexandria, Commons-Utils |
| */ |
| public class FileUtils { |
| |
| /** |
| * Instances should NOT be constructed in standard programming. |
| */ |
| public FileUtils() { |
| super(); |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Deletes a directory recursively. |
| * |
| * @param directory directory to delete |
| * @throws IOException in case deletion is unsuccessful |
| * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory |
| */ |
| public static void deleteDirectory(final File directory) throws IOException { |
| if (!directory.exists()) { |
| return; |
| } |
| |
| if (!isSymlink(directory)) { |
| cleanDirectory(directory); |
| } |
| |
| if (!directory.delete()) { |
| final String message = |
| "Unable to delete directory " + directory + "."; |
| throw new IOException(message); |
| } |
| } |
| |
| /** |
| * Cleans a directory without deleting it. |
| * |
| * @param directory directory to clean |
| * @throws IOException in case cleaning is unsuccessful |
| * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory |
| */ |
| public static void cleanDirectory(final File directory) throws IOException { |
| if (!directory.exists()) { |
| final String message = directory + " does not exist"; |
| throw new IllegalArgumentException(message); |
| } |
| |
| if (!directory.isDirectory()) { |
| final String message = directory + " is not a directory"; |
| throw new IllegalArgumentException(message); |
| } |
| |
| final File[] files = directory.listFiles(); |
| if (files == null) { // null if security restricted |
| throw new IOException("Failed to list contents of " + directory); |
| } |
| |
| IOException exception = null; |
| for (File file : files) { |
| try { |
| forceDelete(file); |
| } catch (IOException ioe) { |
| exception = ioe; |
| } |
| } |
| |
| if (null != exception) { |
| throw exception; |
| } |
| } |
| |
| //----------------------------------------------------------------------- |
| /** |
| * Deletes a file. If file is a directory, delete it and all sub-directories. |
| * <p> |
| * The difference between File.delete() and this method are: |
| * <ul> |
| * <li>A directory to be deleted does not have to be empty.</li> |
| * <li>You get exceptions when a file or directory cannot be deleted. |
| * (java.io.File methods returns a boolean)</li> |
| * </ul> |
| * |
| * @param file file or directory to delete, must not be {@code null} |
| * @throws NullPointerException if the directory is {@code null} |
| * @throws FileNotFoundException if the file was not found |
| * @throws IOException in case deletion is unsuccessful |
| */ |
| public static void forceDelete(final File file) throws IOException { |
| if (file.isDirectory()) { |
| deleteDirectory(file); |
| } else { |
| final boolean filePresent = file.exists(); |
| if (!file.delete()) { |
| if (!filePresent) { |
| throw new FileNotFoundException("File does not exist: " + file); |
| } |
| final String message = |
| "Unable to delete file: " + file; |
| throw new IOException(message); |
| } |
| } |
| } |
| |
| /** |
| * Schedules a file to be deleted when JVM exits. |
| * If file is directory delete it and all sub-directories. |
| * |
| * @param file file or directory to delete, must not be {@code null} |
| * @throws NullPointerException if the file is {@code null} |
| * @throws IOException in case deletion is unsuccessful |
| */ |
| public static void forceDeleteOnExit(final File file) throws IOException { |
| if (file.isDirectory()) { |
| deleteDirectoryOnExit(file); |
| } else { |
| file.deleteOnExit(); |
| } |
| } |
| |
| /** |
| * Schedules a directory recursively for deletion on JVM exit. |
| * |
| * @param directory directory to delete, must not be {@code null} |
| * @throws NullPointerException if the directory is {@code null} |
| * @throws IOException in case deletion is unsuccessful |
| */ |
| private static void deleteDirectoryOnExit(final File directory) throws IOException { |
| if (!directory.exists()) { |
| return; |
| } |
| |
| directory.deleteOnExit(); |
| if (!isSymlink(directory)) { |
| cleanDirectoryOnExit(directory); |
| } |
| } |
| |
| /** |
| * Cleans a directory without deleting it. |
| * |
| * @param directory directory to clean, must not be {@code null} |
| * @throws NullPointerException if the directory is {@code null} |
| * @throws IOException in case cleaning is unsuccessful |
| */ |
| private static void cleanDirectoryOnExit(final File directory) throws IOException { |
| if (!directory.exists()) { |
| String message = directory + " does not exist"; |
| throw new IllegalArgumentException(message); |
| } |
| |
| if (!directory.isDirectory()) { |
| String message = directory + " is not a directory"; |
| throw new IllegalArgumentException(message); |
| } |
| |
| File[] files = directory.listFiles(); |
| if (files == null) { // null if security restricted |
| throw new IOException("Failed to list contents of " + directory); |
| } |
| |
| IOException exception = null; |
| for (File file : files) { |
| try { |
| forceDeleteOnExit(file); |
| } catch (IOException ioe) { |
| exception = ioe; |
| } |
| } |
| |
| if (null != exception) { |
| throw exception; |
| } |
| } |
| |
| /** |
| * Makes a directory, including any necessary but nonexistent parent |
| * directories. If a file already exists with specified name but it is |
| * not a directory then an IOException is thrown. |
| * If the directory cannot be created (or does not already exist) |
| * then an IOException is thrown. |
| * |
| * @param directory directory to create, must not be {@code null} |
| * @throws NullPointerException if the directory is {@code null} |
| * @throws IOException if the directory cannot be created or the file already exists but is not a directory |
| */ |
| public static void forceMkdir(final File directory) throws IOException { |
| if (directory.exists()) { |
| if (!directory.isDirectory()) { |
| final String message = |
| "File " |
| + directory |
| + " exists and is " |
| + "not a directory. Unable to create directory."; |
| throw new IOException(message); |
| } |
| } else { |
| if (!directory.mkdirs()) { |
| // Double-check that some other thread or process hasn't made |
| // the directory in the background |
| if (!directory.isDirectory()) { |
| final String message = |
| "Unable to create directory " + directory; |
| throw new IOException(message); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Makes any necessary but nonexistent parent directories for a given File. If the parent directory cannot be |
| * created then an IOException is thrown. |
| * |
| * @param file file with parent to create, must not be {@code null} |
| * @throws NullPointerException if the file is {@code null} |
| * @throws IOException if the parent directory cannot be created |
| * @since 2.5 |
| */ |
| public static void forceMkdirParent(final File file) throws IOException { |
| final File parent = file.getParentFile(); |
| if (parent == null) { |
| return; |
| } |
| forceMkdir(parent); |
| } |
| |
| |
| /** |
| * Determines whether the specified file is a Symbolic Link rather than an actual file. |
| * <p> |
| * Will not return true if there is a Symbolic Link anywhere in the path, |
| * only if the specific file is. |
| * <p> |
| * <b>Note:</b> the current implementation always returns {@code false} if |
| * the system is detected as Windows using |
| * {@link File#separatorChar} == '\\' |
| * |
| * @param file the file to check |
| * @return true if the file is a Symbolic Link |
| * @throws IOException if an IO error occurs while checking the file |
| * @since 2.0 |
| */ |
| public static boolean isSymlink(File file) throws IOException { |
| if (file == null) { |
| throw new NullPointerException("File must not be null"); |
| } |
| //FilenameUtils.isSystemWindows() |
| if (File.separatorChar == '\\') { |
| return false; |
| } |
| File fileInCanonicalDir = null; |
| if (file.getParent() == null) { |
| fileInCanonicalDir = file; |
| } else { |
| File canonicalDir = file.getParentFile().getCanonicalFile(); |
| fileInCanonicalDir = new File(canonicalDir, file.getName()); |
| } |
| |
| if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| } |