commit aaaf96f17ca0167f3fad3f2f63b9258d2e3ffcc1 Author: Bob Peterson <bob@ganesha.peterson> Date: Fri Jan 22 15:07:19 2010 -0600 lost+found link count and connections were not properly managed When the lost+found directory was created, function createi in libgfs2 incremented the di_nlink link count for the root directory, but fsck.gfs2 did not increment the nlink value in the hash table. As a result, pass4 doesn't find and fix the discrepancy. Subsequent runs of fsck.gfs2 detect the problem, though. This patch adjusts for the new link and keeps everything sane. In a similar fashion, if the dinode being moved to lost+found happens to be a directory, the links were not properly adjusted for that directory's ".." dentry. This patch also takes care of that so the links don't get off. rhbz#455300 diff --git a/gfs2/fsck/lost_n_found.c b/gfs2/fsck/lost_n_found.c index d9bed19..7a4d4b3 100644 --- a/gfs2/fsck/lost_n_found.c +++ b/gfs2/fsck/lost_n_found.c @@ -40,6 +40,7 @@ int add_inode_to_lf(struct gfs2_inode *ip){ char tmp_name[256]; __be32 inode_type; + struct dir_info *di; if(!lf_dip) { uint8_t q; @@ -48,6 +49,13 @@ int add_inode_to_lf(struct gfs2_inode *ip){ lf_dip = createi(ip->i_sbd->md.rooti, "lost+found", S_IFDIR | 0700, 0); + /* createi will have incremented the di_nlink link count for + the root directory. We must increment the nlink value + in the hash table to keep them in sync so that pass4 can + detect and fix any discrepancies. */ + set_link_count(ip->i_sbd->sd_sb.sb_root_dir.no_addr, + ip->i_sbd->md.rooti->i_di.di_nlink); + q = block_type(lf_dip->i_di.di_num.no_addr); if(q != gfs2_inode_dir) { /* This is a new lost+found directory, so set its @@ -72,6 +80,15 @@ int add_inode_to_lf(struct gfs2_inode *ip){ ip->i_sbd->md.rooti->i_di.di_num.no_addr, "\"..\""); } + log_info( _("lost+found directory is dinode %lld (0x%llx)\n"), + (unsigned long long)lf_dip->i_di.di_num.no_addr, + (unsigned long long)lf_dip->i_di.di_num.no_addr); + di = dirtree_find(lf_dip->i_di.di_num.no_addr); + if (di) { + log_info( _("Marking lost+found inode connected\n")); + di->checked = 1; + di = NULL; + } } if(ip->i_di.di_num.no_addr == lf_dip->i_di.di_num.no_addr) { log_err( _("Trying to add lost+found to itself...skipping")); @@ -84,6 +101,33 @@ int add_inode_to_lf(struct gfs2_inode *ip){ (unsigned long long)ip->i_di.di_num.no_addr, (unsigned long long)ip->i_di.di_num.no_addr); + /* If there's a pre-existing .. directory entry, we have to + back out the links. */ + di = dirtree_find(ip->i_di.di_num.no_addr); + if (di && gfs2_check_range(ip->i_sbd, di->dotdot_parent) == 0){ + struct gfs2_inode *dip; + + log_debug(_("Directory %lld (0x%llx) already had a " + "\"..\" link to %lld (0x%llx).\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)di->dotdot_parent, + (unsigned long long)di->dotdot_parent); + decrement_link(di->dotdot_parent, + ip->i_di.di_num.no_addr, + _(".. unlinked, moving to lost+found")); + dip = fsck_load_inode(ip->i_sbd, di->dotdot_parent); + dip->i_di.di_nlink--; + log_debug(_("Decrementing its links to %d\n"), + dip->i_di.di_nlink); + bmodified(dip->i_bh); + fsck_inode_put(&dip); + di = NULL; + } else + log_debug(_("Couldn't find a valid \"..\" entry " + "for orphan directory %lld (0x%llx)\n"), + (unsigned long long)ip->i_di.di_num.no_addr, + (unsigned long long)ip->i_di.di_num.no_addr); if(gfs2_dirent_del(ip, "..", 2)) log_warn( _("add_inode_to_lf: Unable to remove " "\"..\" directory entry.\n"));