/*****************************************************************************
******************************************************************************
**
** gfs2_convert - convert a gfs1 filesystem into a gfs2 filesystem.
**
******************************************************************************
*****************************************************************************/
#include "clusterautoconfig.h"
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <ctype.h>
#include <termios.h>
#include <libintl.h>
#include <locale.h>
#define _(String) gettext(String)
#include <linux/types.h>
#include <linux/gfs2_ondisk.h>
#include <logging.h>
#include "osi_list.h"
#include "copyright.cf"
#include "libgfs2.h"
/* The following declares are needed because gfs2 can't have */
/* dependencies on gfs1: */
#define RGRP_STUFFED_BLKS(sb) (((sb)->sb_bsize - sizeof(struct gfs2_rgrp)) * GFS2_NBBY)
#define RGRP_BITMAP_BLKS(sb) (((sb)->sb_bsize - sizeof(struct gfs2_meta_header)) * GFS2_NBBY)
/* Define some gfs1 constants from gfs1's gfs_ondisk.h */
#define GFS_METATYPE_NONE (0)
#define GFS_METATYPE_SB (1) /* Super-Block */
#define GFS_METATYPE_RG (2) /* Resource Group Header */
#define GFS_METATYPE_RB (3) /* Resource Group Block Alloc BitBlock */
#define GFS_METATYPE_DI (4) /* "Disk" inode (dinode) */
#define GFS_METATYPE_IN (5) /* Indirect dinode block list */
#define GFS_METATYPE_LF (6) /* Leaf dinode block list */
#define GFS_METATYPE_JD (7) /* Journal Data */
#define GFS_METATYPE_LH (8) /* Log Header (gfs_log_header) */
#define GFS_METATYPE_LD (9) /* Log Descriptor (gfs_log_descriptor) */
#define GFS_METATYPE_EA (10) /* Extended Attribute */
#define GFS_METATYPE_ED (11) /* Extended Attribute data */
/* GFS1 Dinode types */
#define GFS_FILE_NON (0)
#define GFS_FILE_REG (1) /* regular file */
#define GFS_FILE_DIR (2) /* directory */
#define GFS_FILE_LNK (5) /* link */
#define GFS_FILE_BLK (7) /* block device node */
#define GFS_FILE_CHR (8) /* character device node */
#define GFS_FILE_FIFO (101) /* fifo/pipe */
#define GFS_FILE_SOCK (102) /* socket */
#define GFS_FORMAT_SB (100) /* Super-Block */
#define GFS_FORMAT_FS (1309) /* Filesystem (all-encompassing) */
#define GFS_FORMAT_MULTI (1401) /* Multi-Host */
#define DIV_RU(x, y) (((x) + (y) - 1) / (y))
struct inode_dir_block {
osi_list_t list;
uint64_t di_addr;
uint64_t di_paddr; /* Parent dir inode addr */
};
struct inode_block {
osi_list_t list;
uint64_t di_addr;
};
struct blocklist {
osi_list_t list;
uint64_t block;
struct metapath mp;
int height;
char *ptrbuf;
};
struct gfs2_options {
char *device;
unsigned int yes:1;
unsigned int no:1;
unsigned int query:1;
};
struct gfs_sb raw_gfs1_ondisk_sb;
struct gfs2_sbd sb2;
struct inode_block dirs_to_fix; /* linked list of directories to fix */
struct inode_dir_block cdpns_to_fix; /* linked list of cdpn symlinks */
int seconds;
struct timeval tv;
uint64_t dirs_fixed;
uint64_t cdpns_fixed;
uint64_t dirents_fixed;
struct gfs_jindex *sd_jindex = NULL; /* gfs1 journal index in memory */
int gfs2_inptrs;
uint64_t gfs2_heightsize[GFS2_MAX_META_HEIGHT];
uint64_t gfs2_jheightsize[GFS2_MAX_META_HEIGHT];
uint32_t gfs2_max_height;
uint32_t gfs2_max_jheight;
uint64_t jindex_addr = 0, rindex_addr = 0;
int print_level = MSG_NOTICE;
unsigned orig_journals = 0;
/* ------------------------------------------------------------------------- */
/* This function is for libgfs's sake. */
/* ------------------------------------------------------------------------- */
void print_it(const char *label, const char *fmt, const char *fmt2, ...)
{
va_list args;
va_start(args, fmt2);
printf("%s: ", label);
vprintf(fmt, args);
va_end(args);
}
/* ------------------------------------------------------------------------- */
/* convert_bitmaps - Convert gfs1 bitmaps to gfs2 bitmaps. */
/* Fixes all unallocated metadata bitmap states (which are */
/* valid in gfs1 but invalid in gfs2). */
/* ------------------------------------------------------------------------- */
static void convert_bitmaps(struct gfs2_sbd *sdp, struct rgrp_tree *rg)
{
uint32_t blk;
int x, y;
struct gfs2_rindex *ri;
unsigned char state;
ri = &rg->ri;
for (blk = 0; blk < ri->ri_length; blk++) {
struct gfs2_bitmap *bi;
x = (blk) ? sizeof(struct gfs2_meta_header) :
sizeof(struct gfs2_rgrp);
bi = &rg->bits[blk];
for (; x < sdp->bsize; x++)
for (y = 0; y < GFS2_NBBY; y++) {
state = (bi->bi_bh->b_data[x] >>
(GFS2_BIT_SIZE * y)) & 0x03;
if (state == 0x02) {/* unallocated metadata state invalid */
bi->bi_bh->b_data[x] &= ~(0x02 << (GFS2_BIT_SIZE * y));
bmodified(bi->bi_bh);
}
}
}
}/* convert_bitmaps */
/* ------------------------------------------------------------------------- */
/* convert_rgs - Convert gfs1 resource groups to gfs2. */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int convert_rgs(struct gfs2_sbd *sbp)
{
struct rgrp_tree *rgd;
struct osi_node *n, *next = NULL;
struct gfs_rgrp *rgd1;
int rgs = 0;
/* --------------------------------- */
/* Now convert its rgs into gfs2 rgs */
/* --------------------------------- */
for (n = osi_first(&sbp->rgtree); n; n = next) {
next = osi_next(n);
rgd = (struct rgrp_tree *)n;
rgd1 = (struct gfs_rgrp *)&rgd->rg; /* recast as gfs1 structure */
/* rg_freemeta is a gfs1 structure, so libgfs2 doesn't know to */
/* convert from be to cpu. We must do it now. */
rgd->rg.rg_free = rgd1->rg_free + be32_to_cpu(rgd1->rg_freemeta);
/* Zero it out so we don't add it again in case something breaks */
/* later on in the process and we have to re-run convert */
rgd1->rg_freemeta = 0;
sbp->blks_total += rgd->ri.ri_data;
sbp->blks_alloced += (rgd->ri.ri_data - rgd->rg.rg_free);
sbp->dinodes_alloced += rgd1->rg_useddi;
convert_bitmaps(sbp, rgd);
/* Write the updated rgrp to the gfs2 buffer */
gfs2_rgrp_out(&rgd->rg, rgd->bits[0].bi_bh->b_data);
bmodified(rgd->bits[0].bi_bh);
rgs++;
if (rgs % 100 == 0) {
printf(".");
fflush(stdout);
}
}
return 0;
}/* superblock_cvt */
/* ------------------------------------------------------------------------- */
/* calc_gfs2_tree_height - calculate new dinode height as if this is gfs2 */
/* */
/* This is similar to calc_tree_height in libgfs2 but at the point this */
/* function is called, I have the wrong (gfs1 not gfs2) constants in place. */
/* ------------------------------------------------------------------------- */
static unsigned int calc_gfs2_tree_height(struct gfs2_inode *ip, uint64_t size)
{
uint64_t *arr;
unsigned int max, height;
if (ip->i_di.di_size > size)
size = ip->i_di.di_size;
if (S_ISDIR(ip->i_di.di_mode)) {
arr = gfs2_jheightsize;
max = gfs2_max_jheight;
} else {
arr = gfs2_heightsize;
max = gfs2_max_height;
}
for (height = 0; height < max; height++)
if (arr[height] >= size)
break;
/* If calc_gfs2_tree_height was called, the dinode is not stuffed or
we would have returned before this point. After the call, a call is
made to fix_metatree, which unstuffs the dinode. Therefore, the
smallest height that can result after this call is 1. */
if (!height)
height = 1;
return height;
}
/* ------------------------------------------------------------------------- */
/* mp_gfs1_to_gfs2 - convert a gfs1 metapath to a gfs2 metapath. */
/* ------------------------------------------------------------------------- */
static void mp_gfs1_to_gfs2(struct gfs2_sbd *sbp, int gfs1_h, int gfs2_h,
struct metapath *gfs1mp, struct metapath *gfs2mp)
{
uint64_t lblock;
int h;
uint64_t gfs1factor[GFS2_MAX_META_HEIGHT];
uint64_t gfs2factor[GFS2_MAX_META_HEIGHT];
/* figure out multiplication factors for each height - gfs1 */
memset(&gfs1factor, 0, sizeof(gfs1factor));
gfs1factor[gfs1_h - 1] = 1ull;
for (h = gfs1_h - 1; h > 0; h--)
gfs1factor[h - 1] = gfs1factor[h] * sbp->sd_inptrs;
/* figure out multiplication factors for each height - gfs2 */
memset(&gfs2factor, 0, sizeof(gfs2factor));
gfs2factor[gfs2_h - 1] = 1ull;
for (h = gfs2_h - 1; h > 0; h--)
gfs2factor[h - 1] = gfs2factor[h] * gfs2_inptrs;
/* Convert from gfs1 to a logical block */
lblock = 0;
for (h = 0; h < gfs1_h; h++)
lblock += (gfs1mp->mp_list[h] * gfs1factor[h]);
/* Convert from a logical block back to gfs2 */
memset(gfs2mp, 0, sizeof(*gfs2mp));
for (h = 0; h < gfs2_h; h++) {
/* Can't use do_div here because the factors are too large. */
gfs2mp->mp_list[h] = lblock / gfs2factor[h];
lblock %= gfs2factor[h];
}
}
/* ------------------------------------------------------------------------- */
/* fix_metatree - Fix up the metatree to match the gfs2 metapath info */
/* Similar to gfs2_writei in libgfs2 but we're only */
/* interested in rearranging the metadata while leaving the */
/* actual data blocks intact. */
/* ------------------------------------------------------------------------- */
static void fix_metatree(struct gfs2_sbd *sbp, struct gfs2_inode *ip,
struct blocklist *blk, uint64_t *first_nonzero_ptr,
unsigned int size)
{
uint64_t block;
struct gfs2_buffer_head *bh;
unsigned int amount, ptramt;
int hdrsize, h, copied = 0, new;
struct gfs2_meta_header mh;
char *srcptr = (char *)first_nonzero_ptr;
mh.mh_magic = GFS2_MAGIC;
mh.mh_type = GFS2_METATYPE_IN;
mh.mh_format = GFS2_FORMAT_IN;
if (!ip->i_di.di_height)
unstuff_dinode(ip);
ptramt = blk->mp.mp_list[blk->height] * sizeof(uint64_t);
amount = size;
while (copied < size) {
bh = ip->i_bh;
/* First, build up the metatree */
for (h = 0; h < blk->height; h++) {
new = 0;
lookup_block(ip, bh, h, &blk->mp, 1, &new, &block);
if (bh != ip->i_bh)
brelse(bh);
if (!block)
break;
bh = bread(sbp, block);
if (new)
memset(bh->b_data, 0, sbp->bsize);
gfs2_meta_header_out(&mh, bh->b_data);
bmodified(bh);
}
hdrsize = blk->height ? sizeof(struct gfs2_meta_header) :
sizeof(struct gfs2_dinode);
if (amount > sbp->bsize - hdrsize - ptramt)
amount = sbp->bsize - hdrsize - ptramt;
memcpy(bh->b_data + hdrsize + ptramt, (char *)srcptr, amount);
srcptr += amount;
bmodified(bh);
if (bh != ip->i_bh)
brelse(bh);
copied += amount;
if (hdrsize + ptramt + amount >= sbp->bsize) {
/* advance to the next metablock */
blk->mp.mp_list[blk->height] +=
(amount / sizeof(uint64_t));
for (h = blk->height; h > 0; h--) {
if (blk->mp.mp_list[h] >= gfs2_inptrs) {
blk->mp.mp_list[h] = 0;
blk->mp.mp_list[h - 1]++;
continue;
}
break;
}
}
amount = size - copied;
ptramt = 0;
}
}
/* ------------------------------------------------------------------------- */
/* adjust_indirect_blocks - convert all gfs_indirect blocks to gfs2. */
/* */
/* This function converts all gfs_indirect blocks to GFS2. The difference */
/* is that gfs1 indirect block has a 64-byte chunk of reserved space that */
/* gfs2 does not. Since GFS block locations (relative to the start of the */
/* file have their locations defined by the offset from the end of the */
/* structure, all block pointers must be shifted. */
/* */
/* Stuffed inodes don't need to be shifted at since there are no indirect */
/* blocks. Inodes with height 1 don't need to be shifted either, because */
/* the dinode size is the same between gfs and gfs2 (232 bytes), and */
/* therefore you can fit the same number of block pointers after the dinode */
/* structure. For the normal 4K block size, that's 483 pointers. For 1K */
/* blocks, it's 99 pointers. */
/* */
/* At height 2 things get complex. GFS1 reserves an area of 64 (0x40) bytes */
/* at the start of the indirect block, so for 4K blocks, you can fit 501 */
/* pointers. GFS2 doesn't reserve that space, so you can fit 509 pointers. */
/* For 1K blocks, it's 117 pointers in GFS1 and 125 in GFS2. */
/* */
/* That means, for example, that if you have 4K blocks, a 946MB file will */
/* require a height of 3 for GFS, but only a height of 2 for GFS2. */
/* There isn't a good way to shift the pointers around from one height to */
/* another, so the only way to do it is to rebuild all those indirect blocks */
/* from empty ones. */
/* */
/* For example, with a 1K block size, if you do: */
/* */
/* dd if=/mnt/gfs/big of=/tmp/tocompare skip=496572346368 bs=1024 count=1 */
/* */
/* the resulting metadata paths will look vastly different for the data: */
/* */
/* height 0 1 2 3 4 5 */
/* GFS1: 0x16 0x4b 0x70 0x11 0x5e 0x48 */
/* GFS2: 0x10 0x21 0x78 0x05 0x14 0x76 */
/* */
/* To complicate matters, we can't really require free space. A user might */
/* be trying to migrate a "full" gfs1 file system to GFS2. After we */
/* convert the journals to GFS2, we might have more free space, so we can */
/* allocate blocks at that time. */
/* */
/* Assumes: GFS1 values are in place for diptrs and inptrs. */
/* */
/* Returns: 0 on success, -1 on failure */
/* */
/* Adapted from fsck.gfs2 metawalk.c's build_and_check_metalist */
/* ------------------------------------------------------------------------- */
static void jdata_mp_gfs1_to_gfs2(struct gfs2_sbd *sbp, int gfs1_h, int gfs2_h,
struct metapath *gfs1mp, struct metapath *gfs2mp,
unsigned int *len, uint64_t dinode_size)
{
uint64_t offset;
int h;
uint64_t gfs1factor[GFS2_MAX_META_HEIGHT];
uint64_t gfs2factor[GFS2_MAX_META_HEIGHT];
/* figure out multiplication factors for each height - gfs1 */
memset(&gfs1factor, 0, sizeof(gfs1factor));
gfs1factor[gfs1_h - 1] = sbp->bsize - sizeof(struct gfs2_meta_header);
for (h = gfs1_h - 1; h > 0; h--)
gfs1factor[h - 1] = gfs1factor[h] * sbp->sd_inptrs;
/* figure out multiplication factors for each height - gfs2 */
memset(&gfs2factor, 0, sizeof(gfs2factor));
gfs2factor[gfs2_h] = 1ull;
gfs2factor[gfs2_h - 1] = sbp->bsize;
for (h = gfs2_h - 1; h > 0; h--)
gfs2factor[h - 1] = gfs2factor[h] * gfs2_inptrs;
/* Convert from gfs1 to an offset */
offset = 0;
for (h = 0; h < gfs1_h; h++)
offset += (gfs1mp->mp_list[h] * gfs1factor[h]);
if (dinode_size - offset < *len)
*len = dinode_size - offset;
/* Convert from an offset back to gfs2 */
memset(gfs2mp, 0, sizeof(*gfs2mp));
for (h = 0; h <= gfs2_h; h++) {
/* Can't use do_div here because the factors are too large. */
gfs2mp->mp_list[h] = offset / gfs2factor[h];
offset %= gfs2factor[h];
}
}
static uint64_t fix_jdatatree(struct gfs2_sbd *sbp, struct gfs2_inode *ip,
struct blocklist *blk, char *srcptr,
unsigned int size)
{
uint64_t block;
struct gfs2_buffer_head *bh;
unsigned int amount, ptramt;
int h, copied = 0, new = 0;
struct gfs2_meta_header mh;
mh.mh_magic = GFS2_MAGIC;
mh.mh_type = GFS2_METATYPE_IN;
mh.mh_format = GFS2_FORMAT_IN;
if (!ip->i_di.di_height)
unstuff_dinode(ip);
ptramt = blk->mp.mp_list[blk->height];
amount = size;
while (copied < size) {
bh = ip->i_bh;
/* First, build up the metatree */
for (h = 0; h < blk->height; h++) {
new = 0;
lookup_block(ip, bh, h, &blk->mp, 1, &new, &block);
if (bh != ip->i_bh)
brelse(bh);
if (!block)
break;
bh = bread(sbp, block);
if (new)
memset(bh->b_data, 0, sbp->bsize);
if (h < (blk->height - 1)) {
gfs2_meta_header_out(&mh, bh->b_data);
bmodified(bh);
}
}
if (amount > sbp->bsize - ptramt)
amount = sbp->bsize - ptramt;
memcpy(bh->b_data + ptramt, (char *)srcptr, amount);
srcptr += amount;
bmodified(bh);
if (bh != ip->i_bh)
brelse(bh);
copied += amount;
if (ptramt + amount >= sbp->bsize) {
/* advance to the next metablock */
blk->mp.mp_list[blk->height] += amount;
for (h = blk->height; h > 0; h--) {
if (blk->mp.mp_list[h] >= gfs2_inptrs) {
blk->mp.mp_list[h] = 0;
blk->mp.mp_list[h - 1]++;
continue;
}
break;
}
}
amount = size - copied;
ptramt = 0;
}
return block;
}
static int get_inode_metablocks(struct gfs2_sbd *sbp, struct gfs2_inode *ip, struct blocklist *blocks)
{
struct blocklist *blk, *newblk;
struct gfs2_buffer_head *bh, *dibh = ip->i_bh;
osi_list_t *tmp;
uint64_t *ptr1, block;
int h, ptrnum;
int bufsize = sbp->bsize - sizeof(struct gfs_indirect);
/* Add dinode block to the list */
blk = malloc(sizeof(struct blocklist));
if (!blk) {
log_crit(_("Error: Can't allocate memory for indirect block fix\n"));
return -1;
}
memset(blk, 0, sizeof(*blk));
blk->block = dibh->b_blocknr;
blk->ptrbuf = malloc(bufsize);
if (!blk->ptrbuf) {
log_crit(_("Error: Can't allocate memory"
" for file conversion.\n"));
free(blk);
return -1;
}
memset(blk->ptrbuf, 0, bufsize);
/* Fill in the pointers from the dinode buffer */
memcpy(blk->ptrbuf, dibh->b_data + sizeof(struct gfs_dinode),
sbp->bsize - sizeof(struct gfs_dinode));
/* Zero out the pointers so we can fill them in later. */
memset(dibh->b_data + sizeof(struct gfs_dinode), 0,
sbp->bsize - sizeof(struct gfs_dinode));
osi_list_add_prev(&blk->list, &blocks->list);
/* Now run the metadata chain and build lists of all metadata blocks */
osi_list_foreach(tmp, &blocks->list) {
blk = osi_list_entry(tmp, struct blocklist, list);
if (blk->height >= ip->i_di.di_height - 1)
continue;
for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
if (!*ptr1)
continue;
block = be64_to_cpu(*ptr1);
newblk = malloc(sizeof(struct blocklist));
if (!newblk) {
log_crit(_("Error: Can't allocate memory for indirect block fix.\n"));
return -1;
}
memset(newblk, 0, sizeof(*newblk));
newblk->ptrbuf = malloc(bufsize);
if (!newblk->ptrbuf) {
/* FIXME: This message should be different, to not conflit with the above file conversion */
log_crit(_("Error: Can't allocate memory for file conversion.\n"));
free(newblk);
return -1;
}
memset(newblk->ptrbuf, 0, bufsize);
newblk->block = block;
newblk->height = blk->height + 1;
/* Build the metapointer list from our predecessors */
for (h = 0; h < blk->height; h++)
newblk->mp.mp_list[h] = blk->mp.mp_list[h];
newblk->mp.mp_list[h] = ptrnum;
/* Queue it to be processed later on in the loop. */
osi_list_add_prev(&newblk->list, &blocks->list);
/* read the new metadata block's pointers */
bh = bread(sbp, block);
memcpy(newblk->ptrbuf, bh->b_data + sizeof(struct gfs_indirect), bufsize);
/* Zero the buffer so we can fill it in later */
memset(bh->b_data + sizeof(struct gfs_indirect), 0, bufsize);
bmodified(bh);
brelse(bh);
/* Free the block so we can reuse it. This allows us to
convert a "full" file system. */
ip->i_di.di_blocks--;
gfs2_free_block(sbp, block);
}
}
return 0;
}
static int fix_ind_reg_or_dir(struct gfs2_sbd *sbp, struct gfs2_inode *ip, uint32_t di_height,
uint32_t gfs2_hgt, struct blocklist *blk, struct blocklist *blocks)
{
unsigned int len, bufsize;
uint64_t *ptr1, *ptr2;
int ptrnum;
struct metapath gfs2mp;
bufsize = sbp->bsize - sizeof(struct gfs_indirect);
len = bufsize;
/* Skip zero pointers at the start of the buffer. This may
seem pointless, but the gfs1 blocks won't align with the
gfs2 blocks. That means that a single block write of
gfs1's pointers is likely to span two blocks on gfs2.
That's a problem if the file system is full.
So I'm trying to truncate the data at the start and end
of the buffers (i.e. write only what we need to). */
for (ptr1 = (uint64_t *)blk->ptrbuf, ptrnum = 0;
ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
if (*ptr1 != 0x00)
break;
len -= sizeof(uint64_t);
}
/* Skip zero bytes at the end of the buffer */
ptr2 = (uint64_t *)(blk->ptrbuf + bufsize) - 1;
while (len > 0 && *ptr2 == 0) {
ptr2--;
len -= sizeof(uint64_t);
}
blk->mp.mp_list[di_height - 1] = ptrnum;
mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &blk->mp, &gfs2mp);
memcpy(&blk->mp, &gfs2mp, sizeof(struct metapath));
blk->height -= di_height - gfs2_hgt;
if (len) {
fix_metatree(sbp, ip, blk, ptr1, len);
ip->i_di.di_goal_meta = be64_to_cpu(*ptr2);
}
return 0;
}
static int fix_ind_jdata(struct gfs2_sbd *sbp, struct gfs2_inode *ip, uint32_t di_height,
uint32_t gfs2_hgt, uint64_t dinode_size, struct blocklist *blk,
struct blocklist *blocks)
{
/*FIXME: Messages here should be different, to not conflit with messages in get_inode_metablocks */
struct blocklist *newblk;
unsigned int len, bufsize;
uint64_t *ptr1, block;
int ptrnum, h;
struct metapath gfs2mp;
struct gfs2_buffer_head *bh;
bufsize = sbp->bsize - sizeof(struct gfs2_meta_header);
/*
* For each metadata block that holds jdata block pointers,
* get the blk pointers and copy them block by block
*/
for (ptr1 = (uint64_t *) blk->ptrbuf, ptrnum = 0;
ptrnum < sbp->sd_inptrs; ptr1++, ptrnum++) {
if (!*ptr1)
continue;
block = be64_to_cpu(*ptr1);
newblk = malloc(sizeof(struct blocklist));
if (!newblk) {
log_crit(_("Error: Can't allocate memory for indirect block fix.\n"));
return -1;
}
memset(newblk, 0, sizeof(*newblk));
newblk->ptrbuf = malloc(bufsize);
if (!newblk->ptrbuf) {
log_crit(_("Error: Can't allocate memory for file conversion.\n"));
free(newblk);
return -1;
}
memset(newblk->ptrbuf, 0, bufsize);
newblk->block = block;
newblk->height = blk->height + 1;
/* Build the metapointer list from our predecessors */
for (h=0; h < blk->height; h++)
newblk->mp.mp_list[h] = blk->mp.mp_list[h];
newblk->mp.mp_list[h] = ptrnum;
bh = bread(sbp, block);
/* This is a data block. i.e newblk->height == ip->i_di.di_height */
/* read in the jdata block */
memcpy(newblk->ptrbuf, bh->b_data +
sizeof(struct gfs2_meta_header), bufsize);
memset(bh->b_data + sizeof(struct gfs2_meta_header), 0, bufsize);
bmodified(bh);
brelse(bh);
/* Free the block so we can reuse it. This allows us to
convert a "full" file system */
ip->i_di.di_blocks--;
gfs2_free_block(sbp, block);
len = bufsize;
jdata_mp_gfs1_to_gfs2(sbp, di_height, gfs2_hgt, &newblk->mp, &gfs2mp,
&len, dinode_size);
memcpy(&newblk->mp, &gfs2mp, sizeof(struct metapath));
newblk->height -= di_height - gfs2_hgt;
if (len)
ip->i_di.di_goal_meta = fix_jdatatree(sbp, ip, newblk,
newblk->ptrbuf, len);
free(newblk->ptrbuf);
free(newblk);
}
return 0;
}
static int adjust_indirect_blocks(struct gfs2_sbd *sbp, struct gfs2_inode *ip)
{
uint64_t dinode_size;
uint32_t gfs2_hgt, di_height;
osi_list_t *tmp=NULL, *x;
struct blocklist blocks, *blk;
int error = 0;
int isdir = S_ISDIR(ip->i_di.di_mode); /* is always jdata */
int isjdata = ((GFS2_DIF_JDATA & ip->i_di.di_flags) && !isdir);
int isreg = (!isjdata && !isdir);
int issys = (GFS2_DIF_SYSTEM & ip->i_di.di_flags);
/* regular files and dirs are same upto height=2
jdata files (not dirs) are same only when height=0 */
if (((isreg||isdir) && ip->i_di.di_height <= 1) ||
(isjdata && ip->i_di.di_height == 0)) {
if (!issys)
ip->i_di.di_goal_meta = ip->i_di.di_num.no_addr;
return 0; /* nothing to do */
}
osi_list_init(&blocks.list);
error = get_inode_metablocks(sbp, ip, &blocks);
if (error)
goto out;
/* The gfs2 height may be different. We need to rebuild the
metadata tree to the gfs2 height. */
gfs2_hgt = calc_gfs2_tree_height(ip, ip->i_di.di_size);
/* Save off the size because we're going to empty the contents
and add the data blocks back in later. */
dinode_size = ip->i_di.di_size;
ip->i_di.di_size = 0ULL;
di_height = ip->i_di.di_height;
ip->i_di.di_height = 0;
/* Now run through the block list a second time. If the block
is a data block, rewrite the data to the gfs2 offset. */
osi_list_foreach_safe(tmp, &blocks.list, x) {
blk = osi_list_entry(tmp, struct blocklist, list);
/* If it's not metadata that holds data block pointers
(i.e. metadata pointing to other metadata) */
if (blk->height != di_height - 1) {
osi_list_del(tmp);
free(blk->ptrbuf);
free(blk);
continue;
}
if (isreg || isdir) /* more or less same way to deal with either */
error = fix_ind_reg_or_dir(sbp, ip, di_height,
gfs2_hgt, blk, &blocks);
else if (isjdata)
error = fix_ind_jdata(sbp, ip, di_height, gfs2_hgt,
dinode_size, blk, &blocks);
if (error)
goto out;
osi_list_del(tmp);
free(blk->ptrbuf);
free(blk);
}
ip->i_di.di_size = dinode_size;
/* Set the new dinode height, which may or may not have changed. */
/* The caller will take it from the ip and write it to the buffer */
ip->i_di.di_height = gfs2_hgt;
return error;
out:
while (!osi_list_empty(&blocks.list)) {
blk = osi_list_entry(tmp, struct blocklist, list);
osi_list_del(&blocks.list);
free(blk->ptrbuf);
free(blk);
}
return error;
}
const char *cdpn[14] = {"{hostname}", "{mach}", "{os}", "{uid}", "{gid}", "{sys}", "{jid}",
"@hostname", "@mach", "@os", "@uid", "@gid", "@sys", "@jid"};
static int has_cdpn(const char *str)
{
int i;
for (i=0; i<14; i++)
if (strstr(str, cdpn[i]) != NULL)
return 1;
return 0;
}
static int fix_cdpn_symlink(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh, struct gfs2_inode *ip)
{
char *linkptr = NULL;
if (ip->i_di.di_height != 0)
return 0;
linkptr = bh->b_data + sizeof(struct gfs_dinode);
if (has_cdpn(linkptr)) {
struct inode_dir_block *fix;
/* Save the symlink di_addr. We'll find the parent di_addr later */
fix = malloc(sizeof(struct inode_dir_block));
if (!fix) {
log_crit(_("Error: out of memory.\n"));
return -1;
}
memset(fix, 0, sizeof(struct inode_dir_block));
fix->di_addr = ip->i_di.di_num.no_addr;
osi_list_add_prev((osi_list_t *)&fix->list,
(osi_list_t *)&cdpns_to_fix);
}
return 0;
}
/*
* fix_xattr -
* Extended attributes can be either direct (in the ip->i_di.di_eattr block) or
* then can be at a maximum of 1 indirect level. Multiple levels of indirection
* are not supported. If the di_eattr block contains extended attribute data,
* i.e block type = GFS_METATYPE_EA, we ignore it.
* If the di_eattr block contains block pointers to extended attributes we need
* to fix the header. gfs1 uses gfs_indirect as the header which is 64 bytes
* bigger than gfs2_meta_header that gfs2 uses.
*/
static int fix_xattr(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh, struct gfs2_inode *ip)
{
int len, old_hdr_sz, new_hdr_sz;
struct gfs2_buffer_head *eabh;
char *buf;
/* Read in the i_di.di_eattr block */
eabh = bread(sbp, ip->i_di.di_eattr);
if (!gfs2_check_meta(eabh, GFS_METATYPE_IN)) {/* if it is an indirect block */
len = sbp->bsize - sizeof(struct gfs_indirect);
buf = malloc(len);
if (!buf) {
/*FIXME: Same message as fix_cdpn_symlink */
log_crit(_("Error: out of memory.\n"));
return -1;
}
old_hdr_sz = sizeof(struct gfs_indirect);
new_hdr_sz = sizeof(struct gfs2_meta_header);
memcpy(buf, eabh->b_data + old_hdr_sz, sbp->bsize - old_hdr_sz);
memset(eabh->b_data + new_hdr_sz, 0, sbp->bsize - new_hdr_sz);
memcpy(eabh->b_data + new_hdr_sz, buf, len);
free(buf);
bmodified(eabh);
}
brelse(eabh);
return 0;
}
/* ------------------------------------------------------------------------- */
/* adjust_inode - change an inode from gfs1 to gfs2 */
/* */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int adjust_inode(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh)
{
struct gfs2_inode *inode;
struct inode_block *fixdir;
int inode_was_gfs1;
inode = lgfs2_gfs_inode_get(sbp, bh);
if (inode == NULL) {
log_crit(_("Error reading inode: %s\n"), strerror(errno));
return -1;
}
inode_was_gfs1 = (inode->i_di.di_num.no_formal_ino ==
inode->i_di.di_num.no_addr);
/* Fix the inode number: */
inode->i_di.di_num.no_formal_ino = sbp->md.next_inum;
/* Fix the inode type: gfs1 uses di_type, gfs2 uses di_mode. */
inode->i_di.di_mode &= ~S_IFMT;
switch (inode->i_di.__pad1) { /* formerly di_type */
case GFS_FILE_DIR: /* directory */
inode->i_di.di_mode |= S_IFDIR;
/* Add this directory to the list of dirs to fix later. */
fixdir = malloc(sizeof(struct inode_block));
if (!fixdir) {
/*FIXME: Same message as fix_cdpn_symlink */
log_crit(_("Error: out of memory.\n"));
goto err_freei;
}
memset(fixdir, 0, sizeof(struct inode_block));
fixdir->di_addr = inode->i_di.di_num.no_addr;
osi_list_add_prev((osi_list_t *)&fixdir->list,
(osi_list_t *)&dirs_to_fix);
break;
case GFS_FILE_REG: /* regular file */
inode->i_di.di_mode |= S_IFREG;
break;
case GFS_FILE_LNK: /* symlink */
inode->i_di.di_mode |= S_IFLNK;
break;
case GFS_FILE_BLK: /* block device */
inode->i_di.di_mode |= S_IFBLK;
break;
case GFS_FILE_CHR: /* character device */
inode->i_di.di_mode |= S_IFCHR;
break;
case GFS_FILE_FIFO: /* fifo / pipe */
inode->i_di.di_mode |= S_IFIFO;
break;
case GFS_FILE_SOCK: /* socket */
inode->i_di.di_mode |= S_IFSOCK;
break;
}
/* ----------------------------------------------------------- */
/* gfs2 inodes are slightly different from gfs1 inodes in that */
/* di_goal_meta has shifted locations and di_goal_data has */
/* changed from 32-bits to 64-bits. The following code */
/* adjusts for the shift. */
/* */
/* Note: It may sound absurd, but we need to check if this */
/* inode has already been converted to gfs2 or if it's */
/* still a gfs1 inode. That's just in case there was a */
/* prior attempt to run gfs2_convert that never finished */
/* (due to power out, ctrl-c, kill, segfault, whatever.) */
/* If it is unconverted gfs1 we want to do a full */
/* conversion. If it's a gfs2 inode from a prior run, */
/* we still need to renumber the inode, but here we */
/* don't want to shift the data around. */
/* ----------------------------------------------------------- */
if (inode_was_gfs1) {
struct gfs_dinode *gfs1_dinode_struct;
int ret = 0;
gfs1_dinode_struct = (struct gfs_dinode *)&inode->i_di;
inode->i_di.di_goal_meta = inode->i_di.di_goal_data;
inode->i_di.di_goal_data = 0; /* make sure the upper 32b are 0 */
inode->i_di.di_goal_data = gfs1_dinode_struct->di_goal_dblk;
inode->i_di.di_generation = 0;
if (adjust_indirect_blocks(sbp, inode))
goto err_freei;
/* Check for cdpns */
if (S_ISLNK(inode->i_di.di_mode)) {
ret = fix_cdpn_symlink(sbp, bh, inode);
if (ret)
goto err_freei;
}
/* Check for extended attributes */
if (inode->i_di.di_eattr) {
ret = fix_xattr(sbp, bh, inode);
if (ret)
goto err_freei;
}
}
bmodified(inode->i_bh);
inode_put(&inode); /* does gfs2_dinode_out if modified */
sbp->md.next_inum++; /* update inode count */
return 0;
err_freei:
inode_put(&inode);
return -1;
} /* adjust_inode */
static int next_rg_meta(struct rgrp_tree *rgd, uint64_t *block, int first)
{
struct gfs2_bitmap *bits = NULL;
uint32_t length = rgd->ri.ri_length;
uint32_t blk = (first)? 0: (uint32_t)((*block + 1) - rgd->ri.ri_data0);
int i;
if (!first && (*block < rgd->ri.ri_data0)) {
fprintf(stderr, "next_rg_meta: Start block is outside rgrp bounds.\n");
exit(1);
}
for (i = 0; i < length; i++){
bits = &rgd->bits[i];
if (blk < bits->bi_len * GFS2_NBBY)
break;
blk -= bits->bi_len * GFS2_NBBY;
}
for (; i < length; i++){
bits = &rgd->bits[i];
blk = gfs2_bitfit((uint8_t *)bits->bi_bh->b_data + bits->bi_offset,
bits->bi_len, blk, GFS2_BLKST_DINODE);
if(blk != BFITNOENT){
*block = blk + (bits->bi_start * GFS2_NBBY) +
rgd->ri.ri_data0;
break;
}
blk = 0;
}
if (i == length)
return -1;
return 0;
}
static int next_rg_metatype(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
uint64_t *block, uint32_t type, int first)
{
struct gfs2_buffer_head *bh = NULL;
do{
if (bh)
brelse(bh);
if (next_rg_meta(rgd, block, first))
return -1;
bh = bread(sdp, *block);
first = 0;
} while(gfs2_check_meta(bh, type));
brelse(bh);
return 0;
}
/* ------------------------------------------------------------------------- */
/* inode_renumber - renumber the inodes */
/* */
/* In gfs1, the inode number WAS the inode address. In gfs2, the inodes are */
/* numbered sequentially. */
/* */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int inode_renumber(struct gfs2_sbd *sbp, uint64_t root_inode_addr, osi_list_t *cdpn_to_fix)
{
struct rgrp_tree *rgd;
struct osi_node *n, *next = NULL;
uint64_t block = 0;
struct gfs2_buffer_head *bh;
int first;
int error = 0;
int rgs_processed = 0;
log_notice(_("Converting inodes.\n"));
sbp->md.next_inum = 1; /* starting inode numbering */
gettimeofday(&tv, NULL);
seconds = tv.tv_sec;
/* ---------------------------------------------------------------- */
/* Traverse the resource groups to figure out where the inodes are. */
/* ---------------------------------------------------------------- */
for (n = osi_first(&sbp->rgtree); n; n = next) {
next = osi_next(n);
rgd = (struct rgrp_tree *)n;
rgs_processed++;
first = 1;
while (1) { /* for all inodes in the resource group */
gettimeofday(&tv, NULL);
/* Put out a warm, fuzzy message every second so the customer */
/* doesn't think we hung. (This may take a long time). */
if (tv.tv_sec - seconds) {
seconds = tv.tv_sec;
log_notice(_("\r%llu inodes from %d rgs converted."),
(unsigned long long)sbp->md.next_inum,
rgs_processed);
fflush(stdout);
}
/* Get the next metadata block. Break out if we reach the end. */
/* We have to check all metadata blocks because the bitmap may */
/* be "11" (used meta) for both inodes and indirect blocks. */
/* We need to process the inodes and change the indirect blocks */
/* to have a bitmap type of "01" (data). */
if (next_rg_metatype(sbp, rgd, &block, 0, first))
break;
/* If this is the root inode block, remember it for later: */
if (block == root_inode_addr) {
sbp->sd_sb.sb_root_dir.no_addr = block;
sbp->sd_sb.sb_root_dir.no_formal_ino = sbp->md.next_inum;
}
bh = bread(sbp, block);
if (!gfs2_check_meta(bh, GFS_METATYPE_DI)) {/* if it is an dinode */
/* Skip the rindex and jindex inodes for now. */
if (block != rindex_addr && block != jindex_addr) {
error = adjust_inode(sbp, bh);
if (error)
return error;
}
} else { /* It's metadata, but not an inode, so fix the bitmap. */
int blk, buf_offset;
int bitmap_byte; /* byte within the bitmap to fix */
int byte_bit; /* bit within the byte */
/* Figure out the absolute bitmap byte we need to fix. */
/* ignoring structure offsets and bitmap blocks for now. */
bitmap_byte = (block - rgd->ri.ri_data0) / GFS2_NBBY;
byte_bit = (block - rgd->ri.ri_data0) % GFS2_NBBY;
/* Now figure out which bitmap block the byte is on */
for (blk = 0; blk < rgd->ri.ri_length; blk++) {
struct gfs2_bitmap *bi = &rgd->bits[blk];
/* figure out offset of first bitmap byte for this map: */
buf_offset = (blk) ? sizeof(struct gfs2_meta_header) :
sizeof(struct gfs2_rgrp);
/* if it's on this page */
if (buf_offset + bitmap_byte < sbp->bsize) {
bi->bi_bh->b_data[buf_offset + bitmap_byte] &=
~(0x03 << (GFS2_BIT_SIZE * byte_bit));
bi->bi_bh->b_data[buf_offset + bitmap_byte] |=
(0x01 << (GFS2_BIT_SIZE * byte_bit));
bmodified(bi->bi_bh);
break;
}
bitmap_byte -= (sbp->bsize - buf_offset);
}
}
brelse(bh);
first = 0;
} /* while 1 */
} /* for all rgs */
log_notice(_("\r%llu inodes from %d rgs converted."),
(unsigned long long)sbp->md.next_inum, rgs_processed);
fflush(stdout);
return 0;
}/* inode_renumber */
/* ------------------------------------------------------------------------- */
/* fetch_inum - fetch an inum entry from disk, given its block */
/* ------------------------------------------------------------------------- */
static int fetch_inum(struct gfs2_sbd *sbp, uint64_t iblock,
struct gfs2_inum *inum, uint64_t *eablk)
{
struct gfs2_inode *fix_inode;
fix_inode = lgfs2_inode_read(sbp, iblock);
if (fix_inode == NULL)
return 1;
inum->no_formal_ino = fix_inode->i_di.di_num.no_formal_ino;
inum->no_addr = fix_inode->i_di.di_num.no_addr;
if (eablk)
*eablk = fix_inode->i_di.di_eattr;
inode_put(&fix_inode);
return 0;
}/* fetch_inum */
/* ------------------------------------------------------------------------- */
/* process_dirent_info - fix one dirent (directory entry) buffer */
/* */
/* We changed inode numbers, so we must update that number into the */
/* directory entries themselves. */
/* */
/* Returns: 0 on success, -1 on failure, -EISDIR when dentmod marked DT_DIR */
/* ------------------------------------------------------------------------- */
static int process_dirent_info(struct gfs2_inode *dip, struct gfs2_sbd *sbp,
struct gfs2_buffer_head *bh, int dir_entries, uint64_t dentmod)
{
int error = 0;
struct gfs2_dirent *dent;
int de; /* directory entry index */
error = gfs2_dirent_first(dip, bh, &dent);
if (error != IS_LEAF && error != IS_DINODE) {
log_crit(_("Error retrieving directory.\n"));
return -1;
}
error = 0;
/* Go through every dirent in the buffer and process it. */
/* Turns out you can't trust dir_entries is correct. */
for (de = 0; ; de++) {
struct gfs2_inum inum;
int dent_was_gfs1;
if (dentmod) {
if (dent->de_type == cpu_to_be16(DT_LNK)
&& cpu_to_be64(dent->de_inum.no_addr) == dentmod) {
dent->de_type = cpu_to_be16(DT_DIR);
error = -EISDIR;
break;
}
goto skip_next;
}
gettimeofday(&tv, NULL);
/* Do more warm fuzzy stuff for the customer. */
dirents_fixed++;
if (tv.tv_sec - seconds) {
seconds = tv.tv_sec;
log_notice(_("\r%llu directories, %llu dirents fixed."),
(unsigned long long)dirs_fixed,
(unsigned long long)dirents_fixed);
fflush(stdout);
}
/* fix the dirent's inode number based on the inode */
gfs2_inum_in(&inum, (char *)&dent->de_inum);
dent_was_gfs1 = (dent->de_inum.no_addr == dent->de_inum.no_formal_ino);
if (inum.no_formal_ino) { /* if not a sentinel (placeholder) */
error = fetch_inum(sbp, inum.no_addr, &inum, NULL);
if (error) {
log_crit(_("Error retrieving inode 0x%llx\n"),
(unsigned long long)inum.no_addr);
break;
}
/* fix the dirent's inode number from the fetched inum. */
dent->de_inum.no_formal_ino = cpu_to_be64(inum.no_formal_ino);
}
/* Fix the dirent's filename hash: They are the same as gfs1 */
/* dent->de_hash = cpu_to_be32(gfs2_disk_hash((char *)(dent + 1), */
/* be16_to_cpu(dent->de_name_len))); */
/* Fix the dirent's file type. Gfs1 used home-grown values. */
/* Gfs2 uses standard values from include/linux/fs.h */
/* Only do this if the dent was a true gfs1 dent, and not a */
/* gfs2 dent converted from a previously aborted run. */
if (dent_was_gfs1) {
switch be16_to_cpu(dent->de_type) {
case GFS_FILE_NON:
dent->de_type = cpu_to_be16(DT_UNKNOWN);
break;
case GFS_FILE_REG: /* regular file */
dent->de_type = cpu_to_be16(DT_REG);
break;
case GFS_FILE_DIR: /* directory */
dent->de_type = cpu_to_be16(DT_DIR);
break;
case GFS_FILE_LNK: /* link */
dent->de_type = cpu_to_be16(DT_LNK);
break;
case GFS_FILE_BLK: /* block device node */
dent->de_type = cpu_to_be16(DT_BLK);
break;
case GFS_FILE_CHR: /* character device node */
dent->de_type = cpu_to_be16(DT_CHR);
break;
case GFS_FILE_FIFO: /* fifo/pipe */
dent->de_type = cpu_to_be16(DT_FIFO);
break;
case GFS_FILE_SOCK: /* socket */
dent->de_type = cpu_to_be16(DT_SOCK);
break;
}
}
/*
* Compare this dirent address with every one in the
* cdpns_to_fix list to find if this directory (dip) is
* a cdpn symlink's parent. If so add it to the list element
*/
if (dent->de_type == cpu_to_be16(DT_LNK)) {
osi_list_t *tmp;
struct inode_dir_block *fix;
osi_list_foreach(tmp, &cdpns_to_fix.list) {
fix = osi_list_entry(tmp, struct inode_dir_block, list);
if (fix->di_addr == inum.no_addr)
fix->di_paddr = dip->i_di.di_num.no_addr;
}
}
skip_next:
error = gfs2_dirent_next(dip, bh, &dent);
if (error) {
if (error == -ENOENT) /* beyond the end of this bh */
error = 0;
break;
}
} /* for every directory entry */
return error;
}/* process_dirent_info */
/* ------------------------------------------------------------------------- */
/* fix_one_directory_exhash - fix one directory's inode numbers. */
/* */
/* This is for exhash directories, where the inode has a list of "leaf" */
/* blocks, each of which is a buffer full of dirents that must be processed. */
/* */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int fix_one_directory_exhash(struct gfs2_sbd *sbp, struct gfs2_inode *dip, uint64_t dentmod)
{
struct gfs2_buffer_head *bh_leaf;
int error;
uint64_t leaf_block, prev_leaf_block;
uint32_t leaf_num;
prev_leaf_block = 0;
/* for all the leafs, get the leaf block and process the dirents inside */
for (leaf_num = 0; ; leaf_num++) {
uint64_t buf;
struct gfs2_leaf leaf;
error = gfs2_readi(dip, (char *)&buf, leaf_num * sizeof(uint64_t),
sizeof(uint64_t));
if (!error) /* end of file */
return 0; /* success */
else if (error != sizeof(uint64_t)) {
log_crit(_("fix_one_directory_exhash: error reading directory.\n"));
return -1;
}
else {
leaf_block = be64_to_cpu(buf);
error = 0;
}
leaf_chain:
/* leaf blocks may be repeated, so skip the duplicates: */
if (leaf_block == prev_leaf_block) /* same block? */
continue; /* already converted */
prev_leaf_block = leaf_block;
/* read the leaf buffer in */
error = gfs2_get_leaf(dip, leaf_block, &bh_leaf);
if (error) {
log_crit(_("Error reading leaf %llx\n"),
(unsigned long long)leaf_block);
break;
}
gfs2_leaf_in(&leaf, bh_leaf->b_data);
error = process_dirent_info(dip, sbp, bh_leaf, leaf.lf_entries, dentmod);
bmodified(bh_leaf);
brelse(bh_leaf);
if (dentmod && error == -EISDIR) /* dentmod was marked DT_DIR, break out */
break;
if (leaf.lf_next) { /* leaf has a leaf chain, process leaves in chain */
leaf_block = leaf.lf_next;
error = 0;
goto leaf_chain;
}
} /* for leaf_num */
return 0;
}/* fix_one_directory_exhash */
static int process_directory(struct gfs2_sbd *sbp, uint64_t dirblock, uint64_t dentmod)
{
struct gfs2_inode *dip;
int error = 0;
/* read in the directory inode */
dip = lgfs2_inode_read(sbp, dirblock);
if (dip == NULL)
return -1;
/* fix the directory: either exhash (leaves) or linear (stuffed) */
if (dip->i_di.di_flags & GFS2_DIF_EXHASH) {
if (fix_one_directory_exhash(sbp, dip, dentmod)) {
log_crit(_("Error fixing exhash directory.\n"));
inode_put(&dip);
return -1;
}
} else {
error = process_dirent_info(dip, sbp, dip->i_bh, dip->i_di.di_entries, dentmod);
if (error && error != -EISDIR) {
log_crit(_("Error fixing linear directory.\n"));
inode_put(&dip);
return -1;
}
}
bmodified(dip->i_bh);
inode_put(&dip);
return 0;
}
/* ------------------------------------------------------------------------- */
/* fix_directory_info - sync new inode numbers with directory info */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int fix_directory_info(struct gfs2_sbd *sbp, osi_list_t *dir_to_fix)
{
osi_list_t *tmp, *fix;
struct inode_block *dir_iblk;
uint64_t dirblock;
uint32_t gfs1_inptrs = sbp->sd_inptrs;
/* Directory inodes have been converted to gfs2, use gfs2 inptrs */
sbp->sd_inptrs = (sbp->bsize - sizeof(struct gfs2_meta_header))
/ sizeof(uint64_t);
dirs_fixed = 0;
dirents_fixed = 0;
gettimeofday(&tv, NULL);
seconds = tv.tv_sec;
log_notice(_("\nFixing file and directory information.\n"));
fflush(stdout);
tmp = NULL;
/* for every directory in the list */
for (fix = dir_to_fix->next; fix != dir_to_fix; fix = fix->next) {
if (tmp) {
osi_list_del(tmp);
free(tmp);
}
tmp = fix; /* remember the addr to free next time */
dirs_fixed++;
/* figure out the directory inode block and read it in */
dir_iblk = (struct inode_block *)fix;
dirblock = dir_iblk->di_addr; /* addr of dir inode */
if (process_directory(sbp, dirblock, 0)) {
log_crit(_("Error processing directory\n"));
return -1;
}
}
/* Free the last entry in memory: */
if (tmp) {
osi_list_del(tmp);
free(tmp);
}
sbp->sd_inptrs = gfs1_inptrs;
return 0;
}/* fix_directory_info */
/* ------------------------------------------------------------------------- */
/* fix_cdpn_symlinks - convert cdpn symlinks to empty directories */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int fix_cdpn_symlinks(struct gfs2_sbd *sbp, osi_list_t *cdpn_to_fix)
{
osi_list_t *tmp, *x;
int error = 0;
cdpns_fixed = 0;
osi_list_foreach_safe(tmp, cdpn_to_fix, x) {
struct gfs2_inum fix, dir;
struct inode_dir_block *l_fix;
struct gfs2_buffer_head *bh = NULL;
struct gfs2_inode *fix_inode;
uint64_t eablk;
l_fix = osi_list_entry(tmp, struct inode_dir_block, list);
osi_list_del(tmp);
/* convert symlink to empty dir */
error = fetch_inum(sbp, l_fix->di_addr, &fix, &eablk);
if (error) {
log_crit(_("Error retrieving inode at block %llx\n"),
(unsigned long long)l_fix->di_addr);
break;
}
error = fetch_inum(sbp, l_fix->di_paddr, &dir, NULL);
if (error) {
log_crit(_("Error retrieving inode at block %llx\n"),
(unsigned long long)l_fix->di_paddr);
break;
}
/* initialize the symlink inode to be a directory */
error = init_dinode(sbp, &bh, &fix, S_IFDIR | 0755, 0, &dir);
if (error != 0)
return -1;
fix_inode = lgfs2_inode_get(sbp, bh);
if (fix_inode == NULL)
return -1;
fix_inode->i_di.di_eattr = eablk; /*fix extended attribute */
inode_put(&fix_inode);
bmodified(bh);
brelse(bh);
/* fix the parent directory dirent entry for this inode */
error = process_directory(sbp, l_fix->di_paddr, l_fix->di_addr);
if (error) {
log_crit(_("Error trying to fix cdpn dentry\n"));
break;
}
free(l_fix);
cdpns_fixed++;
}
return error;
} /* fix_cdpn_symlinks */
/* ------------------------------------------------------------------------- */
/* Fetch gfs1 jindex structure from buffer */
/* ------------------------------------------------------------------------- */
static void gfs1_jindex_in(struct gfs_jindex *jindex, char *buf)
{
struct gfs_jindex *str = (struct gfs_jindex *)buf;
jindex->ji_addr = be64_to_cpu(str->ji_addr);
jindex->ji_nsegment = be32_to_cpu(str->ji_nsegment);
memset(jindex->ji_reserved, 0, 64);
}
/* ------------------------------------------------------------------------- */
/* read_gfs1_jiindex - read the gfs1 jindex file. */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int read_gfs1_jiindex(struct gfs2_sbd *sdp)
{
struct gfs2_inode *ip = sdp->md.jiinode;
char buf[sizeof(struct gfs_jindex)];
unsigned int j;
int error=0;
unsigned int tmp_mode = 0;
if(ip->i_di.di_size % sizeof(struct gfs_jindex) != 0){
log_crit(_("The size reported in the journal index"
" inode is not a\n"
"\tmultiple of the size of a journal index.\n"));
return -1;
}
if(!(sd_jindex = (struct gfs_jindex *)malloc(ip->i_di.di_size))) {
log_crit(_("Unable to allocate journal index\n"));
return -1;
}
if(!memset(sd_jindex, 0, ip->i_di.di_size)) {
log_crit(_("Unable to zero journal index\n"));
return -1;
}
/* ugly hack
* Faking the gfs1_jindex inode as a directory to gfs2_readi
* so it skips the metaheader struct in the data blocks
* in the inode. gfs2_jindex inode doesn't have metaheaders
* in the data blocks */
tmp_mode = ip->i_di.di_mode;
ip->i_di.di_mode &= ~S_IFMT;
ip->i_di.di_mode |= S_IFDIR;
for (j = 0; ; j++) {
struct gfs_jindex *journ;
error = gfs2_readi(ip, buf, j * sizeof(struct gfs_jindex),
sizeof(struct gfs_jindex));
if(!error)
break;
if (error != sizeof(struct gfs_jindex)){
log_crit(_("An error occurred while reading the"
" journal index file.\n"));
goto fail;
}
journ = sd_jindex + j;
gfs1_jindex_in(journ, buf);
sdp->jsize = (journ->ji_nsegment * 16 * sdp->bsize) >> 20;
}
ip->i_di.di_mode = tmp_mode;
if(j * sizeof(struct gfs_jindex) != ip->i_di.di_size){
log_crit(_("journal inode size invalid\n"));
goto fail;
}
sdp->md.journals = orig_journals = j;
return 0;
fail:
free(sd_jindex);
return -1;
}
static int sanity_check(struct gfs2_sbd *sdp)
{
int error = 0;
if (!raw_gfs1_ondisk_sb.sb_quota_di.no_addr) {
log_crit(_("Error: Superblock Quota inode address is NULL\n"));
error = 1;
}
if (!raw_gfs1_ondisk_sb.sb_license_di.no_addr) {
log_crit(_("Error: Superblock Statfs inode address is NULL\n"));
error = 1;
}
if (!raw_gfs1_ondisk_sb.sb_seg_size) {
log_crit(_("Error: Superblock segment size is zero\n"));
error = 1;
}
return error;
}
/* ------------------------------------------------------------------------- */
/* init - initialization code */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int init(struct gfs2_sbd *sbp, struct gfs2_options *opts)
{
struct gfs2_buffer_head *bh;
int rgcount;
struct gfs2_inum inum;
memset(sbp, 0, sizeof(struct gfs2_sbd));
if ((sbp->device_fd = open(opts->device, O_RDWR)) < 0) {
perror(opts->device);
exit(-1);
}
/* --------------------------------- */
/* initialize the incore superblock */
/* --------------------------------- */
sbp->sd_sb.sb_header.mh_magic = GFS2_MAGIC;
sbp->sd_sb.sb_header.mh_type = GFS2_METATYPE_SB;
sbp->sd_sb.sb_header.mh_format = GFS2_FORMAT_SB;
osi_list_init((osi_list_t *)&dirs_to_fix);
osi_list_init((osi_list_t *)&cdpns_to_fix);
/* ---------------------------------------------- */
/* Initialize lists and read in the superblock. */
/* ---------------------------------------------- */
sbp->jsize = GFS2_DEFAULT_JSIZE;
sbp->rgsize = GFS2_DEFAULT_RGSIZE;
sbp->qcsize = GFS2_DEFAULT_QCSIZE;
sbp->time = time(NULL);
sbp->blks_total = 0; /* total blocks - total them up later */
sbp->blks_alloced = 0; /* blocks allocated - total them up later */
sbp->dinodes_alloced = 0; /* dinodes allocated - total them up later */
sbp->sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
sbp->bsize = sbp->sd_sb.sb_bsize;
sbp->rgtree.osi_node = NULL;
if (compute_constants(sbp)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
bh = bread(sbp, GFS2_SB_ADDR >> sbp->sd_fsb2bb_shift);
memcpy(&raw_gfs1_ondisk_sb, (struct gfs_sb *)bh->b_data,
sizeof(struct gfs_sb));
gfs2_sb_in(&sbp->sd_sb, bh->b_data);
jindex_addr = be64_to_cpu(raw_gfs1_ondisk_sb.sb_jindex_di.no_addr);
rindex_addr = be64_to_cpu(raw_gfs1_ondisk_sb.sb_rindex_di.no_addr);
sbp->bsize = sbp->sd_sb.sb_bsize;
sbp->fssize = lseek(sbp->device_fd, 0, SEEK_END) / sbp->sd_sb.sb_bsize;
sbp->sd_inptrs = (sbp->bsize - sizeof(struct gfs_indirect)) /
sizeof(uint64_t);
sbp->sd_diptrs = (sbp->bsize - sizeof(struct gfs_dinode)) /
sizeof(uint64_t);
sbp->sd_jbsize = sbp->bsize - sizeof(struct gfs2_meta_header);
brelse(bh);
if (compute_heightsize(sbp->bsize, sbp->sd_heightsize, &sbp->sd_max_height,
sbp->bsize, sbp->sd_diptrs, sbp->sd_inptrs)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
if (compute_heightsize(sbp->bsize, sbp->sd_jheightsize, &sbp->sd_max_jheight,
sbp->sd_jbsize, sbp->sd_diptrs, sbp->sd_inptrs)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
/* -------------------------------------------------------- */
/* Our constants are for gfs1. Need some for gfs2 as well. */
/* -------------------------------------------------------- */
gfs2_inptrs = (sbp->bsize - sizeof(struct gfs2_meta_header)) /
sizeof(uint64_t); /* How many ptrs can we fit on a block? */
memset(gfs2_heightsize, 0, sizeof(gfs2_heightsize));
if (compute_heightsize(sbp->bsize, gfs2_heightsize, &gfs2_max_height,
sbp->bsize, sbp->sd_diptrs, gfs2_inptrs)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
memset(gfs2_jheightsize, 0, sizeof(gfs2_jheightsize));
if (compute_heightsize(sbp->bsize, gfs2_jheightsize, &gfs2_max_jheight,
sbp->sd_jbsize, sbp->sd_diptrs, gfs2_inptrs)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
/* ---------------------------------------------- */
/* Make sure we're really gfs1 */
/* ---------------------------------------------- */
if (sbp->sd_sb.sb_fs_format != GFS_FORMAT_FS ||
sbp->sd_sb.sb_header.mh_type != GFS_METATYPE_SB ||
sbp->sd_sb.sb_header.mh_format != GFS_FORMAT_SB ||
sbp->sd_sb.sb_multihost_format != GFS_FORMAT_MULTI) {
log_crit(_("Error: %s does not look like a gfs1 filesystem.\n"), opts->device);
close(sbp->device_fd);
exit(-1);
}
/* get gfs1 rindex inode - gfs1's rindex inode ptr became __pad2 */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_rindex_di);
sbp->md.riinode = lgfs2_gfs_inode_read(sbp, inum.no_addr);
if (sbp->md.riinode == NULL) {
log_crit(_("Could not read resource group index: %s\n"), strerror(errno));
exit(-1);
}
/* get gfs1 jindex inode - gfs1's journal index inode ptr became master */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_jindex_di);
sbp->md.jiinode = lgfs2_inode_read(sbp, inum.no_addr);
if (sbp->md.jiinode == NULL) {
log_crit(_("Could not read journal index: %s\n"), strerror(errno));
exit(-1);
}
/* read in the journal index data */
read_gfs1_jiindex(sbp);
/* read in the resource group index data: */
/* We've got a slight dilemma here. In gfs1, we used to have a meta */
/* header in front of the rgindex pages. In gfs2, we don't. That's */
/* apparently only for directories. So we need to fake out libgfs2 */
/* so that it adjusts for the metaheader by faking out the inode to */
/* look like a directory, temporarily. */
sbp->md.riinode->i_di.di_mode &= ~S_IFMT;
sbp->md.riinode->i_di.di_mode |= S_IFDIR;
printf(_("Examining file system"));
if (gfs1_ri_update(sbp, 0, &rgcount, 0)){
log_crit(_("Unable to fill in resource group information.\n"));
return -1;
}
printf("\n");
fflush(stdout);
inode_put(&sbp->md.riinode);
inode_put(&sbp->md.jiinode);
log_debug(_("%d rgs found.\n"), rgcount);
return 0;
}/* fill_super_block */
/* ------------------------------------------------------------------------- */
/* give_warning - give the all-important warning message. */
/* ------------------------------------------------------------------------- */
static void give_warning(void)
{
printf(_("This program will convert a gfs1 filesystem to a " \
"gfs2 filesystem.\n"));
printf(_("WARNING: This can't be undone. It is strongly advised " \
"that you:\n\n"));
printf(_(" 1. Back up your entire filesystem first.\n"));
printf(_(" 2. Run fsck.gfs2 first to ensure filesystem integrity.\n"));
printf(_(" 3. Make sure the filesystem is NOT mounted from any node.\n"));
printf(_(" 4. Make sure you have the latest software versions.\n"));
}/* give_warning */
/* ------------------------------------------------------------------------- */
/* version - print version information */
/* ------------------------------------------------------------------------- */
static void version(void)
{
log_notice(_("gfs2_convert version %s (built %s %s)\n"), VERSION,
__DATE__, __TIME__);
log_notice("%s\n\n", REDHAT_COPYRIGHT);
}
/* ------------------------------------------------------------------------- */
/* usage - print usage information */
/* ------------------------------------------------------------------------- */
static void usage(const char *name)
{
give_warning();
printf(_("\nUsage:\n"));
printf(_("%s [-hnqvVy] <device>\n\n"), name);
printf("Flags:\n");
printf(_("\th - print this help message\n"));
printf(_("\tn - assume 'no' to all questions\n"));
printf(_("\tq - quieter output\n"));
printf(_("\tv - more verbose output\n"));
printf(_("\tV - print version information\n"));
printf(_("\ty - assume 'yes' to all questions\n"));
}/* usage */
/* ------------------------------------------------------------------------- */
/* process_parameters */
/* ------------------------------------------------------------------------- */
static void process_parameters(int argc, char **argv, struct gfs2_options *opts)
{
int c;
opts->yes = 0;
opts->no = 0;
if (argc == 1) {
usage(argv[0]);
exit(0);
}
while((c = getopt(argc, argv, "hnqvyV")) != -1) {
switch(c) {
case 'h':
usage(argv[0]);
exit(0);
break;
case 'n':
opts->no = 1;
break;
case 'q':
decrease_verbosity();
break;
case 'v':
increase_verbosity();
break;
case 'V':
exit(0);
case 'y':
opts->yes = 1;
break;
default:
fprintf(stderr,_("Parameter not understood: %c\n"), c);
usage(argv[0]);
exit(0);
}
}
if(argc > optind) {
opts->device = argv[optind];
} else {
fprintf(stderr, _("No device specified. Please use '-h' for help\n"));
exit(1);
}
} /* process_parameters */
/* ------------------------------------------------------------------------- */
/* rgrp_length - Calculate the length of a resource group */
/* @size: The total size of the resource group */
/* ------------------------------------------------------------------------- */
static uint64_t rgrp_length(uint64_t size, struct gfs2_sbd *sdp)
{
uint64_t bitbytes = RGRP_BITMAP_BLKS(&sdp->sd_sb) + 1;
uint64_t stuff = RGRP_STUFFED_BLKS(&sdp->sd_sb) + 1;
uint64_t blocks = 1;
if (size >= stuff) {
size -= stuff;
while (size > bitbytes) {
blocks++;
size -= bitbytes;
}
if (size)
blocks++;
}
return blocks;
}/* rgrp_length */
/* ------------------------------------------------------------------------- */
/* journ_space_to_rg - convert gfs1 journal space to gfs2 rg space. */
/* */
/* In gfs1, the journals were kept separate from the files and directories. */
/* They had a dedicated section of the fs carved out for them. */
/* In gfs2, the journals are just files like any other, (but still hidden). */
/* Therefore, the old journal space has to be converted to normal resource */
/* group space. */
/* */
/* Returns: 0 on success, -1 on failure */
/* ------------------------------------------------------------------------- */
static int journ_space_to_rg(struct gfs2_sbd *sdp)
{
int error = 0;
int j, x;
struct gfs_jindex *jndx;
struct rgrp_tree *rgd, *rgdhigh;
struct osi_node *n, *next = NULL;
struct gfs2_meta_header mh;
uint64_t ri_addr;
mh.mh_magic = GFS2_MAGIC;
mh.mh_type = GFS2_METATYPE_RB;
mh.mh_format = GFS2_FORMAT_RB;
log_notice(_("Converting journal space to rg space.\n"));
/* Go through each journal, converting them one by one */
for (j = 0; j < orig_journals; j++) { /* for each journal */
uint64_t size;
jndx = &sd_jindex[j];
/* go through all rg index entries, keeping track of the
highest that's still in the first subdevice.
Note: we really should go through all of the rgindex because
we might have had rg's added by gfs_grow, and journals added
by jadd. gfs_grow adds rgs out of order, so we can't count
on them being in ascending order. */
rgdhigh = NULL;
for (n = osi_first(&sdp->rgtree); n; n = next) {
next = osi_next(n);
rgd = (struct rgrp_tree *)n;
if (rgd->ri.ri_addr < jndx->ji_addr &&
((rgdhigh == NULL) ||
(rgd->ri.ri_addr > rgdhigh->ri.ri_addr)))
rgdhigh = rgd;
} /* for each rg */
if (!rgdhigh) { /* if we somehow didn't find one. */
log_crit(_("Error: No suitable rg found for journal.\n"));
return -1;
}
log_info(_("Addr 0x%llx comes after rg at addr 0x%llx\n"),
(unsigned long long)jndx->ji_addr,
(unsigned long long)rgdhigh->ri.ri_addr);
ri_addr = jndx->ji_addr;
/* Allocate a new rgd entry which includes rg and ri. */
rgd = rgrp_insert(&sdp->rgtree, ri_addr);
/* convert the gfs1 rgrp into a new gfs2 rgrp */
size = jndx->ji_nsegment *
be32_to_cpu(raw_gfs1_ondisk_sb.sb_seg_size);
rgd->rg.rg_header.mh_magic = GFS2_MAGIC;
rgd->rg.rg_header.mh_type = GFS2_METATYPE_RG;
rgd->rg.rg_header.mh_format = GFS2_FORMAT_RG;
rgd->rg.rg_flags = 0;
rgd->rg.rg_dinodes = 0;
rgd->ri.ri_addr = jndx->ji_addr; /* new rg addr becomes ji addr */
rgd->ri.ri_length = rgrp_length(size, sdp); /* aka bitblocks */
rgd->ri.ri_data0 = jndx->ji_addr + rgd->ri.ri_length;
rgd->ri.ri_data = size - rgd->ri.ri_length;
/* Round down to nearest multiple of GFS2_NBBY */
while (rgd->ri.ri_data & 0x03)
rgd->ri.ri_data--;
sdp->blks_total += rgd->ri.ri_data; /* For statfs file update */
rgd->rg.rg_free = rgd->ri.ri_data;
rgd->ri.ri_bitbytes = rgd->ri.ri_data / GFS2_NBBY;
if (gfs2_compute_bitstructs(sdp->sd_sb.sb_bsize, rgd)) {
log_crit(_("gfs2_convert: Error converting bitmaps.\n"));
exit(-1);
}
for (x = 0; x < rgd->ri.ri_length; x++)
rgd->bits[x].bi_bh = bget(sdp, rgd->ri.ri_addr + x);
convert_bitmaps(sdp, rgd);
for (x = 0; x < rgd->ri.ri_length; x++) {
if (x)
gfs2_meta_header_out(&mh, rgd->bits[x].bi_bh->b_data);
else
gfs2_rgrp_out(&rgd->rg, rgd->bits[x].bi_bh->b_data);
bmodified(rgd->bits[x].bi_bh);
}
} /* for each journal */
return error;
}/* journ_space_to_rg */
/* ------------------------------------------------------------------------- */
/* update_inode_file - update the inode file with the new next_inum */
/* ------------------------------------------------------------------------- */
static void update_inode_file(struct gfs2_sbd *sdp)
{
struct gfs2_inode *ip = sdp->md.inum;
uint64_t buf;
int count;
buf = cpu_to_be64(sdp->md.next_inum);
count = gfs2_writei(ip, &buf, 0, sizeof(uint64_t));
if (count != sizeof(uint64_t)) {
fprintf(stderr, "update_inode_file\n");
exit(1);
}
log_debug(_("\nNext Inum: %llu\n"), (unsigned long long)sdp->md.next_inum);
}/* update_inode_file */
/* ------------------------------------------------------------------------- */
/* write_statfs_file - write the statfs file */
/* ------------------------------------------------------------------------- */
static void write_statfs_file(struct gfs2_sbd *sdp)
{
struct gfs2_inode *ip = sdp->md.statfs;
struct gfs2_statfs_change sc;
char buf[sizeof(struct gfs2_statfs_change)];
int count;
sc.sc_total = sdp->blks_total;
sc.sc_free = sdp->blks_total - sdp->blks_alloced;
sc.sc_dinodes = sdp->dinodes_alloced;
gfs2_statfs_change_out(&sc, buf);
count = gfs2_writei(ip, buf, 0, sizeof(struct gfs2_statfs_change));
if (count != sizeof(struct gfs2_statfs_change)) {
fprintf(stderr, "do_init (2)\n");
exit(1);
}
}/* write_statfs_file */
/* ------------------------------------------------------------------------- */
/* remove_obsolete_gfs1 - remove obsolete gfs1 inodes. */
/* ------------------------------------------------------------------------- */
static void remove_obsolete_gfs1(struct gfs2_sbd *sbp)
{
struct gfs2_inum inum;
log_notice(_("Removing obsolete GFS1 file system structures.\n"));
fflush(stdout);
/* Delete the old gfs1 Journal index: */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_jindex_di);
gfs2_freedi(sbp, inum.no_addr);
/* Delete the old gfs1 rgindex: */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_rindex_di);
gfs2_freedi(sbp, inum.no_addr);
/* Delete the old gfs1 Quota file: */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_quota_di);
gfs2_freedi(sbp, inum.no_addr);
/* Delete the old gfs1 License file: */
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_license_di);
gfs2_freedi(sbp, inum.no_addr);
}
/* ------------------------------------------------------------------------- */
/* lifted from libgfs2/structures.c */
/* ------------------------------------------------------------------------- */
static int conv_build_jindex(struct gfs2_sbd *sdp)
{
unsigned int j;
sdp->md.jiinode = createi(sdp->master_dir, "jindex", S_IFDIR | 0700,
GFS2_DIF_SYSTEM);
if (sdp->md.jiinode == NULL) {
return errno;
}
sdp->md.journal = malloc(sdp->md.journals *
sizeof(struct gfs2_inode *));
if (sdp->md.journal == NULL) {
return errno;
}
for (j = 0; j < sdp->md.journals; j++) {
char name[256];
printf(_("Writing journal #%d..."), j + 1);
fflush(stdout);
sprintf(name, "journal%u", j);
sdp->md.journal[j] = createi(sdp->md.jiinode, name, S_IFREG |
0600, GFS2_DIF_SYSTEM);
write_journal(sdp->md.journal[j], sdp->bsize,
sdp->jsize << 20 >> sdp->sd_sb.sb_bsize_shift);
inode_put(&sdp->md.journal[j]);
printf(_("done.\n"));
fflush(stdout);
}
free(sdp->md.journal);
inode_put(&sdp->md.jiinode);
return 0;
}
static unsigned int total_file_blocks(struct gfs2_sbd *sdp,
uint64_t filesize, int journaled)
{
unsigned int data_blks = 0, meta_blks = 0;
unsigned int max, height, bsize;
uint64_t *arr;
/* Now find the total meta blocks required for data_blks */
if (filesize <= sdp->bsize - sizeof(struct gfs2_dinode)) {
goto out;
}
if (journaled) {
arr = sdp->sd_jheightsize;
max = sdp->sd_max_jheight;
bsize = sdp->sd_jbsize;
} else {
arr = sdp->sd_heightsize;
max = sdp->sd_max_height;
bsize = sdp->bsize;
}
data_blks = DIV_RU(filesize, bsize); /* total data blocks reqd */
for (height = 0; height < max; height++)
if (arr[height] >= filesize)
break;
if (height == 1) {
goto out;
}
meta_blks = DIV_RU(data_blks, sdp->sd_inptrs);
out:
return data_blks + meta_blks;
}
/* We check if the GFS2 filesystem files/structures created after the call to
* check_fit() in main() will fit in the currently available free blocks
*/
static int check_fit(struct gfs2_sbd *sdp)
{
unsigned int blks_need = 0, blks_avail = sdp->blks_total - sdp->blks_alloced;
/* build_master() */
blks_need++; /*creation of master dir inode - 1 block */
/* conv_build_jindex() */
{
blks_need++; /* creation of 'jindex' disk inode */
/* creation of journals */
blks_need += sdp->md.journals *
total_file_blocks(sdp, sdp->jsize << 20, 1);
}
/* build_per_node() */
{
blks_need++; /* creation of 'per_node' dir inode */
/* njourn x (inum_range + statfs_change + quota_change inodes) */
blks_need += sdp->md.journals * 3;
/* quota change inodes are prealloced */
blks_need += sdp->md.journals *
total_file_blocks(sdp, sdp->qcsize << 20, 1);
}
/* build_inum() */
blks_need++; /* creation of 'inum' disk inode */
/* build_statfs() */
blks_need++; /* creation of 'statfs' disk inode */
/* build_rindex() */
{
struct osi_node *n, *next = NULL;
unsigned int rg_count = 0;
blks_need++; /* creationg of 'rindex' disk inode */
/* find the total # of rindex entries, gives size of rindex inode */
for (n = osi_first(&sdp->rgtree); n; n = next) {
next = osi_next(n);
rg_count++;
}
blks_need += total_file_blocks(sdp, rg_count *
sizeof(struct gfs2_rindex), 1);
}
/* build_quota() */
blks_need++; /* quota inode block and uid=gid=0 quota - total 1 block */
/* Up until this point we require blks_need blocks. We don't
* include the blocks freed by the next step (remove_obsolete_gfs1)
* because it's possible for us to exceed the available blocks
* before this step */
return blks_avail > blks_need;
}
/* We fetch the old quota inode block and copy the contents of the block
* (minus the struct gfs2_dinode) into the new quota block. We update the
* inode height/size of the new quota file to that of the old one and set the
* old quota inode height/size to zero, so only the inode block gets freed.
*/
static void copy_quotas(struct gfs2_sbd *sdp)
{
struct gfs2_inum inum;
struct gfs2_inode *oq_ip, *nq_ip;
int err;
err = gfs2_lookupi(sdp->master_dir, "quota", 5, &nq_ip);
if (err) {
fprintf(stderr, _("Couldn't lookup new quota file: %d\n"), err);
exit(1);
}
gfs2_inum_in(&inum, (char *)&raw_gfs1_ondisk_sb.sb_quota_di);
oq_ip = lgfs2_inode_read(sdp, inum.no_addr);
if (oq_ip == NULL) {
fprintf(stderr, _("Couldn't lookup old quota file: %s\n"), strerror(errno));
exit(1);
}
nq_ip->i_di.di_height = oq_ip->i_di.di_height;
nq_ip->i_di.di_size = oq_ip->i_di.di_size;
nq_ip->i_di.di_blocks = oq_ip->i_di.di_blocks;
memcpy(nq_ip->i_bh->b_data + sizeof(struct gfs2_dinode),
oq_ip->i_bh->b_data + sizeof(struct gfs2_dinode),
sdp->bsize - sizeof(struct gfs2_dinode));
oq_ip->i_di.di_height = 0;
oq_ip->i_di.di_size = 0;
bmodified(nq_ip->i_bh);
inode_put(&nq_ip);
bmodified(oq_ip->i_bh);
inode_put(&oq_ip);
}
static int gfs2_query(struct gfs2_options *opts, const char *dev)
{
int res = 0;
if(opts->yes)
return 1;
if(opts->no)
return 0;
opts->query = TRUE;
while (1) {
char *line = NULL;
size_t len = 0;
int ret;
printf(_("Convert %s from GFS1 to GFS2? (y/n)"), dev);
fflush(stdout);
ret = getline(&line, &len, stdin);
res = rpmatch(line);
free(line);
if (ret <= 0)
continue;
if (res == 1 || res == 0)
break;
/* Unrecognized input; go again. */
}
opts->query = FALSE;
return res;
}
int main(int argc, char **argv)
{
int error;
struct gfs2_buffer_head *bh;
struct gfs2_options opts;
/* Set i18n support to gfs2_convert */
setlocale(LC_ALL, "");
textdomain("gfs2-utils");
version();
process_parameters(argc, argv, &opts);
error = init(&sb2, &opts);
/*
* Check for some common fs errors
*/
if (!error) {
if (sanity_check(&sb2)) {
log_crit(_("%s is not a clean gfs filesystem. Please use the"
" fsck.gfs2 utility to correct these errors and"
" try again.\n"), opts.device);
exit(0);
}
}
/* ---------------------------------------------- */
/* Make them seal their fate. */
/* ---------------------------------------------- */
if (!error) {
give_warning();
if (!gfs2_query(&opts, opts.device)) {
log_crit(_("%s not converted.\n"), opts.device);
close(sb2.device_fd);
exit(0);
}
}
/* ---------------------------------------------- */
/* Convert incore gfs1 sb to gfs2 sb */
/* ---------------------------------------------- */
if (!error) {
log_notice(_("Converting resource groups."));
fflush(stdout);
error = convert_rgs(&sb2);
log_notice("\n");
if (error)
log_crit(_("%s: Unable to convert resource groups.\n"), opts.device);
fsync(sb2.device_fd); /* write the buffers to disk */
}
/* ---------------------------------------------- */
/* Renumber the inodes consecutively. */
/* ---------------------------------------------- */
if (!error) {
/* Add a string notifying inode converstion start? */
error = inode_renumber(&sb2, sb2.sd_sb.sb_root_dir.no_addr,
(osi_list_t *)&cdpns_to_fix);
if (error)
log_crit(_("\n%s: Error renumbering inodes.\n"), opts.device);
fsync(sb2.device_fd); /* write the buffers to disk */
}
/* ---------------------------------------------- */
/* Fix the directories to match the new numbers. */
/* ---------------------------------------------- */
if (!error) {
error = fix_directory_info(&sb2, (osi_list_t *)&dirs_to_fix);
log_notice(_("\r%llu directories, %llu dirents fixed."),
(unsigned long long)dirs_fixed,
(unsigned long long)dirents_fixed);
fflush(stdout);
if (error)
log_crit(_("\n%s: Error fixing directories.\n"), opts.device);
}
/* ---------------------------------------------- */
/* Convert cdpn symlinks to empty dirs */
/* ---------------------------------------------- */
if (!error) {
error = fix_cdpn_symlinks(&sb2, (osi_list_t *)&cdpns_to_fix);
log_notice(_("\r%llu cdpn symlinks moved to empty directories."),
(unsigned long long)cdpns_fixed);
fflush(stdout);
if (error)
log_crit(_("\n%s: Error fixing cdpn symlinks.\n"), opts.device);
}
/* ---------------------------------------------- */
/* Convert journal space to rg space */
/* ---------------------------------------------- */
if (!error) {
log_notice(_("\nConverting journals.\n"));
error = journ_space_to_rg(&sb2);
if (error)
log_crit(_("%s: Error converting journal space.\n"), opts.device);
fsync(sb2.device_fd); /* write the buffers to disk */
}
/* ---------------------------------------------- */
/* Create our system files and directories. */
/* ---------------------------------------------- */
if (!error) {
int jreduce = 0;
/* Now we've got to treat it as a gfs2 file system */
if (compute_constants(&sb2)) {
log_crit("%s\n", _("Failed to compute file system constants"));
exit(-1);
}
/* Check if all the files we're about to create will
* fit into the space remaining on the device */
while (!check_fit(&sb2)) {
sb2.jsize--; /* reduce jsize by 1MB each time */
jreduce = 1;
}
if (jreduce)
log_notice(_("Reduced journal size to %u MB to accommodate "
"GFS2 file system structures.\n"), sb2.jsize);
/* Build the master subdirectory. */
build_master(&sb2); /* Does not do inode_put */
sb2.sd_sb.sb_master_dir = sb2.master_dir->i_di.di_num;
/* Build empty journal index file. */
error = conv_build_jindex(&sb2);
if (error) {
log_crit(_("Error: could not build jindex: %s\n"), strerror(error));
exit(-1);
}
log_notice(_("Building GFS2 file system structures.\n"));
/* Build the per-node directories */
error = build_per_node(&sb2);
if (error) {
log_crit(_("Error building per-node directories: %s\n"),
strerror(error));
exit(-1);
}
/* Create the empty inode number file */
error = build_inum(&sb2); /* Does not do inode_put */
if (error) {
log_crit(_("Error building inum inode: %s\n"),
strerror(error));
exit(-1);
}
gfs2_lookupi(sb2.master_dir, "inum", 4, &sb2.md.inum);
/* Create the statfs file */
error = build_statfs(&sb2); /* Does not do inode_put */
if (error) {
log_crit(_("Error building statfs inode: %s\n"),
strerror(error));
exit(-1);
}
gfs2_lookupi(sb2.master_dir, "statfs", 6, &sb2.md.statfs);
do_init_statfs(&sb2);
/* Create the resource group index file */
error = build_rindex(&sb2);
if (error) {
log_crit(_("Error building rindex inode: %s\n"),
strerror(error));
exit(-1);
}
/* Create the quota file */
error = build_quota(&sb2);
if (error) {
log_crit(_("Error building quota inode: %s\n"),
strerror(error));
exit(-1);
}
/* Copy out the master dinode */
{
struct gfs2_inode *ip = sb2.master_dir;
if (ip->i_bh->b_modified)
gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
}
/* Copy old quotas */
copy_quotas(&sb2);
update_inode_file(&sb2);
/* Now delete the now-obsolete gfs1 files: */
remove_obsolete_gfs1(&sb2);
write_statfs_file(&sb2);
inode_put(&sb2.master_dir);
inode_put(&sb2.md.inum);
inode_put(&sb2.md.statfs);
fsync(sb2.device_fd); /* write the buffers to disk */
/* Now free all the in memory */
gfs2_rgrp_free(&sb2.rgtree);
log_notice(_("Committing changes to disk.\n"));
fflush(stdout);
/* Set filesystem type in superblock to gfs2. We do this at the */
/* end because if the tool is interrupted in the middle, we want */
/* it to not reject the partially converted fs as already done */
/* when it's run a second time. */
bh = bread(&sb2, LGFS2_SB_ADDR(&sb2));
sb2.sd_sb.sb_fs_format = GFS2_FORMAT_FS;
sb2.sd_sb.sb_multihost_format = GFS2_FORMAT_MULTI;
gfs2_sb_out(&sb2.sd_sb, bh->b_data);
bmodified(bh);
brelse(bh);
error = fsync(sb2.device_fd);
if (error)
perror(opts.device);
else
log_notice(_("%s: filesystem converted successfully to gfs2.\n"), opts.device);
}
close(sb2.device_fd);
if (sd_jindex)
free(sd_jindex);
exit(0);
}