PageRenderTime 107ms CodeModel.GetById 10ms app.highlight 89ms RepoModel.GetById 1ms app.codeStats 1ms

/src/init_directories.cc

http://ext3grep.googlecode.com/
C++ | 671 lines | 545 code | 43 blank | 83 comment | 142 complexity | b74304c633086cc60c415ee6503205f8 MD5 | raw file
  1// ext3grep -- An ext3 file system investigation and undelete tool
  2//
  3//! @file init_directories.cc Implementation of init_directories (stage 2).
  4//
  5// Copyright (C) 2008, by
  6// 
  7// Carlo Wood, Run on IRC <carlo@alinoe.com>
  8// RSA-1024 0x624ACAD5 1997-01-26                    Sign & Encrypt
  9// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
 10// 
 11// This program is free software: you can redistribute it and/or modify
 12// it under the terms of the GNU General Public License as published by
 13// the Free Software Foundation, either version 2 of the License, or
 14// (at your option) any later version.
 15// 
 16// This program is distributed in the hope that it will be useful,
 17// but WITHOUT ANY WARRANTY; without even the implied warranty of
 18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19// GNU General Public License for more details.
 20// 
 21// You should have received a copy of the GNU General Public License
 22// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 23
 24#ifndef USE_PCH
 25#include "sys.h"
 26#include <sys/types.h>
 27#include <sys/stat.h>
 28#include <unistd.h>
 29#include <cerrno>
 30#include <sstream>
 31#endif
 32
 33#include "locate.h"
 34#include "init_directories.h"
 35#include "blocknr_vector_type.h"
 36#include "Parent.h"
 37#include "forward_declarations.h"
 38#include "commandline.h"
 39#include "get_block.h"
 40#include "journal.h"
 41#include "dir_inode_to_block.h"
 42
 43all_directories_type all_directories;
 44inode_to_directory_type inode_to_directory;
 45
 46Directory::Directory(uint32_t inode_number, int first_block) : M_inode_number(inode_number), M_blocks(1)
 47#ifdef DEBUG
 48    , M_extended_blocks_added(false)
 49#endif
 50{
 51  std::list<DirectoryBlock>::iterator iter = M_blocks.begin();
 52  iter->read_block(first_block, iter);
 53}
 54
 55typedef std::map<uint32_t, blocknr_vector_type> inode_to_extended_blocks_map_type;
 56
 57bool init_directories_action(ext3_dir_entry_2 const& dir_entry, Inode const&, bool, bool, bool, bool, bool, bool, Parent* parent, void*)
 58{
 59  // Get the inode number.
 60  uint32_t const inode_number = dir_entry.inode;
 61
 62  // If this is a new directory, skip iterating into it if we already processed it.
 63  // If it's directory '.' we need to continue with this function.
 64  if (dir_entry.name_len != 1 || dir_entry.name[0] != '.')
 65  {
 66    inode_to_directory_type::iterator iter = inode_to_directory.find(inode_number);
 67    return iter != inode_to_directory.end();
 68  }
 69
 70  // If we get here then that means that 'inode_number' is a directory that
 71  // we have a first block for (dir_entry is for entry "."). We possibly found
 72  // more directory start blocks for this inode number in which case we'll
 73  // get here more than once for this inode number.
 74
 75  // Get the first block.
 76  int first_block = dir_inode_to_block(inode_number);
 77  if (first_block == -1)
 78  {
 79    std::cout << std::flush;
 80    std::cerr << "ERROR: dir_inode_to_block(" << inode_number << ") returned -1.\n";
 81  }
 82  ASSERT(first_block != -1);
 83
 84  // Store a new entry in the all_directories container.
 85  std::pair<all_directories_type::iterator, bool> res =
 86      all_directories.insert(all_directories_type::value_type(parent->dirname(false), Directory(inode_number, first_block)));
 87  if (!res.second)	// Did we already see this path before? Make sure the inode is consistent.
 88  {
 89    if (inode_number == res.first->second.inode_number() && first_block == res.first->second.first_block())
 90    {
 91      //std::cout << "Aborting recursion of " << parent->dirname(commandline_show_path_inodes) << '\n';
 92      return true;	// Abort recursion.
 93    }
 94    std::cout << "Directory \"" << parent->dirname(commandline_show_path_inodes) << "\" is linked to both inode/block " <<
 95        inode_number << '/' << first_block << " as well as " << res.first->second.inode_number() << '/' << res.first->second.first_block() << "\n";
 96    // If we don't do anything here, the assertion `directory.inode_number() == iter->first' in init_directories() will fail.
 97    // See http://groups.google.com/group/ext3grep/browse_thread/thread/38bbcc9bba214240/987815a7bba17190?hl=en#987815a7bba17190
 98    
 99    // Lets call inode_number/first_block 'new', and inode_number()/first_block() 'old'.
100    int sequence_number_new = last_undeleted_directory_inode_refering_to_block(inode_number, first_block);
101    int sequence_number_old = last_undeleted_directory_inode_refering_to_block(res.first->second.inode_number(), res.first->second.first_block());
102    
103    if (sequence_number_new == sequence_number_old)
104    {
105      std::cout << std::endl;
106      std::cerr << "WARNING: ext3grep currently does not support this situation:\n";
107      std::cerr << "         last_undeleted_directory_inode_refering_to_block(" << inode_number << ", " << first_block << ") = " << sequence_number_new << '\n';
108      std::cerr << "         last_undeleted_directory_inode_refering_to_block(" << res.first->second.inode_number() << ", " << res.first->second.first_block() << ") = " << sequence_number_old << '\n';
109      std::cerr << "Since most people don't like it if ext3grep aborts; we'll randomly pick one of them. It could be the WRONG one though." << std::endl;
110      sequence_number_new = 0;	// Drop the new one.
111    }
112
113    if (sequence_number_new > sequence_number_old)
114    {
115      // The new inode/block pair is newer, therefore we keep 'inode_number' and replace all_directories element.
116      std::cout << "Replacing " << res.first->second.inode_number() << '/' << res.first->second.first_block() <<
117          " (sequence " << sequence_number_old << ") with " << inode_number << '/' << first_block;
118      if (sequence_number_new == std::numeric_limits<int>::max())
119	std::cout << " (allocated";
120      else
121	std::cout << " (sequence " << sequence_number_new;
122      std::cout << ").\n";
123      inode_to_directory_type::iterator iter = inode_to_directory.find(res.first->second.inode_number());
124      ASSERT(iter != inode_to_directory.end());
125      ASSERT(iter->second == res.first);
126      inode_to_directory.erase(iter);
127      all_directories.erase(res.first);
128      res = all_directories.insert(all_directories_type::value_type(parent->dirname(false), Directory(inode_number, first_block)));
129      ASSERT(res.second);
130    }
131    else
132    {
133      // The old inode/block pair is newer, therefore we keep the all_directories element
134      // and do not insert the new inode in inode_to_directory. Moreover, we consider
135      // the current directory block to unusable, so we abort recursion of it.
136      std::cout << "Keeping " << res.first->second.inode_number() << '/' << res.first->second.first_block();
137      if (sequence_number_old == std::numeric_limits<int>::max())
138        std::cout << " (allocated";
139      else
140	std::cout << " (sequence " << sequence_number_old;
141      std::cout << ") over " << inode_number << '/' << first_block << " (sequence " << sequence_number_new << ").\n";
142      return true;	// Abort recursion.
143    }
144
145    // Consistency check:
146    ASSERT(inode_number == res.first->second.inode_number());
147  }
148  std::pair<inode_to_directory_type::iterator, bool> res2 =
149      inode_to_directory.insert(inode_to_directory_type::value_type(inode_number, res.first));
150  if (!res2.second)	// Did we get here with this inode number before? Make sure the path is consistent!
151  {
152    if (inode_number == res2.first->second->second.inode_number() && res.first == res2.first->second)
153    {
154      //std::cout << "Aborting recursion of " << parent->dirname(commandline_show_path_inodes) << '\n';
155      return true;	// Abort recursion.
156    }
157
158    std::cout << "Inode number " << inode_number << " is linked to both, " << parent->dirname(commandline_show_path_inodes) << " as well as " << res2.first->second->first << "!\n";
159    bool new_path = path_exists(parent->dirname(false));
160    bool old_path = path_exists(res2.first->second->first);
161    if (new_path && !old_path)
162    {
163      std::cout << "Using \"" << parent->dirname(commandline_show_path_inodes) << "\" as \"" << res2.first->second->first << " doesn't exist in the locate database.\n";
164      inode_to_directory.erase(res2.first);
165      std::pair<inode_to_directory_type::iterator, bool> res3 =
166	  inode_to_directory.insert(inode_to_directory_type::value_type(inode_number, res.first));
167      ASSERT(res3.second);
168    }
169    else if (!new_path && old_path)
170      std::cout << "Keeping \"" << res2.first->second->first << "\" as \"" << parent->dirname(commandline_show_path_inodes) << " doesn't exist in the locate database.\n";
171    else if (!new_path && !old_path)
172      std::cout << "WARNING: Neither exist in the locate database (you might want to add one). Keeping \"" << res2.first->second->first << "\".\n";
173    ASSERT(!(new_path && old_path));
174  }
175  return false;
176}
177
178struct extended_directory_action_data_st {
179  int blocknr;
180  std::map<uint32_t, int> linked;	// inode to count (number of times a linked dir_entry refers to it).
181  std::map<uint32_t, int> unlinked;	// inode to count (number of times an unlinked dir_entry refers to it).
182};
183
184bool filename_heuristics_action(ext3_dir_entry_2 const& dir_entry, Inode const& UNUSED(inode),
185    bool UNUSED(deleted), bool UNUSED(allocated), bool UNUSED(reallocated), bool UNUSED(zero_inode), bool UNUSED(linked), bool UNUSED(filtered),
186    Parent*, void* data)
187{
188  std::set<std::string>* filesnames = reinterpret_cast<std::set<std::string>*>(data);
189  std::string filename(dir_entry.name, dir_entry.name_len);
190  filesnames->insert(filename);
191  return false;
192}
193
194#ifdef CPPGRAPH
195void iterate_over_directory__with__filename_heuristics_action(void) { (void)filename_heuristics_action(*(ext3_dir_entry_2 const*)NULL, *(Inode const*)NULL, 0, 0, 0, 0, 0, 0, NULL, NULL); }
196#endif
197
198bool extended_directory_action(ext3_dir_entry_2 const& dir_entry, Inode const& inode,
199    bool UNUSED(deleted), bool UNUSED(allocated), bool reallocated, bool zero_inode, bool linked, bool UNUSED(filtered), Parent*, void* ptr)
200{
201  extended_directory_action_data_st* data = reinterpret_cast<extended_directory_action_data_st*>(ptr);
202  bool is_maybe_directory = true;	// Maybe, because if !feature_incompat_filetype then it isn't
203  					// garanteed that the contents of the inode still belong to this entry.
204  if (feature_incompat_filetype)
205    is_maybe_directory = (dir_entry.file_type & 7) == EXT3_FT_DIR;
206  else if (!zero_inode && !reallocated)
207    is_maybe_directory = is_directory(inode);  
208  if (is_maybe_directory && !zero_inode)
209  {
210    int blocknr2 = dir_inode_to_block(dir_entry.inode);
211    if (blocknr2 == -1)
212    {
213      // Don't print this message if !feature_incompat_filetype && reallocated
214      // because it more likely to just not being a directory inode.
215      if (feature_incompat_filetype || !reallocated)
216	std::cout << "Cannot find a directory block for inode " << dir_entry.inode << ".\n";
217      return true;
218    }
219    static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
220    get_block(blocknr2, block_buf);
221    ext3_dir_entry_2 const* dir_entry2 = reinterpret_cast<ext3_dir_entry_2 const*>(block_buf);
222    ASSERT(dir_entry2->inode == dir_entry.inode);
223    dir_entry2 = reinterpret_cast<ext3_dir_entry_2 const*>(block_buf + dir_entry2->rec_len);
224    ASSERT(dir_entry2->name_len == 2 && dir_entry2->name[0] == '.' && dir_entry2->name[1] == '.');
225    ASSERT(dir_entry2->inode);
226    std::map<uint32_t, int>& inode_to_count(linked ? data->linked : data->unlinked);
227    std::map<uint32_t, int>::iterator iter = inode_to_count.find(dir_entry2->inode);
228    if (iter == inode_to_count.end())
229      inode_to_count[dir_entry2->inode] = 1;
230    else
231      ++(inode_to_count[dir_entry2->inode]);
232  }
233  return false;
234}
235
236#ifdef CPPGRAPH
237void iterate_over_directory__with__extended_directory_action(void) { (void)extended_directory_action(*(ext3_dir_entry_2 const*)NULL, *(Inode const*)NULL, 0, 0, 0, 0, 0, 0, NULL, NULL); }
238#endif
239
240bool find_inode_number_of_extended_directory_block(int blocknr, unsigned char* block_buf, uint32_t& inode_number, uint32_t& inode_from_journal)
241{
242  block_to_dir_inode_map_type::iterator iter = block_to_dir_inode_map.find(blocknr);
243  inode_from_journal = (iter == block_to_dir_inode_map.end()) ? 0 : iter->second;
244  get_block(blocknr, block_buf);
245  extended_directory_action_data_st data;
246  data.blocknr = blocknr;
247#ifdef CPPGRAPH
248  // Let cppgraph know that we call extended_directory_action from here.
249  iterate_over_directory__with__extended_directory_action();
250#endif
251  ++no_filtering;
252  iterate_over_directory(block_buf, blocknr, extended_directory_action, NULL, &data);
253  --no_filtering;
254  bool linked = (data.linked.size() > 0);
255  std::map<uint32_t, int>& inode_to_count(linked ? data.linked : data.unlinked);
256  inode_number = 0;
257  if (inode_to_count.size() > 0)
258  {
259    if (inode_to_count.size() > 1)
260    {
261      if (inode_from_journal)
262	std::cout << "Extended directory at " << blocknr << " has entries that appear to be directories, but their parent directory inode is not consistent.\n";
263      else
264      {
265	std::cout << "WARNING: extended directory at " << blocknr << " has entries that appear to be directories, "
266	    "but their parent directory inode is not consistent! I can't make this decision for you. "
267	    "You will have to manually pick an inode for this block number. The inodes that I found are (although ALL could be wrong):\n";
268	for (std::map<uint32_t, int>::iterator iter = inode_to_count.begin(); iter != inode_to_count.end(); ++iter)
269	{
270	  std::cout << "  " << iter->first << " (" << iter->second;
271	  if (iter->second == 1)
272	    std::cout << " time)\n";
273	  else
274	    std::cout << " times)\n";
275	}
276      }
277    }
278    else
279    {
280      inode_number = inode_to_count.begin()->first;
281      bool journal_disagrees_with_found_directory_inodes = inode_from_journal && inode_from_journal != inode_number;
282      if (journal_disagrees_with_found_directory_inodes)
283      {
284	std::cout << "Extended directory at " << blocknr << " appears to contains " << inode_to_count.begin()->second <<
285	    ' ' << (linked ? "linked" : "unlinked") << " directory whose parent directory has inode " <<
286	    inode_number << " but according to the journal it should be " << inode_from_journal << ". Using the latter.\n";
287	// We trust our journal based algorithm more because the content of
288	// inodes can hardly be trusted: they can be reused and not refer
289	// to this dir entry at all. Especially in the case of !feature_incompat_filetype
290	// where even regular files' inodes can have been reused (by directories)
291	// this will happen frequently, but independent of the frequency at which
292	// it occurs, the journal is simply more reliable.
293	inode_number = inode_from_journal;
294	// However...
295	if (linked)
296	  std::cout << "WARNING: We really only expect that to happen for unlinked directory entries. Have a look at block " << blocknr << '\n';
297	if (inode_to_count.begin()->second > 1)
298	  std::cout << "WARNING: It's suspiciously weird that there are more than one such \"directories\". Have a look at block " << blocknr << '\n';
299      }
300      else
301	std::cout << "Extended directory at " << blocknr << " belongs to inode " << inode_number <<
302	    " (from " << inode_to_count.begin()->second << ' ' << (linked ? "linked" : "unlinked") << " directories).\n";
303    }
304  }
305  if (!inode_number)	// Not found yet?
306  {
307#ifdef CPPGRAPH
308    // Let cppgraph know that we call filename_heuristics_action from here.
309    iterate_over_directory__with__filename_heuristics_action();
310#endif
311    // Do some heuristics on the filenames.
312    std::set<std::string> filenames;
313    ++no_filtering;
314    iterate_over_directory(block_buf, blocknr, filename_heuristics_action, NULL, &filenames);
315    --no_filtering;
316    if (filenames.empty())
317    {
318      if (inode_from_journal)
319      {
320	std::cout << "Extended directory at " << blocknr << " belongs to inode " << inode_from_journal << " (empty; from journal)).\n";
321	inode_number = inode_from_journal;
322      }
323      else
324	std::cout << "Could not find an inode for empty extended directory at " << blocknr << '\n';
325    }
326    else
327    {
328      std::string dir = parent_directory(blocknr, filenames); 
329      if (dir.empty())
330      {
331	if (inode_from_journal)
332	{
333	  std::cout << "Extended directory at " << blocknr << " belongs to inode " << inode_from_journal << " (from journal).\n";
334	  inode_number = inode_from_journal;
335	}
336	else
337	  std::cout << "Could not find an inode for extended directory at " << blocknr << ", disregarding it's contents.\n";
338      }
339      else
340      {
341	all_directories_type::iterator directory_iter = all_directories.find(dir);
342	if (directory_iter == all_directories.end())
343	{
344	  std::cout << "Extended directory at " << blocknr << " belongs to directory " << dir << " which isn't in all_directories yet.\n";
345	  return true;	// Needs to be processed again later.
346	}
347	else
348	{
349	  inode_number = directory_iter->second.inode_number();
350	  std::cout << "Extended directory at " << blocknr << " belongs to inode " << inode_number << '\n';
351	  if (inode_from_journal && inode_from_journal != inode_number)
352	    std::cout << "WARNING: according to the journal it should have been inode " << inode_from_journal << "!?\n";
353	}
354      }
355    }
356  }
357  return false;	// Done
358}
359
360void init_directories(void)
361{
362  static bool initialized = false;
363  if (initialized)
364    return;
365  initialized = true;
366
367  DoutEntering(dc::notice, "init_directories()");
368
369  std::string device_name_basename = device_name.substr(device_name.find_last_of('/') + 1);
370  std::string cache_stage2 = device_name_basename + ".ext3grep.stage2";
371  struct stat sb;
372  bool have_cache = !(stat(cache_stage2.c_str(), &sb) == -1);
373  if (have_cache)
374  {
375    if (does_not_end_on_END(cache_stage2))
376      have_cache = false;
377  }
378  else if (errno != ENOENT)
379  {
380    int error = errno;
381    std::cout << std::flush;
382    std::cerr << progname << ": failed to open " << cache_stage2 << ": " << strerror(error) << std::endl;
383    exit(EXIT_FAILURE);
384  }
385  if (!have_cache)
386  {
387    init_dir_inode_to_block_cache();
388    unsigned char* block_buf = new unsigned char [block_size_];
389
390    inode_to_extended_blocks_map_type inode_to_extended_blocks_map;
391
392    // Run over all extended directory blocks.
393    for (std::vector<int>::iterator iter = extended_blocks.begin(); iter != extended_blocks.end(); ++iter)
394    {
395      int blocknr = *iter;
396
397      uint32_t inode_number;
398      uint32_t inode_from_journal;
399      bool needs_reprocessing = find_inode_number_of_extended_directory_block(blocknr, block_buf, inode_number, inode_from_journal);
400
401      if (needs_reprocessing)
402      {
403	// FIXME: should be processed again after adding extended directory blocks to all_directories!
404	std::cout << "FIXME: Extended directory at " << blocknr << " belongs to non-existent directory!\n";
405	// Fall back to journal.
406	if (inode_from_journal)
407	{
408	  std::cout << "Extended directory at " << blocknr << " belongs to inode " << inode_from_journal << " (fall back to journal).\n";
409	  inode_number = inode_from_journal;
410	}
411      }
412
413      if (inode_number)
414      {
415	// Add the found inode number of this extended block to inode_to_extended_blocks_map.
416	inode_to_extended_blocks_map_type::iterator iter2 = inode_to_extended_blocks_map.find(inode_number);
417	if (iter2 != inode_to_extended_blocks_map.end())
418	  iter2->second.push_back(blocknr);
419	else
420	{
421	  blocknr_vector_type bv;
422	  bv.blocknr = 0;
423	  bv.push_back(blocknr);
424	  inode_to_extended_blocks_map.insert(inode_to_extended_blocks_map_type::value_type(inode_number, bv));
425	}
426      }
427    }
428
429    // Get root inode.
430    InodePointer root_inode(get_inode(EXT3_ROOT_INO));
431    Parent parent(root_inode, EXT3_ROOT_INO);
432    // Get the block that refers to inode EXT3_ROOT_INO.
433    int root_blocknr = dir_inode_to_block(EXT3_ROOT_INO);
434    ASSERT(root_blocknr != -1);	// This should be impossible; inode EXT3_ROOT_INO is never wiped(?).
435
436    // Initialize root_extended_blocks to be the extended directory blocks of the root.
437    blocknr_vector_type root_extended_blocks;
438    int root_extended_blocks_size = 0;
439    inode_to_extended_blocks_map_type::iterator root_extended_blocks_iter = inode_to_extended_blocks_map.find(EXT3_ROOT_INO);
440    if (root_extended_blocks_iter != inode_to_extended_blocks_map.end())
441    {
442      root_extended_blocks = root_extended_blocks_iter->second;
443      root_extended_blocks_size = root_extended_blocks.size();
444      ASSERT(root_extended_blocks_size > 0);
445    }
446
447#ifdef CPPGRAPH
448    // Let cppgraph know that we call init_directories_action from here.
449    iterate_over_directory__with__init_directories_action();
450#endif
451
452    // Run over all directory blocks and add all start blocks to all_directories, updating inode_to_directory.
453    int last_extended_block_index = root_extended_blocks_size;
454    for(int blocknr = root_blocknr;; blocknr = root_extended_blocks[--last_extended_block_index])
455    {
456      // Get the contents of this block of the root directory.
457      get_block(blocknr, block_buf);
458      // Iterate over all directory blocks.
459      int depth_store = commandline_depth;
460      commandline_depth = 10000;
461      iterate_over_directory(block_buf, root_blocknr, init_directories_action, &parent, NULL);
462      commandline_depth = depth_store;
463      if (last_extended_block_index == 0)
464        break;
465    }
466
467    // Next, add all extended directory blocks.
468    for (all_directories_type::iterator dir_iter = all_directories.begin(); dir_iter != all_directories.end(); ++dir_iter)
469    {
470#ifdef DEBUG
471      ASSERT(!dir_iter->second.extended_blocks_added());
472#endif
473      uint32_t inode_number = dir_iter->second.inode_number();
474
475      inode_to_extended_blocks_map_type::iterator iter = inode_to_extended_blocks_map.find(inode_number);
476      if (iter != inode_to_extended_blocks_map.end())
477      {
478	blocknr_vector_type bv = iter->second;
479	int const size = bv.size();
480	if (size > 0)
481	{
482	  std::cout << "Adding extended directory block(s) for directory \"" << dir_iter->first << "\"." << std::endl;
483	  unsigned char* block_buf = new unsigned char [block_size_];
484	  for (int j = 0; j < size; ++j)
485	  {
486	    int blocknr = bv[j];
487	    get_block(blocknr, block_buf);
488
489	    // Add extended directory as DirectoryBlock to the corresponding Directory.
490	    dir_iter->second.blocks().push_back(DirectoryBlock());
491	    std::list<DirectoryBlock>::iterator directory_block_iter = dir_iter->second.blocks().end();
492	    --directory_block_iter;
493	    directory_block_iter->read_block(blocknr, directory_block_iter);
494
495	    // Set up a Parent object that will return the correct dirname.
496	    ext3_dir_entry_2 fake_dir_entry;
497	    fake_dir_entry.inode = inode_number;
498	    fake_dir_entry.rec_len = 0;	// Not used
499	    fake_dir_entry.file_type = 0; // Not used
500	    fake_dir_entry.name_len = dir_iter->first.size();
501	    strncpy(fake_dir_entry.name, dir_iter->first.c_str(), fake_dir_entry.name_len);
502	    InodePointer fake_reference(0);
503	    Parent dummy_parent(fake_reference, 0);
504	    InodePointer inoderef(get_inode(inode_number));
505	    Parent parent(&dummy_parent, &fake_dir_entry, inoderef, inode_number);
506	    ASSERT(parent.dirname(false) == dir_iter->first);
507	    // Iterate over all directory blocks that we can reach.
508	    int depth_store = commandline_depth;
509	    commandline_depth = 10000;
510	    iterate_over_directory(block_buf, blocknr, init_directories_action, &parent, NULL);
511	    commandline_depth = depth_store;
512	  }
513	  delete [] block_buf;
514	}
515      }
516#ifdef DEBUG
517      dir_iter->second.set_extended_blocks_added();
518#endif
519    }
520#ifdef DEBUG
521    // The block inside the above loop adds new elements to all_directories. If those are
522    // added AFTER dir_iter then there is no problem because std::map iterators aren't
523    // invalidated by insertion and they will be taken into account later in the
524    // same loop. If dir_iter points to a Directory with a path a/b/c then inode_number 
525    // is the inode number of that 'c' directory. Extended blocks of that directory
526    // only add "." dir entries for recursively found directories, ie a/b/c/d and
527    // are therefore inserted after the current element and processed automatically
528    // in the same loop. Therefore, the following should hold:
529    for (all_directories_type::iterator dir_iter = all_directories.begin(); dir_iter != all_directories.end(); ++dir_iter)
530      ASSERT(dir_iter->second.extended_blocks_added());
531#endif
532
533    all_directories_type::iterator lost_plus_found_directory_iter = all_directories.find("lost+found");
534    ASSERT(lost_plus_found_directory_iter != all_directories.end());
535
536    // Add all remaining extended directory blocks to lost+found.
537    // Also free memory of inode_to_extended_blocks_map.
538    for (inode_to_extended_blocks_map_type::iterator iter = inode_to_extended_blocks_map.begin(); iter != inode_to_extended_blocks_map.end(); ++iter)
539    {
540      uint32_t inode_number = iter->first;
541      blocknr_vector_type bv = iter->second;
542      inode_to_directory_type::iterator directory_iter = inode_to_directory.find(inode_number);
543      if (directory_iter == inode_to_directory.end())	// Not added already?
544      {
545        if (bv.size() == 1)
546	  std::cout << "WARNING: Can't link block";
547        else
548	  std::cout << "WARNING: Can't link blocks";
549	for (size_t j = 0; j < bv.size(); ++j)
550	  std::cout << ' ' << bv[j];
551	std::cout << " to inode " << inode_number << " because that inode cannot be found in the inode_to_directory map. Linking it to lost+found instead!\n";
552	// FIXME: namespace polution. These should be put in lost+found/inode_number or something.
553	for (size_t j = 0; j < bv.size(); ++j)
554	{
555	  int blocknr = bv[j];
556	  // Add extended directory as DirectoryBlock to lost+found.
557	  lost_plus_found_directory_iter->second.blocks().push_back(DirectoryBlock());
558	  std::list<DirectoryBlock>::iterator directory_block_iter = lost_plus_found_directory_iter->second.blocks().end();
559	  --directory_block_iter;
560	  directory_block_iter->read_block(blocknr, directory_block_iter);
561	}
562      }
563      // Free memory.
564      bv.erase();
565    }
566
567    delete [] block_buf;
568    std::cout << '\n';
569
570    std::cout << "Writing analysis so far to '" << cache_stage2 << "'. Delete that file if you want to do this stage again.\n";
571    std::ofstream cache;
572    cache.open(cache_stage2.c_str());
573    cache << "# Stage 2 data for " << device_name << ".\n";
574    cache << "# Inodes path and directory blocks.\n";
575    cache << "# INODE PATH BLOCK [BLOCK ...]\n";
576
577    for (inode_to_directory_type::iterator iter = inode_to_directory.begin(); iter != inode_to_directory.end(); ++iter)
578    {
579      cache << iter->first << " '" << iter->second->first << "'";
580      Directory& directory(iter->second->second);
581      if (directory.inode_number() != iter->first)
582      {
583        std::cerr << "ERROR: inode_to_directory entry with inode number " << iter->first <<
584	    " points to a Directory with inode number " << directory.inode_number() << " (path \"" << iter->second->first << "\")." << std::endl; 
585      }
586      ASSERT(directory.inode_number() == iter->first);
587      for (std::list<DirectoryBlock>::iterator iter2 = directory.blocks().begin(); iter2 != directory.blocks().end(); ++iter2)
588        cache << ' ' << iter2->block();
589      cache << '\n';
590    }
591
592    cache << "# END\n";
593    cache.close();
594  }
595  else
596  {
597    std::cout << "Loading " << cache_stage2 << "..." << std::flush;
598    std::ifstream cache;
599    cache.open(cache_stage2.c_str());
600    if (!cache.is_open())
601    {
602      int error = errno;
603      std::cout << " error" << std::endl;
604      std::cerr << progname << ": failed to open " << cache_stage2 << ": " << strerror(error) << std::endl;
605      exit(EXIT_FAILURE);
606    }
607    int inode;
608    int blocknr;
609    char c;
610    // Skip initial comments.
611    for(;;)
612    {
613      cache.get(c);
614      if (c == '#')
615        cache.ignore(std::numeric_limits<int>::max(), '\n');
616      else
617      {
618        cache.putback(c);
619        break;
620      }
621    }
622    ASSERT(!dir_inode_to_block_cache);
623    dir_inode_to_block_cache = new blocknr_vector_type [inode_count_ + 1];
624    std::memset(dir_inode_to_block_cache, 0, sizeof(blocknr_vector_type) * (inode_count_ + 1));
625    std::stringstream buf;
626    int count = 0;
627    while (cache >> inode)
628    {
629      cache.get(c);
630      ASSERT(c == ' ');
631      cache.get(c);
632      ASSERT(c == '\'');
633      buf.clear();
634      buf.str("");
635      cache.get(*buf.rdbuf(), '\n');
636      if (inode == EXT3_ROOT_INO)	// If the function extracts no elements, it calls setstate(failbit).
637	cache.clear();
638      cache.get(c);	// Extraction stops on end-of-file or on an element that compares equal to delim (which is not extracted).
639      ASSERT(c == '\n');
640      std::string::size_type pos = buf.str().find_last_of('\'');
641      ASSERT(pos != std::string::npos);
642      std::pair<all_directories_type::iterator, bool> res = all_directories.insert(all_directories_type::value_type(buf.str().substr(0, pos), Directory(inode)));
643      ASSERT(res.second);
644      std::pair<inode_to_directory_type::iterator, bool> res2 = inode_to_directory.insert(inode_to_directory_type::value_type(inode, res.first));
645      ASSERT(res2.second);
646      buf.seekg(pos + 1);
647      std::vector<uint32_t> block_numbers;
648      while(buf >> blocknr)
649      {
650        block_numbers.push_back(blocknr);
651	c = buf.get();
652	if (c != ' ')
653	{
654	  ASSERT(buf.eof());
655	  break;
656	}
657      }
658      dir_inode_to_block_cache[inode] = block_numbers;
659      std::list<DirectoryBlock>& blocks(res.first->second.blocks());
660      blocks.resize(block_numbers.size());
661      std::list<DirectoryBlock>::iterator directory_block_iter = blocks.begin();
662      for (std::vector<uint32_t>::iterator block_number_iter = block_numbers.begin();
663          block_number_iter != block_numbers.end(); ++block_number_iter, ++directory_block_iter)
664	directory_block_iter->read_block(*block_number_iter, directory_block_iter);
665      if (++count % 100 == 0)
666        std::cout << '.' << std::flush;
667    }
668    cache.close();
669    std::cout << " done\n";
670  }
671}