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

xfsclone.c

/**
 * xfsclone.c - part of Partclone project
 *
 * Copyright (c) 2007~ Thomas Tsai <thomas at nchc org tw>
 *
 * read xfs super block and bitmap, reference xfs_copy.c
 *
 * 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.
 */

#include <sys/param.h>
#include <xfs/libxfs.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <malloc.h>
#include <stdarg.h>
#include <getopt.h>
#include "partclone.h"
#include "xfsclone.h"
#include "progress.h"
#include "fs_common.h"

char  *EXECNAME = "partclone.xfs";
extern  fs_cmd_opt fs_opt;
int   source_fd = -1;
int     first_residue;

xfs_mount_t     *mp;
xfs_mount_t     mbuf;
libxfs_init_t   xargs;
unsigned int    source_blocksize;       /* source filesystem blocksize */
unsigned int    source_sectorsize;      /* source disk sectorsize */

#define rounddown(x, y) (((x)/(y))*(y))

static void set_bitmap(char* bitmap, uint64_t pos, int length)
{
    uint64_t pos_block;
    uint64_t block_count;
    uint64_t block;

    pos_block   = pos/source_blocksize;
    block_count = length/source_blocksize;

    for (block = pos_block; block < pos_block+block_count; block++){
      bitmap[block] = 1;
      log_mesg(3, 0, 0, fs_opt.debug, "block %i is used\n", block);
    }

}

static void fs_open(char* device)
{

    int               open_flags;
    struct stat     statbuf;
    int               source_is_file = 0;
    xfs_buf_t       *sbp;
    xfs_sb_t        *sb;
    int             tmp_residue;

    /* open up source -- is it a file? */
    open_flags = O_RDONLY;

    if ((source_fd = open(device, open_flags)) < 0)  {
      log_mesg(0, 1, 1, fs_opt.debug, "Couldn't open source partition %s\n", device);
    }else{
      log_mesg(0, 0, 0, fs_opt.debug, "Open %s successfully", device);
      }

    if (fstat(source_fd, &statbuf) < 0)  {
      log_mesg(0, 1, 1, fs_opt.debug, "Couldn't stat source partition\n");
    }

    if (S_ISREG(statbuf.st_mode)){
      log_mesg(1, 0, 0, fs_opt.debug, "source is file\n");
      source_is_file = 1;
    }

    /* prepare the libxfs_init structure */

    memset(&xargs, 0, sizeof(xargs));
    xargs.isdirect = LIBXFS_DIRECT;
    xargs.isreadonly = LIBXFS_ISREADONLY;

    if (source_is_file)  {
      xargs.dname = device;
      xargs.disfile = 1;
    } else
      xargs.volname = device;

    if (libxfs_init(&xargs) == 0)
    {
      log_mesg(0, 1, 1, fs_opt.debug, "libxfs_init error. Please repaire %s partition.\n", device);
    }

    /* prepare the mount structure */
    sbp = libxfs_readbuf(xargs.ddev, XFS_SB_DADDR, 1, 0);
    memset(&mbuf, 0, sizeof(xfs_mount_t));
    sb = &mbuf.m_sb;
    libxfs_sb_from_disk(sb, XFS_BUF_TO_SBP(sbp));

    mp = libxfs_mount(&mbuf, sb, xargs.ddev, xargs.logdev, xargs.rtdev, 1);
    if (mp == NULL) {
      log_mesg(0, 1, 1, fs_opt.debug, "%s filesystem failed to initialize\nAborting.\n", device);
    } else if (mp->m_sb.sb_inprogress)  {
      log_mesg(0, 1, 1, fs_opt.debug, "%s filesystem failed to initialize\nAborting(inprogress).\n", device);
    } else if (mp->m_sb.sb_logstart == 0)  {
      log_mesg(0, 1, 1, fs_opt.debug, "%s has an external log.\nAborting.\n", device);
    } else if (mp->m_sb.sb_rextents != 0)  {
      log_mesg(0, 1, 1, fs_opt.debug, "%s has a real-time section.\nAborting.\n", device);
    }

    source_blocksize = mp->m_sb.sb_blocksize;
    source_sectorsize = mp->m_sb.sb_sectsize;
    log_mesg(2, 0, 0, fs_opt.debug, "source_blocksize %i source_sectorsize = %i\n", source_blocksize, source_sectorsize);

    if (source_blocksize > source_sectorsize)  {
      /* get number of leftover sectors in last block of ag header */

      tmp_residue = ((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize) % source_blocksize;
      first_residue = (tmp_residue == 0) ? 0 :  source_blocksize - tmp_residue;
      log_mesg(2, 0, 0, fs_opt.debug, "first_residue %i tmp_residue %i\n", first_residue, tmp_residue);
    } else if (source_blocksize == source_sectorsize)  {
      first_residue = 0;
    } else  {
      log_mesg(0, 1, 1, fs_opt.debug, "Error:  filesystem block size is smaller than the disk sectorsize.\nAborting XFS copy now.\n");
    }

    /* end of xfs open */
    log_mesg(3, 0, 0, fs_opt.debug, "%s: blcos size= %i\n", __FILE__, mp->m_sb.sb_blocksize);
    log_mesg(3, 0, 0, fs_opt.debug, "%s: total b= %lli\n", __FILE__, mp->m_sb.sb_dblocks);
    log_mesg(3, 0, 0, fs_opt.debug, "%s: free block= %lli\n", __FILE__, mp->m_sb.sb_fdblocks);
    log_mesg(3, 0, 0, fs_opt.debug, "%s: used block= %lli\n", __FILE__, (mp->m_sb.sb_dblocks - mp->m_sb.sb_fdblocks));
    log_mesg(3, 0, 0, fs_opt.debug, "%s: device size= %lli\n", __FILE__, (mp->m_sb.sb_blocksize * mp->m_sb.sb_dblocks));

}

static void fs_close()
{
    libxfs_device_close(xargs.ddev);
    log_mesg(0, 0, 0, fs_opt.debug, "fs_close\n");
}

extern void initial_image_hdr(char* device, image_head* image_hdr)
{
    fs_open(device);
    strncpy(image_hdr->magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE);
    strncpy(image_hdr->fs, xfs_MAGIC, FS_MAGIC_SIZE);
    image_hdr->block_size = mp->m_sb.sb_blocksize;
    image_hdr->totalblock = mp->m_sb.sb_dblocks;
    image_hdr->usedblocks = mp->m_sb.sb_dblocks - mp->m_sb.sb_fdblocks;
    image_hdr->device_size = (image_hdr->totalblock * image_hdr->block_size);
    log_mesg(1, 0, 0, fs_opt.debug, "%s: blcos size= %i\n", __FILE__, mp->m_sb.sb_blocksize);
    log_mesg(1, 0, 0, fs_opt.debug, "%s: total b= %lli\n", __FILE__, mp->m_sb.sb_dblocks);
    log_mesg(1, 0, 0, fs_opt.debug, "%s: free block= %lli\n", __FILE__, mp->m_sb.sb_fdblocks);
    log_mesg(1, 0, 0, fs_opt.debug, "%s: used block= %lli\n", __FILE__, (mp->m_sb.sb_dblocks - mp->m_sb.sb_fdblocks));
    log_mesg(1, 0, 0, fs_opt.debug, "%s: device size= %lli\n", __FILE__, (mp->m_sb.sb_blocksize*mp->m_sb.sb_dblocks));
    fs_close();

}

extern void readbitmap(char* device, image_head image_hdr, char* bitmap, int pui)
{

    xfs_agnumber_t  agno = 0;
    xfs_agblock_t   first_agbno;
    xfs_agnumber_t  num_ags;
    ag_header_t     ag_hdr;
    xfs_daddr_t     read_ag_off;
    int             read_ag_length;
    void            *read_ag_buf = NULL;
    xfs_off_t         read_ag_position;            /* xfs_types.h: typedef __s64 */
    uint64_t          sk, res, s_pos = 0;
    void            *btree_buf_data = NULL;
    int               btree_buf_length;
    xfs_off_t         btree_buf_position;
    xfs_agblock_t   bno;
    uint        current_level;
    uint        btree_levels;
    xfs_daddr_t     begin, next_begin, ag_begin, new_begin, ag_end;
                                    /* xfs_types.h: typedef __s64*/
    xfs_off_t       pos;
    xfs_alloc_ptr_t *ptr;
    xfs_alloc_rec_t *rec_ptr;
    int               length;
    int               i;
    uint64_t        size, sizeb;
    xfs_off_t         w_position;
    int               w_length;
    int               wblocks;
    int               w_size = 1 * 1024 * 1024;
    uint64_t        numblocks = 0;

    xfs_off_t         logstart, logend;
    xfs_off_t         logstart_pos, logend_pos;
    int               log_length;

    struct xfs_btree_block *block;
    uint64_t current_block, block_count;

    int start = 0;
    int bit_size = 1;
    progress_bar        prog;

    uint64_t bused = 0;
    uint64_t bfree = 0;

    /// init progress
    progress_init(&prog, start, image_hdr.totalblock, bit_size);

    fs_open(device);

    first_agbno = (((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize) + first_residue) / source_blocksize;

    num_ags = mp->m_sb.sb_agcount;

    log_mesg(1, 0, 0, fs_opt.debug, "ags = %i\n", num_ags);
    for (agno = 0; agno < num_ags ; agno++)  {
      /* read in first blocks of the ag */

      /* initial settings */
      log_mesg(2, 0, 0, fs_opt.debug, "read ag %i header\n", agno);

      read_ag_off = XFS_AG_DADDR(mp, agno, XFS_SB_DADDR);
      read_ag_length = first_agbno * source_blocksize;
      read_ag_position = (xfs_off_t) read_ag_off * (xfs_off_t) BBSIZE;
      read_ag_buf = malloc(read_ag_length);
      memset(read_ag_buf, 0, read_ag_length);

      log_mesg(2, 0, 0, fs_opt.debug, "seek to read_ag_position %lli\n", read_ag_position);
      sk = lseek(source_fd, read_ag_position, SEEK_SET);
      current_block = (sk/source_blocksize);
      block_count = (read_ag_length/source_blocksize);
      set_bitmap(bitmap, sk, read_ag_length);
      log_mesg(2, 0, 0, fs_opt.debug, "read ag header fd = %llu(%i), length = %i(%i)\n", sk, current_block, read_ag_length, block_count);
      if ((res = read(source_fd, read_ag_buf, read_ag_length)) < 0)  {
          log_mesg(1, 0, 1, fs_opt.debug, "read failure at offset %lld\n", read_ag_position);
      }

      ag_hdr.xfs_sb = (xfs_dsb_t *) (read_ag_buf);
      ASSERT(be32_to_cpu(ag_hdr.xfs_sb->sb_magicnum) == XFS_SB_MAGIC);
      ag_hdr.xfs_agf = (xfs_agf_t *) (read_ag_buf + source_sectorsize);
      ASSERT(be32_to_cpu(ag_hdr.xfs_agf->agf_magicnum) == XFS_AGF_MAGIC);
      ag_hdr.xfs_agi = (xfs_agi_t *) (read_ag_buf + 2 * source_sectorsize);
      ASSERT(be32_to_cpu(ag_hdr.xfs_agi->agi_magicnum) == XFS_AGI_MAGIC);
      ag_hdr.xfs_agfl = (xfs_agfl_t *) (read_ag_buf + 3 * source_sectorsize);
      log_mesg(2, 0, 0, fs_opt.debug, "ag header read ok\n");


      /* save what we need (agf) in the btree buffer */

      btree_buf_data = malloc(source_blocksize);
      memset(btree_buf_data, 0, source_blocksize);
      memmove(btree_buf_data, ag_hdr.xfs_agf, source_sectorsize);
      ag_hdr.xfs_agf = (xfs_agf_t *) btree_buf_data;
      btree_buf_length = source_blocksize;

      ///* traverse btree until we get to the leftmost leaf node */

      bno = be32_to_cpu(ag_hdr.xfs_agf->agf_roots[XFS_BTNUM_BNOi]);
      current_level = 0;
      btree_levels = be32_to_cpu(ag_hdr.xfs_agf->agf_levels[XFS_BTNUM_BNOi]);

      ag_end = XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(ag_hdr.xfs_agf->agf_length) - 1) + source_blocksize / BBSIZE;

      for (;;) {
          /* none of this touches the w_buf buffer */

          current_level++;

          btree_buf_position = pos = (xfs_off_t)XFS_AGB_TO_DADDR(mp,agno,bno) << BBSHIFT;
          btree_buf_length = source_blocksize;

          sk = lseek(source_fd, btree_buf_position, SEEK_SET);
          current_block = (sk/source_blocksize);
          block_count = (btree_buf_length/source_blocksize);
          set_bitmap(bitmap, sk, btree_buf_length);
          log_mesg(2, 0, 0, fs_opt.debug, "read btree sf = %llu(%i), length = %i(%i)\n", sk, current_block, btree_buf_length, block_count);
          read(source_fd, btree_buf_data, btree_buf_length);
          block = (struct xfs_btree_block *)((char *)btree_buf_data + pos - btree_buf_position);

          if (be16_to_cpu(block->bb_level) == 0)
            break;

          ptr = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
          bno = be32_to_cpu(ptr[0]);
      }
      log_mesg(2, 0, 0, fs_opt.debug, "btree read done\n");

      /* align first data copy but don't overwrite ag header */

      pos = read_ag_position >> BBSHIFT;
      length = read_ag_length >> BBSHIFT;
      next_begin = pos + length;
      ag_begin = next_begin;


      ///* handle the rest of the ag */

      for (;;) {
          if (be16_to_cpu(block->bb_level) != 0)  {
            log_mesg(0, 1, 1, fs_opt.debug, "WARNING:  source filesystem inconsistent.\nA leaf btree rec isn't a leaf. Aborting now.\n");
          }

          rec_ptr = XFS_ALLOC_REC_ADDR(mp, block, 1);
          for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++, rec_ptr++)  {
            /* calculate in daddr's */

            begin = next_begin;

            /*
             * protect against pathological case of a
             * hole right after the ag header in a
             * mis-aligned case
             */

            if (begin < ag_begin)
                begin = ag_begin;

            /*
             * round size up to ensure we copy a
             * range bigger than required
             */

            log_mesg(3, 0, 0, fs_opt.debug, "XFS_AGB_TO_DADDR = %llu, agno = %i, be32_to_cpu=%llu\n", XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(rec_ptr->ar_startblock)), agno, be32_to_cpu(rec_ptr->ar_startblock));
            sizeb = XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(rec_ptr->ar_startblock)) - begin;
            size = roundup(sizeb <<BBSHIFT, source_sectorsize);
            log_mesg(3, 0, 0, fs_opt.debug, "BB = %i size %i and sizeb %llu brgin = %llu\n", BBSHIFT, size, sizeb, begin);

            if (size > 0)  {
                /* copy extent */

                log_mesg(2, 0, 0, fs_opt.debug, "copy extent\n");
                w_position = (xfs_off_t)begin << BBSHIFT;

                while (size > 0)  {
                  /*
                   * let lower layer do alignment
                   */
                  if (size > w_size)  {
                      w_length = w_size;
                      size -= w_size;
                      sizeb -= wblocks;
                      numblocks += wblocks;
                  } else  {
                      w_length = size;
                      numblocks += sizeb;
                      size = 0;
                  }

                  //read_wbuf(source_fd, &w_buf, mp);
                  sk = lseek(source_fd, w_position, SEEK_SET);
                  current_block = (sk/source_blocksize);
                  block_count = (w_length/source_blocksize);
                  set_bitmap(bitmap, sk, w_length);
                  log_mesg(2, 0, 0, fs_opt.debug, "read ext sourcefd to w_buf source_fd=%llu(%i), length=%i(%i)\n", sk, current_block, w_length, block_count);
                  sk = lseek(source_fd, w_length, SEEK_CUR);

                  w_position += w_length;
                }
            }

            /* round next starting point down */

            new_begin = XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(rec_ptr->ar_startblock) + be32_to_cpu(rec_ptr->ar_blockcount));
            next_begin = rounddown(new_begin, source_sectorsize >> BBSHIFT);
          }

          if (be32_to_cpu(block->bb_u.s.bb_rightsib) == NULLAGBLOCK){
            log_mesg(2, 0, 0, fs_opt.debug, "NULLAGBLOCK\n");
            break;
          }

          /* read in next btree record block */

          btree_buf_position = pos = (xfs_off_t)XFS_AGB_TO_DADDR(mp, agno, be32_to_cpu(block->bb_u.s.bb_rightsib)) << BBSHIFT;
          btree_buf_length = source_blocksize;

          /* let read_wbuf handle alignment */

          //read_wbuf(source_fd, &btree_buf, mp);
          sk = lseek(source_fd, btree_buf_position, SEEK_SET);
          current_block = (sk/source_blocksize);
          block_count = (btree_buf_length/source_blocksize);
          set_bitmap(bitmap, sk, btree_buf_length);
          log_mesg(2, 0, 0, fs_opt.debug, "read btreebuf fd = %llu(%i), length = %i(%i) \n", sk, current_block, btree_buf_length, block_count);
          read(source_fd, btree_buf_data, btree_buf_length);

          block = (struct xfs_btree_block *)((char *) btree_buf_data + pos - btree_buf_position);
          ASSERT(be32_to_cpu(block->bb_magic) == XFS_ABTB_MAGIC);

      }
      /*
       * write out range of used blocks after last range
       * of free blocks in AG
       */
      if (next_begin < ag_end)  {
          begin = next_begin;

          sizeb = ag_end - begin;
          size = roundup(sizeb << BBSHIFT, source_sectorsize);

          if (size > 0)  {
            /* copy extent */

            w_position = (xfs_off_t) begin << BBSHIFT;

            while (size > 0)  {
                /*
                 * let lower layer do alignment
                 */
                if (size > w_size)  {
                  w_length = w_size;
                  size -= w_size;
                  sizeb -= wblocks;
                  numblocks += wblocks;
                } else  {
                  w_length = size;
                  numblocks += sizeb;
                  size = 0;
                }

                sk = lseek(source_fd, w_position, SEEK_SET);
                current_block = (sk/source_blocksize);
                block_count = (w_length/source_blocksize);
                set_bitmap(bitmap, sk, w_length);
                log_mesg(2, 0, 0, fs_opt.debug, "read ext fd = %llu(%i), length = %i(%i)\n", sk, current_block, w_length, block_count);
                //read_wbuf(source_fd, &w_buf, mp);
                lseek(source_fd, w_length, SEEK_CUR);
                w_position += w_length;
            }
          }
      }

      log_mesg(2, 0, 0, fs_opt.debug, "write a clean log\n");
      log_length = 1 * 1024 * 1024;
      logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
      logstart_pos = rounddown(logstart, (xfs_off_t)log_length);
      if (logstart % log_length)  {  /* unaligned */
          sk = lseek(source_fd, logstart_pos, SEEK_SET);
          current_block = (sk/source_blocksize);
          block_count = (log_length/source_blocksize);
          set_bitmap(bitmap, sk, log_length);
          log_mesg(2, 0, 0, fs_opt.debug, "read log start from %llu(%i) %i(%i)\n", sk, current_block, log_length, block_count);
          sk = lseek(source_fd, log_length, SEEK_CUR);
      }

      logend = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
      logend += XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks);
      logend_pos = rounddown(logend, (xfs_off_t)log_length);
      if (logend % log_length)  {    /* unaligned */
          sk = lseek(source_fd, logend_pos, SEEK_SET);
          current_block = (sk/source_blocksize);
          block_count = (log_length/source_blocksize);
          set_bitmap(bitmap, sk, log_length);
          log_mesg(2, 0, 0, fs_opt.debug, "read log end  from %llu(%i) %i(%i)\n", sk, current_block, log_length, block_count);
          sk = lseek(source_fd, log_length, SEEK_CUR);
      }

      log_mesg(2, 0, 0, fs_opt.debug, "write a clean log done\n");

      update_pui(&prog, (image_hdr.totalblock/num_ags*(agno+1)-1), 0);
    }

    for(current_block = 0; current_block <= image_hdr.totalblock; current_block++){
      if(bitmap[current_block] == 1)
          bused++;
      else
          bfree++;
    }
    log_mesg(0, 0, 0, fs_opt.debug, "bused = %lli, bfree = %lli\n", bused, bfree);

    fs_close();
    update_pui(&prog, 1, 1);

}


Generated by  Doxygen 1.6.0   Back to index