aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Woodhouse <David.Woodhouse@intel.com>2016-02-01 09:04:46 -0500
committerDavid Woodhouse <David.Woodhouse@intel.com>2016-02-25 06:11:28 -0500
commitbe629c62a603e5935f8177fd8a19e014100a259e (patch)
treef94560a8b9a91a2ada6253a55133977f8553b31c /fs
parent49e91e7079febe59a20ca885a87dd1c54240d0f1 (diff)
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
Diffstat (limited to 'fs')
-rw-r--r--fs/jffs2/build.c75
-rw-r--r--fs/jffs2/nodelist.h6
2 files changed, 62 insertions, 19 deletions
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 0ae91ad6df2d..b288c8ae1236 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -50,7 +50,8 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
50 50
51 51
52static void jffs2_build_inode_pass1(struct jffs2_sb_info *c, 52static 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)
54{ 55{
55 struct jffs2_full_dirent *fd; 56 struct jffs2_full_dirent *fd;
56 57
@@ -69,19 +70,21 @@ static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
69 dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", 70 dbg_fsbuild("child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
70 fd->name, fd->ino, ic->ino); 71 fd->name, fd->ino, ic->ino);
71 jffs2_mark_node_obsolete(c, fd->raw); 72 jffs2_mark_node_obsolete(c, fd->raw);
73 /* Clear the ic/raw union so it doesn't cause problems later. */
74 fd->ic = NULL;
72 continue; 75 continue;
73 } 76 }
74 77
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 */
75 if (fd->type == DT_DIR) { 83 if (fd->type == DT_DIR) {
76 if (child_ic->pino_nlink) { 84 child_ic->flags |= INO_FLAGS_IS_DIR;
77 JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", 85 if (child_ic->pino_nlink > 1)
78 fd->name, fd->ino, ic->ino); 86 *dir_hardlinks = 1;
79 /* TODO: What do we do about it? */ 87 }
80 } else {
81 child_ic->pino_nlink = ic->ino;
82 }
83 } else
84 child_ic->pino_nlink++;
85 88
86 dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); 89 dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
87 /* Can't free scan_dents so far. We might need them in pass 2 */ 90 /* 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,
95*/ 98*/
96static int jffs2_build_filesystem(struct jffs2_sb_info *c) 99static int jffs2_build_filesystem(struct jffs2_sb_info *c)
97{ 100{
98 int ret; 101 int ret, i, dir_hardlinks = 0;
99 int i;
100 struct jffs2_inode_cache *ic; 102 struct jffs2_inode_cache *ic;
101 struct jffs2_full_dirent *fd; 103 struct jffs2_full_dirent *fd;
102 struct jffs2_full_dirent *dead_fds = NULL; 104 struct jffs2_full_dirent *dead_fds = NULL;
@@ -120,7 +122,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
120 /* Now scan the directory tree, increasing nlink according to every dirent found. */ 122 /* Now scan the directory tree, increasing nlink according to every dirent found. */
121 for_each_inode(i, c, ic) { 123 for_each_inode(i, c, ic) {
122 if (ic->scan_dents) { 124 if (ic->scan_dents) {
123 jffs2_build_inode_pass1(c, ic); 125 jffs2_build_inode_pass1(c, ic, &dir_hardlinks);
124 cond_resched(); 126 cond_resched();
125 } 127 }
126 } 128 }
@@ -156,6 +158,20 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
156 } 158 }
157 159
158 dbg_fsbuild("pass 2a complete\n"); 160 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 }
159 dbg_fsbuild("freeing temporary data structures\n"); 175 dbg_fsbuild("freeing temporary data structures\n");
160 176
161 /* Finally, we can scan again and free the dirent structs */ 177 /* Finally, we can scan again and free the dirent structs */
@@ -163,6 +179,33 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
163 while(ic->scan_dents) { 179 while(ic->scan_dents) {
164 fd = ic->scan_dents; 180 fd = ic->scan_dents;
165 ic->scan_dents = fd->next; 181 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 }
166 jffs2_free_full_dirent(fd); 209 jffs2_free_full_dirent(fd);
167 } 210 }
168 ic->scan_dents = NULL; 211 ic->scan_dents = NULL;
@@ -241,11 +284,7 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c,
241 284
242 /* Reduce nlink of the child. If it's now zero, stick it on the 285 /* Reduce nlink of the child. If it's now zero, stick it on the
243 dead_fds list to be cleaned up later. Else just free the fd */ 286 dead_fds list to be cleaned up later. Else just free the fd */
244 287 child_ic->pino_nlink--;
245 if (fd->type == DT_DIR)
246 child_ic->pino_nlink = 0;
247 else
248 child_ic->pino_nlink--;
249 288
250 if (!child_ic->pino_nlink) { 289 if (!child_ic->pino_nlink) {
251 dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n", 290 dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n",
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index fa35ff79ab35..0637271f3770 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -194,6 +194,7 @@ struct jffs2_inode_cache {
194#define INO_STATE_CLEARING 6 /* In clear_inode() */ 194#define INO_STATE_CLEARING 6 /* In clear_inode() */
195 195
196#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */ 196#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
197#define INO_FLAGS_IS_DIR 0x02 /* is a directory */
197 198
198#define RAWNODE_CLASS_INODE_CACHE 0 199#define RAWNODE_CLASS_INODE_CACHE 0
199#define RAWNODE_CLASS_XATTR_DATUM 1 200#define RAWNODE_CLASS_XATTR_DATUM 1
@@ -249,7 +250,10 @@ struct jffs2_readinode_info
249 250
250struct jffs2_full_dirent 251struct jffs2_full_dirent
251{ 252{
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 };
253 struct jffs2_full_dirent *next; 257 struct jffs2_full_dirent *next;
254 uint32_t version; 258 uint32_t version;
255 uint32_t ino; /* == zero for unlink */ 259 uint32_t ino; /* == zero for unlink */