001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package org.apache.hadoop.hdfs.server.namenode;
020    
021    import java.io.File;
022    import java.io.FileInputStream;
023    import java.io.IOException;
024    import java.io.BufferedInputStream;
025    import java.io.EOFException;
026    import java.io.DataInputStream;
027    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
028    import org.apache.hadoop.hdfs.server.common.Storage;
029    import org.apache.hadoop.io.IOUtils;
030    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
031    
032    import com.google.common.annotations.VisibleForTesting;
033    
034    /**
035     * An implementation of the abstract class {@link EditLogInputStream}, which
036     * reads edits from a local file.
037     */
038    public class EditLogFileInputStream extends EditLogInputStream {
039      private final File file;
040      private final FileInputStream fStream;
041      final private long firstTxId;
042      final private long lastTxId;
043      private final int logVersion;
044      private final FSEditLogOp.Reader reader;
045      private final FSEditLogLoader.PositionTrackingInputStream tracker;
046      private final boolean isInProgress;
047      
048      /**
049       * Open an EditLogInputStream for the given file.
050       * The file is pretransactional, so has no txids
051       * @param name filename to open
052       * @throws LogHeaderCorruptException if the header is either missing or
053       *         appears to be corrupt/truncated
054       * @throws IOException if an actual IO error occurs while reading the
055       *         header
056       */
057      EditLogFileInputStream(File name)
058          throws LogHeaderCorruptException, IOException {
059        this(name, HdfsConstants.INVALID_TXID, HdfsConstants.INVALID_TXID, false);
060      }
061    
062      /**
063       * Open an EditLogInputStream for the given file.
064       * @param name filename to open
065       * @param firstTxId first transaction found in file
066       * @param lastTxId last transaction id found in file
067       * @throws LogHeaderCorruptException if the header is either missing or
068       *         appears to be corrupt/truncated
069       * @throws IOException if an actual IO error occurs while reading the
070       *         header
071       */
072      public EditLogFileInputStream(File name, long firstTxId, long lastTxId,
073          boolean isInProgress)
074          throws LogHeaderCorruptException, IOException {
075        file = name;
076        fStream = new FileInputStream(name);
077    
078        BufferedInputStream bin = new BufferedInputStream(fStream);
079        tracker = new FSEditLogLoader.PositionTrackingInputStream(bin);
080        DataInputStream in = new DataInputStream(tracker);
081    
082        try {
083          logVersion = readLogVersion(in);
084        } catch (EOFException eofe) {
085          throw new LogHeaderCorruptException("No header found in log");
086        }
087    
088        reader = new FSEditLogOp.Reader(in, logVersion);
089        this.firstTxId = firstTxId;
090        this.lastTxId = lastTxId;
091        this.isInProgress = isInProgress;
092      }
093    
094      @Override
095      public long getFirstTxId() throws IOException {
096        return firstTxId;
097      }
098      
099      @Override
100      public long getLastTxId() throws IOException {
101        return lastTxId;
102      }
103    
104      @Override
105      public String getName() {
106        return file.getPath();
107      }
108    
109      @Override
110      protected FSEditLogOp nextOp() throws IOException {
111        return reader.readOp(false);
112      }
113      
114      @Override
115      protected FSEditLogOp nextValidOp() {
116        try {
117          return reader.readOp(true);
118        } catch (IOException e) {
119          return null;
120        }
121      }
122    
123      @Override
124      public int getVersion() throws IOException {
125        return logVersion;
126      }
127    
128      @Override
129      public long getPosition() {
130        return tracker.getPos();
131      }
132    
133      @Override
134      public void close() throws IOException {
135        fStream.close();
136      }
137    
138      @Override
139      public long length() throws IOException {
140        // file size + size of both buffers
141        return file.length();
142      }
143      
144      @Override
145      public boolean isInProgress() {
146        return isInProgress;
147      }
148      
149      @Override
150      public String toString() {
151        return getName();
152      }
153    
154      static FSEditLogLoader.EditLogValidation validateEditLog(File file) throws IOException {
155        EditLogFileInputStream in;
156        try {
157          in = new EditLogFileInputStream(file);
158        } catch (LogHeaderCorruptException corrupt) {
159          // If the header is malformed or the wrong value, this indicates a corruption
160          FSImage.LOG.warn("Log at " + file + " has no valid header",
161              corrupt);
162          return new FSEditLogLoader.EditLogValidation(0,
163              HdfsConstants.INVALID_TXID, HdfsConstants.INVALID_TXID, true);
164        }
165        
166        try {
167          return FSEditLogLoader.validateEditLog(in);
168        } finally {
169          IOUtils.closeStream(in);
170        }
171      }
172    
173      /**
174       * Read the header of fsedit log
175       * @param in fsedit stream
176       * @return the edit log version number
177       * @throws IOException if error occurs
178       */
179      @VisibleForTesting
180      static int readLogVersion(DataInputStream in)
181          throws IOException, LogHeaderCorruptException {
182        int logVersion;
183        try {
184          logVersion = in.readInt();
185        } catch (EOFException eofe) {
186          throw new LogHeaderCorruptException(
187              "Reached EOF when reading log header");
188        }
189        if (logVersion < HdfsConstants.LAYOUT_VERSION || // future version
190            logVersion > Storage.LAST_UPGRADABLE_LAYOUT_VERSION) { // unsupported
191          throw new LogHeaderCorruptException(
192              "Unexpected version of the file system log file: "
193              + logVersion + ". Current version = "
194              + HdfsConstants.LAYOUT_VERSION + ".");
195        }
196        return logVersion;
197      }
198      
199      /**
200       * Exception indicating that the header of an edits log file is
201       * corrupted. This can be because the header is not present,
202       * or because the header data is invalid (eg claims to be
203       * over a newer version than the running NameNode)
204       */
205      static class LogHeaderCorruptException extends IOException {
206        private static final long serialVersionUID = 1L;
207    
208        private LogHeaderCorruptException(String msg) {
209          super(msg);
210        }
211      }
212    }