Skip to content

Commit be629c6

Browse files
David WoodhouseDavid Woodhouse
authored andcommitted
Fix directory hardlinks from deleted directories
When a directory is deleted, we don't take too much care about killing off all the dirents that belong to it — on the basis that on remount, the scan will conclude that the directory is dead anyway. This doesn't work though, when the deleted directory contained a child directory which was moved *out*. In the early stages of the fs build we can then end up with an apparent hard link, with the child directory appearing both in its true location, and as a child of the original directory which are this stage of the mount process we don't *yet* know is defunct. To resolve this, take out the early special-casing of the "directories shall not have hard links" rule in jffs2_build_inode_pass1(), and let the normal nlink processing happen for directories as well as other inodes. Then later in the build process we can set ic->pino_nlink to the parent inode#, as is required for directories during normal operaton, instead of the nlink. And complain only *then* about hard links which are still in evidence even after killing off all the unreachable paths. Reported-by: Liu Song <liu.song11@zte.com.cn> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Cc: stable@vger.kernel.org
1 parent 49e91e7 commit be629c6

File tree

2 files changed

+62
-19
lines changed

2 files changed

+62
-19
lines changed

fs/jffs2/build.c

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
5050

5151

5252
static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
53-
struct jffs2_inode_cache *ic)
53+
struct jffs2_inode_cache *ic,
54+
int *dir_hardlinks)
5455
{
5556
struct jffs2_full_dirent *fd;
5657

@@ -69,19 +70,21 @@ static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
6970
dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
7071
fd->name, fd->ino, ic->ino);
7172
jffs2_mark_node_obsolete(c, fd->raw);
73+
/* Clear the ic/raw union so it doesn't cause problems later. */
74+
fd->ic = NULL;
7275
continue;
7376
}
7477

78+
/* From this point, fd->raw is no longer used so we can set fd->ic */
79+
fd->ic = child_ic;
80+
child_ic->pino_nlink++;
81+
/* If we appear (at this stage) to have hard-linked directories,
82+
* set a flag to trigger a scan later */
7583
if (fd->type == DT_DIR) {
76-
if (child_ic->pino_nlink) {
77-
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
78-
fd->name, fd->ino, ic->ino);
79-
/* TODO: What do we do about it? */
80-
} else {
81-
child_ic->pino_nlink = ic->ino;
82-
}
83-
} else
84-
child_ic->pino_nlink++;
84+
child_ic->flags |= INO_FLAGS_IS_DIR;
85+
if (child_ic->pino_nlink > 1)
86+
*dir_hardlinks = 1;
87+
}
8588

8689
dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
8790
/* Can't free scan_dents so far. We might need them in pass 2 */
@@ -95,8 +98,7 @@ static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
9598
*/
9699
static int jffs2_build_filesystem(struct jffs2_sb_info *c)
97100
{
98-
int ret;
99-
int i;
101+
int ret, i, dir_hardlinks = 0;
100102
struct jffs2_inode_cache *ic;
101103
struct jffs2_full_dirent *fd;
102104
struct jffs2_full_dirent *dead_fds = NULL;
@@ -120,7 +122,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
120122
/* Now scan the directory tree, increasing nlink according to every dirent found. */
121123
for_each_inode(i, c, ic) {
122124
if (ic->scan_dents) {
123-
jffs2_build_inode_pass1(c, ic);
125+
jffs2_build_inode_pass1(c, ic, &dir_hardlinks);
124126
cond_resched();
125127
}
126128
}
@@ -156,13 +158,54 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
156158
}
157159

158160
dbg_fsbuild("pass 2a complete\n");
161+
162+
if (dir_hardlinks) {
163+
/* If we detected directory hardlinks earlier, *hopefully*
164+
* they are gone now because some of the links were from
165+
* dead directories which still had some old dirents lying
166+
* around and not yet garbage-collected, but which have
167+
* been discarded above. So clear the pino_nlink field
168+
* in each directory, so that the final scan below can
169+
* print appropriate warnings. */
170+
for_each_inode(i, c, ic) {
171+
if (ic->flags & INO_FLAGS_IS_DIR)
172+
ic->pino_nlink = 0;
173+
}
174+
}
159175
dbg_fsbuild("freeing temporary data structures\n");
160176

161177
/* Finally, we can scan again and free the dirent structs */
162178
for_each_inode(i, c, ic) {
163179
while(ic->scan_dents) {
164180
fd = ic->scan_dents;
165181
ic->scan_dents = fd->next;
182+
/* We do use the pino_nlink field to count nlink of
183+
* directories during fs build, so set it to the
184+
* parent ino# now. Now that there's hopefully only
185+
* one. */
186+
if (fd->type == DT_DIR) {
187+
if (!fd->ic) {
188+
/* We'll have complained about it and marked the coresponding
189+
raw node obsolete already. Just skip it. */
190+
continue;
191+
}
192+
193+
/* We *have* to have set this in jffs2_build_inode_pass1() */
194+
BUG_ON(!(fd->ic->flags & INO_FLAGS_IS_DIR));
195+
196+
/* We clear ic->pino_nlink ∀ directories' ic *only* if dir_hardlinks
197+
* is set. Otherwise, we know this should never trigger anyway, so
198+
* we don't do the check. And ic->pino_nlink still contains the nlink
199+
* value (which is 1). */
200+
if (dir_hardlinks && fd->ic->pino_nlink) {
201+
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u is also hard linked from dir ino #%u\n",
202+
fd->name, fd->ino, ic->ino, fd->ic->pino_nlink);
203+
/* Should we unlink it from its previous parent? */
204+
}
205+
206+
/* For directories, ic->pino_nlink holds that parent inode # */
207+
fd->ic->pino_nlink = ic->ino;
208+
}
166209
jffs2_free_full_dirent(fd);
167210
}
168211
ic->scan_dents = NULL;
@@ -241,11 +284,7 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c,
241284

242285
/* Reduce nlink of the child. If it's now zero, stick it on the
243286
dead_fds list to be cleaned up later. Else just free the fd */
244-
245-
if (fd->type == DT_DIR)
246-
child_ic->pino_nlink = 0;
247-
else
248-
child_ic->pino_nlink--;
287+
child_ic->pino_nlink--;
249288

250289
if (!child_ic->pino_nlink) {
251290
dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n",

fs/jffs2/nodelist.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct jffs2_inode_cache {
194194
#define INO_STATE_CLEARING 6 /* In clear_inode() */
195195

196196
#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
197+
#define INO_FLAGS_IS_DIR 0x02 /* is a directory */
197198

198199
#define RAWNODE_CLASS_INODE_CACHE 0
199200
#define RAWNODE_CLASS_XATTR_DATUM 1
@@ -249,7 +250,10 @@ struct jffs2_readinode_info
249250

250251
struct jffs2_full_dirent
251252
{
252-
struct jffs2_raw_node_ref *raw;
253+
union {
254+
struct jffs2_raw_node_ref *raw;
255+
struct jffs2_inode_cache *ic; /* Just during part of build */
256+
};
253257
struct jffs2_full_dirent *next;
254258
uint32_t version;
255259
uint32_t ino; /* == zero for unlink */

0 commit comments

Comments
 (0)