View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.util;
20  
21  import com.google.common.annotations.VisibleForTesting;
22  import com.google.common.collect.Iterators;
23  import com.google.common.primitives.Ints;
24  
25  import java.io.ByteArrayInputStream;
26  import java.io.DataInputStream;
27  import java.io.EOFException;
28  import java.io.FileNotFoundException;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InterruptedIOException;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.Method;
34  import java.net.InetSocketAddress;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.LinkedList;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.concurrent.ArrayBlockingQueue;
46  import java.util.concurrent.ConcurrentHashMap;
47  import java.util.concurrent.ThreadPoolExecutor;
48  import java.util.concurrent.TimeUnit;
49  import java.util.regex.Pattern;
50  
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  import org.apache.hadoop.hbase.classification.InterfaceAudience;
54  import org.apache.hadoop.HadoopIllegalArgumentException;
55  import org.apache.hadoop.conf.Configuration;
56  import org.apache.hadoop.fs.BlockLocation;
57  import org.apache.hadoop.fs.FSDataInputStream;
58  import org.apache.hadoop.fs.FSDataOutputStream;
59  import org.apache.hadoop.fs.FileStatus;
60  import org.apache.hadoop.fs.FileSystem;
61  import org.apache.hadoop.fs.Path;
62  import org.apache.hadoop.fs.PathFilter;
63  import org.apache.hadoop.fs.permission.FsAction;
64  import org.apache.hadoop.fs.permission.FsPermission;
65  import org.apache.hadoop.hbase.ClusterId;
66  import org.apache.hadoop.hbase.HColumnDescriptor;
67  import org.apache.hadoop.hbase.HConstants;
68  import org.apache.hadoop.hbase.HDFSBlocksDistribution;
69  import org.apache.hadoop.hbase.HRegionInfo;
70  import org.apache.hadoop.hbase.RemoteExceptionHandler;
71  import org.apache.hadoop.hbase.TableName;
72  import org.apache.hadoop.hbase.exceptions.DeserializationException;
73  import org.apache.hadoop.hbase.fs.HFileSystem;
74  import org.apache.hadoop.hbase.master.HMaster;
75  import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
76  import org.apache.hadoop.hbase.security.AccessDeniedException;
77  import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter;
78  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
79  import org.apache.hadoop.hbase.protobuf.generated.FSProtos;
80  import org.apache.hadoop.hbase.regionserver.HRegion;
81  import org.apache.hadoop.hdfs.DistributedFileSystem;
82  import org.apache.hadoop.io.IOUtils;
83  import org.apache.hadoop.io.SequenceFile;
84  import org.apache.hadoop.ipc.RemoteException;
85  import org.apache.hadoop.security.UserGroupInformation;
86  import org.apache.hadoop.util.Progressable;
87  import org.apache.hadoop.util.ReflectionUtils;
88  import org.apache.hadoop.util.StringUtils;
89  
90  import static org.apache.hadoop.hbase.HConstants.HBASE_DIR;
91  
92  /**
93   * Utility methods for interacting with the underlying file system.
94   */
95  @InterfaceAudience.Private
96  public abstract class FSUtils {
97    private static final Log LOG = LogFactory.getLog(FSUtils.class);
98  
99    /** Full access permissions (starting point for a umask) */
100   public static final String FULL_RWX_PERMISSIONS = "777";
101   private static final String THREAD_POOLSIZE = "hbase.client.localityCheck.threadPoolSize";
102   private static final int DEFAULT_THREAD_POOLSIZE = 2;
103 
104   /** Set to true on Windows platforms */
105   public static final boolean WINDOWS = System.getProperty("os.name").startsWith("Windows");
106 
107   protected FSUtils() {
108     super();
109   }
110 
111   /**
112    * Sets storage policy for given path according to config setting.
113    * If the passed path is a directory, we'll set the storage policy for all files
114    * created in the future in said directory. Note that this change in storage
115    * policy takes place at the HDFS level; it will persist beyond this RS's lifecycle.
116    * If we're running on a version of HDFS that doesn't support the given storage policy
117    * (or storage policies at all), then we'll issue a log message and continue.
118    *
119    * See http://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/ArchivalStorage.html
120    *
121    * @param fs We only do anything if an instance of DistributedFileSystem
122    * @param conf used to look up storage policy with given key; not modified.
123    * @param path the Path whose storage policy is to be set
124    * @param policyKey e.g. HConstants.WAL_STORAGE_POLICY
125    * @param defaultPolicy usually should be the policy NONE to delegate to HDFS
126    */
127   public static void setStoragePolicy(final FileSystem fs, final Configuration conf,
128       final Path path, final String policyKey, final String defaultPolicy) {
129     String storagePolicy = conf.get(policyKey, defaultPolicy).toUpperCase();
130     if (storagePolicy.equals(defaultPolicy)) {
131       if (LOG.isTraceEnabled()) {
132         LOG.trace("default policy of " + defaultPolicy + " requested, exiting early.");
133       }
134       return;
135     }
136     if (fs instanceof DistributedFileSystem) {
137       DistributedFileSystem dfs = (DistributedFileSystem)fs;
138       // Once our minimum supported Hadoop version is 2.6.0 we can remove reflection.
139       Class<? extends DistributedFileSystem> dfsClass = dfs.getClass();
140       Method m = null;
141       try {
142         m = dfsClass.getDeclaredMethod("setStoragePolicy",
143             new Class<?>[] { Path.class, String.class });
144         m.setAccessible(true);
145       } catch (NoSuchMethodException e) {
146         LOG.info("FileSystem doesn't support"
147             + " setStoragePolicy; --HDFS-6584 not available");
148       } catch (SecurityException e) {
149         LOG.info("Doesn't have access to setStoragePolicy on "
150             + "FileSystems --HDFS-6584 not available", e);
151         m = null; // could happen on setAccessible()
152       }
153       if (m != null) {
154         try {
155           m.invoke(dfs, path, storagePolicy);
156           LOG.info("set " + storagePolicy + " for " + path);
157         } catch (Exception e) {
158           // check for lack of HDFS-7228
159           boolean probablyBadPolicy = false;
160           if (e instanceof InvocationTargetException) {
161             final Throwable exception = e.getCause();
162             if (exception instanceof RemoteException &&
163                 HadoopIllegalArgumentException.class.getName().equals(
164                     ((RemoteException)exception).getClassName())) {
165               LOG.warn("Given storage policy, '" + storagePolicy + "', was rejected and probably " +
166                   "isn't a valid policy for the version of Hadoop you're running. I.e. if you're " +
167                   "trying to use SSD related policies then you're likely missing HDFS-7228. For " +
168                   "more information see the 'ArchivalStorage' docs for your Hadoop release.");
169               LOG.debug("More information about the invalid storage policy.", exception);
170               probablyBadPolicy = true;
171             }
172           }
173           if (!probablyBadPolicy) {
174             // This swallows FNFE, should we be throwing it? seems more likely to indicate dev
175             // misuse than a runtime problem with HDFS.
176             LOG.warn("Unable to set " + storagePolicy + " for " + path, e);
177           }
178         }
179       }
180     } else {
181       LOG.info("FileSystem isn't an instance of DistributedFileSystem; presuming it doesn't " +
182           "support setStoragePolicy.");
183     }
184   }
185 
186   /**
187    * Compare of path component. Does not consider schema; i.e. if schemas
188    * different but <code>path</code> starts with <code>rootPath</code>,
189    * then the function returns true
190    * @param rootPath
191    * @param path
192    * @return True if <code>path</code> starts with <code>rootPath</code>
193    */
194   public static boolean isStartingWithPath(final Path rootPath, final String path) {
195     String uriRootPath = rootPath.toUri().getPath();
196     String tailUriPath = (new Path(path)).toUri().getPath();
197     return tailUriPath.startsWith(uriRootPath);
198   }
199 
200   /**
201    * Compare path component of the Path URI; e.g. if hdfs://a/b/c and /a/b/c, it will compare the
202    * '/a/b/c' part. Does not consider schema; i.e. if schemas different but path or subpath matches,
203    * the two will equate.
204    * @param pathToSearch Path we will be trying to match.
205    * @param pathTail
206    * @return True if <code>pathTail</code> is tail on the path of <code>pathToSearch</code>
207    */
208   public static boolean isMatchingTail(final Path pathToSearch, String pathTail) {
209     return isMatchingTail(pathToSearch, new Path(pathTail));
210   }
211 
212   /**
213    * Compare path component of the Path URI; e.g. if hdfs://a/b/c and /a/b/c, it will compare the
214    * '/a/b/c' part. If you passed in 'hdfs://a/b/c and b/c, it would return true.  Does not consider
215    * schema; i.e. if schemas different but path or subpath matches, the two will equate.
216    * @param pathToSearch Path we will be trying to match.
217    * @param pathTail
218    * @return True if <code>pathTail</code> is tail on the path of <code>pathToSearch</code>
219    */
220   public static boolean isMatchingTail(final Path pathToSearch, final Path pathTail) {
221     if (pathToSearch.depth() != pathTail.depth()) return false;
222     Path tailPath = pathTail;
223     String tailName;
224     Path toSearch = pathToSearch;
225     String toSearchName;
226     boolean result = false;
227     do {
228       tailName = tailPath.getName();
229       if (tailName == null || tailName.length() <= 0) {
230         result = true;
231         break;
232       }
233       toSearchName = toSearch.getName();
234       if (toSearchName == null || toSearchName.length() <= 0) break;
235       // Move up a parent on each path for next go around.  Path doesn't let us go off the end.
236       tailPath = tailPath.getParent();
237       toSearch = toSearch.getParent();
238     } while(tailName.equals(toSearchName));
239     return result;
240   }
241 
242   public static FSUtils getInstance(FileSystem fs, Configuration conf) {
243     String scheme = fs.getUri().getScheme();
244     if (scheme == null) {
245       LOG.warn("Could not find scheme for uri " +
246           fs.getUri() + ", default to hdfs");
247       scheme = "hdfs";
248     }
249     Class<?> fsUtilsClass = conf.getClass("hbase.fsutil." +
250         scheme + ".impl", FSHDFSUtils.class); // Default to HDFS impl
251     FSUtils fsUtils = (FSUtils)ReflectionUtils.newInstance(fsUtilsClass, conf);
252     return fsUtils;
253   }
254 
255   /**
256    * Delete if exists.
257    * @param fs filesystem object
258    * @param dir directory to delete
259    * @return True if deleted <code>dir</code>
260    * @throws IOException e
261    */
262   public static boolean deleteDirectory(final FileSystem fs, final Path dir)
263   throws IOException {
264     return fs.exists(dir) && fs.delete(dir, true);
265   }
266 
267   /**
268    * Delete the region directory if exists.
269    * @param conf
270    * @param hri
271    * @return True if deleted the region directory.
272    * @throws IOException
273    */
274   public static boolean deleteRegionDir(final Configuration conf, final HRegionInfo hri)
275   throws IOException {
276     Path rootDir = getRootDir(conf);
277     FileSystem fs = rootDir.getFileSystem(conf);
278     return deleteDirectory(fs,
279       new Path(getTableDir(rootDir, hri.getTable()), hri.getEncodedName()));
280   }
281 
282   /**
283    * Return the number of bytes that large input files should be optimally
284    * be split into to minimize i/o time.
285    *
286    * use reflection to search for getDefaultBlockSize(Path f)
287    * if the method doesn't exist, fall back to using getDefaultBlockSize()
288    *
289    * @param fs filesystem object
290    * @return the default block size for the path's filesystem
291    * @throws IOException e
292    */
293   public static long getDefaultBlockSize(final FileSystem fs, final Path path) throws IOException {
294     Method m = null;
295     Class<? extends FileSystem> cls = fs.getClass();
296     try {
297       m = cls.getMethod("getDefaultBlockSize", new Class<?>[] { Path.class });
298     } catch (NoSuchMethodException e) {
299       LOG.info("FileSystem doesn't support getDefaultBlockSize");
300     } catch (SecurityException e) {
301       LOG.info("Doesn't have access to getDefaultBlockSize on FileSystems", e);
302       m = null; // could happen on setAccessible()
303     }
304     if (m == null) {
305       return fs.getDefaultBlockSize(path);
306     } else {
307       try {
308         Object ret = m.invoke(fs, path);
309         return ((Long)ret).longValue();
310       } catch (Exception e) {
311         throw new IOException(e);
312       }
313     }
314   }
315 
316   /*
317    * Get the default replication.
318    *
319    * use reflection to search for getDefaultReplication(Path f)
320    * if the method doesn't exist, fall back to using getDefaultReplication()
321    *
322    * @param fs filesystem object
323    * @param f path of file
324    * @return default replication for the path's filesystem
325    * @throws IOException e
326    */
327   public static short getDefaultReplication(final FileSystem fs, final Path path) throws IOException {
328     Method m = null;
329     Class<? extends FileSystem> cls = fs.getClass();
330     try {
331       m = cls.getMethod("getDefaultReplication", new Class<?>[] { Path.class });
332     } catch (NoSuchMethodException e) {
333       LOG.info("FileSystem doesn't support getDefaultReplication");
334     } catch (SecurityException e) {
335       LOG.info("Doesn't have access to getDefaultReplication on FileSystems", e);
336       m = null; // could happen on setAccessible()
337     }
338     if (m == null) {
339       return fs.getDefaultReplication(path);
340     } else {
341       try {
342         Object ret = m.invoke(fs, path);
343         return ((Number)ret).shortValue();
344       } catch (Exception e) {
345         throw new IOException(e);
346       }
347     }
348   }
349 
350   /**
351    * Returns the default buffer size to use during writes.
352    *
353    * The size of the buffer should probably be a multiple of hardware
354    * page size (4096 on Intel x86), and it determines how much data is
355    * buffered during read and write operations.
356    *
357    * @param fs filesystem object
358    * @return default buffer size to use during writes
359    */
360   public static int getDefaultBufferSize(final FileSystem fs) {
361     return fs.getConf().getInt("io.file.buffer.size", 4096);
362   }
363 
364   /**
365    * Create the specified file on the filesystem. By default, this will:
366    * <ol>
367    * <li>overwrite the file if it exists</li>
368    * <li>apply the umask in the configuration (if it is enabled)</li>
369    * <li>use the fs configured buffer size (or 4096 if not set)</li>
370    * <li>use the configured column family replication or default replication if
371    * {@link HColumnDescriptor#DEFAULT_DFS_REPLICATION}</li>
372    * <li>use the default block size</li>
373    * <li>not track progress</li>
374    * </ol>
375    * @param conf configurations
376    * @param fs {@link FileSystem} on which to write the file
377    * @param path {@link Path} to the file to write
378    * @param perm permissions
379    * @param favoredNodes
380    * @return output stream to the created file
381    * @throws IOException if the file cannot be created
382    */
383   public static FSDataOutputStream create(Configuration conf, FileSystem fs, Path path,
384       FsPermission perm, InetSocketAddress[] favoredNodes) throws IOException {
385     if (fs instanceof HFileSystem) {
386       FileSystem backingFs = ((HFileSystem)fs).getBackingFs();
387       if (backingFs instanceof DistributedFileSystem) {
388         // Try to use the favoredNodes version via reflection to allow backwards-
389         // compatibility.
390         short replication = Short.parseShort(conf.get(HColumnDescriptor.DFS_REPLICATION,
391           String.valueOf(HColumnDescriptor.DEFAULT_DFS_REPLICATION)));
392         try {
393           return (FSDataOutputStream) (DistributedFileSystem.class.getDeclaredMethod("create",
394             Path.class, FsPermission.class, boolean.class, int.class, short.class, long.class,
395             Progressable.class, InetSocketAddress[].class).invoke(backingFs, path, perm, true,
396             getDefaultBufferSize(backingFs),
397             replication > 0 ? replication : getDefaultReplication(backingFs, path),
398             getDefaultBlockSize(backingFs, path), null, favoredNodes));
399         } catch (InvocationTargetException ite) {
400           // Function was properly called, but threw it's own exception.
401           throw new IOException(ite.getCause());
402         } catch (NoSuchMethodException e) {
403           LOG.debug("DFS Client does not support most favored nodes create; using default create");
404           if (LOG.isTraceEnabled()) LOG.trace("Ignoring; use default create", e);
405         } catch (IllegalArgumentException e) {
406           LOG.debug("Ignoring (most likely Reflection related exception) " + e);
407         } catch (SecurityException e) {
408           LOG.debug("Ignoring (most likely Reflection related exception) " + e);
409         } catch (IllegalAccessException e) {
410           LOG.debug("Ignoring (most likely Reflection related exception) " + e);
411         }
412       }
413     }
414     return create(fs, path, perm, true);
415   }
416 
417   /**
418    * Create the specified file on the filesystem. By default, this will:
419    * <ol>
420    * <li>apply the umask in the configuration (if it is enabled)</li>
421    * <li>use the fs configured buffer size (or 4096 if not set)</li>
422    * <li>use the default replication</li>
423    * <li>use the default block size</li>
424    * <li>not track progress</li>
425    * </ol>
426    *
427    * @param fs {@link FileSystem} on which to write the file
428    * @param path {@link Path} to the file to write
429    * @param perm
430    * @param overwrite Whether or not the created file should be overwritten.
431    * @return output stream to the created file
432    * @throws IOException if the file cannot be created
433    */
434   public static FSDataOutputStream create(FileSystem fs, Path path,
435       FsPermission perm, boolean overwrite) throws IOException {
436     if (LOG.isTraceEnabled()) {
437       LOG.trace("Creating file=" + path + " with permission=" + perm + ", overwrite=" + overwrite);
438     }
439     return fs.create(path, perm, overwrite, getDefaultBufferSize(fs),
440         getDefaultReplication(fs, path), getDefaultBlockSize(fs, path), null);
441   }
442 
443   /**
444    * Get the file permissions specified in the configuration, if they are
445    * enabled.
446    *
447    * @param fs filesystem that the file will be created on.
448    * @param conf configuration to read for determining if permissions are
449    *          enabled and which to use
450    * @param permssionConfKey property key in the configuration to use when
451    *          finding the permission
452    * @return the permission to use when creating a new file on the fs. If
453    *         special permissions are not specified in the configuration, then
454    *         the default permissions on the the fs will be returned.
455    */
456   public static FsPermission getFilePermissions(final FileSystem fs,
457       final Configuration conf, final String permssionConfKey) {
458     boolean enablePermissions = conf.getBoolean(
459         HConstants.ENABLE_DATA_FILE_UMASK, false);
460 
461     if (enablePermissions) {
462       try {
463         FsPermission perm = new FsPermission(FULL_RWX_PERMISSIONS);
464         // make sure that we have a mask, if not, go default.
465         String mask = conf.get(permssionConfKey);
466         if (mask == null)
467           return FsPermission.getFileDefault();
468         // appy the umask
469         FsPermission umask = new FsPermission(mask);
470         return perm.applyUMask(umask);
471       } catch (IllegalArgumentException e) {
472         LOG.warn(
473             "Incorrect umask attempted to be created: "
474                 + conf.get(permssionConfKey)
475                 + ", using default file permissions.", e);
476         return FsPermission.getFileDefault();
477       }
478     }
479     return FsPermission.getFileDefault();
480   }
481 
482   /**
483    * Checks to see if the specified file system is available
484    *
485    * @param fs filesystem
486    * @throws IOException e
487    */
488   public static void checkFileSystemAvailable(final FileSystem fs)
489   throws IOException {
490     if (!(fs instanceof DistributedFileSystem)) {
491       return;
492     }
493     IOException exception = null;
494     DistributedFileSystem dfs = (DistributedFileSystem) fs;
495     try {
496       if (dfs.exists(new Path("/"))) {
497         return;
498       }
499     } catch (IOException e) {
500       exception = RemoteExceptionHandler.checkIOException(e);
501     }
502     try {
503       fs.close();
504     } catch (Exception e) {
505       LOG.error("file system close failed: ", e);
506     }
507     IOException io = new IOException("File system is not available");
508     io.initCause(exception);
509     throw io;
510   }
511 
512   /**
513    * We use reflection because {@link DistributedFileSystem#setSafeMode(
514    * HdfsConstants.SafeModeAction action, boolean isChecked)} is not in hadoop 1.1
515    *
516    * @param dfs
517    * @return whether we're in safe mode
518    * @throws IOException
519    */
520   private static boolean isInSafeMode(DistributedFileSystem dfs) throws IOException {
521     boolean inSafeMode = false;
522     try {
523       Method m = DistributedFileSystem.class.getMethod("setSafeMode", new Class<?> []{
524           org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.class, boolean.class});
525       inSafeMode = (Boolean) m.invoke(dfs,
526         org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.SAFEMODE_GET, true);
527     } catch (Exception e) {
528       if (e instanceof IOException) throw (IOException) e;
529 
530       // Check whether dfs is on safemode.
531       inSafeMode = dfs.setSafeMode(
532         org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.SAFEMODE_GET);
533     }
534     return inSafeMode;
535   }
536 
537   /**
538    * Check whether dfs is in safemode.
539    * @param conf
540    * @throws IOException
541    */
542   public static void checkDfsSafeMode(final Configuration conf)
543   throws IOException {
544     boolean isInSafeMode = false;
545     FileSystem fs = FileSystem.get(conf);
546     if (fs instanceof DistributedFileSystem) {
547       DistributedFileSystem dfs = (DistributedFileSystem)fs;
548       isInSafeMode = isInSafeMode(dfs);
549     }
550     if (isInSafeMode) {
551       throw new IOException("File system is in safemode, it can't be written now");
552     }
553   }
554 
555   /**
556    * Verifies current version of file system
557    *
558    * @param fs filesystem object
559    * @param rootdir root hbase directory
560    * @return null if no version file exists, version string otherwise.
561    * @throws IOException e
562    * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
563    */
564   public static String getVersion(FileSystem fs, Path rootdir)
565   throws IOException, DeserializationException {
566     Path versionFile = new Path(rootdir, HConstants.VERSION_FILE_NAME);
567     FileStatus[] status = null;
568     try {
569       // hadoop 2.0 throws FNFE if directory does not exist.
570       // hadoop 1.0 returns null if directory does not exist.
571       status = fs.listStatus(versionFile);
572     } catch (FileNotFoundException fnfe) {
573       return null;
574     }
575     if (status == null || status.length == 0) return null;
576     String version = null;
577     byte [] content = new byte [(int)status[0].getLen()];
578     FSDataInputStream s = fs.open(versionFile);
579     try {
580       IOUtils.readFully(s, content, 0, content.length);
581       if (ProtobufUtil.isPBMagicPrefix(content)) {
582         version = parseVersionFrom(content);
583       } else {
584         // Presume it pre-pb format.
585         InputStream is = new ByteArrayInputStream(content);
586         DataInputStream dis = new DataInputStream(is);
587         try {
588           version = dis.readUTF();
589         } finally {
590           dis.close();
591         }
592       }
593     } catch (EOFException eof) {
594       LOG.warn("Version file was empty, odd, will try to set it.");
595     } finally {
596       s.close();
597     }
598     return version;
599   }
600 
601   /**
602    * Parse the content of the ${HBASE_ROOTDIR}/hbase.version file.
603    * @param bytes The byte content of the hbase.version file.
604    * @return The version found in the file as a String.
605    * @throws DeserializationException
606    */
607   static String parseVersionFrom(final byte [] bytes)
608   throws DeserializationException {
609     ProtobufUtil.expectPBMagicPrefix(bytes);
610     int pblen = ProtobufUtil.lengthOfPBMagic();
611     FSProtos.HBaseVersionFileContent.Builder builder =
612       FSProtos.HBaseVersionFileContent.newBuilder();
613     try {
614       ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen);
615       return builder.getVersion();
616     } catch (IOException e) {
617       // Convert
618       throw new DeserializationException(e);
619     }
620   }
621 
622   /**
623    * Create the content to write into the ${HBASE_ROOTDIR}/hbase.version file.
624    * @param version Version to persist
625    * @return Serialized protobuf with <code>version</code> content and a bit of pb magic for a prefix.
626    */
627   static byte [] toVersionByteArray(final String version) {
628     FSProtos.HBaseVersionFileContent.Builder builder =
629       FSProtos.HBaseVersionFileContent.newBuilder();
630     return ProtobufUtil.prependPBMagic(builder.setVersion(version).build().toByteArray());
631   }
632 
633   /**
634    * Verifies current version of file system
635    *
636    * @param fs file system
637    * @param rootdir root directory of HBase installation
638    * @param message if true, issues a message on System.out
639    *
640    * @throws IOException e
641    * @throws DeserializationException
642    */
643   public static void checkVersion(FileSystem fs, Path rootdir, boolean message)
644   throws IOException, DeserializationException {
645     checkVersion(fs, rootdir, message, 0, HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS);
646   }
647 
648   /**
649    * Verifies current version of file system
650    *
651    * @param fs file system
652    * @param rootdir root directory of HBase installation
653    * @param message if true, issues a message on System.out
654    * @param wait wait interval
655    * @param retries number of times to retry
656    *
657    * @throws IOException e
658    * @throws DeserializationException
659    */
660   public static void checkVersion(FileSystem fs, Path rootdir,
661       boolean message, int wait, int retries)
662   throws IOException, DeserializationException {
663     String version = getVersion(fs, rootdir);
664     if (version == null) {
665       if (!metaRegionExists(fs, rootdir)) {
666         // rootDir is empty (no version file and no root region)
667         // just create new version file (HBASE-1195)
668         setVersion(fs, rootdir, wait, retries);
669         return;
670       }
671     } else if (version.compareTo(HConstants.FILE_SYSTEM_VERSION) == 0) return;
672 
673     // version is deprecated require migration
674     // Output on stdout so user sees it in terminal.
675     String msg = "HBase file layout needs to be upgraded."
676       + " You have version " + version
677       + " and I want version " + HConstants.FILE_SYSTEM_VERSION
678       + ". Consult http://hbase.apache.org/book.html for further information about upgrading HBase."
679       + " Is your hbase.rootdir valid? If so, you may need to run "
680       + "'hbase hbck -fixVersionFile'.";
681     if (message) {
682       System.out.println("WARNING! " + msg);
683     }
684     throw new FileSystemVersionException(msg);
685   }
686 
687   /**
688    * Sets version of file system
689    *
690    * @param fs filesystem object
691    * @param rootdir hbase root
692    * @throws IOException e
693    */
694   public static void setVersion(FileSystem fs, Path rootdir)
695   throws IOException {
696     setVersion(fs, rootdir, HConstants.FILE_SYSTEM_VERSION, 0,
697       HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS);
698   }
699 
700   /**
701    * Sets version of file system
702    *
703    * @param fs filesystem object
704    * @param rootdir hbase root
705    * @param wait time to wait for retry
706    * @param retries number of times to retry before failing
707    * @throws IOException e
708    */
709   public static void setVersion(FileSystem fs, Path rootdir, int wait, int retries)
710   throws IOException {
711     setVersion(fs, rootdir, HConstants.FILE_SYSTEM_VERSION, wait, retries);
712   }
713 
714 
715   /**
716    * Sets version of file system
717    *
718    * @param fs filesystem object
719    * @param rootdir hbase root directory
720    * @param version version to set
721    * @param wait time to wait for retry
722    * @param retries number of times to retry before throwing an IOException
723    * @throws IOException e
724    */
725   public static void setVersion(FileSystem fs, Path rootdir, String version,
726       int wait, int retries) throws IOException {
727     Path versionFile = new Path(rootdir, HConstants.VERSION_FILE_NAME);
728     Path tempVersionFile = new Path(rootdir, HConstants.HBASE_TEMP_DIRECTORY + Path.SEPARATOR +
729       HConstants.VERSION_FILE_NAME);
730     while (true) {
731       try {
732         // Write the version to a temporary file
733         FSDataOutputStream s = fs.create(tempVersionFile);
734         try {
735           s.write(toVersionByteArray(version));
736           s.close();
737           s = null;
738           // Move the temp version file to its normal location. Returns false
739           // if the rename failed. Throw an IOE in that case.
740           if (!fs.rename(tempVersionFile, versionFile)) {
741             throw new IOException("Unable to move temp version file to " + versionFile);
742           }
743         } finally {
744           // Cleaning up the temporary if the rename failed would be trying
745           // too hard. We'll unconditionally create it again the next time
746           // through anyway, files are overwritten by default by create().
747 
748           // Attempt to close the stream on the way out if it is still open.
749           try {
750             if (s != null) s.close();
751           } catch (IOException ignore) { }
752         }
753         LOG.info("Created version file at " + rootdir.toString() + " with version=" + version);
754         return;
755       } catch (IOException e) {
756         if (retries > 0) {
757           LOG.debug("Unable to create version file at " + rootdir.toString() + ", retrying", e);
758           fs.delete(versionFile, false);
759           try {
760             if (wait > 0) {
761               Thread.sleep(wait);
762             }
763           } catch (InterruptedException ie) {
764             throw (InterruptedIOException)new InterruptedIOException().initCause(ie);
765           }
766           retries--;
767         } else {
768           throw e;
769         }
770       }
771     }
772   }
773 
774   /**
775    * Checks that a cluster ID file exists in the HBase root directory
776    * @param fs the root directory FileSystem
777    * @param rootdir the HBase root directory in HDFS
778    * @param wait how long to wait between retries
779    * @return <code>true</code> if the file exists, otherwise <code>false</code>
780    * @throws IOException if checking the FileSystem fails
781    */
782   public static boolean checkClusterIdExists(FileSystem fs, Path rootdir,
783       int wait) throws IOException {
784     while (true) {
785       try {
786         Path filePath = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME);
787         return fs.exists(filePath);
788       } catch (IOException ioe) {
789         if (wait > 0) {
790           LOG.warn("Unable to check cluster ID file in " + rootdir.toString() +
791               ", retrying in "+wait+"msec: "+StringUtils.stringifyException(ioe));
792           try {
793             Thread.sleep(wait);
794           } catch (InterruptedException e) {
795             throw (InterruptedIOException)new InterruptedIOException().initCause(e);
796           }
797         } else {
798           throw ioe;
799         }
800       }
801     }
802   }
803 
804   /**
805    * Returns the value of the unique cluster ID stored for this HBase instance.
806    * @param fs the root directory FileSystem
807    * @param rootdir the path to the HBase root directory
808    * @return the unique cluster identifier
809    * @throws IOException if reading the cluster ID file fails
810    */
811   public static ClusterId getClusterId(FileSystem fs, Path rootdir)
812   throws IOException {
813     Path idPath = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME);
814     ClusterId clusterId = null;
815     FileStatus status = fs.exists(idPath)? fs.getFileStatus(idPath):  null;
816     if (status != null) {
817       int len = Ints.checkedCast(status.getLen());
818       byte [] content = new byte[len];
819       FSDataInputStream in = fs.open(idPath);
820       try {
821         in.readFully(content);
822       } catch (EOFException eof) {
823         LOG.warn("Cluster ID file " + idPath.toString() + " was empty");
824       } finally{
825         in.close();
826       }
827       try {
828         clusterId = ClusterId.parseFrom(content);
829       } catch (DeserializationException e) {
830         throw new IOException("content=" + Bytes.toString(content), e);
831       }
832       // If not pb'd, make it so.
833       if (!ProtobufUtil.isPBMagicPrefix(content)) {
834         String cid = null;
835         in = fs.open(idPath);
836         try {
837           cid = in.readUTF();
838           clusterId = new ClusterId(cid);
839         } catch (EOFException eof) {
840           LOG.warn("Cluster ID file " + idPath.toString() + " was empty");
841         } finally {
842           in.close();
843         }
844         rewriteAsPb(fs, rootdir, idPath, clusterId);
845       }
846       return clusterId;
847     } else {
848       LOG.warn("Cluster ID file does not exist at " + idPath.toString());
849     }
850     return clusterId;
851   }
852 
853   /**
854    * @param cid
855    * @throws IOException
856    */
857   private static void rewriteAsPb(final FileSystem fs, final Path rootdir, final Path p,
858       final ClusterId cid)
859   throws IOException {
860     // Rewrite the file as pb.  Move aside the old one first, write new
861     // then delete the moved-aside file.
862     Path movedAsideName = new Path(p + "." + System.currentTimeMillis());
863     if (!fs.rename(p, movedAsideName)) throw new IOException("Failed rename of " + p);
864     setClusterId(fs, rootdir, cid, 100);
865     if (!fs.delete(movedAsideName, false)) {
866       throw new IOException("Failed delete of " + movedAsideName);
867     }
868     LOG.debug("Rewrote the hbase.id file as pb");
869   }
870 
871   /**
872    * Writes a new unique identifier for this cluster to the "hbase.id" file
873    * in the HBase root directory
874    * @param fs the root directory FileSystem
875    * @param rootdir the path to the HBase root directory
876    * @param clusterId the unique identifier to store
877    * @param wait how long (in milliseconds) to wait between retries
878    * @throws IOException if writing to the FileSystem fails and no wait value
879    */
880   public static void setClusterId(FileSystem fs, Path rootdir, ClusterId clusterId,
881       int wait) throws IOException {
882     while (true) {
883       try {
884         Path idFile = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME);
885         Path tempIdFile = new Path(rootdir, HConstants.HBASE_TEMP_DIRECTORY +
886           Path.SEPARATOR + HConstants.CLUSTER_ID_FILE_NAME);
887         // Write the id file to a temporary location
888         FSDataOutputStream s = fs.create(tempIdFile);
889         try {
890           s.write(clusterId.toByteArray());
891           s.close();
892           s = null;
893           // Move the temporary file to its normal location. Throw an IOE if
894           // the rename failed
895           if (!fs.rename(tempIdFile, idFile)) {
896             throw new IOException("Unable to move temp version file to " + idFile);
897           }
898         } finally {
899           // Attempt to close the stream if still open on the way out
900           try {
901             if (s != null) s.close();
902           } catch (IOException ignore) { }
903         }
904         if (LOG.isDebugEnabled()) {
905           LOG.debug("Created cluster ID file at " + idFile.toString() + " with ID: " + clusterId);
906         }
907         return;
908       } catch (IOException ioe) {
909         if (wait > 0) {
910           LOG.warn("Unable to create cluster ID file in " + rootdir.toString() +
911               ", retrying in " + wait + "msec: " + StringUtils.stringifyException(ioe));
912           try {
913             Thread.sleep(wait);
914           } catch (InterruptedException e) {
915             throw (InterruptedIOException)new InterruptedIOException().initCause(e);
916           }
917         } else {
918           throw ioe;
919         }
920       }
921     }
922   }
923 
924   /**
925    * Verifies root directory path is a valid URI with a scheme
926    *
927    * @param root root directory path
928    * @return Passed <code>root</code> argument.
929    * @throws IOException if not a valid URI with a scheme
930    */
931   public static Path validateRootPath(Path root) throws IOException {
932     try {
933       URI rootURI = new URI(root.toString());
934       String scheme = rootURI.getScheme();
935       if (scheme == null) {
936         throw new IOException("Root directory does not have a scheme");
937       }
938       return root;
939     } catch (URISyntaxException e) {
940       IOException io = new IOException("Root directory path is not a valid " +
941         "URI -- check your " + HBASE_DIR + " configuration");
942       io.initCause(e);
943       throw io;
944     }
945   }
946 
947   /**
948    * Checks for the presence of the WAL log root path (using the provided conf object) in the given path. If
949    * it exists, this method removes it and returns the String representation of remaining relative path.
950    * @param path
951    * @param conf
952    * @return String representation of the remaining relative path
953    * @throws IOException
954    */
955   public static String removeWALRootPath(Path path, final Configuration conf) throws IOException {
956     Path root = getWALRootDir(conf);
957     String pathStr = path.toString();
958     // check that the path is absolute... it has the root path in it.
959     if (!pathStr.startsWith(root.toString())) return pathStr;
960     // if not, return as it is.
961     return pathStr.substring(root.toString().length() + 1);// remove the "/" too.
962   }
963 
964   /**
965    * If DFS, check safe mode and if so, wait until we clear it.
966    * @param conf configuration
967    * @param wait Sleep between retries
968    * @throws IOException e
969    */
970   public static void waitOnSafeMode(final Configuration conf,
971     final long wait)
972   throws IOException {
973     FileSystem fs = FileSystem.get(conf);
974     if (!(fs instanceof DistributedFileSystem)) return;
975     DistributedFileSystem dfs = (DistributedFileSystem)fs;
976     // Make sure dfs is not in safe mode
977     while (isInSafeMode(dfs)) {
978       LOG.info("Waiting for dfs to exit safe mode...");
979       try {
980         Thread.sleep(wait);
981       } catch (InterruptedException e) {
982         throw (InterruptedIOException)new InterruptedIOException().initCause(e);
983       }
984     }
985   }
986 
987   /**
988    * Return the 'path' component of a Path.  In Hadoop, Path is an URI.  This
989    * method returns the 'path' component of a Path's URI: e.g. If a Path is
990    * <code>hdfs://example.org:9000/hbase_trunk/TestTable/compaction.dir</code>,
991    * this method returns <code>/hbase_trunk/TestTable/compaction.dir</code>.
992    * This method is useful if you want to print out a Path without qualifying
993    * Filesystem instance.
994    * @param p Filesystem Path whose 'path' component we are to return.
995    * @return Path portion of the Filesystem
996    */
997   public static String getPath(Path p) {
998     return p.toUri().getPath();
999   }
1000 
1001   /**
1002    * @param c configuration
1003    * @return {@link Path} to hbase root directory: i.e. {@value org.apache.hadoop.hbase.HConstants#HBASE_DIR} from
1004    * configuration as a qualified Path.
1005    * @throws IOException e
1006    */
1007   public static Path getRootDir(final Configuration c) throws IOException {
1008     Path p = new Path(c.get(HBASE_DIR));
1009     FileSystem fs = p.getFileSystem(c);
1010     return p.makeQualified(fs);
1011   }
1012 
1013   public static void setRootDir(final Configuration c, final Path root) throws IOException {
1014     c.set(HBASE_DIR, root.toString());
1015   }
1016 
1017   public static void setFsDefault(final Configuration c, final Path root) throws IOException {
1018     c.set("fs.defaultFS", root.toString());    // for hadoop 0.21+
1019   }
1020 
1021   public static FileSystem getRootDirFileSystem(final Configuration c) throws IOException {
1022     Path p = getRootDir(c);
1023     return p.getFileSystem(c);
1024   }
1025 
1026   /**
1027    * @param c configuration
1028    * @return {@link Path} to hbase log root directory: i.e. {@value org.apache.hadoop.hbase.fs.HFileSystem#HBASE_WAL_DIR} from
1029    * configuration as a qualified Path. Defaults to {@value org.apache.hadoop.hbase.HConstants#HBASE_DIR}
1030    * @throws IOException e
1031    */
1032   public static Path getWALRootDir(final Configuration c) throws IOException {
1033     Path p = new Path(c.get(HFileSystem.HBASE_WAL_DIR, c.get(HBASE_DIR)));
1034     if (!isValidWALRootDir(p, c)) {
1035       return FSUtils.getRootDir(c);
1036     }
1037     FileSystem fs = p.getFileSystem(c);
1038     return p.makeQualified(fs);
1039   }
1040 
1041   @VisibleForTesting
1042   public static void setWALRootDir(final Configuration c, final Path root) throws IOException {
1043     c.set(HFileSystem.HBASE_WAL_DIR, root.toString());
1044   }
1045 
1046   public static FileSystem getWALFileSystem(final Configuration c) throws IOException {
1047     Path p = getWALRootDir(c);
1048     return p.getFileSystem(c);
1049   }
1050 
1051   private static boolean isValidWALRootDir(Path walDir, final Configuration c) throws IOException {
1052     Path rootDir = FSUtils.getRootDir(c);
1053     if (walDir != rootDir) {
1054       if (walDir.toString().startsWith(rootDir.toString() + "/")) {
1055         throw new IllegalStateException("Illegal WAL directory specified. " +
1056             "WAL directories are not permitted to be under the root directory if set.");
1057       }
1058     }
1059     return true;
1060   }
1061 
1062   /**
1063    * Returns the WAL region directory based on the region info
1064    * @param conf configuration to determine WALRootDir
1065    * @param regionInfo used to get region and table
1066    * @return the region directory used to store WALs under the WALRootDir
1067    * @throws IOException if there is an exception determining the WALRootDir
1068    */
1069   public static Path getWALRegionDir(final Configuration conf,
1070       final HRegionInfo regionInfo)
1071       throws IOException {
1072     return new Path(getWALTableDir(conf, regionInfo.getTable()),
1073         regionInfo.getEncodedName());
1074   }
1075 
1076   /**
1077    * Checks if meta region exists
1078    *
1079    * @param fs file system
1080    * @param rootdir root directory of HBase installation
1081    * @return true if exists
1082    * @throws IOException e
1083    */
1084   @SuppressWarnings("deprecation")
1085   public static boolean metaRegionExists(FileSystem fs, Path rootdir)
1086   throws IOException {
1087     Path metaRegionDir =
1088       HRegion.getRegionDir(rootdir, HRegionInfo.FIRST_META_REGIONINFO);
1089     return fs.exists(metaRegionDir);
1090   }
1091 
1092   /**
1093    * Compute HDFS blocks distribution of a given file, or a portion of the file
1094    * @param fs file system
1095    * @param status file status of the file
1096    * @param start start position of the portion
1097    * @param length length of the portion
1098    * @return The HDFS blocks distribution
1099    */
1100   static public HDFSBlocksDistribution computeHDFSBlocksDistribution(
1101     final FileSystem fs, FileStatus status, long start, long length)
1102     throws IOException {
1103     HDFSBlocksDistribution blocksDistribution = new HDFSBlocksDistribution();
1104     BlockLocation [] blockLocations =
1105       fs.getFileBlockLocations(status, start, length);
1106     for(BlockLocation bl : blockLocations) {
1107       String [] hosts = bl.getHosts();
1108       long len = bl.getLength();
1109       blocksDistribution.addHostsAndBlockWeight(hosts, len);
1110     }
1111 
1112     return blocksDistribution;
1113   }
1114 
1115   // TODO move this method OUT of FSUtils. No dependencies to HMaster
1116   /**
1117    * Returns the total overall fragmentation percentage. Includes hbase:meta and
1118    * -ROOT- as well.
1119    *
1120    * @param master  The master defining the HBase root and file system.
1121    * @return A map for each table and its percentage.
1122    * @throws IOException When scanning the directory fails.
1123    */
1124   public static int getTotalTableFragmentation(final HMaster master)
1125   throws IOException {
1126     Map<String, Integer> map = getTableFragmentation(master);
1127     return map != null && map.size() > 0 ? map.get("-TOTAL-") : -1;
1128   }
1129 
1130   /**
1131    * Runs through the HBase rootdir and checks how many stores for each table
1132    * have more than one file in them. Checks -ROOT- and hbase:meta too. The total
1133    * percentage across all tables is stored under the special key "-TOTAL-".
1134    *
1135    * @param master  The master defining the HBase root and file system.
1136    * @return A map for each table and its percentage.
1137    *
1138    * @throws IOException When scanning the directory fails.
1139    */
1140   public static Map<String, Integer> getTableFragmentation(
1141     final HMaster master)
1142   throws IOException {
1143     Path path = getRootDir(master.getConfiguration());
1144     // since HMaster.getFileSystem() is package private
1145     FileSystem fs = path.getFileSystem(master.getConfiguration());
1146     return getTableFragmentation(fs, path);
1147   }
1148 
1149   /**
1150    * Runs through the HBase rootdir and checks how many stores for each table
1151    * have more than one file in them. Checks -ROOT- and hbase:meta too. The total
1152    * percentage across all tables is stored under the special key "-TOTAL-".
1153    *
1154    * @param fs  The file system to use.
1155    * @param hbaseRootDir  The root directory to scan.
1156    * @return A map for each table and its percentage.
1157    * @throws IOException When scanning the directory fails.
1158    */
1159   public static Map<String, Integer> getTableFragmentation(
1160     final FileSystem fs, final Path hbaseRootDir)
1161   throws IOException {
1162     Map<String, Integer> frags = new HashMap<String, Integer>();
1163     int cfCountTotal = 0;
1164     int cfFragTotal = 0;
1165     PathFilter regionFilter = new RegionDirFilter(fs);
1166     PathFilter familyFilter = new FamilyDirFilter(fs);
1167     List<Path> tableDirs = getTableDirs(fs, hbaseRootDir);
1168     for (Path d : tableDirs) {
1169       int cfCount = 0;
1170       int cfFrag = 0;
1171       FileStatus[] regionDirs = fs.listStatus(d, regionFilter);
1172       for (FileStatus regionDir : regionDirs) {
1173         Path dd = regionDir.getPath();
1174         // else its a region name, now look in region for families
1175         FileStatus[] familyDirs = fs.listStatus(dd, familyFilter);
1176         for (FileStatus familyDir : familyDirs) {
1177           cfCount++;
1178           cfCountTotal++;
1179           Path family = familyDir.getPath();
1180           // now in family make sure only one file
1181           FileStatus[] familyStatus = fs.listStatus(family);
1182           if (familyStatus.length > 1) {
1183             cfFrag++;
1184             cfFragTotal++;
1185           }
1186         }
1187       }
1188       // compute percentage per table and store in result list
1189       frags.put(FSUtils.getTableName(d).getNameAsString(),
1190         cfCount == 0? 0: Math.round((float) cfFrag / cfCount * 100));
1191     }
1192     // set overall percentage for all tables
1193     frags.put("-TOTAL-",
1194       cfCountTotal == 0? 0: Math.round((float) cfFragTotal / cfCountTotal * 100));
1195     return frags;
1196   }
1197 
1198   /**
1199    * Returns the {@link org.apache.hadoop.fs.Path} object representing the table directory under
1200    * path rootdir
1201    *
1202    * @param rootdir qualified path of HBase root directory
1203    * @param tableName name of table
1204    * @return {@link org.apache.hadoop.fs.Path} for table
1205    */
1206   public static Path getTableDir(Path rootdir, final TableName tableName) {
1207     return new Path(getNamespaceDir(rootdir, tableName.getNamespaceAsString()),
1208         tableName.getQualifierAsString());
1209   }
1210 
1211   /**
1212    * Returns the Table directory under the WALRootDir for the specified table name
1213    * @param conf configuration used to get the WALRootDir
1214    * @param tableName Table to get the directory for
1215    * @return a path to the WAL table directory for the specified table
1216    * @throws IOException if there is an exception determining the WALRootDir
1217    */
1218   public static Path getWALTableDir(final Configuration conf, final TableName tableName)
1219       throws IOException {
1220     return new Path(new Path(getWALRootDir(conf), tableName.getNamespaceAsString()),
1221         tableName.getQualifierAsString());
1222   }
1223 
1224   /**
1225    * Returns the {@link org.apache.hadoop.hbase.TableName} object representing
1226    * the table directory under
1227    * path rootdir
1228    *
1229    * @param tablePath path of table
1230    * @return {@link org.apache.hadoop.fs.Path} for table
1231    */
1232   public static TableName getTableName(Path tablePath) {
1233     return TableName.valueOf(tablePath.getParent().getName(), tablePath.getName());
1234   }
1235 
1236   /**
1237    * Returns the {@link org.apache.hadoop.fs.Path} object representing
1238    * the namespace directory under path rootdir
1239    *
1240    * @param rootdir qualified path of HBase root directory
1241    * @param namespace namespace name
1242    * @return {@link org.apache.hadoop.fs.Path} for table
1243    */
1244   public static Path getNamespaceDir(Path rootdir, final String namespace) {
1245     return new Path(rootdir, new Path(HConstants.BASE_NAMESPACE_DIR,
1246         new Path(namespace)));
1247   }
1248 
1249   /**
1250    * A {@link PathFilter} that returns only regular files.
1251    */
1252   static class FileFilter implements PathFilter {
1253     private final FileSystem fs;
1254 
1255     public FileFilter(final FileSystem fs) {
1256       this.fs = fs;
1257     }
1258 
1259     @Override
1260     public boolean accept(Path p) {
1261       try {
1262         return fs.isFile(p);
1263       } catch (IOException e) {
1264         LOG.debug("unable to verify if path=" + p + " is a regular file", e);
1265         return false;
1266       }
1267     }
1268   }
1269 
1270   /**
1271    * Directory filter that doesn't include any of the directories in the specified blacklist
1272    */
1273   public static class BlackListDirFilter implements PathFilter {
1274     private final FileSystem fs;
1275     private List<String> blacklist;
1276 
1277     /**
1278      * Create a filter on the givem filesystem with the specified blacklist
1279      * @param fs filesystem to filter
1280      * @param directoryNameBlackList list of the names of the directories to filter. If
1281      *          <tt>null</tt>, all directories are returned
1282      */
1283     @SuppressWarnings("unchecked")
1284     public BlackListDirFilter(final FileSystem fs, final List<String> directoryNameBlackList) {
1285       this.fs = fs;
1286       blacklist =
1287         (List<String>) (directoryNameBlackList == null ? Collections.emptyList()
1288           : directoryNameBlackList);
1289     }
1290 
1291     @Override
1292     public boolean accept(Path p) {
1293       boolean isValid = false;
1294       try {
1295         if (isValidName(p.getName())) {
1296           isValid = fs.getFileStatus(p).isDirectory();
1297         } else {
1298           isValid = false;
1299         }
1300       } catch (IOException e) {
1301         LOG.warn("An error occurred while verifying if [" + p.toString()
1302             + "] is a valid directory. Returning 'not valid' and continuing.", e);
1303       }
1304       return isValid;
1305     }
1306 
1307     protected boolean isValidName(final String name) {
1308       return !blacklist.contains(name);
1309     }
1310   }
1311 
1312   /**
1313    * A {@link PathFilter} that only allows directories.
1314    */
1315   public static class DirFilter extends BlackListDirFilter {
1316 
1317     public DirFilter(FileSystem fs) {
1318       super(fs, null);
1319     }
1320   }
1321 
1322   /**
1323    * A {@link PathFilter} that returns usertable directories. To get all directories use the
1324    * {@link BlackListDirFilter} with a <tt>null</tt> blacklist
1325    */
1326   public static class UserTableDirFilter extends BlackListDirFilter {
1327     public UserTableDirFilter(FileSystem fs) {
1328       super(fs, HConstants.HBASE_NON_TABLE_DIRS);
1329     }
1330 
1331     protected boolean isValidName(final String name) {
1332       if (!super.isValidName(name))
1333         return false;
1334 
1335       try {
1336         TableName.isLegalTableQualifierName(Bytes.toBytes(name));
1337       } catch (IllegalArgumentException e) {
1338         LOG.info("INVALID NAME " + name);
1339         return false;
1340       }
1341       return true;
1342     }
1343   }
1344 
1345   /**
1346    * Heuristic to determine whether is safe or not to open a file for append
1347    * Looks both for dfs.support.append and use reflection to search
1348    * for SequenceFile.Writer.syncFs() or FSDataOutputStream.hflush()
1349    * @param conf
1350    * @return True if append support
1351    */
1352   public static boolean isAppendSupported(final Configuration conf) {
1353     boolean append = conf.getBoolean("dfs.support.append", false);
1354     if (append) {
1355       try {
1356         // TODO: The implementation that comes back when we do a createWriter
1357         // may not be using SequenceFile so the below is not a definitive test.
1358         // Will do for now (hdfs-200).
1359         SequenceFile.Writer.class.getMethod("syncFs", new Class<?> []{});
1360         append = true;
1361       } catch (SecurityException e) {
1362       } catch (NoSuchMethodException e) {
1363         append = false;
1364       }
1365     }
1366     if (!append) {
1367       // Look for the 0.21, 0.22, new-style append evidence.
1368       try {
1369         FSDataOutputStream.class.getMethod("hflush", new Class<?> []{});
1370         append = true;
1371       } catch (NoSuchMethodException e) {
1372         append = false;
1373       }
1374     }
1375     return append;
1376   }
1377 
1378   /**
1379    * @param conf
1380    * @return True if this filesystem whose scheme is 'hdfs'.
1381    * @throws IOException
1382    */
1383   public static boolean isHDFS(final Configuration conf) throws IOException {
1384     FileSystem fs = FileSystem.get(conf);
1385     String scheme = fs.getUri().getScheme();
1386     return scheme.equalsIgnoreCase("hdfs");
1387   }
1388 
1389   /**
1390    * Recover file lease. Used when a file might be suspect
1391    * to be had been left open by another process.
1392    * @param fs FileSystem handle
1393    * @param p Path of file to recover lease
1394    * @param conf Configuration handle
1395    * @throws IOException
1396    */
1397   public abstract void recoverFileLease(final FileSystem fs, final Path p,
1398       Configuration conf, CancelableProgressable reporter) throws IOException;
1399 
1400   public static List<Path> getTableDirs(final FileSystem fs, final Path rootdir)
1401       throws IOException {
1402     List<Path> tableDirs = new LinkedList<Path>();
1403 
1404     for(FileStatus status :
1405         fs.globStatus(new Path(rootdir,
1406             new Path(HConstants.BASE_NAMESPACE_DIR, "*")))) {
1407       tableDirs.addAll(FSUtils.getLocalTableDirs(fs, status.getPath()));
1408     }
1409     return tableDirs;
1410   }
1411 
1412   /**
1413    * @param fs
1414    * @param rootdir
1415    * @return All the table directories under <code>rootdir</code>. Ignore non table hbase folders such as
1416    * .logs, .oldlogs, .corrupt folders.
1417    * @throws IOException
1418    */
1419   public static List<Path> getLocalTableDirs(final FileSystem fs, final Path rootdir)
1420       throws IOException {
1421     // presumes any directory under hbase.rootdir is a table
1422     FileStatus[] dirs = fs.listStatus(rootdir, new UserTableDirFilter(fs));
1423     List<Path> tabledirs = new ArrayList<Path>(dirs.length);
1424     for (FileStatus dir: dirs) {
1425       tabledirs.add(dir.getPath());
1426     }
1427     return tabledirs;
1428   }
1429 
1430   /**
1431    * Checks if the given path is the one with 'recovered.edits' dir.
1432    * @param path
1433    * @return True if we recovered edits
1434    */
1435   public static boolean isRecoveredEdits(Path path) {
1436     return path.toString().contains(HConstants.RECOVERED_EDITS_DIR);
1437   }
1438 
1439   /**
1440    * Filter for all dirs that don't start with '.'
1441    */
1442   public static class RegionDirFilter implements PathFilter {
1443     // This pattern will accept 0.90+ style hex region dirs and older numeric region dir names.
1444     final public static Pattern regionDirPattern = Pattern.compile("^[0-9a-f]*$");
1445     final FileSystem fs;
1446 
1447     public RegionDirFilter(FileSystem fs) {
1448       this.fs = fs;
1449     }
1450 
1451     @Override
1452     public boolean accept(Path rd) {
1453       if (!regionDirPattern.matcher(rd.getName()).matches()) {
1454         return false;
1455       }
1456 
1457       try {
1458         return fs.getFileStatus(rd).isDirectory();
1459       } catch (IOException ioe) {
1460         // Maybe the file was moved or the fs was disconnected.
1461         LOG.warn("Skipping file " + rd +" due to IOException", ioe);
1462         return false;
1463       }
1464     }
1465   }
1466 
1467   /**
1468    * Given a particular table dir, return all the regiondirs inside it, excluding files such as
1469    * .tableinfo
1470    * @param fs A file system for the Path
1471    * @param tableDir Path to a specific table directory &lt;hbase.rootdir&gt;/&lt;tabledir&gt;
1472    * @return List of paths to valid region directories in table dir.
1473    * @throws IOException
1474    */
1475   public static List<Path> getRegionDirs(final FileSystem fs, final Path tableDir) throws IOException {
1476     // assumes we are in a table dir.
1477     FileStatus[] rds = fs.listStatus(tableDir, new RegionDirFilter(fs));
1478     List<Path> regionDirs = new ArrayList<Path>(rds.length);
1479     for (FileStatus rdfs: rds) {
1480       Path rdPath = rdfs.getPath();
1481       regionDirs.add(rdPath);
1482     }
1483     return regionDirs;
1484   }
1485 
1486   /**
1487    * Filter for all dirs that are legal column family names.  This is generally used for colfam
1488    * dirs &lt;hbase.rootdir&gt;/&lt;tabledir&gt;/&lt;regiondir&gt;/&lt;colfamdir&gt;.
1489    */
1490   public static class FamilyDirFilter implements PathFilter {
1491     final FileSystem fs;
1492 
1493     public FamilyDirFilter(FileSystem fs) {
1494       this.fs = fs;
1495     }
1496 
1497     @Override
1498     public boolean accept(Path rd) {
1499       try {
1500         // throws IAE if invalid
1501         HColumnDescriptor.isLegalFamilyName(Bytes.toBytes(rd.getName()));
1502       } catch (IllegalArgumentException iae) {
1503         // path name is an invalid family name and thus is excluded.
1504         return false;
1505       }
1506 
1507       try {
1508         return fs.getFileStatus(rd).isDirectory();
1509       } catch (IOException ioe) {
1510         // Maybe the file was moved or the fs was disconnected.
1511         LOG.warn("Skipping file " + rd +" due to IOException", ioe);
1512         return false;
1513       }
1514     }
1515   }
1516 
1517   /**
1518    * Given a particular region dir, return all the familydirs inside it
1519    *
1520    * @param fs A file system for the Path
1521    * @param regionDir Path to a specific region directory
1522    * @return List of paths to valid family directories in region dir.
1523    * @throws IOException
1524    */
1525   public static List<Path> getFamilyDirs(final FileSystem fs, final Path regionDir) throws IOException {
1526     // assumes we are in a region dir.
1527     FileStatus[] fds = fs.listStatus(regionDir, new FamilyDirFilter(fs));
1528     List<Path> familyDirs = new ArrayList<Path>(fds.length);
1529     for (FileStatus fdfs: fds) {
1530       Path fdPath = fdfs.getPath();
1531       familyDirs.add(fdPath);
1532     }
1533     return familyDirs;
1534   }
1535 
1536   public static List<Path> getReferenceFilePaths(final FileSystem fs, final Path familyDir) throws IOException {
1537     FileStatus[] fds = fs.listStatus(familyDir, new ReferenceFileFilter(fs));
1538     List<Path> referenceFiles = new ArrayList<Path>(fds.length);
1539     for (FileStatus fdfs: fds) {
1540       Path fdPath = fdfs.getPath();
1541       referenceFiles.add(fdPath);
1542     }
1543     return referenceFiles;
1544   }
1545 
1546   /**
1547    * Filter for HFiles that excludes reference files.
1548    */
1549   public static class HFileFilter implements PathFilter {
1550     final FileSystem fs;
1551 
1552     public HFileFilter(FileSystem fs) {
1553       this.fs = fs;
1554     }
1555 
1556     @Override
1557     public boolean accept(Path rd) {
1558       try {
1559         // only files
1560         return !fs.getFileStatus(rd).isDirectory() && StoreFileInfo.isHFile(rd);
1561       } catch (IOException ioe) {
1562         // Maybe the file was moved or the fs was disconnected.
1563         LOG.warn("Skipping file " + rd +" due to IOException", ioe);
1564         return false;
1565       }
1566     }
1567   }
1568 
1569   public static class ReferenceFileFilter implements PathFilter {
1570 
1571     private final FileSystem fs;
1572 
1573     public ReferenceFileFilter(FileSystem fs) {
1574       this.fs = fs;
1575     }
1576 
1577     @Override
1578     public boolean accept(Path rd) {
1579       try {
1580         // only files can be references.
1581         return !fs.getFileStatus(rd).isDirectory() && StoreFileInfo.isReference(rd);
1582       } catch (IOException ioe) {
1583         // Maybe the file was moved or the fs was disconnected.
1584         LOG.warn("Skipping file " + rd +" due to IOException", ioe);
1585         return false;
1586       }
1587     }
1588   }
1589 
1590 
1591   /**
1592    * @param conf
1593    * @return Returns the filesystem of the hbase rootdir.
1594    * @throws IOException
1595    */
1596   public static FileSystem getCurrentFileSystem(Configuration conf)
1597   throws IOException {
1598     return getRootDir(conf).getFileSystem(conf);
1599   }
1600 
1601 
1602   /**
1603    * Runs through the HBase rootdir/tablename and creates a reverse lookup map for
1604    * table StoreFile names to the full Path.
1605    * <br>
1606    * Example...<br>
1607    * Key = 3944417774205889744  <br>
1608    * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744
1609    *
1610    * @param map map to add values.  If null, this method will create and populate one to return
1611    * @param fs  The file system to use.
1612    * @param hbaseRootDir  The root directory to scan.
1613    * @param tableName name of the table to scan.
1614    * @return Map keyed by StoreFile name with a value of the full Path.
1615    * @throws IOException When scanning the directory fails.
1616    */
1617   public static Map<String, Path> getTableStoreFilePathMap(Map<String, Path> map,
1618   final FileSystem fs, final Path hbaseRootDir, TableName tableName)
1619   throws IOException {
1620     return getTableStoreFilePathMap(map, fs, hbaseRootDir, tableName, null);
1621   }
1622 
1623   /**
1624    * Runs through the HBase rootdir/tablename and creates a reverse lookup map for
1625    * table StoreFile names to the full Path.
1626    * <br>
1627    * Example...<br>
1628    * Key = 3944417774205889744  <br>
1629    * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744
1630    *
1631    * @param map map to add values.  If null, this method will create and populate one to return
1632    * @param fs  The file system to use.
1633    * @param hbaseRootDir  The root directory to scan.
1634    * @param tableName name of the table to scan.
1635    * @param errors ErrorReporter instance or null
1636    * @return Map keyed by StoreFile name with a value of the full Path.
1637    * @throws IOException When scanning the directory fails.
1638    */
1639   public static Map<String, Path> getTableStoreFilePathMap(Map<String, Path> map,
1640   final FileSystem fs, final Path hbaseRootDir, TableName tableName, ErrorReporter errors)
1641   throws IOException {
1642     if (map == null) {
1643       map = new HashMap<String, Path>();
1644     }
1645 
1646     // only include the directory paths to tables
1647     Path tableDir = FSUtils.getTableDir(hbaseRootDir, tableName);
1648     // Inside a table, there are compaction.dir directories to skip.  Otherwise, all else
1649     // should be regions.
1650     PathFilter familyFilter = new FamilyDirFilter(fs);
1651     FileStatus[] regionDirs = fs.listStatus(tableDir, new RegionDirFilter(fs));
1652     for (FileStatus regionDir : regionDirs) {
1653       if (null != errors) {
1654         errors.progress();
1655       }
1656       Path dd = regionDir.getPath();
1657       // else its a region name, now look in region for families
1658       FileStatus[] familyDirs = fs.listStatus(dd, familyFilter);
1659       for (FileStatus familyDir : familyDirs) {
1660         if (null != errors) {
1661           errors.progress();
1662         }
1663         Path family = familyDir.getPath();
1664         if (family.getName().equals(HConstants.RECOVERED_EDITS_DIR)) {
1665           continue;
1666         }
1667         // now in family, iterate over the StoreFiles and
1668         // put in map
1669         FileStatus[] familyStatus = fs.listStatus(family);
1670         for (FileStatus sfStatus : familyStatus) {
1671           if (null != errors) {
1672             errors.progress();
1673           }
1674           Path sf = sfStatus.getPath();
1675           map.put( sf.getName(), sf);
1676         }
1677       }
1678     }
1679     return map;
1680   }
1681 
1682   public static int getRegionReferenceFileCount(final FileSystem fs, final Path p) {
1683     int result = 0;
1684     try {
1685       for (Path familyDir:getFamilyDirs(fs, p)){
1686         result += getReferenceFilePaths(fs, familyDir).size();
1687       }
1688     } catch (IOException e) {
1689       LOG.warn("Error Counting reference files.", e);
1690     }
1691     return result;
1692   }
1693 
1694   /**
1695    * Runs through the HBase rootdir and creates a reverse lookup map for
1696    * table StoreFile names to the full Path.
1697    * <br>
1698    * Example...<br>
1699    * Key = 3944417774205889744  <br>
1700    * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744
1701    *
1702    * @param fs  The file system to use.
1703    * @param hbaseRootDir  The root directory to scan.
1704    * @return Map keyed by StoreFile name with a value of the full Path.
1705    * @throws IOException When scanning the directory fails.
1706    */
1707   public static Map<String, Path> getTableStoreFilePathMap(
1708     final FileSystem fs, final Path hbaseRootDir)
1709   throws IOException {
1710     return getTableStoreFilePathMap(fs, hbaseRootDir, null);
1711   }
1712 
1713   /**
1714    * Runs through the HBase rootdir and creates a reverse lookup map for
1715    * table StoreFile names to the full Path.
1716    * <br>
1717    * Example...<br>
1718    * Key = 3944417774205889744  <br>
1719    * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744
1720    *
1721    * @param fs  The file system to use.
1722    * @param hbaseRootDir  The root directory to scan.
1723    * @param errors ErrorReporter instance or null
1724    * @return Map keyed by StoreFile name with a value of the full Path.
1725    * @throws IOException When scanning the directory fails.
1726    */
1727   public static Map<String, Path> getTableStoreFilePathMap(
1728     final FileSystem fs, final Path hbaseRootDir, ErrorReporter errors)
1729   throws IOException {
1730     Map<String, Path> map = new HashMap<String, Path>();
1731 
1732     // if this method looks similar to 'getTableFragmentation' that is because
1733     // it was borrowed from it.
1734 
1735     // only include the directory paths to tables
1736     for (Path tableDir : FSUtils.getTableDirs(fs, hbaseRootDir)) {
1737       getTableStoreFilePathMap(map, fs, hbaseRootDir,
1738           FSUtils.getTableName(tableDir), errors);
1739     }
1740     return map;
1741   }
1742 
1743   /**
1744    * Calls fs.listStatus() and treats FileNotFoundException as non-fatal
1745    * This accommodates differences between hadoop versions, where hadoop 1
1746    * does not throw a FileNotFoundException, and return an empty FileStatus[]
1747    * while Hadoop 2 will throw FileNotFoundException.
1748    *
1749    * @param fs file system
1750    * @param dir directory
1751    * @param filter path filter
1752    * @return null if dir is empty or doesn't exist, otherwise FileStatus array
1753    */
1754   public static FileStatus [] listStatus(final FileSystem fs,
1755       final Path dir, final PathFilter filter) throws IOException {
1756     FileStatus [] status = null;
1757     try {
1758       status = filter == null ? fs.listStatus(dir) : fs.listStatus(dir, filter);
1759     } catch (FileNotFoundException fnfe) {
1760       // if directory doesn't exist, return null
1761       if (LOG.isTraceEnabled()) {
1762         LOG.trace(dir + " doesn't exist");
1763       }
1764     }
1765     if (status == null || status.length < 1) return null;
1766     return status;
1767   }
1768 
1769   /**
1770    * Calls fs.listStatus() and treats FileNotFoundException as non-fatal
1771    * This would accommodates differences between hadoop versions
1772    *
1773    * @param fs file system
1774    * @param dir directory
1775    * @return null if dir is empty or doesn't exist, otherwise FileStatus array
1776    */
1777   public static FileStatus[] listStatus(final FileSystem fs, final Path dir) throws IOException {
1778     return listStatus(fs, dir, null);
1779   }
1780 
1781   /**
1782    * Calls fs.delete() and returns the value returned by the fs.delete()
1783    *
1784    * @param fs
1785    * @param path
1786    * @param recursive
1787    * @return the value returned by the fs.delete()
1788    * @throws IOException
1789    */
1790   public static boolean delete(final FileSystem fs, final Path path, final boolean recursive)
1791       throws IOException {
1792     return fs.delete(path, recursive);
1793   }
1794 
1795   /**
1796    * Calls fs.exists(). Checks if the specified path exists
1797    *
1798    * @param fs
1799    * @param path
1800    * @return the value returned by fs.exists()
1801    * @throws IOException
1802    */
1803   public static boolean isExists(final FileSystem fs, final Path path) throws IOException {
1804     return fs.exists(path);
1805   }
1806 
1807   /**
1808    * Filters FileStatuses in an array and returns a list
1809    *
1810    * @param input   An array of FileStatuses
1811    * @param filter  A required filter to filter the array
1812    * @return        A list of FileStatuses
1813    */
1814   public static List<FileStatus> filterFileStatuses(FileStatus[] input,
1815       FileStatusFilter filter) {
1816     if (input == null) return null;
1817     return filterFileStatuses(Iterators.forArray(input), filter);
1818   }
1819 
1820   /**
1821    * Filters FileStatuses in an iterator and returns a list
1822    *
1823    * @param input   An iterator of FileStatuses
1824    * @param filter  A required filter to filter the array
1825    * @return        A list of FileStatuses
1826    */
1827   public static List<FileStatus> filterFileStatuses(Iterator<FileStatus> input,
1828       FileStatusFilter filter) {
1829     if (input == null) return null;
1830     ArrayList<FileStatus> results = new ArrayList<>();
1831     while (input.hasNext()) {
1832       FileStatus f = input.next();
1833       if (filter.accept(f)) {
1834         results.add(f);
1835       }
1836     }
1837     return results;
1838   }
1839 
1840   /**
1841    * Calls fs.listStatus() and treats FileNotFoundException as non-fatal
1842    * This accommodates differences between hadoop versions, where hadoop 1
1843    * does not throw a FileNotFoundException, and return an empty FileStatus[]
1844    * while Hadoop 2 will throw FileNotFoundException.
1845    *
1846    * @param fs file system
1847    * @param dir directory
1848    * @param filter file status filter
1849    * @return null if dir is empty or doesn't exist, otherwise FileStatus list
1850    */
1851   public static List<FileStatus> listStatusWithStatusFilter(final FileSystem fs,
1852       final Path dir, final FileStatusFilter filter) throws IOException {
1853     FileStatus [] status = null;
1854     try {
1855       status = fs.listStatus(dir);
1856     } catch (FileNotFoundException fnfe) {
1857       // if directory doesn't exist, return null
1858       if (LOG.isTraceEnabled()) {
1859         LOG.trace(dir + " doesn't exist");
1860       }
1861     }
1862 
1863     if (status == null || status.length < 1)  {
1864       return null;
1865     }
1866 
1867     if (filter == null) {
1868       return Arrays.asList(status);
1869     } else {
1870       List<FileStatus> status2 = filterFileStatuses(status, filter);
1871       if (status2 == null || status2.isEmpty()) {
1872         return null;
1873       } else {
1874         return status2;
1875       }
1876     }
1877   }
1878 
1879   /**
1880    * Throw an exception if an action is not permitted by a user on a file.
1881    *
1882    * @param ugi
1883    *          the user
1884    * @param file
1885    *          the file
1886    * @param action
1887    *          the action
1888    */
1889   public static void checkAccess(UserGroupInformation ugi, FileStatus file,
1890       FsAction action) throws AccessDeniedException {
1891     if (ugi.getShortUserName().equals(file.getOwner())) {
1892       if (file.getPermission().getUserAction().implies(action)) {
1893         return;
1894       }
1895     } else if (contains(ugi.getGroupNames(), file.getGroup())) {
1896       if (file.getPermission().getGroupAction().implies(action)) {
1897         return;
1898       }
1899     } else if (file.getPermission().getOtherAction().implies(action)) {
1900       return;
1901     }
1902     throw new AccessDeniedException("Permission denied:" + " action=" + action
1903         + " path=" + file.getPath() + " user=" + ugi.getShortUserName());
1904   }
1905 
1906   private static boolean contains(String[] groups, String user) {
1907     for (String group : groups) {
1908       if (group.equals(user)) {
1909         return true;
1910       }
1911     }
1912     return false;
1913   }
1914 
1915   /**
1916    * Log the current state of the filesystem from a certain root directory
1917    * @param fs filesystem to investigate
1918    * @param root root file/directory to start logging from
1919    * @param LOG log to output information
1920    * @throws IOException if an unexpected exception occurs
1921    */
1922   public static void logFileSystemState(final FileSystem fs, final Path root, Log LOG)
1923       throws IOException {
1924     LOG.debug("Current file system:");
1925     logFSTree(LOG, fs, root, "|-");
1926   }
1927 
1928   /**
1929    * Recursive helper to log the state of the FS
1930    *
1931    * @see #logFileSystemState(FileSystem, Path, Log)
1932    */
1933   private static void logFSTree(Log LOG, final FileSystem fs, final Path root, String prefix)
1934       throws IOException {
1935     FileStatus[] files = FSUtils.listStatus(fs, root, null);
1936     if (files == null) return;
1937 
1938     for (FileStatus file : files) {
1939       if (file.isDirectory()) {
1940         LOG.debug(prefix + file.getPath().getName() + "/");
1941         logFSTree(LOG, fs, file.getPath(), prefix + "---");
1942       } else {
1943         LOG.debug(prefix + file.getPath().getName());
1944       }
1945     }
1946   }
1947 
1948   public static boolean renameAndSetModifyTime(final FileSystem fs, final Path src, final Path dest)
1949       throws IOException {
1950     // set the modify time for TimeToLive Cleaner
1951     fs.setTimes(src, EnvironmentEdgeManager.currentTime(), -1);
1952     return fs.rename(src, dest);
1953   }
1954 
1955   /**
1956    * This function is to scan the root path of the file system to get the
1957    * degree of locality for each region on each of the servers having at least
1958    * one block of that region.
1959    * This is used by the tool {@link org.apache.hadoop.hbase.master.RegionPlacementMaintainer}
1960    *
1961    * @param conf
1962    *          the configuration to use
1963    * @return the mapping from region encoded name to a map of server names to
1964    *           locality fraction
1965    * @throws IOException
1966    *           in case of file system errors or interrupts
1967    */
1968   public static Map<String, Map<String, Float>> getRegionDegreeLocalityMappingFromFS(
1969       final Configuration conf) throws IOException {
1970     return getRegionDegreeLocalityMappingFromFS(
1971         conf, null,
1972         conf.getInt(THREAD_POOLSIZE, DEFAULT_THREAD_POOLSIZE));
1973 
1974   }
1975 
1976   /**
1977    * This function is to scan the root path of the file system to get the
1978    * degree of locality for each region on each of the servers having at least
1979    * one block of that region.
1980    *
1981    * @param conf
1982    *          the configuration to use
1983    * @param desiredTable
1984    *          the table you wish to scan locality for
1985    * @param threadPoolSize
1986    *          the thread pool size to use
1987    * @return the mapping from region encoded name to a map of server names to
1988    *           locality fraction
1989    * @throws IOException
1990    *           in case of file system errors or interrupts
1991    */
1992   public static Map<String, Map<String, Float>> getRegionDegreeLocalityMappingFromFS(
1993       final Configuration conf, final String desiredTable, int threadPoolSize)
1994       throws IOException {
1995     Map<String, Map<String, Float>> regionDegreeLocalityMapping =
1996         new ConcurrentHashMap<String, Map<String, Float>>();
1997     getRegionLocalityMappingFromFS(conf, desiredTable, threadPoolSize, null,
1998         regionDegreeLocalityMapping);
1999     return regionDegreeLocalityMapping;
2000   }
2001 
2002   /**
2003    * This function is to scan the root path of the file system to get either the
2004    * mapping between the region name and its best locality region server or the
2005    * degree of locality of each region on each of the servers having at least
2006    * one block of that region. The output map parameters are both optional.
2007    *
2008    * @param conf
2009    *          the configuration to use
2010    * @param desiredTable
2011    *          the table you wish to scan locality for
2012    * @param threadPoolSize
2013    *          the thread pool size to use
2014    * @param regionToBestLocalityRSMapping
2015    *          the map into which to put the best locality mapping or null
2016    * @param regionDegreeLocalityMapping
2017    *          the map into which to put the locality degree mapping or null,
2018    *          must be a thread-safe implementation
2019    * @throws IOException
2020    *           in case of file system errors or interrupts
2021    */
2022   private static void getRegionLocalityMappingFromFS(
2023       final Configuration conf, final String desiredTable,
2024       int threadPoolSize,
2025       Map<String, String> regionToBestLocalityRSMapping,
2026       Map<String, Map<String, Float>> regionDegreeLocalityMapping)
2027       throws IOException {
2028     FileSystem fs =  FileSystem.get(conf);
2029     Path rootPath = FSUtils.getRootDir(conf);
2030     long startTime = EnvironmentEdgeManager.currentTime();
2031     Path queryPath;
2032     // The table files are in ${hbase.rootdir}/data/<namespace>/<table>/*
2033     if (null == desiredTable) {
2034       queryPath = new Path(new Path(rootPath, HConstants.BASE_NAMESPACE_DIR).toString() + "/*/*/*/");
2035     } else {
2036       queryPath = new Path(FSUtils.getTableDir(rootPath, TableName.valueOf(desiredTable)).toString() + "/*/");
2037     }
2038 
2039     // reject all paths that are not appropriate
2040     PathFilter pathFilter = new PathFilter() {
2041       @Override
2042       public boolean accept(Path path) {
2043         // this is the region name; it may get some noise data
2044         if (null == path) {
2045           return false;
2046         }
2047 
2048         // no parent?
2049         Path parent = path.getParent();
2050         if (null == parent) {
2051           return false;
2052         }
2053 
2054         String regionName = path.getName();
2055         if (null == regionName) {
2056           return false;
2057         }
2058 
2059         if (!regionName.toLowerCase().matches("[0-9a-f]+")) {
2060           return false;
2061         }
2062         return true;
2063       }
2064     };
2065 
2066     FileStatus[] statusList = fs.globStatus(queryPath, pathFilter);
2067 
2068     if (null == statusList) {
2069       return;
2070     } else {
2071       LOG.debug("Query Path: " + queryPath + " ; # list of files: " +
2072           statusList.length);
2073     }
2074 
2075     // lower the number of threads in case we have very few expected regions
2076     threadPoolSize = Math.min(threadPoolSize, statusList.length);
2077 
2078     // run in multiple threads
2079     ThreadPoolExecutor tpe = new ThreadPoolExecutor(threadPoolSize,
2080         threadPoolSize, 60, TimeUnit.SECONDS,
2081         new ArrayBlockingQueue<Runnable>(statusList.length));
2082     try {
2083       // ignore all file status items that are not of interest
2084       for (FileStatus regionStatus : statusList) {
2085         if (null == regionStatus) {
2086           continue;
2087         }
2088 
2089         if (!regionStatus.isDirectory()) {
2090           continue;
2091         }
2092 
2093         Path regionPath = regionStatus.getPath();
2094         if (null == regionPath) {
2095           continue;
2096         }
2097 
2098         tpe.execute(new FSRegionScanner(fs, regionPath,
2099             regionToBestLocalityRSMapping, regionDegreeLocalityMapping));
2100       }
2101     } finally {
2102       tpe.shutdown();
2103       int threadWakeFrequency = conf.getInt(HConstants.THREAD_WAKE_FREQUENCY,
2104           60 * 1000);
2105       try {
2106         // here we wait until TPE terminates, which is either naturally or by
2107         // exceptions in the execution of the threads
2108         while (!tpe.awaitTermination(threadWakeFrequency,
2109             TimeUnit.MILLISECONDS)) {
2110           // printing out rough estimate, so as to not introduce
2111           // AtomicInteger
2112           LOG.info("Locality checking is underway: { Scanned Regions : "
2113               + tpe.getCompletedTaskCount() + "/"
2114               + tpe.getTaskCount() + " }");
2115         }
2116       } catch (InterruptedException e) {
2117         throw (InterruptedIOException)new InterruptedIOException().initCause(e);
2118       }
2119     }
2120 
2121     long overhead = EnvironmentEdgeManager.currentTime() - startTime;
2122     String overheadMsg = "Scan DFS for locality info takes " + overhead + " ms";
2123 
2124     LOG.info(overheadMsg);
2125   }
2126 
2127   /**
2128    * Do our short circuit read setup.
2129    * Checks buffer size to use and whether to do checksumming in hbase or hdfs.
2130    * @param conf
2131    */
2132   public static void setupShortCircuitRead(final Configuration conf) {
2133     // Check that the user has not set the "dfs.client.read.shortcircuit.skip.checksum" property.
2134     boolean shortCircuitSkipChecksum =
2135       conf.getBoolean("dfs.client.read.shortcircuit.skip.checksum", false);
2136     boolean useHBaseChecksum = conf.getBoolean(HConstants.HBASE_CHECKSUM_VERIFICATION, true);
2137     if (shortCircuitSkipChecksum) {
2138       LOG.warn("Configuration \"dfs.client.read.shortcircuit.skip.checksum\" should not " +
2139         "be set to true." + (useHBaseChecksum ? " HBase checksum doesn't require " +
2140         "it, see https://issues.apache.org/jira/browse/HBASE-6868." : ""));
2141       assert !shortCircuitSkipChecksum; //this will fail if assertions are on
2142     }
2143     checkShortCircuitReadBufferSize(conf);
2144   }
2145 
2146   /**
2147    * Check if short circuit read buffer size is set and if not, set it to hbase value.
2148    * @param conf
2149    */
2150   public static void checkShortCircuitReadBufferSize(final Configuration conf) {
2151     final int defaultSize = HConstants.DEFAULT_BLOCKSIZE * 2;
2152     final int notSet = -1;
2153     // DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_BUFFER_SIZE_KEY is only defined in h2
2154     final String dfsKey = "dfs.client.read.shortcircuit.buffer.size";
2155     int size = conf.getInt(dfsKey, notSet);
2156     // If a size is set, return -- we will use it.
2157     if (size != notSet) return;
2158     // But short circuit buffer size is normally not set.  Put in place the hbase wanted size.
2159     int hbaseSize = conf.getInt("hbase." + dfsKey, defaultSize);
2160     conf.setIfUnset(dfsKey, Integer.toString(hbaseSize));
2161   }
2162 }