1N/A/*
1N/A libparted - a library for manipulating disk partitions
1N/A Copyright (C) 2004-2005, 2007, 2009-2010 Free Software Foundation,
1N/A Inc.
1N/A
1N/A This program is free software; you can redistribute it and/or modify
1N/A it under the terms of the GNU General Public License as published by
1N/A the Free Software Foundation; either version 3 of the License, or
1N/A (at your option) any later version.
1N/A
1N/A This program is distributed in the hope that it will be useful,
1N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A GNU General Public License for more details.
1N/A
1N/A You should have received a copy of the GNU General Public License
1N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
1N/A*/
1N/A
1N/A#ifndef DISCOVER_ONLY
1N/A
1N/A#include <config.h>
1N/A
1N/A#include <parted/parted.h>
1N/A#include <parted/endian.h>
1N/A#include <parted/debug.h>
1N/A#include <stdint.h>
1N/A
1N/A#if ENABLE_NLS
1N/A# include <libintl.h>
1N/A# define _(String) dgettext (PACKAGE, String)
1N/A#else
1N/A# define _(String) (String)
1N/A#endif /* ENABLE_NLS */
1N/A
1N/A#include "hfs.h"
1N/A
1N/A#include "cache.h"
1N/A
1N/Astatic HfsCPrivateCacheTable*
1N/Ahfsc_new_cachetable(unsigned int size)
1N/A{
1N/A HfsCPrivateCacheTable* ret;
1N/A
1N/A ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret));
1N/A if (!ret) return NULL;
1N/A
1N/A ret->next_cache = NULL;
1N/A ret->table_size = size;
1N/A ret->table_first_free = 0;
1N/A
1N/A ret->table = ped_malloc(sizeof(*ret->table)*size);
1N/A if (!ret->table) { free(ret); return NULL; }
1N/A memset(ret->table, 0, sizeof(*ret->table)*size);
1N/A
1N/A return ret;
1N/A}
1N/A
1N/AHfsCPrivateCache*
1N/Ahfsc_new_cache(unsigned int block_number, unsigned int file_number)
1N/A{
1N/A unsigned int cachetable_size, i;
1N/A HfsCPrivateCache* ret;
1N/A
1N/A ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret));
1N/A if (!ret) return NULL;
1N/A ret->block_number = block_number;
1N/A /* following code avoid integer overflow */
1N/A ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ?
1N/A ( block_number >> CR_SHIFT ) + 1 :
1N/A ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT
1N/A ;
1N/A
1N/A ret->linked_ref = (HfsCPrivateExtent**)
1N/A ped_malloc( sizeof(*ret->linked_ref)
1N/A * ret->linked_ref_size );
1N/A if (!ret->linked_ref) { free(ret); return NULL; }
1N/A
1N/A cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST;
1N/A if (cachetable_size < file_number) cachetable_size = (unsigned) -1;
1N/A ret->first_cachetable_size = cachetable_size;
1N/A ret->table_list = hfsc_new_cachetable(cachetable_size);
1N/A if (!ret->table_list) {
1N/A free(ret->linked_ref);
1N/A free(ret);
1N/A return NULL;
1N/A }
1N/A ret->last_table = ret->table_list;
1N/A
1N/A for (i = 0; i < ret->linked_ref_size; ++i)
1N/A ret->linked_ref[i] = NULL;
1N/A
1N/A ret->needed_alloc_size = 0;
1N/A
1N/A return ret;
1N/A}
1N/A
1N/Astatic void
1N/Ahfsc_delete_cachetable(HfsCPrivateCacheTable* list)
1N/A{
1N/A HfsCPrivateCacheTable* next;
1N/A
1N/A while (list) {
1N/A free (list->table);
1N/A next = list->next_cache;
1N/A free (list);
1N/A list = next;
1N/A }
1N/A}
1N/A
1N/Avoid
1N/Ahfsc_delete_cache(HfsCPrivateCache* cache)
1N/A{
1N/A hfsc_delete_cachetable(cache->table_list);
1N/A free(cache->linked_ref);
1N/A free(cache);
1N/A}
1N/A
1N/AHfsCPrivateExtent*
1N/Ahfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
1N/A uint32_t block, uint16_t offset, uint8_t sbb,
1N/A uint8_t where, uint8_t ref_index)
1N/A{
1N/A HfsCPrivateExtent* ext;
1N/A unsigned int idx = start >> CR_SHIFT;
1N/A
1N/A PED_ASSERT(idx < cache->linked_ref_size, return NULL);
1N/A
1N/A for (ext = cache->linked_ref[idx];
1N/A ext && start != ext->ext_start;
1N/A ext = ext->next);
1N/A
1N/A if (ext) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_ERROR,
1N/A PED_EXCEPTION_CANCEL,
1N/A _("Trying to register an extent starting at block "
1N/A "0x%X, but another one already exists at this "
1N/A "position. You should check the file system!"),
1N/A start);
1N/A return NULL;
1N/A }
1N/A
1N/A if ( cache->last_table->table_first_free
1N/A == cache->last_table->table_size ) {
1N/A cache->last_table->next_cache =
1N/A hfsc_new_cachetable( ( cache->first_cachetable_size
1N/A / CR_NEW_ALLOC_DIV )
1N/A + CR_ADD_CST );
1N/A if (!cache->last_table->next_cache)
1N/A return NULL;
1N/A cache->last_table = cache->last_table->next_cache;
1N/A }
1N/A
1N/A ext = cache->last_table->table+(cache->last_table->table_first_free++);
1N/A
1N/A ext->ext_start = start;
1N/A ext->ext_length = length;
1N/A ext->ref_block = block;
1N/A ext->ref_offset = offset;
1N/A ext->sect_by_block = sbb;
1N/A ext->where = where;
1N/A ext->ref_index = ref_index;
1N/A
1N/A ext->next = cache->linked_ref[idx];
1N/A cache->linked_ref[idx] = ext;
1N/A
1N/A cache->needed_alloc_size = cache->needed_alloc_size >
1N/A (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ?
1N/A cache->needed_alloc_size :
1N/A (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb;
1N/A
1N/A return ext;
1N/A}
1N/A
1N/AHfsCPrivateExtent*
1N/Ahfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start)
1N/A{
1N/A HfsCPrivateExtent* ret;
1N/A unsigned int idx = start >> CR_SHIFT;
1N/A
1N/A PED_ASSERT(idx < cache->linked_ref_size, return NULL);
1N/A
1N/A for (ret = cache->linked_ref[idx];
1N/A ret && start != ret->ext_start;
1N/A ret = ret->next);
1N/A
1N/A return ret;
1N/A}
1N/A
1N/A/* Can't fail if extent begining at old_start exists */
1N/A/* Returns 0 if no such extent, or on error */
1N/AHfsCPrivateExtent*
1N/Ahfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
1N/A uint32_t new_start)
1N/A{
1N/A HfsCPrivateExtent** ppext;
1N/A HfsCPrivateExtent* pext;
1N/A
1N/A unsigned int idx1 = old_start >> CR_SHIFT;
1N/A unsigned int idx2 = new_start >> CR_SHIFT;
1N/A
1N/A PED_ASSERT(idx1 < cache->linked_ref_size, return NULL);
1N/A PED_ASSERT(idx2 < cache->linked_ref_size, return NULL);
1N/A
1N/A for (pext = cache->linked_ref[idx2];
1N/A pext && new_start != pext->ext_start;
1N/A pext = pext->next);
1N/A
1N/A if (pext) {
1N/A ped_exception_throw (
1N/A PED_EXCEPTION_BUG,
1N/A PED_EXCEPTION_CANCEL,
1N/A _("Trying to move an extent from block Ox%X to block "
1N/A "Ox%X, but another one already exists at this "
1N/A "position. This should not happen!"),
1N/A old_start, new_start);
1N/A return NULL;
1N/A }
1N/A
1N/A for (ppext = &(cache->linked_ref[idx1]);
1N/A (*ppext) && old_start != (*ppext)->ext_start;
1N/A ppext = &((*ppext)->next));
1N/A
1N/A if (!(*ppext)) return NULL;
1N/A
1N/A /* removing the extent from the cache */
1N/A pext = *ppext;
1N/A (*ppext) = pext->next;
1N/A
1N/A /* change ext_start and insert the extent again */
1N/A pext->ext_start = new_start;
1N/A pext->next = cache->linked_ref[idx2];
1N/A cache->linked_ref[idx2] = pext;
1N/A
1N/A return pext;
1N/A}
1N/A
1N/A#endif /* DISCOVER_ONLY */