Logo Search packages:      
Sourcecode: parted version File versions

clstdup.c

/*
    libparted
    Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <string.h>

#include "fat.h"

#ifndef DISCOVER_ONLY

static int
needs_duplicating (const FatOpContext* ctx, FatFragment frag)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatCluster  cluster = fat_frag_to_cluster (ctx->old_fs, frag);
      FatClusterFlag    flag;

      PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2,
                return 0);

      flag = fat_get_fragment_flag (ctx->old_fs, frag);
      switch (flag) {
      case FAT_FLAG_FREE:
            return 0;

      case FAT_FLAG_DIRECTORY:
            return 1;

      case FAT_FLAG_FILE:
            return fat_op_context_map_static_fragment (ctx, frag) == -1;
      
      case FAT_FLAG_BAD:
            return 0;
      }

      return 0;
}

static int
search_next_fragment (FatOpContext* ctx)
{
      FatSpecific*      fs_info = FAT_SPECIFIC (ctx->old_fs);

      for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) {
            if (needs_duplicating (ctx, ctx->buffer_offset))
                  return 1;
      }
      return 0;   /* all done! */
}

static int
read_marked_fragments (FatOpContext* ctx, FatFragment length)
{
      FatSpecific*            fs_info = FAT_SPECIFIC (ctx->old_fs);
      int               status;
      FatFragment       i;

      ped_exception_fetch_all ();
      status = fat_read_fragments (ctx->old_fs, fs_info->buffer,
                             ctx->buffer_offset, length);
      ped_exception_leave_all ();
      if (status)
            return 1;

      ped_exception_catch ();

/* something bad happened, so read fragments one by one.  (The error may
   have occured on an unused fragment: who cares) */
      for (i = 0; i < length; i++) {
            if (ctx->buffer_map [i]) {
                  if (!fat_read_fragment (ctx->old_fs,
                        fs_info->buffer + i * fs_info->frag_size,
                        ctx->buffer_offset + i))
                        return 0;
            }
      }

      return 1;
}

static int
fetch_fragments (FatOpContext* ctx)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatFragment fetch_length = 0;
      FatFragment frag;

      for (frag = 0; frag < ctx->buffer_frags; frag++)
            ctx->buffer_map [frag] = -1;

      for (frag = 0;
           frag < ctx->buffer_frags
            && ctx->buffer_offset + frag < old_fs_info->frag_count;
           frag++) {
            if (needs_duplicating (ctx, ctx->buffer_offset + frag)) {
                  ctx->buffer_map [frag] = 1;
                  fetch_length = frag + 1;
            }
      }

      if (!read_marked_fragments (ctx, fetch_length))
            return 0;

      return 1;
}

/*****************************************************************************
 * here starts the write code.  All assumes that ctx->buffer_map [first] and
 * ctx->buffer_map [last] are occupied by fragments that need to be duplicated.
 *****************************************************************************/

/* finds the first fragment that is not going to get overwritten (that needs to
   get read in) */
static FatFragment
get_first_underlay (const FatOpContext* ctx, int first, int last)
{
      int         old;
      FatFragment new;

      PED_ASSERT (first <= last, return 0);

      new = ctx->buffer_map [first];
      for (old = first + 1; old <= last; old++) {
            if (ctx->buffer_map [old] == -1)
                  continue;
            new++;
            if (ctx->buffer_map [old] != new)
                  return new;
      }
      return -1;
}

/* finds the last fragment that is not going to get overwritten (that needs to
   get read in) */
static FatFragment
get_last_underlay (const FatOpContext* ctx, int first, int last)
{
      int         old;
      FatFragment new;

      PED_ASSERT (first <= last, return 0);

      new = ctx->buffer_map [last];
      for (old = last - 1; old >= first; old--) {
            if (ctx->buffer_map [old] == -1)
                  continue;
            new--;
            if (ctx->buffer_map [old] != new)
                  return new;
      }
      return -1;
}

/* "underlay" refers to the "static" fragments, that remain unchanged.
 * when writing large chunks at a time, we don't want to clobber these,
 * so we read them in, and write them back again.  MUCH quicker that way.
 */
static int
quick_group_write_read_underlay (FatOpContext* ctx, int first, int last)
{
      FatSpecific*      new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      FatFragment first_underlay;
      FatFragment last_underlay;
      FatFragment underlay_length;

      PED_ASSERT (first <= last, return 0);

      first_underlay = get_first_underlay (ctx, first, last);
      if (first_underlay == -1)
            return 1;
      last_underlay = get_last_underlay (ctx, first, last);

      PED_ASSERT (first_underlay <= last_underlay, return 0);

      underlay_length = last_underlay - first_underlay + 1;
      if (!fat_read_fragments (ctx->new_fs,
                        new_fs_info->buffer 
                           + (first_underlay - ctx->buffer_map [first])
                              * new_fs_info->frag_size,
                        first_underlay,
                        underlay_length))
            return 0;
      return 1;
}

/* quick_group_write() makes no attempt to recover from errors - just
 * does things fast.  If there is an error, slow_group_write() is
 * called.
 *    Note: we do syncing writes, to make sure there isn't any
 * error writing out.  It's rather difficult recovering from errors
 * further on.
 */
static int
quick_group_write (FatOpContext* ctx, int first, int last)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      int               active_length;
      int               i;
      int               offset;

      PED_ASSERT (first <= last, return 0);

      ped_exception_fetch_all ();
      if (!quick_group_write_read_underlay (ctx, first, last))
            goto error;

      for (i = first; i <= last; i++) {
            if (ctx->buffer_map [i] == -1)
                  continue;

            offset = ctx->buffer_map [i] - ctx->buffer_map [first];
            memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size,
                  old_fs_info->buffer + i * new_fs_info->frag_size,
                  new_fs_info->frag_size);
      }

      active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1;
      if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer,
                               ctx->buffer_map [first], active_length))
            goto error;

      ped_exception_leave_all ();
      return 1;

error:
      ped_exception_catch ();
      ped_exception_leave_all ();
      return 0;
}

/* Writes fragments out, one at a time, avoiding errors on redundant writes
 * on damaged parts of the disk we already know about.  If there's an error
 * on one of the required fragments, it gets marked as bad, and a replacement
 * is found.
 */
static int
slow_group_write (FatOpContext* ctx, int first, int last)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      int               i;

      PED_ASSERT (first <= last, return 0);

      for (i = first; i <= last; i++) {
            if (ctx->buffer_map [i] == -1)
                  continue;

            while (!fat_write_sync_fragment (ctx->new_fs,
                        old_fs_info->buffer + i * old_fs_info->frag_size,
                        ctx->buffer_map [i])) {
                  fat_table_set_bad (new_fs_info->fat,
                                 ctx->buffer_map [i]);
                  ctx->buffer_map [i] = fat_table_alloc_cluster
                                    (new_fs_info->fat);
                  if (ctx->buffer_map [i] == 0)
                        return 0;
            }
      }
      return 1;
}

static int
update_remap (FatOpContext* ctx, int first, int last)
{
      int         i;

      PED_ASSERT (first <= last, return 0);

      for (i = first; i <= last; i++) {
            if (ctx->buffer_map [i] == -1)
                  continue;
            ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i];
      }

      return 1;
}

static int
group_write (FatOpContext* ctx, int first, int last)
{
      PED_ASSERT (first <= last, return 0);

      if (!quick_group_write (ctx, first, last)) {
            if (!slow_group_write (ctx, first, last))
                  return 0;
      }
      if (!update_remap (ctx, first, last))
            return 0;
      return 1;
}

/* assumes fragment size and new_fs's cluster size are equal */
static int
write_fragments (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatSpecific*            new_fs_info = FAT_SPECIFIC (ctx->new_fs);
      int               group_start;
      int               group_end = -1;   /* shut gcc up! */
      FatFragment       mapped_length;
      FatFragment       i;
      FatCluster        new_cluster;

      PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count, return 0);

      group_start = -1;
      for (i = 0; i < ctx->buffer_frags; i++) {
            if (ctx->buffer_map [i] == -1)
                  continue;

            ctx->frags_duped++;

            new_cluster = fat_table_alloc_cluster (new_fs_info->fat);
            if (!new_cluster)
                  return 0;
            fat_table_set_eof (new_fs_info->fat, new_cluster);
            ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs,
                                             new_cluster);

            if (group_start == -1)
                  group_start = group_end = i;

            PED_ASSERT (ctx->buffer_map [i]
                        >= ctx->buffer_map [group_start],
                      return 0);

            mapped_length = ctx->buffer_map [i]
                        - ctx->buffer_map [group_start] + 1;
            if (mapped_length <= ctx->buffer_frags) {
                  group_end = i;
            } else {
                  /* ran out of room in the buffer, so write this group,
                   * and start a new one...
                   */
                  if (!group_write (ctx, group_start, group_end))
                        return 0;
                  group_start = group_end = i;
            }
      }

      PED_ASSERT (group_start != -1, return 0);

      if (!group_write (ctx, group_start, group_end))
            return 0;
      return 1;
}

/*  default all fragments to unmoved
 */
static void
init_remap (FatOpContext* ctx)
{
      FatSpecific*            old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatFragment       i;

      for (i = 0; i < old_fs_info->frag_count; i++)
            ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i);
}

static FatFragment
count_frags_to_dup (FatOpContext* ctx)
{
      FatSpecific*      fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatFragment i;
      FatFragment total;

      total = 0;

      for (i = 0; i < fs_info->frag_count; i++) {
            if (needs_duplicating (ctx, i))
                  total++;
      }

      return total;
}

/*  duplicates unreachable file clusters, and all directory clusters
 */
int
fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer)
{
      FatSpecific*      old_fs_info = FAT_SPECIFIC (ctx->old_fs);
      FatFragment total_frags_to_dup;

      init_remap (ctx);
      total_frags_to_dup = count_frags_to_dup (ctx);

      ped_timer_reset (timer);
      ped_timer_set_state_name (timer, "moving data");

      ctx->buffer_offset = 0;
      ctx->frags_duped = 0;
      while (search_next_fragment (ctx)) {
            ped_timer_update (
                  timer, 1.0 * ctx->frags_duped / total_frags_to_dup);

            if (!fetch_fragments (ctx))
                  return 0;
            if (!write_fragments (ctx))
                  return 0;
            ctx->buffer_offset += ctx->buffer_frags;
      }

      ped_timer_update (timer, 1.0);
      return 1;
}

#endif /* !DISCOVER_ONLY */

Generated by  Doxygen 1.6.0   Back to index