[PATCH]jffs2:bugfix for should not appeared directory hard link

liu.song11 at zte.com.cn liu.song11 at zte.com.cn
Thu Feb 12 20:09:40 PST 2015


David Woodhouse <dwmw2 at infradead.org> wrote on 2015-02-12 21:43:19:

> From: David Woodhouse <dwmw2 at infradead.org>
> To: liu.song11 at zte.com.cn,
> Cc: linux-mtd at lists.infradead.org, wang.bo116 at zte.com.cn, deng.chao1 at zte.com.cn, jiang.xuexin at zte.com.cn, cui.yunfeng at zte.com.cn
> Date: 2015-02-12 21:43
> Subject: Re: [PATCH]jffs2:bugfix for should not appeared directory hard link
>
> On Fri, 2014-10-24 at 17:26 +0800, liu.song11 at zte.com.cn wrote:
> >
> > When jffs2 with summary, during the mounting period, in
> > jffs2_build_remove_unlinked_inode will handle unlinke inode, but this
> > will cause unlinked directory's deletion  dirent erased before
> > it's obsoleted dirent.So if the directory has renamed, then could
> > cause a hard link when we remount jffs2.
>
> > We can stable reproduce the hard link only by following process.
> > 1. we mount jffs2 in /mnt
> > 2. create directories /mnt/SW1/, /mnt/SW2/
> > 3. create directory /mnt/old/
> > 4. rename /mnt/SW1/ to /mnt/old/SW1/, /mnt/SW2/ to /mnt/old/SW2/
> > 5. do some data write. don't touch these directories.
> > 6. rename /mnt/old/SW1/ to /mnt/SW1/, /mnt/old/SW2/ to /mnt/SW2/
> > 7. delete /mnt/old/
> >
> > do above process 3 - 7 several times, then reboot, during the mounting
> > process, we could found
> > /mnt/SW1/ and /mnt/old/SW1/ become hard link,/mnt/SW2/
> > and /mnt/old/SW2/ become hard link. So we
> > do some job to fix this bug.We have tested this patch and the hard
> > link error will never occur.
>
> Hi, apologies for the delay in looking at this.
>
> I'm trying to understand the root cause of the problem. It looks like
> the real problem here is that the /mnt/old/ directory seems to exist,
> when it shouldn't?
>
> It shouldn't *matter* that we erased the deletion dirents from /mnt/old/
> leaving potentially *valid* dirents pointing to the "SW1" and "SW2"
> inodes, should it? Because the /mnt/ directory *itself* shouldn't exist.
>
> As far as I can tell, your patch seems to focus on the dirents pointing
> to the SW1 and SW2 directories. But I think that's the wrong approach.
>
> On mounting the file system again after /mnt/old was supposed to have
> been deleted, *why* is it still appearing? Can you capture debug
> messages over a serial console with CONFIG_JFFS2_FS_DEBUG=2?
>
> I'd like to see the part in step #7 where it actually writes the
> deletion dirent for /mnt/foo, followed by the full log of the remount
> where it erroneously decides that /mnt/foo still exists.
>
> Thanks.
>
> --
> dwmw2

Hi David,

Thanks for your attention.
Here we use a jffs2 filesystem's image to describe this problem step by step.
1. we choose /mnt as the mount point of jffs2(mount -t jffs2 /dev/mtdblock0 /mnt)
   mtdblock0 has just been formatted in jffs2 format, so there is nothing under /mnt after we mount it.

2. we create directory SW1, SW2, SW3, SW4, old under /mnt in sequence, now we have /mnt/SW1/, /mnt/SW2/,
   /mnt/SW3/, /mnt/SW4/,/mnt/old/. We could know,/mnt/SW1's ino number is 2, /mnt/SW2's ino number is 3,
   /mnt/SW3's ino number is 4,/mnt/SW4's ino number is 5.

3. mv /mnt/SW1 /mnt/old/SW1
   mv /mnt/SW2 /mnt/old/SW2
   mv /mnt/SW3 /mnt/old/SW3
   mv /mnt/SW4 /mnt/old/SW4

4. mv /mnt/old/SW1 /mnt/SW1
   mv /mnt/old/SW2 /mnt/SW2
   mv /mnt/old/SW3 /mnt/SW3
   mv /mnt/old/SW4 /mnt/SW4

5. rmdir /mnt/old
6. mkdir /mnt/old
7. goto step 3

In this loop, there only /mnt/old's ino number will changed.

Before the problem happen, here we print the dirents' info(after step 3 and before step4).
We can see,/mnt/old/SW1's pino is 71, that means /mnt/old's ino number is 71.
The dirents all in same eraseblock(0x0e600000 - 0x0e61FFFF).
-------------------------------------------------------------
ino = 2, name = SW1, version = 2, pino = 71, ofs = 0x0e6186e8
ino = 3, name = SW2, version = 3, pino = 71, ofs = 0x0e618740
ino = 4, name = SW3, version = 4, pino = 71, ofs = 0x0e618798
ino = 5, name = SW4, version = 5, pino = 71, ofs = 0x0e6187f0
ino = 71, name = old, version = 299, pino = 1, ofs = 0x0e6186bc
-------------------------------------------------------------

After step5 and before step6, we print the dirents' info.
We can see, SW1 - SW4 deletion dirents' all in same eraseblock(0x05680000 - 0x0569FFFF).
--------------------------------------------------------------
ino = 0, name = SW1, version = 6, pino = 71, ofs = 0x056803e4
ino = 0, name = SW2, version = 7, pino = 71, ofs = 0x0568043c
ino = 0, name = SW3, version = 8, pino = 71, ofs = 0x05680494
ino = 0, name = SW4, version = 9, pino = 71, ofs = 0x056804ec
ino = 2, name = SW1, version = 2, pino = 71, ofs = 0x0e6186e8
ino = 3, name = SW2, version = 3, pino = 71, ofs = 0x0e618740
ino = 4, name = SW3, version = 4, pino = 71, ofs = 0x0e618798
ino = 5, name = SW4, version = 5, pino = 71, ofs = 0x0e6187f0
ino = 0, name = old, version = 298, pino = 1, ofs = 0x0e6185c4
ino = 71, name = old, version = 299, pino = 1, ofs = 0x0e6186bc
---------------------------------------------------------------
Here, we stop and remount jffs2. Because ino = 0, name = old, version = 298, pino = 1, ofs = 0x0e6185c4 exist,
so /mnt/old will be handled in "jffs2_build_remove_unlinked_inode". In this function, /mnt/old/(ino:71) 's child dirent
will been mark obsolete.
--------------------------------------------------------------
ino = 0, name = SW1, version = 6, pino = 71, ofs = 0x056803e4 <---mark obsolete in jffs2_build_remove_unlinked_inode
ino = 0, name = SW2, version = 7, pino = 71, ofs = 0x0568043c <---mark obsolete in jffs2_build_remove_unlinked_inode
ino = 0, name = SW3, version = 8, pino = 71, ofs = 0x05680494 <---mark obsolete in jffs2_build_remove_unlinked_inode
ino = 0, name = SW4, version = 9, pino = 71, ofs = 0x056804ec <---mark obsolete in jffs2_build_remove_unlinked_inode
ino = 2, name = SW1, version = 2, pino = 71, ofs = 0x0e6186e8 <---mark obsolete by ino = 0, name = SW1, version = 6, pino = 71, ofs = 0x056803e4 in jffs2_add_fd_to_list
ino = 3, name = SW2, version = 3, pino = 71, ofs = 0x0e618740 <---mark obsolete by ino = 0, name = SW2, version = 7, pino = 71, ofs = 0x0568043c in jffs2_add_fd_to_list
ino = 4, name = SW3, version = 4, pino = 71, ofs = 0x0e618798 <---mark obsolete by ino = 0, name = SW3, version = 8, pino = 71, ofs = 0x05680494 in jffs2_add_fd_to_list
ino = 5, name = SW4, version = 5, pino = 71, ofs = 0x0e6187f0 <---mark obsolete by ino = 0, name = SW4, version = 9, pino = 71, ofs = 0x056804ec in jffs2_add_fd_to_list
---------------------------------------------------------------

There is a situation here, eraseblock(0x05680000 - 0x0569FFFF) could be all dirty, and erased before eraseblock(0x0e600000 - 0x0e61FFFF) which is not all dirty.
If this situation has happened, then /mnt/old/SW1 - /mnt/old/SW4 's deletion dirents has erased before /mnt/old/SW1 - /mnt/old/SW4 's dirents erase.

Now we remount jffs2, and will found these print.There /mnt/SW1(ino:2) and /mnt/old/SW1(ino:2) both exist, the two dirent both point to inode which ino number is 2.
However, /mnt/old(ino :71)'s deletion dirent is exist. Then in "jffs2_build_remove_unlinked_inode" will mark its child obsolete. Because /mnt/old/SW1(pino : 71, ino :2)
doesn't have its deletion dirent, then in "jffs2_build_remove_unlinked_inode" will execute to this line "child_ic->pino_nlink = 0;" and make inode(ino : 2) unlinked.
inode(ino:3,ino:4,ino:5) is same as inode(ino:2).
-----------------------------------------------------------------
ino = 2, name = SW1, version = 2, pino = 71, ofs = 0x0e6186e8
ino = 3, name = SW2, version = 3, pino = 71, ofs = 0x0e618740
ino = 4, name = SW3, version = 4, pino = 71, ofs = 0x0e618798
ino = 5, name = SW4, version = 5, pino = 71, ofs = 0x0e6187f0
child dir "SW1" (ino #2) of dir ino #71 appears to be a hard link
child dir "SW2" (ino #3) of dir ino #71 appears to be a hard link
child dir "SW3" (ino #4) of dir ino #71 appears to be a hard link
child dir "SW4" (ino #5) of dir ino #71 appears to be a hard link
------------------------------------------------------------------

Here, the state is below.
/mnt/SW1 <---dirent exist but inode's unlinked by "child_ic->pino_nlink = 0;"
/mnt/SW2 <---dirent exist but inode's unlinked by "child_ic->pino_nlink = 0;"
/mnt/SW3 <---dirent exist but inode's unlinked by "child_ic->pino_nlink = 0;"
/mnt/SW4 <---dirent exist but inode's unlinked by "child_ic->pino_nlink = 0;"
/mnt/old <--- deleted
/mnt/old/SW1 <--- doesn't have deletion dirent but mark obsolete in "jffs2_build_remove_unlinked_inode"
/mnt/old/SW2 <--- doesn't have deletion dirent but mark obsolete in "jffs2_build_remove_unlinked_inode"
/mnt/old/SW3 <--- doesn't have deletion dirent but mark obsolete in "jffs2_build_remove_unlinked_inode"
/mnt/old/SW4 <--- doesn't have deletion dirent but mark obsolete in "jffs2_build_remove_unlinked_inode"

So if we want to access /mnt/SW1, we will get this "no data nodes found for ino #2 ","./SW1: Input/output error", because there has no valid data nodes.

In this patch, we avoid to mark the deletion dirent of unlinked dirent's child obsolete in "jffs2_build_remove_unlinked_inode" and handle it in gc,
which could mark the deletion dirent obsolete safely.

Thanks
Best regards
--------------------------------------------------------
ZTE Information Security Notice: The information contained in this mail (and any attachment transmitted herewith) is privileged and confidential and is intended for the exclusive use of the addressee(s).  If you are not an intended recipient, any disclosure, reproduction, distribution or other dissemination or use of the information contained is strictly prohibited.  If you have received this mail in error, please delete it and notify us immediately.


More information about the linux-mtd mailing list