Logo Search packages:      
Sourcecode: hfsplus version File versions  Download package

btreecheck.c

/*
 * libhfs - library for reading and writing Macintosh HFS volumes
 *
 * This code checks the btreee structures of a HFS+ volume for correctnes.
 *
 * Copyright (C) 2000 Klaus Halfmann <klaus.halfmann@feri.de>
 * Original 1996-1998 Robert Leslie <rob@mars.org>
 * Additional work by  Brad Boyer (flar@pants.nu)  
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: btreecheck.c,v 1.1.1.1 2002/03/05 19:50:29 klaus Exp $
 */

# ifdef HAVE_CONFIG_H
#  include "config.h"
# endif
 
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <limits.h>
# include <errno.h>
# include <time.h>

# include "libhfsp.h"
# include "volume.h"
# include "fscheck.h"
# include "btree.h"
# include "record.h"
# include "hfstime.h"
# include "unicode.h"
# include "swab.h"

/** helper function to print those Apple 4 byte Signatures */
static inline void print_sig(UInt32 sig)
{
    printf("%c%c%c%c" , 
      ((char*)&sig)[0], ((char*)&sig)[1], 
      ((char*)&sig)[2], ((char*)&sig)[3]);
}

/* print the key of a record */
static void record_print_key(hfsp_cat_key* key)
{
    char buf[255]; // mh this _might_ overflow 
    unicode_uni2asc(buf, &key->name, 255);   
    printf("parent cnid         : %ld\n",   key->parent_cnid);
    printf("name                : %s\n", buf);
}

/* Check the btree header as far as possible.
 */
static int fscheck_checkbtree(btree* bt)
{
    int           result          = 0;
    btree_head* head        = &bt->head;
    UInt16  node_size   = head->node_size;
    UInt32  node_count  = head->node_count;
    UInt32  blocksize   = bt->vol->vol.blocksize;
    
    // head->depth  to be checked later
    if (node_size % HFSP_BLOCKSZ)
    {
      printf("node_size %d not a multiple of HFSP_BLOCKSZ %d\n", 
            node_size, HFSP_BLOCKSZ);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    /* This is quite ok for a newly created extends-tree 
    if (!head->root)
    {
      printf("root node must not be 0\n");
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    */
    if (head->root >= node_count)
    {
      printf("root node out of range %lX >= %lX\n",
            head->root, node_count);
      result |= FSCK_ERR; // This is really evil
    }
    if (head->leaf_head >= node_count)
    {
      printf("leaf_head out of range %lX >= %lX\n", 
            head->leaf_head, node_count);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    if (head->leaf_tail >= node_count)
    {
      printf("leaf_head out of range %lX >= %lX\n", 
            head->leaf_tail, node_count);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    if (head->max_key_len < HFSP_CAT_KEY_MIN_LEN)
    {
      printf("max key len small %d < %d\n",
            head->max_key_len, HFSP_CAT_KEY_MIN_LEN);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    if (head->max_key_len > HFSP_CAT_KEY_MAX_LEN)
    {
      printf("max key to large %d > %d\n", 
            head->max_key_len, HFSP_CAT_KEY_MAX_LEN);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    if (head->free_nodes >= node_count)
    {
      printf("free_nodes out of range %lX >= %lX\n", 
            head->free_nodes, node_count);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    // head->reserved1  nothing to check here
    if (head->clump_size % blocksize)
    {
      printf("clump_size %ld not a multiple of blocksize %ld\n", 
            head->free_nodes, blocksize);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    if (head->btree_type != 0)
    {
      printf("Unexpected btree_type %d\n" , head->btree_type);
      result |= FSCK_ERR; // dont know how to fix that by now
    }
    // head->reserved2  nothing to check here
    if (head->attributes & HFSPLUS_TREE_RESERVED)
    {
      printf("Unexpected bits in btree header node attributes %lX\n",
          head->attributes);
      result |= FSCK_ERR; // dont know how to fix that by now
    }

    return result;
}

/* print btree header node information */
static void btree_printhead(btree_head* head)
{
    UInt32 attr;
    printf("  depth       : %#X\n",  head->depth);
    printf("  root        : %#lX\n", head->root);
    printf("  leaf_count  : %#lX\n", head->leaf_count);
    printf("  leaf_head   : %#lX\n", head->leaf_head);
    printf("  leaf_tail   : %#lX\n", head->leaf_tail);
    printf("  node_size   : %#X\n",  head->node_size);
    printf("  max_key_len : %#X\n",  head->max_key_len);
    printf("  node_count  : %#lX\n", head->node_count);
    printf("  free_nodes  : %#lX\n", head->free_nodes);
    printf("  reserved1   : %#X\n",  head->reserved1);
    printf("  clump_size  : %#lX\n", head->clump_size);
    printf("  btree_type  : %#X\n",  head->btree_type);
    attr = head->attributes;
    printf("  reserved2   : %#X\n",  head->reserved2);
    if (attr & HFSPLUS_BAD_CLOSE)
        printf(" HFSPLUS_BAD_CLOSE *** ");
    else
        printf(" !HFSPLUS_BAD_CLOSE");
    if (attr & HFSPLUS_TREE_BIGKEYS)
        printf(" HFSPLUS_TREE_BIGKEYS ");
    else
        printf("  !HFSPLUS_TREE_BIGKEYS");
    if (attr & HFSPLUS_TREE_VAR_NDXKEY_SIZE)
        printf(" HFSPLUS_TREE_VAR_NDXKEY_SIZE");
    else
        printf(" !HFSPLUS_TREE_VAR_NDXKEY_SIZE");
    if (attr & HFSPLUS_TREE_UNUSED)
        printf(" HFSPLUS_TREE_UNUSED ***\n");
    printf("\n");
}

/* Dump a node descriptor to stdout */

static void print_node_desc(UInt32 nodeIndex, btree_node_desc* node)
{
    printf("Node descriptor for Node %ld\n", nodeIndex);
    printf("next     : %#lX\n", node->next);
    printf("prev     : %#lX\n", node->prev);
    printf("height   : %#X\n",  node->height);
    printf("num_rec  : %d\n",   node->num_rec);
    printf("reserved : %#X\n",  node->reserved);
    printf("height   : %#X\n",  node->height);
    switch(node->kind)
    { 
      case HFSP_NODE_NDX  :
          printf("HFSP_NODE_NDX\n");
          break;
      case HFSP_NODE_HEAD :
          printf("HFSP_NODE_HEAD\n");
          break;
      case HFSP_NODE_MAP  :
          printf("HFSP_NODE_MAP\n");
          break;
      case HFSP_NODE_LEAF :
          printf("HFSP_NODE_LEAF\n");
          break;
      default:
          printf("*** Unknown Node type ***\n");
    } 
} 

/** intialize the btree with the first entry in the fork */
static int fscheck_btree_init(btree* bt, volume* vol, hfsp_fork_raw* fork)
{ 
    void          *p;
    char          buf[vol->blksize]; 
    UInt16        node_size;
    btree_node_desc*    node = &bt->head_node;
    int                 result;
    int                 alloc_size;

    bt->vol = vol;
    bt->fork      = fork;
    p = volume_readfromfork(vol, buf, fork, 0, 1,
             HFSP_EXTENT_DATA, bt->cnid);
    if (!p)
    {
      printf("Unable to read block 1 of b*tree for cnid:%ld\n", bt->cnid);
      return FSCK_ERR;
    }
    p = btree_readnode(node, p);
    if (node->prev != 0)
    {
      printf("Backlink of header node is not zero (%lX) \n", node->prev);
      return FSCK_ERR; // ToDo: We might ignore it but ???
    }
    if (node->kind != HFSP_NODE_HEAD)
    {
      printf("Unexpected node kind (%d) for node Header\n", node->kind);
      return FSCK_ERR; // ToDo: We might ignore it but ???
    }
    p = btree_readhead(&bt->head, p);

    node_size = bt->head.node_size;
    bt->blkpernode = node_size / vol->blksize;

    if (bt->blkpernode == 0)  // maybe the other way round ?
      bt->nodeperblk = vol->blksize / node_size;
    else
    {
      if (bt->blkpernode * vol->blksize != node_size)
      {
          printf("node_size (%X) is no multiple of block size (%X)\n", 
                node_size, bt->blkpernode);
          return FSCK_ERR; // Thats fatal as of now
      }
    }
    alloc_size = node_size - HEADER_RESERVEDOFFSET; // sizeof(node_desc) + sizeof(header) 
    /* Sometimes the node_size is bigger than the volume-blocksize
     * so here I reread the node when needed */
    { // need new block for allocation
      char nodebuf[node_size];
      if (bt->blkpernode > 1)
      {
          p = volume_readfromfork(vol, nodebuf, fork, 0, bt->blkpernode,
             HFSP_EXTENT_DATA, bt->cnid);
          p = ((char*) p) + HEADER_RESERVEDOFFSET; // skip header
      }
      
      bt->alloc_bits = malloc(alloc_size);
      if (!bt->alloc_bits)
          return ENOMEM;
      memcpy(bt->alloc_bits, p, alloc_size);
    }

    result = fscheck_checkbtree(bt);
    if (fsck_data.verbose)
      btree_printhead(&bt->head);

    node_cache_init(&bt->cache, bt, bt->head.depth + EXTRA_CACHESIZE);

    return result;
}

/** Intialize catalog btree */
int fscheck_init_cat(btree* bt, volume* vol, hfsp_fork_raw* fork)
{ 
    int result = fscheck_btree_init(bt,vol,fork);     // super (...)
    bt->cnid  = HFSP_CAT_CNID;
    bt->kcomp = record_key_compare;
    bt->kread = record_readkey;
    bt->rread = record_readentry;
    bt->max_rec_size = sizeof(hfsp_cat_entry);
    return result;
}

/** Intialize catalog btree */
int fscheck_init_extent(btree* bt, volume* vol, hfsp_fork_raw* fork)
{ 
    int result = fscheck_btree_init(bt,vol,fork);     // super (...)
    bt->cnid   = HFSP_EXT_CNID;
    bt->kcomp  = record_extent_key_compare;
    bt->kread  = record_extent_readkey;
    bt->rread = record_extent_readrecord;
    bt->max_rec_size = sizeof(hfsp_extent);
    return result;
}

/* Used to create the extents btree */
int fscheck_create_extents_tree(volume* vol)
{
    btree* result = (btree*) ALLOC(btree*, sizeof(btree));
    int    retval = 0;
    if (!result)
    {
      printf("No memory for extents btree\n");
      return FSCK_ERR;
    }
    if (FSCK_FATAL & (retval =
      fscheck_init_extent(result, vol, &vol->vol.ext_file)))
    {
      vol->extents = NULL;    
      return retval;
    }
    vol->extents = result;
    return retval;
}

/* returns pointer to key given by index in current node.
 * Same as btree_key_by_index, but with more checks etc.
 *
 * Assumes that current node is not NODE_HEAD ...
 * index may be == num_rec in whcih case a pointer
 * to the first free byte is returned ...
 */   
static void* checkbtree_key_by_index(btree* bt, UInt32 node, node_buf* buf, UInt16 index)
{
    UInt16        node_size, off_pos;
    btree_record_offset offset;

    if (index > buf->desc.num_rec)  // oops out of range
    {
      fprintf(stderr,"checkbtree_key_by_index: index out of range %u > %u\n",
                index, buf->desc.num_rec);
      return NULL;
    }
    node_size         = bt->head.node_size; 
      // The offsets are found at the end of the node ...
    off_pos     = node_size - (index +1) * sizeof(btree_record_offset);
      // position of offset at end of node
   if (off_pos >= node_size)  // oops out of range
    {
      fprintf(stderr,"checkbtree_key_by_index: off_pos out of range "
            "%X >= %X\n", off_pos, node_size);
      return NULL;
    }
    offset = *((btree_record_offset*) (buf->node + off_pos));
    if (offset >= node_size)  // oops out of range
    {
      fprintf (stderr, "checkbtree_key_by_index: offset out of range %X >= %X\n",
            offset, node_size);
      return NULL;
    }
    if (fsck_data.verbose)
    {
      printf("Node %4ld, Record %2d is at pos %04X,"
             "Backptr is at offset %04X\n", node, index, offset, off_pos);
    }
    // now we have the offset and can read the key ...
#if BYTE_ORDER == LITTLE_ENDIAN
    return buf->node + bswap_16(offset);
#else
    return buf->node + offset;
#endif
}

/* Try to fix a node when the backpointers are broken.
 *
 * This is done by crawling forward as long as the keys
 * are valid and checking/adjusting the backpointers.
 * This may result in the loss of records in case a
 * key (or record) is damaged. 
 */
static int fscheck_fix_node(btree* bt, UInt32 nodeIndex)
{
    int                result  = FSCK_NOERR;
    node_buf*          node    = btree_node_by_index(bt, nodeIndex, NODE_CLEAN);
    btree_node_desc* desc    = &node->desc;
    UInt16       num_rec = desc->num_rec;
    UInt16       i;
    int                isindex = desc->kind == HFSP_NODE_NDX;
    void*        current = node->node + 0x0E; // sizeof (btree_node_desc)
    char         kbuf[bt->head.max_key_len]; // dummy key to skip over
    char         buf[bt->max_rec_size]; 
    fprintf(stderr, "Node %lu with %u records is damaged trying to fix ***\n",
            nodeIndex, num_rec);
    for (i=0; i < num_rec; i++)
    {
      void *p     = checkbtree_key_by_index(bt, nodeIndex, node, i);
      if (!p)
          return result | FSCK_ERR;
      if (p != current)
      {
          fprintf(stderr, 
            "Key %u in Node %lu is damaged "
            "rest of keys will be droppend ***\n", i,nodeIndex);
          break;
      }
      p = bt->kread(p, kbuf); // Read the key
      if (!isindex)
          p = bt->rread(p, buf);
    }
    if (i < num_rec)
    {
      fprintf(stderr, 
          "Code to drop damaged record not yet implemented ***.\n");
    }
    // ToDo: check for pointer to free area, too
    return result;
}


/* recursive function to check a node (given by its index).
 *
 * In case of an index node is descends into the subnodes.
 */
static int fscheck_btree_node(btree* bt, UInt32 nodeIndex, hfsp_key** key1, hfsp_key** key2)
{
    int                result  = FSCK_NOERR;
    node_buf*          node    = btree_node_by_index(bt, nodeIndex, NODE_CLEAN);
    btree_node_desc* desc    = &node->desc;
    UInt16       num_rec = desc->num_rec;
    UInt16       i;
    int                isindex = desc->kind == HFSP_NODE_NDX;
    hfsp_key*          tmp;
    void*        previous = ((char*)node) + 0x0E; // sizeof btree_node_desc

    if (fsck_data.verbose)
      print_node_desc(nodeIndex, desc);
    for (i=0; i<num_rec; i++)
    {
      void  *p;
      UInt32      index;

      // node may become invalid due to cache flushing
      node  = btree_node_by_index(bt, nodeIndex, NODE_CLEAN);
      p     = checkbtree_key_by_index(bt, nodeIndex, node, i);
      if (!p)
          return result | FSCK_ERR;
      if (p < previous)
      {   // This may happen when the cache entry was flushed, but per
          // design of the cache this should not happen, mmh
          printf("Backpointers in Node %ld index %d out of order "
               "(%p >= %p)\n", nodeIndex, i, p, previous);
          result |= FSCK_FSCORR;    // Hope we can correct that later
      }
      previous = p;
      p = bt->kread(p, *key1); // Read the key
      if (!p)
      {
          result |= FSCK_ERR;

          // Lets try to fix that Error ....
          fscheck_fix_node(bt, nodeIndex);
          if (fsck_data.ignoreErr)
            continue; // Hope this will work
          return result;
      }
      if ((*key2)->key_length)
      {
          int comp = bt->kcomp(*key1, *key2);
          if (comp > 0)
          {
            printf("Invalid key order in node %ld record %d\n key1=",
                  nodeIndex, i);
            record_print_key((hfsp_cat_key*) *key1);
            printf("Invalid key order key2=\n");
            record_print_key((hfsp_cat_key*) *key2);
            result |= FSCK_FSCORR;  // Hope we can correct that later
          }
          if (comp == 0 && i > 0) // equal to key in parent node is ok
          {
            printf("Duplicate key in node %ld record %d key1=\n",
                  nodeIndex, i);
            record_print_key((hfsp_cat_key*) *key1);
            printf("Duplicate key key2=\n");
            record_print_key((hfsp_cat_key*) *key2);
            result |= FSCK_FSCORR;  // Hope we can correct that later
          }
      }
      tmp   = *key1;    // Swap buffers for next compare
      *key1 = *key2;
      *key2 = tmp;
      if (isindex)
      {
          index = bswabU32_inc(p);
          result |= fscheck_btree_node(bt, index, key1, key2);
      }
      if (result & FSCK_FATAL)
          break;
    }
    return result;
}

/** Check a complete btree by traversing it in-oder */
int fscheck_btree(btree *bt)
{
    UInt16  maxkeylen = bt->head.max_key_len;
    int           result = FSCK_NOERR;
    char    keybuf1[maxkeylen];
    char    keybuf2[maxkeylen];
    hfsp_key*     key1 = (hfsp_key*) keybuf1;   // Alternating buffers
    hfsp_key*     key2 = (hfsp_key*) keybuf2;   // for key Compare
    
    key2->key_length = 0; // So first compare can be skipped

    result = fscheck_btree_node(bt, bt->head.root, &key1, &key2);

    return result;
}
    
/* print Quickdraw Point */
static void record_print_Point(Point* p)
{
    printf("[ v=%d, h=%d ]", p->v, p->h);
}

/* print Quickdraw Rect */
static void record_print_Rect(Rect* r)
{
    printf("[ top=%d, left=%d, bottom=%d, right=%d  ]",
           r->top, r->left, r->bottom, r->right);
}

/* print permissions */
static void record_print_perm(hfsp_perm* perm)
{
    printf("owner               : %ld\n",  perm->owner);
    printf("group               : %ld\n",  perm->group);
    printf("perm                : 0x%lX\n",perm->mode);
    printf("dev                 : %ld\n",  perm->dev);
}

/* print Directory info */
static void record_print_DInfo(DInfo* dinfo)
{
    printf(  "frRect              : ");    record_print_Rect(&dinfo->frRect);
    printf("\nfrFlags             : 0X%X\n",    dinfo->frFlags);
    printf(  "frLocation          : ");    record_print_Point(&dinfo->frLocation);
    printf("\nfrView              : 0X%X\n",    dinfo->frView);
}

/* print extended Directory info */
static void record_print_DXInfo(DXInfo* xinfo)
{
    printf(  "frScroll            : ");    record_print_Point(&xinfo->frScroll);
    printf("\nfrOpenChain         : %ld\n",  xinfo->frOpenChain);
    printf(  "frUnused            : %d\n",   xinfo->frUnused);
    printf(  "frComment           : %d\n",   xinfo->frComment);
    printf(  "frPutAway           : %ld\n",  xinfo->frPutAway);
}

static void record_print_folder(hfsp_cat_folder* folder)
{
    printf("flags               : 0x%X\n",      folder->flags);
    printf("valence             : 0x%lX\n",     folder->valence);
    printf("id                  : %ld\n", folder->id);
    printf("create_date         : %s",    get_atime(folder->create_date));
    printf("content_mod_date    : %s",    get_atime(folder->content_mod_date));
    printf("attribute_mod_date  : %s",    get_atime(folder->attribute_mod_date));
    printf("access_date         : %s",    get_atime(folder->access_date));
    printf("backup_date         : %s",    get_atime(folder->backup_date));
    record_print_perm   (&folder->permissions);
    record_print_DInfo  (&folder->user_info);
    record_print_DXInfo (&folder->finder_info);
    printf("text_encoding       : 0x%lX\n",     folder->text_encoding);
    printf("reserved            : 0x%lX\n",     folder->reserved);
}

/* print File info */
static void record_print_FInfo(FInfo* finfo)
{
    printf(  "fdType              : %4.4s\n", (char*) &finfo->fdType);
    printf(  "fdCreator           : %4.4s\n", (char*) &finfo->fdCreator);
    printf(  "fdFlags             : 0X%X\n", finfo->fdFlags);
    printf(  "fdLocation          : ");     record_print_Point(&finfo->fdLocation);
    printf("\nfdFldr              : %d\n",  finfo->fdFldr);
}
 
/* print extended File info */
static void record_print_FXInfo(FXInfo* xinfo)
{
    printf(  "fdIconID            : %d\n",   xinfo->fdIconID);
    // xinfo -> fdUnused;
    printf(  "fdComment           : %d\n",   xinfo->fdComment);
    printf(  "fdPutAway           : %ld\n",  xinfo->fdPutAway);
} 

/* print file entry */
static void record_print_file(hfsp_cat_file* file)
{
    printf("flags               : 0x%X\n",      file->flags);
    printf("reserved1           : 0x%lX\n",     file->reserved1);
    printf("id                  : %ld\n", file->id);
    printf("create_date         : %s",    get_atime(file->create_date));
    printf("content_mod_date    : %s",    get_atime(file->content_mod_date));
    printf("attribute_mod_date  : %s",    get_atime(file->attribute_mod_date));
    printf("access_date         : %s",    get_atime(file->access_date));
    printf("backup_date         : %s",    get_atime(file->backup_date));
    record_print_perm   (&file->permissions);
    record_print_FInfo  (&file->user_info);
    record_print_FXInfo (&file->finder_info);
    printf("text_encoding       : 0x%lX\n",     file->text_encoding);
    printf("reserved            : 0x%lX\n",     file->reserved2);
    printf("Datafork:\n");
    print_fork (&file->data_fork);
    printf("Rsrcfork:\n");
    print_fork (&file->res_fork);
}

/* print info for a file or folder thread */
static void record_print_thread(hfsp_cat_thread* entry)
{
    char buf[255]; // mh this _might_ overflow 
    unicode_uni2asc(buf, &entry->nodeName, 255);   
    printf("parent cnid         : %ld\n", entry->parentID);
    printf("name                : %s\n" , buf);
}

/* print the information for a record */
static void record_print_entry(hfsp_cat_entry* entry)
{
    switch (entry->type)
    {
      case HFSP_FOLDER:
          printf("=== Folder ===\n");
          return record_print_folder(&entry->u.folder);
      case HFSP_FILE:
          printf("=== File ===\n");
          return record_print_file  (&entry->u.file);
      case HFSP_FOLDER_THREAD:
          printf("=== Folder Thread ===\n");
          return record_print_thread(&entry->u.thread);
      case HFSP_FILE_THREAD:
          printf("=== File Thread ==\n");
          return record_print_thread(&entry->u.thread);
      default:
          printf("=== Unknown Record Type ===\n");
    };
}

/* Dump all the record information to stdout */
void record_print(record* r)
{
    printf ("*** Key index       : %u\n", r->keyind);
    record_print_key  (&r->key);
    record_print_entry(&r->record);
}

/** Check the key of a catalog record */
static int fscheck_unistr255(hfsp_unistr255* name)
{
    int result = FSCK_NOERR;

    if (name->strlen > 255)
    {
      printf("strlen in name %d > 255\n", name->strlen);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }

    return result;
}

/** Check the key of a catalog record */
static int fscheck_cat_key(record* r)
{
    int               result  = FSCK_NOERR;
    hfsp_cat_key*   key     = &r->key;
    hfsp_unistr255* name    = &key->name;
    volume*     vol         = r->tree->vol;
    UInt32      cnid    = vol->vol.next_cnid;

    result |= fscheck_unistr255(name);

    if (key->key_length != ((name->strlen << 1) + 6))
    {
      printf("key_length in key %3d does not match %3d name\n", 
                key->key_length, name->strlen);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }

    if (key->parent_cnid >= cnid)
    {
      printf("parent_cnid %ld >= volume next cnid %ld\n", 
                key->parent_cnid, cnid);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }
    
    return result;
}

/** Check a macintosh time 
 *
 *  errname is the anem of the field to show on errror
 */
static int fscheck_mactime(UInt32 time, char* errname)
{
    /* This happens so often that be better ignore it
    if (!time)
      printf("Warning %s is 0\n", errname);
    */
    if (time > fsck_data.macNow)
      printf("Warning %21.21s is in the future: (%lX) %s", 
            errname, time, get_atime(time));

    return FSCK_NOERR;  // Those are not really bad, just annoying
}

/** Check the file part of a catalog record */
static int fscheck_file(btree* tree, hfsp_cat_file* file)
{
    volume* vol       = tree->vol;
    int         result  = FSCK_NOERR;
    UInt32  cnid    = vol->vol.next_cnid;

    if (file->flags & HFSP_FILE_RESERVED)
      printf("Warning unknown file flags: %X\n", file->flags);

    if (fsck_data.maxCnid < file->id)
      fsck_data.maxCnid = file->id;

    // file->reserved1  // Nothing to check here
    if (file->id >= cnid)
    {
      printf("file id %ld >= volume next cnid %ld\n", 
                file->id, cnid);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }
    result |= fscheck_mactime(file->create_date,      "file create_date");
    result |= fscheck_mactime(file->content_mod_date, "file content_mod_date");
    result |= fscheck_mactime(file->attribute_mod_date,     "file attribute_mod_date");
    result |= fscheck_mactime(file->access_date,      "file access_date");
    result |= fscheck_mactime(file->backup_date,      "file backup_date");
    /*
    // folder->permissions // dont know how tho check these
    Nothing to be checked here (but finder may become confused, hmm)
    file->user_info;
    file->finder_info;
    file->text_encoding;
    file->reserved;
    */
    result |= check_forkalloc(vol, &file->data_fork);
    result |= check_forkalloc(vol, &file->res_fork);
    return result;
}

/** Check the folder part of a catalog record */
static int fscheck_folder(btree* tree, hfsp_cat_folder* folder)
{
    UInt32  cnid    = tree->vol->vol.next_cnid;
    int         result  = FSCK_NOERR;

    if (folder->flags & HFSP_FOLDER_RESERVED)
      printf("Warning unknown folder flags: %X\n", folder->flags);

    if (fsck_data.maxCnid < folder->id)
      fsck_data.maxCnid = folder->id;

    // folder->valence  // to be checked later
    if (folder->id >= cnid)
    {
      printf("Folder id %ld >= volume next cnid %ld\n", 
                folder->id, cnid);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }
    result |= fscheck_mactime(folder->create_date,     "folder create_date");
    result |= fscheck_mactime(folder->content_mod_date,  "folder content_mod_date");
    result |= fscheck_mactime(folder->attribute_mod_date,"folder attribute_mod_date");
    result |= fscheck_mactime(folder->access_date,     "folder access_date");
    result |= fscheck_mactime(folder->backup_date,     "folder backup_date");
    /*
    // folder->permissions // dont know how tho check these
    Nothing to be checked here (but finder may become confused, hmm)
    folder->user_info;
    folder->finder_info;
    folder->text_encoding;
    folder->reserved;
    */
    
    return result;
}

/** Check the entry part of a catalog record */
static int fscheck_thread(btree* tree, hfsp_cat_thread* thread)
{
    UInt32  cnid    = tree->vol->vol.next_cnid;
    int         result  = fscheck_unistr255(&thread->nodeName);

    if (thread->parentID >= cnid)
    {
      printf("Thread parentID %ld >= volume next cnid %ld\n", 
                thread->parentID, cnid);
      result |= FSCK_FSCORR; // hope we can fix that some time
    }
    return result;
}
 

/** Check the entry part of a catalog record */
static int fscheck_entry(record* r)
{
    hfsp_cat_entry  *entry = &r->record;
    btree       *tree  = r->tree;
    switch (entry->type)
    {
      case HFSP_FOLDER:
          return fscheck_folder(tree, &entry->u.folder);
      case HFSP_FILE:
          return fscheck_file  (tree, &entry->u.file);
      case HFSP_FOLDER_THREAD:
          return fscheck_thread(tree, &entry->u.thread);
      case HFSP_FILE_THREAD:
          return fscheck_thread(tree, &entry->u.thread);
      default:
          printf("Unknown Record Type %X\n", entry->type);
          return FSCK_FSCORR; // Hope we can fix it some time
    }
}

/** Check a record as a directory, file, extent_node etc. */
static int fscheck_record(record* r)
{
    int         result  = fscheck_cat_key(r);
    
    result |= fscheck_entry(r);
    if (fsck_data.verbose)
      record_print(r);

    return  result;
}

/* find correct node record for given node and *pindex.
 *
 * index of record in this (or next) node
 */
static node_buf* fscheck_prepare_next(btree* tree, UInt16 node_index, 
                              UInt16* pindex, int* fsckerr)
{
    node_buf*           buf = btree_node_by_index(tree, node_index, NODE_CLEAN);
    btree_node_desc*    desc;
    UInt32        numrec;

    if (!buf)
      return buf;
    desc   = &buf->desc;
    numrec = desc->num_rec;
    if (*pindex >= numrec) // move on to next node
    {
      UInt16 next = desc->next;
      *pindex = 0;
      if (!next   /* is there a next node ? */
      ||  !( buf = btree_node_by_index(tree, next, NODE_CLEAN)))
          return NULL;
        if (!btree_check_nodealloc(tree, next))
      {
          printf("node %d not allocated in node Map\n", next);
          *fsckerr = *fsckerr | FSCK_FSCORR; /* Maybe we can correct that one time */
      }
    }
    return buf;
}

/* intialize the catalog record with the given index entry in the btree. 
 *
 * Special version to check for consistency of backpointers.
 *
 * r  the record used as read buffer.
 * bt the btree we care for
 */
int fscheck_record_init(record* r, btree* bt, node_buf* buf, UInt16 index)
{
    void *p,*p1,*p2;
    int diff;
    r-> tree   = bt;
    p = p1 = checkbtree_key_by_index(bt,r->node_index,buf,index);
    if (!p)
      return -1;
    p = record_readkey  (p, &r->key);
    if (!p)
      return -1;
    p = record_readentry(p, &r->record);
    if (!p)
      return -1;
    r->node_index = buf->index;
    r-> keyind    = index;
    p2 = checkbtree_key_by_index(bt,r->node_index,buf,index+1);
    diff = (int) (p2 - p);
    if (diff)     // The difference may still be correct in case of a hole in the
    {       // structure (should happen while debugging only)
      fprintf(stderr, 
          "Unexpected difference in Node %d, Record %d "
          ": %d (%d/%d) (%p,%p)\n",
          r->node_index, index, diff , p - p1, p2 - p1, p, p2);
      record_print(r);
    }

    return 0;
}


/* move record foreward to next entry in leaf nodes.
 *
 * In case of an error the value of *r is undefined ! 
 */
int fscheck_record_next(record* r, int* fsckerr)
{
    btree*  tree  = r->tree;
    UInt16  index = r->keyind +1;
    UInt32  parent;
    node_buf*     buf   = fscheck_prepare_next(
          tree, r->node_index, &index, fsckerr);
    
    if (!buf)
      return ENOENT;    // No (more) such file or directory
    
    parent = r->key.parent_cnid;

    if (fscheck_record_init(r, tree, buf, index))
    {
      printf("Unable to read record %d in node %d"
            ,index, r->node_index);
      return -1;
    }

    return 0;
}

/** Check all files in leaf nodes */
int fscheck_files(volume* vol)
{
    int           result  = FSCK_NOERR;
    btree*  catalog = &vol->catalog;
    node_buf*     buf   = 
      btree_node_by_index(catalog,catalog->head.leaf_head, NODE_CLEAN);
    // void*      p     = btree_key_by_index(catalog,buf,0);
    record  r;

    if (!btree_check_nodealloc(catalog, catalog->head.leaf_head))
    {
      printf("leaf_head %ld not allocated in node Map\n",
            catalog->head.leaf_head);
      result |= FSCK_FSCORR; /* Maybe we can correct that one time */
    }

    if (fscheck_record_init(&r, catalog, buf, 0))
    {
      printf("Unable to read initial leaf record\n");
      return FSCK_ERR;
    }

    do {
      result |= fscheck_record(&r);
      if (result & FSCK_FATAL)
          return result;
    } while (!fscheck_record_next(&r, &result));

    return result;
}

Generated by  Doxygen 1.6.0   Back to index