1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hdfs.server.datanode;
19 
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 
25 import org.apache.hadoop.classification.InterfaceAudience;
26 import org.apache.hadoop.hdfs.protocol.Block;
27 import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
28 import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi;
29 import org.apache.hadoop.hdfs.server.datanode.fsdataset.LengthInputStream;
30 
31 /** Provide utility methods for Datanode. */
32 @InterfaceAudience.Private
33 public class DatanodeUtil {
34   public static final String UNLINK_BLOCK_SUFFIX = ".unlinked";
35 
36   public static final String DISK_ERROR = "Possible disk error: ";
37 
38   private static final String SEP = System.getProperty("file.separator");
39 
40   /** Get the cause of an I/O exception if caused by a possible disk error
41    * @param ioe an I/O exception
42    * @return cause if the I/O exception is caused by a possible disk error;
43    *         null otherwise.
44    */
getCauseIfDiskError(IOException ioe)45   static IOException getCauseIfDiskError(IOException ioe) {
46     if (ioe.getMessage()!=null && ioe.getMessage().startsWith(DISK_ERROR)) {
47       return (IOException)ioe.getCause();
48     } else {
49       return null;
50     }
51   }
52 
53   /**
54    * Create a new file.
55    * @throws IOException
56    * if the file already exists or if the file cannot be created.
57    */
createTmpFile(Block b, File f)58   public static File createTmpFile(Block b, File f) throws IOException {
59     if (f.exists()) {
60       throw new IOException("Failed to create temporary file for " + b
61           + ".  File " + f + " should not be present, but is.");
62     }
63     // Create the zero-length temp file
64     final boolean fileCreated;
65     try {
66       fileCreated = f.createNewFile();
67     } catch (IOException ioe) {
68       throw new IOException(DISK_ERROR + "Failed to create " + f, ioe);
69     }
70     if (!fileCreated) {
71       throw new IOException("Failed to create temporary file for " + b
72           + ".  File " + f + " should be creatable, but is already present.");
73     }
74     return f;
75   }
76 
77   /**
78    * @return the meta name given the block name and generation stamp.
79    */
getMetaName(String blockName, long generationStamp)80   public static String getMetaName(String blockName, long generationStamp) {
81     return blockName + "_" + generationStamp + Block.METADATA_EXTENSION;
82   }
83 
84   /** @return the unlink file. */
getUnlinkTmpFile(File f)85   public static File getUnlinkTmpFile(File f) {
86     return new File(f.getParentFile(), f.getName()+UNLINK_BLOCK_SUFFIX);
87   }
88 
89   /**
90    * Checks whether there are any files anywhere in the directory tree rooted
91    * at dir (directories don't count as files). dir must exist
92    * @return true if there are no files
93    * @throws IOException if unable to list subdirectories
94    */
dirNoFilesRecursive(File dir)95   public static boolean dirNoFilesRecursive(File dir) throws IOException {
96     File[] contents = dir.listFiles();
97     if (contents == null) {
98       throw new IOException("Cannot list contents of " + dir);
99     }
100     for (File f : contents) {
101       if (!f.isDirectory() || (f.isDirectory() && !dirNoFilesRecursive(f))) {
102         return false;
103       }
104     }
105     return true;
106   }
107 
108   /**
109    * Get the directory where a finalized block with this ID should be stored.
110    * Do not attempt to create the directory.
111    * @param root the root directory where finalized blocks are stored
112    * @param blockId
113    * @return
114    */
idToBlockDir(File root, long blockId)115   public static File idToBlockDir(File root, long blockId) {
116     int d1 = (int)((blockId >> 16) & 0xff);
117     int d2 = (int)((blockId >> 8) & 0xff);
118     String path = DataStorage.BLOCK_SUBDIR_PREFIX + d1 + SEP +
119         DataStorage.BLOCK_SUBDIR_PREFIX + d2;
120     return new File(root, path);
121   }
122 
123   /**
124    * @return the FileInputStream for the meta data of the given block.
125    * @throws FileNotFoundException
126    *           if the file not found.
127    * @throws ClassCastException
128    *           if the underlying input stream is not a FileInputStream.
129    */
getMetaDataInputStream( ExtendedBlock b, FsDatasetSpi<?> data)130   public static FileInputStream getMetaDataInputStream(
131       ExtendedBlock b, FsDatasetSpi<?> data) throws IOException {
132     final LengthInputStream lin = data.getMetaDataInputStream(b);
133     if (lin == null) {
134       throw new FileNotFoundException("Meta file for " + b + " not found.");
135     }
136     return (FileInputStream)lin.getWrappedStream();
137   }
138 }
139