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.FileStatus;
027 import org.apache.hadoop.fs.LocalFileSystem;
028 import org.apache.hadoop.fs.Path;
029 import org.apache.hadoop.fs.permission.FsAction;
030 import org.apache.hadoop.fs.permission.FsPermission;
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 public static class DiskErrorException extends IOException {
040 public DiskErrorException(String msg) {
041 super(msg);
042 }
043 }
044
045 public static class DiskOutOfSpaceException extends IOException {
046 public DiskOutOfSpaceException(String msg) {
047 super(msg);
048 }
049 }
050
051 /**
052 * The semantics of mkdirsWithExistsCheck method is different from the mkdirs
053 * method provided in the Sun's java.io.File class in the following way:
054 * While creating the non-existent parent directories, this method checks for
055 * the existence of those directories if the mkdir fails at any point (since
056 * that directory might have just been created by some other process).
057 * If both mkdir() and the exists() check fails for any seemingly
058 * non-existent directory, then we signal an error; Sun's mkdir would signal
059 * an error (return false) if a directory it is attempting to create already
060 * exists or the mkdir fails.
061 * @param dir
062 * @return true on success, false on failure
063 */
064 public static boolean mkdirsWithExistsCheck(File dir) {
065 if (dir.mkdir() || dir.exists()) {
066 return true;
067 }
068 File canonDir = null;
069 try {
070 canonDir = dir.getCanonicalFile();
071 } catch (IOException e) {
072 return false;
073 }
074 String parent = canonDir.getParent();
075 return (parent != null) &&
076 (mkdirsWithExistsCheck(new File(parent)) &&
077 (canonDir.mkdir() || canonDir.exists()));
078 }
079
080 /**
081 * Create the directory if it doesn't exist and
082 * @param dir
083 * @throws DiskErrorException
084 */
085 public static void checkDir(File dir) throws DiskErrorException {
086 if (!mkdirsWithExistsCheck(dir))
087 throw new DiskErrorException("can not create directory: "
088 + dir.toString());
089
090 if (!dir.isDirectory())
091 throw new DiskErrorException("not a directory: "
092 + dir.toString());
093
094 if (!dir.canRead())
095 throw new DiskErrorException("directory is not readable: "
096 + dir.toString());
097
098 if (!dir.canWrite())
099 throw new DiskErrorException("directory is not writable: "
100 + dir.toString());
101 }
102
103 /**
104 * Create the directory or check permissions if it already exists.
105 *
106 * The semantics of mkdirsWithExistsAndPermissionCheck method is different
107 * from the mkdirs method provided in the Sun's java.io.File class in the
108 * following way:
109 * While creating the non-existent parent directories, this method checks for
110 * the existence of those directories if the mkdir fails at any point (since
111 * that directory might have just been created by some other process).
112 * If both mkdir() and the exists() check fails for any seemingly
113 * non-existent directory, then we signal an error; Sun's mkdir would signal
114 * an error (return false) if a directory it is attempting to create already
115 * exists or the mkdir fails.
116 *
117 * @param localFS local filesystem
118 * @param dir directory to be created or checked
119 * @param expected expected permission
120 * @throws IOException
121 */
122 public static void mkdirsWithExistsAndPermissionCheck(
123 LocalFileSystem localFS, Path dir, FsPermission expected)
124 throws IOException {
125 File directory = localFS.pathToFile(dir);
126 boolean created = false;
127
128 if (!directory.exists())
129 created = mkdirsWithExistsCheck(directory);
130
131 if (created || !localFS.getFileStatus(dir).getPermission().equals(expected))
132 localFS.setPermission(dir, expected);
133 }
134
135 /**
136 * Create the local directory if necessary, check permissions and also ensure
137 * it can be read from and written into.
138 *
139 * @param localFS local filesystem
140 * @param dir directory
141 * @param expected permission
142 * @throws DiskErrorException
143 * @throws IOException
144 */
145 public static void checkDir(LocalFileSystem localFS, Path dir,
146 FsPermission expected)
147 throws DiskErrorException, IOException {
148 mkdirsWithExistsAndPermissionCheck(localFS, dir, expected);
149
150 FileStatus stat = localFS.getFileStatus(dir);
151 FsPermission actual = stat.getPermission();
152
153 if (!stat.isDirectory())
154 throw new DiskErrorException("not a directory: "+ dir.toString());
155
156 FsAction user = actual.getUserAction();
157 if (!user.implies(FsAction.READ))
158 throw new DiskErrorException("directory is not readable: "
159 + dir.toString());
160
161 if (!user.implies(FsAction.WRITE))
162 throw new DiskErrorException("directory is not writable: "
163 + dir.toString());
164
165 if (!user.implies(FsAction.EXECUTE))
166 throw new DiskErrorException("directory is not listable: "
167 + dir.toString());
168 }
169 }