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, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.util; 020 021 import java.io.File; 022 import java.io.IOException; 023 024 import org.apache.hadoop.classification.InterfaceAudience; 025 import org.apache.hadoop.classification.InterfaceStability; 026 import org.apache.hadoop.fs.FileUtil; 027 import org.apache.hadoop.fs.LocalFileSystem; 028 import org.apache.hadoop.fs.Path; 029 import org.apache.hadoop.fs.permission.FsPermission; 030 import org.apache.hadoop.util.Shell; 031 032 /** 033 * Class that provides utility functions for checking disk problem 034 */ 035 @InterfaceAudience.Private 036 @InterfaceStability.Unstable 037 public class DiskChecker { 038 039 private static final long SHELL_TIMEOUT = 10 * 1000; 040 041 public static class DiskErrorException extends IOException { 042 public DiskErrorException(String msg) { 043 super(msg); 044 } 045 046 public DiskErrorException(String msg, Throwable cause) { 047 super(msg, cause); 048 } 049 } 050 051 public static class DiskOutOfSpaceException extends IOException { 052 public DiskOutOfSpaceException(String msg) { 053 super(msg); 054 } 055 } 056 057 /** 058 * The semantics of mkdirsWithExistsCheck method is different from the mkdirs 059 * method provided in the Sun's java.io.File class in the following way: 060 * While creating the non-existent parent directories, this method checks for 061 * the existence of those directories if the mkdir fails at any point (since 062 * that directory might have just been created by some other process). 063 * If both mkdir() and the exists() check fails for any seemingly 064 * non-existent directory, then we signal an error; Sun's mkdir would signal 065 * an error (return false) if a directory it is attempting to create already 066 * exists or the mkdir fails. 067 * @param dir 068 * @return true on success, false on failure 069 */ 070 public static boolean mkdirsWithExistsCheck(File dir) { 071 if (dir.mkdir() || dir.exists()) { 072 return true; 073 } 074 File canonDir = null; 075 try { 076 canonDir = dir.getCanonicalFile(); 077 } catch (IOException e) { 078 return false; 079 } 080 String parent = canonDir.getParent(); 081 return (parent != null) && 082 (mkdirsWithExistsCheck(new File(parent)) && 083 (canonDir.mkdir() || canonDir.exists())); 084 } 085 086 /** 087 * Create the directory if it doesn't exist and check that dir is readable, 088 * writable and executable 089 * 090 * @param dir 091 * @throws DiskErrorException 092 */ 093 public static void checkDir(File dir) throws DiskErrorException { 094 if (!mkdirsWithExistsCheck(dir)) { 095 throw new DiskErrorException("Can not create directory: " 096 + dir.toString()); 097 } 098 checkDirAccess(dir); 099 } 100 101 /** 102 * Create the directory or check permissions if it already exists. 103 * 104 * The semantics of mkdirsWithExistsAndPermissionCheck method is different 105 * from the mkdirs method provided in the Sun's java.io.File class in the 106 * following way: 107 * While creating the non-existent parent directories, this method checks for 108 * the existence of those directories if the mkdir fails at any point (since 109 * that directory might have just been created by some other process). 110 * If both mkdir() and the exists() check fails for any seemingly 111 * non-existent directory, then we signal an error; Sun's mkdir would signal 112 * an error (return false) if a directory it is attempting to create already 113 * exists or the mkdir fails. 114 * 115 * @param localFS local filesystem 116 * @param dir directory to be created or checked 117 * @param expected expected permission 118 * @throws IOException 119 */ 120 public static void mkdirsWithExistsAndPermissionCheck( 121 LocalFileSystem localFS, Path dir, FsPermission expected) 122 throws IOException { 123 File directory = localFS.pathToFile(dir); 124 boolean created = false; 125 126 if (!directory.exists()) 127 created = mkdirsWithExistsCheck(directory); 128 129 if (created || !localFS.getFileStatus(dir).getPermission().equals(expected)) 130 localFS.setPermission(dir, expected); 131 } 132 133 /** 134 * Create the local directory if necessary, check permissions and also ensure 135 * it can be read from and written into. 136 * 137 * @param localFS local filesystem 138 * @param dir directory 139 * @param expected permission 140 * @throws DiskErrorException 141 * @throws IOException 142 */ 143 public static void checkDir(LocalFileSystem localFS, Path dir, 144 FsPermission expected) 145 throws DiskErrorException, IOException { 146 mkdirsWithExistsAndPermissionCheck(localFS, dir, expected); 147 checkDirAccess(localFS.pathToFile(dir)); 148 } 149 150 /** 151 * Checks that the given file is a directory and that the current running 152 * process can read, write, and execute it. 153 * 154 * @param dir File to check 155 * @throws DiskErrorException if dir is not a directory, not readable, not 156 * writable, or not executable 157 */ 158 private static void checkDirAccess(File dir) throws DiskErrorException { 159 if (!dir.isDirectory()) { 160 throw new DiskErrorException("Not a directory: " 161 + dir.toString()); 162 } 163 164 checkAccessByFileMethods(dir); 165 } 166 167 /** 168 * Checks that the current running process can read, write, and execute the 169 * given directory by using methods of the File object. 170 * 171 * @param dir File to check 172 * @throws DiskErrorException if dir is not readable, not writable, or not 173 * executable 174 */ 175 private static void checkAccessByFileMethods(File dir) 176 throws DiskErrorException { 177 if (!FileUtil.canRead(dir)) { 178 throw new DiskErrorException("Directory is not readable: " 179 + dir.toString()); 180 } 181 182 if (!FileUtil.canWrite(dir)) { 183 throw new DiskErrorException("Directory is not writable: " 184 + dir.toString()); 185 } 186 187 if (!FileUtil.canExecute(dir)) { 188 throw new DiskErrorException("Directory is not executable: " 189 + dir.toString()); 190 } 191 } 192 }