blob: 463d53f88fdfe656169d6d434a8f48b92226a37d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs.server.datanode;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.server.datanode.FSDataset.FSVolume;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.HardLink;
import org.apache.hadoop.io.IOUtils;
/**
* This class is used by the datanode to maintain the map from a block
* to its metadata.
*/
class DatanodeBlockInfo {
private FSVolume volume; // volume where the block belongs
private File file; // block file
private boolean detached; // copy-on-write done for block
DatanodeBlockInfo(FSVolume vol, File file) {
this.volume = vol;
this.file = file;
detached = false;
}
DatanodeBlockInfo(FSVolume vol) {
this.volume = vol;
this.file = null;
detached = false;
}
FSVolume getVolume() {
return volume;
}
File getFile() {
return file;
}
/**
* Is this block already detached?
*/
boolean isDetached() {
return detached;
}
/**
* Block has been successfully detached
*/
void setDetached() {
detached = true;
}
/**
* Copy specified file into a temporary file. Then rename the
* temporary file to the original name. This will cause any
* hardlinks to the original file to be removed. The temporary
* files are created in the detachDir. The temporary files will
* be recovered (especially on Windows) on datanode restart.
*/
private void detachFile(File file, Block b) throws IOException {
File tmpFile = volume.createDetachFile(b, file.getName());
try {
IOUtils.copyBytes(new FileInputStream(file),
new FileOutputStream(tmpFile),
16*1024, true);
if (file.length() != tmpFile.length()) {
throw new IOException("Copy of file " + file + " size " + file.length()+
" into file " + tmpFile +
" resulted in a size of " + tmpFile.length());
}
FileUtil.replaceFile(tmpFile, file);
} catch (IOException e) {
boolean done = tmpFile.delete();
if (!done) {
DataNode.LOG.info("detachFile failed to delete temporary file " +
tmpFile);
}
throw e;
}
}
/**
* Returns true if this block was copied, otherwise returns false.
*/
boolean detachBlock(Block block, int numLinks) throws IOException {
if (isDetached()) {
return false;
}
if (file == null || volume == null) {
throw new IOException("detachBlock:Block not found. " + block);
}
File meta = FSDataset.getMetaFile(file, block);
if (meta == null) {
throw new IOException("Meta file not found for block " + block);
}
if (HardLink.getLinkCount(file) > numLinks) {
DataNode.LOG.info("CopyOnWrite for block " + block);
detachFile(file, block);
}
if (HardLink.getLinkCount(meta) > numLinks) {
detachFile(meta, block);
}
setDetached();
return true;
}
public String toString() {
return getClass().getSimpleName() + "(volume=" + volume
+ ", file=" + file + ", detached=" + detached + ")";
}
}