PageRenderTime 62ms CodeModel.GetById 9ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

/src/indirect_blocks.cc

http://ext3grep.googlecode.com/
C++ | 395 lines | 302 code | 24 blank | 69 comment | 83 complexity | bafe57e5919e6ca6f06c24b261d2d573 MD5 | raw file
  1// ext3grep -- An ext3 file system investigation and undelete tool
  2//
  3//! @file indirect_blocks.cc Implementation dealing with (double/tripple) indirect blocks.
  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// Stanislaw T. Findeisen <sf181257 at students mimuw edu pl>
 12//
 13// This program is free software: you can redistribute it and/or modify
 14// it under the terms of the GNU General Public License as published by
 15// the Free Software Foundation, either version 2 of the License, or
 16// (at your option) any later version.
 17// 
 18// This program is distributed in the hope that it will be useful,
 19// but WITHOUT ANY WARRANTY; without even the implied warranty of
 20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21// GNU General Public License for more details.
 22// 
 23// You should have received a copy of the GNU General Public License
 24// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 25//
 26// ChangeLog
 27//
 28// 2008-07-07  STF
 29//     * (is_indirect_block): Add. Heuristic detection of indirect
 30//       blocks based solely on their content.
 31//
 32// 2008-07-10  Carlo Wood  <carlo@alinoe.com>
 33//     * (is_indirect_block):
 34//       -Add SKIPZEROES.
 35//       - Call is_data_block_number.
 36//       - Return false if there are only ZEROES.
 37//       - Bug fix: Abort loops when reaching the ZEROES.
 38//       - Only use an array on the stack if the block numbers are less than the
 39//         size of one group apart (instead of allocating and clearing 32 MB on
 40//         the stack every time).
 41//	- Use return value of std::set<>::insert instead of calling std::set<>::count.
 42//
 43// 2008-10-13  Carlo Wood  <carlo@alinoe.com>
 44//     * (is_indirect_block):
 45//       - SKIPZEROES must be 0: zeroes a completely legal in any indirect block.
 46
 47#ifndef USE_PCH
 48#include "sys.h"
 49#endif
 50
 51#include "indirect_blocks.h"
 52#include "get_block.h"
 53#include "is_blockdetection.h"
 54#include "forward_declarations.h"
 55#include "endian_conversion.h"
 56#include "superblock.h"
 57
 58//-----------------------------------------------------------------------------
 59//
 60// Indirect blocks
 61//
 62
 63void find_block_action(int blocknr, int, void* ptr)
 64{
 65  find_block_data_st& data(*reinterpret_cast<find_block_data_st*>(ptr));
 66  if (blocknr == data.block_looking_for)
 67    data.found_block = true;
 68}
 69
 70#ifdef CPPGRAPH
 71void iterate_over_all_blocks_of__with__find_block_action(void) { find_block_action(0, 0, NULL); }
 72#endif
 73
 74void print_directory_action(int blocknr, int, void*)
 75{
 76  static bool using_static_buffer = false;
 77  ASSERT(!using_static_buffer);
 78  static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
 79  unsigned char* block = get_block(blocknr, block_buf);
 80  using_static_buffer = true;
 81  ext3_dir_entry_2* dir_entry = reinterpret_cast<ext3_dir_entry_2*>(block);
 82  if (dir_entry->rec_len < block_size_)	// The directory could be entirely empty (unused).
 83    print_directory(block, blocknr);
 84  using_static_buffer = false;
 85}
 86
 87#ifdef CPPGRAPH
 88void iterate_over_all_blocks_of__with__print_directory_action(void) { print_directory_action(0, 0, NULL); }
 89#endif
 90
 91bool iterate_over_all_blocks_of_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
 92{
 93  if (diagnose)
 94    std::cout << "Processing indirect block " << block << ": " << std::flush;
 95  unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
 96  __le32* block_ptr = (__le32*)get_block(block, block_buf);
 97  unsigned int i = 0;
 98  while (i < block_size_ / sizeof(__le32))
 99  {
100    if (block_ptr[i] || (indirect_mask & hole_bit))
101    {
102      if (!is_block_number(block_ptr[i]))
103      {
104        if (diagnose)
105	  std::cout << "entry " << i << " contains block number " << block_ptr[i] << ", which is too large." << std::endl;
106        break;
107      }
108      if (!diagnose)
109	action(block_ptr[i], file_block_nr, data);
110    }
111    ++i;
112    ++file_block_nr;
113  }
114  bool result = (i < block_size_ / sizeof(__le32));
115  if (diagnose && !result)
116    std::cout << "OK" << std::endl;
117  return result;
118}
119
120bool iterate_over_all_blocks_of_double_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
121{
122  if (diagnose)
123    std::cout << "Start processing double indirect block " << block << '.' << std::endl;
124  unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
125  __le32* block_ptr = (__le32*)get_block(block, block_buf);
126  unsigned int i = 0;
127  unsigned int const limit = block_size_ >> 2;
128  while (i < limit)
129  {
130    if (block_ptr[i] || (indirect_mask & hole_bit))
131    {
132      if (!is_block_number(block_ptr[i]))
133      {
134        if (diagnose)
135	  std::cout << "Entry " << i << " of double indirect block " << block << " contains block number " << block_ptr[i] << ", which is too large." << std::endl;
136        break;
137      }
138      if ((indirect_mask & indirect_bit) && !diagnose)
139        action(block_ptr[i], -1, data);
140      if ((indirect_mask & direct_bit))
141      {
142        if (iterate_over_all_blocks_of_indirect_block(block_ptr[i], file_block_nr, action, data, indirect_mask, diagnose))
143	  break;
144      }
145      else
146	file_block_nr += limit;
147    }
148    else
149      file_block_nr += limit;
150    ++i;
151  }
152  if (diagnose)
153    std::cout << "End processing double indirect block " << block << '.' << std::endl;
154  return i < block_size_ / sizeof(__le32);
155}
156
157bool iterate_over_all_blocks_of_tripple_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
158{
159  if (diagnose)
160    std::cout << "Start processing tripple indirect block " << block << '.' << std::endl;
161  unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
162  __le32* block_ptr = (__le32*)get_block(block, block_buf);
163  unsigned int i = 0;
164  unsigned int const limit = block_size_ >> 2;
165  while (i < limit)
166  {
167    if (block_ptr[i] || (indirect_mask & hole_bit))
168    {
169      if (!is_block_number(block_ptr[i]))
170      {
171        if (diagnose)
172	  std::cout << "Entry " << i << " of tripple indirect block " << block << " contains block number " << block_ptr[i] << ", which is too large." << std::endl;
173        break;
174      }
175      if ((indirect_mask & indirect_bit) && !diagnose)
176        action(block_ptr[i], -1, data);
177      if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[i], file_block_nr, action, data, indirect_mask, diagnose))
178        break;
179    }
180    else
181      file_block_nr += limit * limit;
182    ++i;
183  }
184  if (diagnose)
185    std::cout << "End processing tripple indirect block " << block << '.' << std::endl;
186  return i < limit;
187}
188
189// Returns true if an indirect block was encountered that doesn't look like an indirect block anymore.
190bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
191{
192  if (is_symlink(inode) && inode.blocks() == 0)
193    return false;		// Block pointers contain text.
194  __le32 const* block_ptr = inode.block();
195  if (diagnose)
196    std::cout << "Processing direct blocks..." << std::flush;
197  int file_block_nr = 0;
198  unsigned int const limit = block_size_ >> 2;
199  if ((indirect_mask & direct_bit))
200  {
201    for (int i = 0; i < EXT3_NDIR_BLOCKS; ++i, ++file_block_nr)
202      if (block_ptr[i] || (indirect_mask & hole_bit))
203      {
204        if (diagnose)
205	  std::cout << ' ' << block_ptr[i] << std::flush;
206	else
207	  action(block_ptr[i], file_block_nr, data);
208      }
209  }
210  else
211    file_block_nr += EXT3_NDIR_BLOCKS;
212  if (diagnose)
213    std::cout << std::endl;
214  if (block_ptr[EXT3_IND_BLOCK] || (indirect_mask & hole_bit))
215  {
216    if (!is_block_number(block_ptr[EXT3_IND_BLOCK]))
217    {
218      std::cout << std::flush;
219      std::cerr << "\nWARNING: The indirect block number of inode " << inode_number <<
220          " (or a journal copy thereof) doesn't look like a block number (it is too large, "
221	  "block number " << EXT3_IND_BLOCK << " in it's block list is too large (" <<
222	  block_ptr[EXT3_IND_BLOCK] << ")). Treating this as if one of the indirect blocks "
223	  "were overwritten, although this is a more serious corruption." << std::endl;
224      return true;
225    }
226    if ((indirect_mask & indirect_bit) && !diagnose)
227      action(block_ptr[EXT3_IND_BLOCK], -1, data);
228    if ((indirect_mask & direct_bit))
229    {
230      if (iterate_over_all_blocks_of_indirect_block(block_ptr[EXT3_IND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
231	return true;
232    }
233  }
234  else
235    file_block_nr += limit;
236  if (block_ptr[EXT3_DIND_BLOCK] || (indirect_mask & hole_bit))
237  {
238    if (!is_block_number(block_ptr[EXT3_DIND_BLOCK]))
239    {
240      std::cout << std::flush;
241      std::cerr << "WARNING: The double indirect block number of inode " << inode_number <<
242          " (or a journal copy thereof) doesn't look like a block number (it is too large, "
243	  "block number " << EXT3_DIND_BLOCK << " in it's block list is too large (" <<
244	  block_ptr[EXT3_DIND_BLOCK] << ")). Treating this as if one of the indirect blocks "
245	  "were overwritten, although this is a more serious corruption." << std::endl;
246      return true;
247    }
248    if ((indirect_mask & indirect_bit) && !diagnose)
249      action(block_ptr[EXT3_DIND_BLOCK], -1, data);
250    if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[EXT3_DIND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
251      return true;
252  }
253  else
254    file_block_nr += limit * limit;
255  if (block_ptr[EXT3_TIND_BLOCK] || (indirect_mask & hole_bit))
256  {
257    if (!is_block_number(block_ptr[EXT3_TIND_BLOCK]))
258    {
259      std::cout << std::flush;
260      std::cerr << "WARNING: The tripple indirect block number of inode " << inode_number <<
261          " (or a journal copy thereof) doesn't look like a block number (it is too large, "
262	  "block number " << EXT3_TIND_BLOCK << " in it's block list is too large (" <<
263	  block_ptr[EXT3_TIND_BLOCK] << ")). Treating this as if one of the indirect blocks "
264	  "were overwritten, although this is a more serious corruption." << std::endl;
265      return true;
266    }
267    if ((indirect_mask & indirect_bit) && !diagnose)
268      action(block_ptr[EXT3_TIND_BLOCK], -1, data);
269    if (iterate_over_all_blocks_of_tripple_indirect_block(block_ptr[EXT3_TIND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
270      return true;
271  }
272  return false;
273}
274
275// See header file for description.
276// Define this to return false if any [bi] is zero, otherwise
277// only false is returned when the first block is zero.
278
279// This must be 0.
280#define SKIPZEROES 0
281bool is_indirect_block(unsigned char* block_ptr, bool verbose)
282{
283  // Number of 32-bit values per block.
284  int const values_per_block = block_size_ / sizeof(__le32);
285
286  // Block values.
287  uint32_t blockVals[values_per_block];
288
289  uint32_t vmin = 0xffffffff;
290  uint32_t vmax = 0;
291  bool hasZero = false;
292
293  // Search for zeroes, min and max.
294  for (int i = 0, offset = 0; i < values_per_block; ++i, offset += sizeof(__le32))
295  {
296    uint32_t v = __le32_to_cpu(read_le32(block_ptr + offset));
297    blockVals[i] = v;
298
299    if (v)
300    {
301#if SKIPZEROES
302      if (hasZero)
303      {
304	if (verbose)
305	  std::cout << "Found non-zero after zero!" << std::endl;
306        // There already was 0, now it is not 0 --- this might be an indirect block of a file with 'holes'.
307	// However, fail and return false.
308        return false;
309      }
310#endif
311      if (!is_data_block_number(v))
312      {
313        // This is not a valid block pointer.
314	if (verbose)
315	  std::cout << "Invalid block pointer!" << std::endl;
316        return false;
317      }
318
319#if !SKIPZEROES
320      if (hasZero)
321        continue;
322#endif
323
324      if (v < vmin)
325        vmin = v;
326      if (vmax < v)
327        vmax = v;
328    }
329    else
330      hasZero = true;
331  }
332
333  if (vmax == 0)
334  {
335    if (verbose)
336    {
337      std::cout << "Block with only zeroes!" << std::endl;
338      std::cout << std::flush;
339      std::cerr << "WARNING: is_indirect_block() was called for a block with ONLY zeroes. "
340	  "The correct return value depends on where we were called from. This is not "
341	  "implemented yet!" << std::endl;
342    }
343    // This should return 'true' if we're called from is_double_indirect_block or is_tripple_indirect_block:
344    // it should not lead to failure namely. In any case, we can definitely not be sure we return the
345    // correct value; a block with only zeroes can theoretically be anything.
346    return false;	// Only zeroes.
347  }
348
349  // Maximum number of bytes to allocate in an array.
350  uint32_t const max_array_size = blocks_per_group(super_block);
351
352  // [2] Search for duplicate entries.
353  if (vmax - vmin < max_array_size)
354  {
355    char t[max_array_size];
356    std::memset(t, 0, sizeof(t));
357
358    for (int i = 0; i < values_per_block; ++i)
359    {
360      uint32_t v = blockVals[i];
361      if (!v)
362        break;
363      if (t[v - vmin])
364      {
365        // Value already present!
366	if (verbose)
367	  std::cout << "Duplicated values!" << std::endl;
368        return false;
369      }
370      t[v - vmin] = 1;
371    }
372
373    return true;
374  }
375  else
376  {
377    // Block is of the form [b1], [b2], ... [bk] ZEROES, but
378    // [b1] ... [bk] spans more than one group.
379    // Use a set<> to check if they are all different.
380    std::set<uint32_t> bvSet;
381    for (int i = 0; i < values_per_block; ++i)
382    {
383      uint32_t v = blockVals[i];
384      if (!v)
385        break;
386      if (!bvSet.insert(v).second)	// Was already inserted?
387      {
388	if (verbose)
389	  std::cout << "Duplicated values!" << std::endl;
390        return false;
391      }
392    }
393    return true;
394  }
395}