commit 4e102cbef05a8d57b063b56346ca439cd580c924 Author: Abhijith Das <adas@redhat.com> Date: Fri Apr 30 10:47:09 2010 -0500 gfs2_convert: gfs2_convert doesn't convert indirectly-pointed extended attributes correctly When the extended attributes for a file don't fit in one fs block, the dinode->di_eattr block becomes an indirect block that contains pointers to blocks that actually contain the xattrs. The gfs1 indirect block header is different (larger) than that of gfs2 and this causes an incorrect conversion. The resulting gfs2 filesystem cannot display the xattrs because of leading nulls in the indirect block. This patch adjusts this correctly. Resolves: rhbz#576040 Signed-off-by: Abhi Das <adas@redhat.com> diff --git a/gfs2/convert/gfs2_convert.c b/gfs2/convert/gfs2_convert.c index 0379d33..f28aac4 100644 --- a/gfs2/convert/gfs2_convert.c +++ b/gfs2/convert/gfs2_convert.c @@ -948,6 +948,45 @@ static int fix_cdpn_symlink(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh, s return ret; } + +/* + * 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 ret = 0, 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) { + 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 ret; +} + /* ------------------------------------------------------------------------- */ /* adjust_inode - change an inode from gfs1 to gfs2 */ /* */ @@ -1039,6 +1078,12 @@ static int adjust_inode(struct gfs2_sbd *sbp, struct gfs2_buffer_head *bh) if (ret) return -1; } + /* Check for extended attributes */ + if (inode->i_di.di_eattr) { + ret = fix_xattr(sbp, bh, inode); + if (ret) + return -1; + } } bmodified(inode->i_bh);