ext4: bring the support of recursive removal of directory back.
authorngkaho1234 <ngkaho1234@gmail.com>
Fri, 15 Jan 2016 15:41:29 +0000 (23:41 +0800)
committerngkaho1234 <ngkaho1234@gmail.com>
Fri, 15 Jan 2016 15:42:27 +0000 (23:42 +0800)
lwext4/ext4.c

index 1684edb0f3511f7bf2f880507f158432ea850dea..25223c04fa3cbba0f3490df7e204f5554bf86085 100644 (file)
@@ -2498,19 +2498,26 @@ int ext4_dir_rm(const char *path)
        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;
 
        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 @@ int ext4_dir_rm(const char *path)
 
        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;
+
+               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;
 
-       if (ext4_inode_get_links_cnt(act.inode) == 2) {
-               /*Truncate*/
-               r = ext4_fs_truncate_inode(&act, 0);
+               }
+
+       } 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;
-       }
 
-       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;
+               ext4_trans_start(mp);
 
-               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);