code: synchronized void recoverTransitionRead(DataNode datanode,
                               NamespaceInfo nsInfo,
                               Collection<File> dataDirs,
                               StartupOption startOpt
                               ) throws IOException {
      if (initialized) {
        // DN storage has been initialized, no need to do anything
        return;
      }
  
      if (FSConstants.LAYOUT_VERSION != nsInfo.getLayoutVersion()) {
        throw new IOException(
            "Data-node and name-node layout versions must be the same. Namenode LV: "
                + nsInfo.getLayoutVersion() + ", current LV: "
                + FSConstants.LAYOUT_VERSION);
      }
      
      // 1. For each data directory calculate its state and 
      // check whether all is consistent before transitioning.
      // Format and recover.
      analyzeStorageDirs(nsInfo, dataDirs, startOpt);
  
      // 2. Do transitions
      // Each storage directory is treated individually.
      // During startup some of them can upgrade or rollback
      // while others could be uptodate for the regular startup.
      doTransition(storageDirs, nsInfo, startOpt);
  
      // 3. make sure we have storage id set - if not - generate new one
      createStorageID(datanode.getPort());
  
      // 4. Update all storages. Some of them might have just been formatted.
      this.writeAll();
  
      this.initialized = true;
    }
docs: Analyze storage directories.
  Recover from previous transitions if required.
  Perform fs state transition if necessary depending on the namespace info.
  Read storage info.
  
  @param nsInfo namespace information
  @param dataDirs array of data storage directories
  @param startOpt startup option
  @throws IOException