shithub: lwext4

Download patch

ref: eec357955403829d0751bf82a4407061a3c52bec
parent: dba02ea68719d50d0fd45d4edf2eac5f81c83fd6
author: ngkaho1234 <ngkaho1234@gmail.com>
date: Fri Jan 15 18:41:29 EST 2016

ext4: bring the support of recursive removal of directory back.

--- a/lwext4/ext4.c
+++ b/lwext4/ext4.c
@@ -2498,13 +2498,18 @@
 	ext4_file f;
 
 	struct ext4_mountpoint *mp = ext4_get_mount(path);
-	struct ext4_inode_ref act, parent;
+	struct ext4_inode_ref act;
+	struct ext4_inode_ref child;
+	struct ext4_dir_iter it;
 
 	uint32_t name_off;
-	uint32_t inode_up, inode_current;
+	uint32_t inode_up;
+	uint32_t inode_current;
+	uint32_t depth = 1;
 
-	bool has_children = false;
+	bool has_children;
 	bool is_goal;
+	bool dir_end;
 
 	if (!mp)
 		return ENOENT;
@@ -2511,6 +2516,8 @@
 
 	EXT4_MP_LOCK(mp);
 
+	struct ext4_fs *const fs = &mp->fs;
+
 	/*Check if exist.*/
 	r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
 	if (r != EOK) {
@@ -2526,74 +2533,188 @@
 
 	ext4_block_cache_write_back(mp->fs.bdev, 1);
 
-	/*Load parent.*/
-	r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
-			&parent);
-	if (r != EOK)
-		goto Finish;
-	r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
-			&act);
-	if (r != EOK) {
-		ext4_fs_put_inode_ref(&parent);
-		goto Finish;
-	}
-	r = ext4_has_children(&has_children, &act);
-	if (r != EOK) {
-		ext4_fs_put_inode_ref(&parent);
-		ext4_fs_put_inode_ref(&act);
-		goto Finish;
-	}
-	if (has_children) {
-		r = ENOTEMPTY;
-		ext4_fs_put_inode_ref(&parent);
-		ext4_fs_put_inode_ref(&act);
-		goto Finish;
-	}
+	do {
 
-	ext4_trans_start(mp);
+		uint64_t act_curr_pos = 0;
+		has_children = false;
+		dir_end = false;
 
-	if (ext4_inode_get_links_cnt(act.inode) == 2) {
-		/*Truncate*/
-		r = ext4_fs_truncate_inode(&act, 0);
+		while (r == EOK && !has_children && !dir_end) {
+
+			/*Load directory node.*/
+			r = ext4_fs_get_inode_ref(fs, inode_current, &act);
+			if (r != EOK) {
+				break;
+			}
+
+			/*Initialize iterator.*/
+			r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
+			if (r != EOK) {
+				ext4_fs_put_inode_ref(&act);
+				break;
+			}
+
+			if (!it.curr) {
+				dir_end = true;
+				goto End;
+			}
+
+			ext4_trans_start(mp);
+
+			/*Get up directory inode when ".." entry*/
+			if ((it.curr->name_len == 2) &&
+			    ext4_is_dots(it.curr->name, it.curr->name_len)) {
+				inode_up = ext4_dir_en_get_inode(it.curr);
+			}
+
+			/*If directory or file entry,  but not "." ".." entry*/
+			if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
+
+				/*Get child inode reference do unlink
+				 * directory/file.*/
+				uint32_t cinode;
+				cinode = ext4_dir_en_get_inode(it.curr);
+				r = ext4_fs_get_inode_ref(fs, cinode, &child);
+				if (r != EOK)
+					goto End;
+
+				/*If directory with no leaf children*/
+				r = ext4_has_children(&has_children, &child);
+				if (r != EOK) {
+					ext4_fs_put_inode_ref(&child);
+					goto End;
+				}
+
+				if (has_children) {
+					/*Has directory children. Go into this
+					 * directory.*/
+					inode_up = inode_current;
+					inode_current = cinode;
+					depth++;
+					ext4_fs_put_inode_ref(&child);
+					goto End;
+				}
+
+				/* Truncate */
+				r = ext4_fs_truncate_inode(&child, 0);
+				if (r != EOK) {
+					ext4_fs_put_inode_ref(&child);
+					goto End;
+				}
+
+				/*No children in child directory or file. Just
+				 * unlink.*/
+				r = ext4_unlink(f.mp, &act, &child,
+						(char *)it.curr->name,
+						it.curr->name_len);
+				if (r != EOK) {
+					ext4_fs_put_inode_ref(&child);
+					goto End;
+				}
+
+				ext4_inode_set_del_time(child.inode, -1L);
+				ext4_inode_set_links_cnt(child.inode, 0);
+				child.dirty = true;
+
+				r = ext4_fs_free_inode(&child);
+				if (r != EOK) {
+					ext4_fs_put_inode_ref(&child);
+					goto End;
+				}
+
+				r = ext4_fs_put_inode_ref(&child);
+				if (r != EOK)
+					goto End;
+
+			}
+
+			r = ext4_dir_iterator_next(&it);
+			if (r != EOK)
+				goto End;
+
+			act_curr_pos = it.curr_off;
+End:
+			ext4_dir_iterator_fini(&it);
+			if (r == EOK)
+				r = ext4_fs_put_inode_ref(&act);
+			else
+				ext4_fs_put_inode_ref(&act);
+
+			if (r != EOK)
+				ext4_trans_abort(mp);
+			else
+				ext4_trans_stop(mp);
+		}
+
+		if (dir_end) {
+			/*Directory iterator reached last entry*/
+			depth--;
+			if (depth)
+				inode_current = inode_up;
+
+		}
+
+	} while (depth);
+
+	/*Last unlink*/
+	if (r == EOK && !depth) {
+		/*Load parent.*/
+		struct ext4_inode_ref parent;
+		r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
+				&parent);
+		if (r != EOK)
+			goto Finish;
+		r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
+				&act);
 		if (r != EOK) {
-			ext4_fs_put_inode_ref(&parent);
 			ext4_fs_put_inode_ref(&act);
 			goto Finish;
 		}
-	}
 
-	 /* Unlink from root of current directory*/
-	r = ext4_unlink(f.mp, &parent, &act,
-			(char *)path, len);
-	if (r != EOK) {
-		ext4_fs_put_inode_ref(&parent);
-		ext4_fs_put_inode_ref(&act);
-		goto Finish;
-	}
+		ext4_trans_start(mp);
 
-	if (ext4_inode_get_links_cnt(act.inode) == 2) {
-		ext4_inode_set_del_time(act.inode, -1L);
-		ext4_inode_set_links_cnt(act.inode, 0);
-		act.dirty = true;
-
-		r = ext4_fs_free_inode(&act);
+		/* In this place all directories should be
+		 * unlinked.
+		 * Last unlink from root of current directory*/
+		r = ext4_unlink(f.mp, &parent, &act,
+				(char *)path, len);
 		if (r != EOK) {
 			ext4_fs_put_inode_ref(&parent);
 			ext4_fs_put_inode_ref(&act);
 			goto Finish;
 		}
-	}
 
-	r = ext4_fs_put_inode_ref(&parent);
-	if (r != EOK)
-		goto Finish;
+		if (ext4_inode_get_links_cnt(act.inode) == 2) {
+			ext4_inode_set_del_time(act.inode, -1L);
+			ext4_inode_set_links_cnt(act.inode, 0);
+			act.dirty = true;
+			/*Turncate*/
+			r = ext4_fs_truncate_inode(&act, 0);
+			if (r != EOK) {
+				ext4_fs_put_inode_ref(&parent);
+				ext4_fs_put_inode_ref(&act);
+				goto Finish;
+			}
 
-	r = ext4_fs_put_inode_ref(&act);
-Finish:
-	if (r != EOK)
-		ext4_trans_abort(mp);
-	else
-		ext4_trans_stop(mp);
+			r = ext4_fs_free_inode(&act);
+			if (r != EOK) {
+				ext4_fs_put_inode_ref(&parent);
+				ext4_fs_put_inode_ref(&act);
+				goto Finish;
+			}
+		}
+
+		r = ext4_fs_put_inode_ref(&parent);
+		if (r != EOK)
+			goto Finish;
+
+		r = ext4_fs_put_inode_ref(&act);
+	Finish:
+		if (r != EOK)
+			ext4_trans_abort(mp);
+		else
+			ext4_trans_stop(mp);
+	}
 
 	ext4_block_cache_write_back(mp->fs.bdev, 0);
 	EXT4_MP_UNLOCK(mp);