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 }