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.common; 19 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.io.RandomAccessFile; 25 import java.nio.channels.FileLock; 26 import java.nio.channels.OverlappingFileLockException; 27 import java.util.ArrayList; 28 import java.util.List; 29 import java.util.Iterator; 30 import java.util.Properties; 31 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.apache.hadoop.hdfs.protocol.FSConstants; 35 import org.apache.hadoop.hdfs.server.common.HdfsConstants.NodeType; 36 import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption; 37 import org.apache.hadoop.fs.FileUtil; 38 39 /** 40 * Storage information file. 41 * <p> 42 * Local storage information is stored in a separate file VERSION. 43 * It contains type of the node, 44 * the storage layout version, the namespace id, and 45 * the fs state creation time. 46 * <p> 47 * Local storage can reside in multiple directories. 48 * Each directory should contain the same VERSION file as the others. 49 * During startup Hadoop servers (name-node and data-nodes) read their local 50 * storage information from them. 51 * <p> 52 * The servers hold a lock for each storage directory while they run so that 53 * other nodes were not able to startup sharing the same storage. 54 * The locks are released when the servers stop (normally or abnormally). 55 * 56 */ 57 public abstract class Storage extends StorageInfo { 58 public static final Log LOG = LogFactory.getLog(Storage.class.getName()); 59 60 // Constants 61 62 // last layout version that did not suppot upgrades 63 protected static final int LAST_PRE_UPGRADE_LAYOUT_VERSION = -3; 64 65 // this corresponds to Hadoop-0.14. 66 public static final int LAST_UPGRADABLE_LAYOUT_VERSION = -7; 67 protected static final String LAST_UPGRADABLE_HADOOP_VERSION = "Hadoop-0.14"; 68 69 /* this should be removed when LAST_UPGRADABLE_LV goes beyond -13. 70 * any upgrade code that uses this constant should also be removed. */ 71 public static final int PRE_GENERATIONSTAMP_LAYOUT_VERSION = -13; 72 73 /** Layout versions of 203 release */ 74 public static final int[] LAYOUT_VERSIONS_203 = {-19, -31}; 75 76 private static final String STORAGE_FILE_LOCK = "in_use.lock"; 77 protected static final String STORAGE_FILE_VERSION = "VERSION"; 78 public static final String STORAGE_DIR_CURRENT = "current"; 79 private static final String STORAGE_DIR_PREVIOUS = "previous"; 80 private static final String STORAGE_TMP_REMOVED = "removed.tmp"; 81 private static final String STORAGE_TMP_PREVIOUS = "previous.tmp"; 82 private static final String STORAGE_TMP_FINALIZED = "finalized.tmp"; 83 private static final String STORAGE_TMP_LAST_CKPT = "lastcheckpoint.tmp"; 84 private static final String STORAGE_PREVIOUS_CKPT = "previous.checkpoint"; 85 86 public enum StorageState { 87 NON_EXISTENT, 88 NOT_FORMATTED, 89 COMPLETE_UPGRADE, 90 RECOVER_UPGRADE, 91 COMPLETE_FINALIZE, 92 COMPLETE_ROLLBACK, 93 RECOVER_ROLLBACK, 94 COMPLETE_CHECKPOINT, 95 RECOVER_CHECKPOINT, 96 NORMAL; 97 } 98 99 /** 100 * An interface to denote storage directory type 101 * Implementations can define a type for storage directory by implementing 102 * this interface. 103 */ 104 public interface StorageDirType { getStorageDirType()105 public StorageDirType getStorageDirType(); isOfType(StorageDirType type)106 public boolean isOfType(StorageDirType type); 107 } 108 109 private NodeType storageType; // Type of the node using this storage 110 protected List<StorageDirectory> storageDirs = new ArrayList<StorageDirectory>(); 111 112 private class DirIterator implements Iterator<StorageDirectory> { 113 StorageDirType dirType; 114 int prevIndex; // for remove() 115 int nextIndex; // for next() 116 DirIterator(StorageDirType dirType)117 DirIterator(StorageDirType dirType) { 118 this.dirType = dirType; 119 this.nextIndex = 0; 120 this.prevIndex = 0; 121 } 122 hasNext()123 public boolean hasNext() { 124 if (storageDirs.isEmpty() || nextIndex >= storageDirs.size()) 125 return false; 126 if (dirType != null) { 127 while (nextIndex < storageDirs.size()) { 128 if (getStorageDir(nextIndex).getStorageDirType().isOfType(dirType)) 129 break; 130 nextIndex++; 131 } 132 if (nextIndex >= storageDirs.size()) 133 return false; 134 } 135 return true; 136 } 137 next()138 public StorageDirectory next() { 139 StorageDirectory sd = getStorageDir(nextIndex); 140 prevIndex = nextIndex; 141 nextIndex++; 142 if (dirType != null) { 143 while (nextIndex < storageDirs.size()) { 144 if (getStorageDir(nextIndex).getStorageDirType().isOfType(dirType)) 145 break; 146 nextIndex++; 147 } 148 } 149 return sd; 150 } 151 remove()152 public void remove() { 153 nextIndex = prevIndex; // restore previous state 154 storageDirs.remove(prevIndex); // remove last returned element 155 hasNext(); // reset nextIndex to correct place 156 } 157 } 158 159 /** 160 * Return default iterator 161 * This iterator returns all entires of storageDirs 162 */ dirIterator()163 public Iterator<StorageDirectory> dirIterator() { 164 return dirIterator(null); 165 } 166 167 /** 168 * Return iterator based on Storage Directory Type 169 * This iterator selects entires of storageDirs of type dirType and returns 170 * them via the Iterator 171 */ dirIterator(StorageDirType dirType)172 public Iterator<StorageDirectory> dirIterator(StorageDirType dirType) { 173 return new DirIterator(dirType); 174 } 175 176 /** 177 * One of the storage directories. 178 */ 179 public class StorageDirectory { 180 File root; // root directory 181 FileLock lock; // storage lock 182 StorageDirType dirType; // storage dir type 183 StorageDirectory(File dir)184 public StorageDirectory(File dir) { 185 // default dirType is null 186 this(dir, null); 187 } 188 StorageDirectory(File dir, StorageDirType dirType)189 public StorageDirectory(File dir, StorageDirType dirType) { 190 this.root = dir; 191 this.lock = null; 192 this.dirType = dirType; 193 } 194 195 /** 196 * Get root directory of this storage 197 */ getRoot()198 public File getRoot() { 199 return root; 200 } 201 202 /** 203 * Get storage directory type 204 */ getStorageDirType()205 public StorageDirType getStorageDirType() { 206 return dirType; 207 } 208 209 /** 210 * Read version file. 211 * 212 * @throws IOException if file cannot be read or contains inconsistent data 213 */ read()214 public void read() throws IOException { 215 read(getVersionFile()); 216 } 217 read(File from)218 public void read(File from) throws IOException { 219 RandomAccessFile file = new RandomAccessFile(from, "rws"); 220 FileInputStream in = null; 221 try { 222 in = new FileInputStream(file.getFD()); 223 file.seek(0); 224 Properties props = new Properties(); 225 props.load(in); 226 getFields(props, this); 227 } finally { 228 if (in != null) { 229 in.close(); 230 } 231 file.close(); 232 } 233 } 234 235 /** 236 * Write version file. 237 * 238 * @throws IOException 239 */ write()240 public void write() throws IOException { 241 corruptPreUpgradeStorage(root); 242 write(getVersionFile()); 243 } 244 write(File to)245 public void write(File to) throws IOException { 246 Properties props = new Properties(); 247 setFields(props, this); 248 RandomAccessFile file = new RandomAccessFile(to, "rws"); 249 FileOutputStream out = null; 250 try { 251 file.seek(0); 252 out = new FileOutputStream(file.getFD()); 253 /* 254 * If server is interrupted before this line, 255 * the version file will remain unchanged. 256 */ 257 props.store(out, null); 258 /* 259 * Now the new fields are flushed to the head of the file, but file 260 * length can still be larger then required and therefore the file can 261 * contain whole or corrupted fields from its old contents in the end. 262 * If server is interrupted here and restarted later these extra fields 263 * either should not effect server behavior or should be handled 264 * by the server correctly. 265 */ 266 file.setLength(out.getChannel().position()); 267 } finally { 268 if (out != null) { 269 out.close(); 270 } 271 file.close(); 272 } 273 } 274 275 /** 276 * Clear and re-create storage directory. 277 * <p> 278 * Removes contents of the current directory and creates an empty directory. 279 * 280 * This does not fully format storage directory. 281 * It cannot write the version file since it should be written last after 282 * all other storage type dependent files are written. 283 * Derived storage is responsible for setting specific storage values and 284 * writing the version file to disk. 285 * 286 * @throws IOException 287 */ clearDirectory()288 public void clearDirectory() throws IOException { 289 File curDir = this.getCurrentDir(); 290 if (curDir.exists()) 291 if (!(FileUtil.fullyDelete(curDir))) 292 throw new IOException("Cannot remove current directory: " + curDir); 293 if (!curDir.mkdirs()) 294 throw new IOException("Cannot create directory " + curDir); 295 } 296 297 /** 298 * Directory {@code current} contains latest files defining 299 * the file system meta-data. 300 * 301 * @return the directory path 302 */ getCurrentDir()303 public File getCurrentDir() { 304 return new File(root, STORAGE_DIR_CURRENT); 305 } 306 307 /** 308 * File {@code VERSION} contains the following fields: 309 * <ol> 310 * <li>node type</li> 311 * <li>layout version</li> 312 * <li>namespaceID</li> 313 * <li>fs state creation time</li> 314 * <li>other fields specific for this node type</li> 315 * </ol> 316 * The version file is always written last during storage directory updates. 317 * The existence of the version file indicates that all other files have 318 * been successfully written in the storage directory, the storage is valid 319 * and does not need to be recovered. 320 * 321 * @return the version file path 322 */ getVersionFile()323 public File getVersionFile() { 324 return new File(new File(root, STORAGE_DIR_CURRENT), STORAGE_FILE_VERSION); 325 } 326 327 /** 328 * File {@code VERSION} from the {@code previous} directory. 329 * 330 * @return the previous version file path 331 */ getPreviousVersionFile()332 public File getPreviousVersionFile() { 333 return new File(new File(root, STORAGE_DIR_PREVIOUS), STORAGE_FILE_VERSION); 334 } 335 336 /** 337 * Directory {@code previous} contains the previous file system state, 338 * which the system can be rolled back to. 339 * 340 * @return the directory path 341 */ getPreviousDir()342 public File getPreviousDir() { 343 return new File(root, STORAGE_DIR_PREVIOUS); 344 } 345 346 /** 347 * {@code previous.tmp} is a transient directory, which holds 348 * current file system state while the new state is saved into the new 349 * {@code current} during upgrade. 350 * If the saving succeeds {@code previous.tmp} will be moved to 351 * {@code previous}, otherwise it will be renamed back to 352 * {@code current} by the recovery procedure during startup. 353 * 354 * @return the directory path 355 */ getPreviousTmp()356 public File getPreviousTmp() { 357 return new File(root, STORAGE_TMP_PREVIOUS); 358 } 359 360 /** 361 * {@code removed.tmp} is a transient directory, which holds 362 * current file system state while the previous state is moved into 363 * {@code current} during rollback. 364 * If the moving succeeds {@code removed.tmp} will be removed, 365 * otherwise it will be renamed back to 366 * {@code current} by the recovery procedure during startup. 367 * 368 * @return the directory path 369 */ getRemovedTmp()370 public File getRemovedTmp() { 371 return new File(root, STORAGE_TMP_REMOVED); 372 } 373 374 /** 375 * {@code finalized.tmp} is a transient directory, which holds 376 * the {@code previous} file system state while it is being removed 377 * in response to the finalize request. 378 * Finalize operation will remove {@code finalized.tmp} when completed, 379 * otherwise the removal will resume upon the system startup. 380 * 381 * @return the directory path 382 */ getFinalizedTmp()383 public File getFinalizedTmp() { 384 return new File(root, STORAGE_TMP_FINALIZED); 385 } 386 387 /** 388 * {@code lastcheckpoint.tmp} is a transient directory, which holds 389 * current file system state while the new state is saved into the new 390 * {@code current} during regular namespace updates. 391 * If the saving succeeds {@code lastcheckpoint.tmp} will be moved to 392 * {@code previous.checkpoint}, otherwise it will be renamed back to 393 * {@code current} by the recovery procedure during startup. 394 * 395 * @return the directory path 396 */ getLastCheckpointTmp()397 public File getLastCheckpointTmp() { 398 return new File(root, STORAGE_TMP_LAST_CKPT); 399 } 400 401 /** 402 * {@code previous.checkpoint} is a directory, which holds the previous 403 * (before the last save) state of the storage directory. 404 * The directory is created as a reference only, it does not play role 405 * in state recovery procedures, and is recycled automatically, 406 * but it may be useful for manual recovery of a stale state of the system. 407 * 408 * @return the directory path 409 */ getPreviousCheckpoint()410 public File getPreviousCheckpoint() { 411 return new File(root, STORAGE_PREVIOUS_CKPT); 412 } 413 414 /** 415 * Check consistency of the storage directory 416 * 417 * @param startOpt a startup option. 418 * 419 * @return state {@link StorageState} of the storage directory 420 * @throws InconsistentFSStateException if directory state is not 421 * consistent and cannot be recovered. 422 * @throws IOException 423 */ analyzeStorage(StartupOption startOpt)424 public StorageState analyzeStorage(StartupOption startOpt) throws IOException { 425 assert root != null : "root is null"; 426 String rootPath = root.getCanonicalPath(); 427 try { // check that storage exists 428 if (!root.exists()) { 429 // storage directory does not exist 430 if (startOpt != StartupOption.FORMAT) { 431 LOG.info("Storage directory " + rootPath + " does not exist"); 432 return StorageState.NON_EXISTENT; 433 } 434 LOG.info(rootPath + " does not exist. Creating..."); 435 if (!root.mkdirs()) 436 throw new IOException("Cannot create directory " + rootPath); 437 } 438 // or is inaccessible 439 if (!root.isDirectory()) { 440 LOG.info(rootPath + "is not a directory"); 441 return StorageState.NON_EXISTENT; 442 } 443 if (!root.canWrite()) { 444 LOG.info("Cannot access storage directory " + rootPath); 445 return StorageState.NON_EXISTENT; 446 } 447 } catch(SecurityException ex) { 448 LOG.info("Cannot access storage directory " + rootPath, ex); 449 return StorageState.NON_EXISTENT; 450 } 451 452 this.lock(); // lock storage if it exists 453 454 if (startOpt == HdfsConstants.StartupOption.FORMAT) 455 return StorageState.NOT_FORMATTED; 456 if (startOpt != HdfsConstants.StartupOption.IMPORT) { 457 //make sure no conversion is required 458 checkConversionNeeded(this); 459 } 460 461 // check whether current directory is valid 462 File versionFile = getVersionFile(); 463 boolean hasCurrent = versionFile.exists(); 464 465 // check which directories exist 466 boolean hasPrevious = getPreviousDir().exists(); 467 boolean hasPreviousTmp = getPreviousTmp().exists(); 468 boolean hasRemovedTmp = getRemovedTmp().exists(); 469 boolean hasFinalizedTmp = getFinalizedTmp().exists(); 470 boolean hasCheckpointTmp = getLastCheckpointTmp().exists(); 471 472 if (!(hasPreviousTmp || hasRemovedTmp 473 || hasFinalizedTmp || hasCheckpointTmp)) { 474 // no temp dirs - no recovery 475 if (hasCurrent) 476 return StorageState.NORMAL; 477 if (hasPrevious) 478 throw new InconsistentFSStateException(root, 479 "version file in current directory is missing."); 480 return StorageState.NOT_FORMATTED; 481 } 482 483 if ((hasPreviousTmp?1:0) + (hasRemovedTmp?1:0) 484 + (hasFinalizedTmp?1:0) + (hasCheckpointTmp?1:0) > 1) 485 // more than one temp dirs 486 throw new InconsistentFSStateException(root, 487 "too many temporary directories."); 488 489 // # of temp dirs == 1 should either recover or complete a transition 490 if (hasCheckpointTmp) { 491 return hasCurrent ? StorageState.COMPLETE_CHECKPOINT 492 : StorageState.RECOVER_CHECKPOINT; 493 } 494 495 if (hasFinalizedTmp) { 496 if (hasPrevious) 497 throw new InconsistentFSStateException(root, 498 STORAGE_DIR_PREVIOUS + " and " + STORAGE_TMP_FINALIZED 499 + "cannot exist together."); 500 return StorageState.COMPLETE_FINALIZE; 501 } 502 503 if (hasPreviousTmp) { 504 if (hasPrevious) 505 throw new InconsistentFSStateException(root, 506 STORAGE_DIR_PREVIOUS + " and " + STORAGE_TMP_PREVIOUS 507 + " cannot exist together."); 508 if (hasCurrent) 509 return StorageState.COMPLETE_UPGRADE; 510 return StorageState.RECOVER_UPGRADE; 511 } 512 513 assert hasRemovedTmp : "hasRemovedTmp must be true"; 514 if (!(hasCurrent ^ hasPrevious)) 515 throw new InconsistentFSStateException(root, 516 "one and only one directory " + STORAGE_DIR_CURRENT 517 + " or " + STORAGE_DIR_PREVIOUS 518 + " must be present when " + STORAGE_TMP_REMOVED 519 + " exists."); 520 if (hasCurrent) 521 return StorageState.COMPLETE_ROLLBACK; 522 return StorageState.RECOVER_ROLLBACK; 523 } 524 525 /** 526 * Complete or recover storage state from previously failed transition. 527 * 528 * @param curState specifies what/how the state should be recovered 529 * @throws IOException 530 */ doRecover(StorageState curState)531 public void doRecover(StorageState curState) throws IOException { 532 File curDir = getCurrentDir(); 533 String rootPath = root.getCanonicalPath(); 534 switch(curState) { 535 case COMPLETE_UPGRADE: // mv previous.tmp -> previous 536 LOG.info("Completing previous upgrade for storage directory " 537 + rootPath); 538 rename(getPreviousTmp(), getPreviousDir()); 539 return; 540 case RECOVER_UPGRADE: // mv previous.tmp -> current 541 LOG.info("Recovering storage directory " + rootPath 542 + " from previous upgrade"); 543 if (curDir.exists()) 544 deleteDir(curDir); 545 rename(getPreviousTmp(), curDir); 546 return; 547 case COMPLETE_ROLLBACK: // rm removed.tmp 548 LOG.info("Completing previous rollback for storage directory " 549 + rootPath); 550 deleteDir(getRemovedTmp()); 551 return; 552 case RECOVER_ROLLBACK: // mv removed.tmp -> current 553 LOG.info("Recovering storage directory " + rootPath 554 + " from previous rollback"); 555 rename(getRemovedTmp(), curDir); 556 return; 557 case COMPLETE_FINALIZE: // rm finalized.tmp 558 LOG.info("Completing previous finalize for storage directory " 559 + rootPath); 560 deleteDir(getFinalizedTmp()); 561 return; 562 case COMPLETE_CHECKPOINT: // mv lastcheckpoint.tmp -> previous.checkpoint 563 LOG.info("Completing previous checkpoint for storage directory " 564 + rootPath); 565 File prevCkptDir = getPreviousCheckpoint(); 566 if (prevCkptDir.exists()) 567 deleteDir(prevCkptDir); 568 rename(getLastCheckpointTmp(), prevCkptDir); 569 return; 570 case RECOVER_CHECKPOINT: // mv lastcheckpoint.tmp -> current 571 LOG.info("Recovering storage directory " + rootPath 572 + " from failed checkpoint"); 573 if (curDir.exists()) 574 deleteDir(curDir); 575 rename(getLastCheckpointTmp(), curDir); 576 return; 577 default: 578 throw new IOException("Unexpected FS state: " + curState); 579 } 580 } 581 582 /** 583 * Lock storage to provide exclusive access. 584 * 585 * <p> Locking is not supported by all file systems. 586 * E.g., NFS does not consistently support exclusive locks. 587 * 588 * <p> If locking is supported we guarantee exculsive access to the 589 * storage directory. Otherwise, no guarantee is given. 590 * 591 * @throws IOException if locking fails 592 */ lock()593 public void lock() throws IOException { 594 this.lock = tryLock(); 595 if (lock == null) { 596 String msg = "Cannot lock storage " + this.root 597 + ". The directory is already locked."; 598 LOG.info(msg); 599 throw new IOException(msg); 600 } 601 } 602 603 /** 604 * Attempts to acquire an exclusive lock on the storage. 605 * 606 * @return A lock object representing the newly-acquired lock or 607 * <code>null</code> if storage is already locked. 608 * @throws IOException if locking fails. 609 */ tryLock()610 FileLock tryLock() throws IOException { 611 boolean deletionHookAdded = false; 612 File lockF = new File(root, STORAGE_FILE_LOCK); 613 if (!lockF.exists()) { 614 lockF.deleteOnExit(); 615 deletionHookAdded = true; 616 } 617 RandomAccessFile file = new RandomAccessFile(lockF, "rws"); 618 FileLock res = null; 619 try { 620 res = file.getChannel().tryLock(); 621 } catch(OverlappingFileLockException oe) { 622 file.close(); 623 return null; 624 } catch(IOException e) { 625 LOG.error("Cannot create lock on " + lockF, e); 626 file.close(); 627 throw e; 628 } 629 if (res != null && !deletionHookAdded) { 630 // If the file existed prior to our startup, we didn't 631 // call deleteOnExit above. But since we successfully locked 632 // the dir, we can take care of cleaning it up. 633 lockF.deleteOnExit(); 634 } 635 return res; 636 } 637 638 /** 639 * Unlock storage. 640 * 641 * @throws IOException 642 */ unlock()643 public void unlock() throws IOException { 644 if (this.lock == null) 645 return; 646 this.lock.release(); 647 lock.channel().close(); 648 lock = null; 649 } 650 } 651 652 /** 653 * Create empty storage info of the specified type 654 */ Storage(NodeType type)655 protected Storage(NodeType type) { 656 super(); 657 this.storageType = type; 658 } 659 Storage(NodeType type, int nsID, long cT)660 protected Storage(NodeType type, int nsID, long cT) { 661 super(FSConstants.LAYOUT_VERSION, nsID, cT); 662 this.storageType = type; 663 } 664 Storage(NodeType type, StorageInfo storageInfo)665 protected Storage(NodeType type, StorageInfo storageInfo) { 666 super(storageInfo); 667 this.storageType = type; 668 } 669 getNumStorageDirs()670 public int getNumStorageDirs() { 671 return storageDirs.size(); 672 } 673 getStorageDir(int idx)674 public StorageDirectory getStorageDir(int idx) { 675 return storageDirs.get(idx); 676 } 677 addStorageDir(StorageDirectory sd)678 protected void addStorageDir(StorageDirectory sd) { 679 storageDirs.add(sd); 680 } 681 isConversionNeeded(StorageDirectory sd)682 public abstract boolean isConversionNeeded(StorageDirectory sd) throws IOException; 683 684 /* 685 * Coversion is no longer supported. So this should throw exception if 686 * conversion is needed. 687 */ checkConversionNeeded(StorageDirectory sd)688 private void checkConversionNeeded(StorageDirectory sd) throws IOException { 689 if (isConversionNeeded(sd)) { 690 //throw an exception 691 checkVersionUpgradable(0); 692 } 693 } 694 695 /** 696 * Checks if the upgrade from the given old version is supported. If 697 * no upgrade is supported, it throws IncorrectVersionException. 698 * 699 * @param oldVersion 700 */ checkVersionUpgradable(int oldVersion)701 protected static void checkVersionUpgradable(int oldVersion) 702 throws IOException { 703 if (oldVersion > LAST_UPGRADABLE_LAYOUT_VERSION) { 704 String msg = "*********** Upgrade is not supported from this older" + 705 " version of storage to the current version." + 706 " Please upgrade to " + LAST_UPGRADABLE_HADOOP_VERSION + 707 " or a later version and then upgrade to current" + 708 " version. Old layout version is " + 709 (oldVersion == 0 ? "'too old'" : (""+oldVersion)) + 710 " and latest layout version this software version can" + 711 " upgrade from is " + LAST_UPGRADABLE_LAYOUT_VERSION + 712 ". ************"; 713 LOG.error(msg); 714 throw new IOException(msg); 715 } 716 717 } 718 719 /** 720 * Get common storage fields. 721 * Should be overloaded if additional fields need to be get. 722 * 723 * @param props 724 * @throws IOException 725 */ getFields(Properties props, StorageDirectory sd )726 protected void getFields(Properties props, 727 StorageDirectory sd 728 ) throws IOException { 729 String sv, st, sid, sct; 730 sv = props.getProperty("layoutVersion"); 731 st = props.getProperty("storageType"); 732 sid = props.getProperty("namespaceID"); 733 sct = props.getProperty("cTime"); 734 if (sv == null || st == null || sid == null || sct == null) 735 throw new InconsistentFSStateException(sd.root, 736 "file " + STORAGE_FILE_VERSION + " is invalid."); 737 int rv = Integer.parseInt(sv); 738 NodeType rt = NodeType.valueOf(st); 739 int rid = Integer.parseInt(sid); 740 long rct = Long.parseLong(sct); 741 if (!storageType.equals(rt) || 742 !((namespaceID == 0) || (rid == 0) || namespaceID == rid)) 743 throw new InconsistentFSStateException(sd.root, 744 "is incompatible with others."); 745 if (rv < FSConstants.LAYOUT_VERSION) // future version 746 throw new IncorrectVersionException(rv, "storage directory " 747 + sd.root.getCanonicalPath()); 748 layoutVersion = rv; 749 storageType = rt; 750 namespaceID = rid; 751 cTime = rct; 752 } 753 754 /** 755 * Set common storage fields. 756 * Should be overloaded if additional fields need to be set. 757 * 758 * @param props 759 * @throws IOException 760 */ setFields(Properties props, StorageDirectory sd )761 protected void setFields(Properties props, 762 StorageDirectory sd 763 ) throws IOException { 764 props.setProperty("layoutVersion", String.valueOf(layoutVersion)); 765 props.setProperty("storageType", storageType.toString()); 766 props.setProperty("namespaceID", String.valueOf(namespaceID)); 767 props.setProperty("cTime", String.valueOf(cTime)); 768 } 769 rename(File from, File to)770 public static void rename(File from, File to) throws IOException { 771 if (!from.renameTo(to)) 772 throw new IOException("Failed to rename " 773 + from.getCanonicalPath() + " to " + to.getCanonicalPath()); 774 } 775 deleteDir(File dir)776 protected static void deleteDir(File dir) throws IOException { 777 if (!FileUtil.fullyDelete(dir)) 778 throw new IOException("Failed to delete " + dir.getCanonicalPath()); 779 } 780 781 /** 782 * Write all data storage files. 783 * @throws IOException 784 */ writeAll()785 public void writeAll() throws IOException { 786 this.layoutVersion = FSConstants.LAYOUT_VERSION; 787 for (Iterator<StorageDirectory> it = storageDirs.iterator(); it.hasNext();) { 788 it.next().write(); 789 } 790 } 791 792 /** 793 * Unlock all storage directories. 794 * @throws IOException 795 */ unlockAll()796 public void unlockAll() throws IOException { 797 for (Iterator<StorageDirectory> it = storageDirs.iterator(); it.hasNext();) { 798 it.next().unlock(); 799 } 800 } 801 802 /** 803 * Check whether underlying file system supports file locking. 804 * 805 * @return <code>true</code> if exclusive locks are supported or 806 * <code>false</code> otherwise. 807 * @throws IOException 808 * @see StorageDirectory#lock() 809 */ isLockSupported(int idx)810 public boolean isLockSupported(int idx) throws IOException { 811 StorageDirectory sd = storageDirs.get(idx); 812 FileLock firstLock = null; 813 FileLock secondLock = null; 814 try { 815 firstLock = sd.lock; 816 if(firstLock == null) { 817 firstLock = sd.tryLock(); 818 if(firstLock == null) 819 return true; 820 } 821 secondLock = sd.tryLock(); 822 if(secondLock == null) 823 return true; 824 } finally { 825 if(firstLock != null && firstLock != sd.lock) { 826 firstLock.release(); 827 firstLock.channel().close(); 828 } 829 if(secondLock != null) { 830 secondLock.release(); 831 secondLock.channel().close(); 832 } 833 } 834 return false; 835 } 836 getRegistrationID(StorageInfo storage)837 public static String getRegistrationID(StorageInfo storage) { 838 return "NS-" + Integer.toString(storage.getNamespaceID()) 839 + "-" + Integer.toString(storage.getLayoutVersion()) 840 + "-" + Long.toString(storage.getCTime()); 841 } 842 843 // Pre-upgrade version compatibility corruptPreUpgradeStorage(File rootDir)844 protected abstract void corruptPreUpgradeStorage(File rootDir) throws IOException; 845 writeCorruptedData(RandomAccessFile file)846 protected void writeCorruptedData(RandomAccessFile file) throws IOException { 847 final String messageForPreUpgradeVersion = 848 "\nThis file is INTENTIONALLY CORRUPTED so that versions\n" 849 + "of Hadoop prior to 0.13 (which are incompatible\n" 850 + "with this directory layout) will fail to start.\n"; 851 852 file.seek(0); 853 file.writeInt(FSConstants.LAYOUT_VERSION); 854 org.apache.hadoop.io.UTF8.writeString(file, ""); 855 file.writeBytes(messageForPreUpgradeVersion); 856 file.getFD().sync(); 857 } 858 is203LayoutVersion(int layoutVersion)859 public static boolean is203LayoutVersion(int layoutVersion) { 860 for (int lv : LAYOUT_VERSIONS_203) { 861 if (lv == layoutVersion) { 862 return true; 863 } 864 } 865 return false; 866 } 867 } 868