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    }