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 }