1.1 --- a/libe2access/include/e2access/image.h Tue Feb 15 22:01:34 2022 +0100
1.2 +++ b/libe2access/include/e2access/image.h Wed Feb 16 00:02:37 2022 +0100
1.3 @@ -70,6 +70,10 @@
1.4
1.5 errcode_t image_remove_by_path(ext2_filsys fs, const char *path);
1.6
1.7 +errcode_t image_remove_directory_test(ext2_filsys fs, ext2_ino_t ino);
1.8 +
1.9 +errcode_t image_remove_parent_decrement(ext2_filsys fs, ext2_ino_t ino);
1.10 +
1.11 errcode_t image_rename(ext2_filsys fs, ext2_ino_t source,
1.12 ext2_ino_t source_parent, const char *source_basename,
1.13 ext2_ino_t target_parent, const char *target_basename);
2.1 --- a/libe2access/lib/src/image.c Tue Feb 15 22:01:34 2022 +0100
2.2 +++ b/libe2access/lib/src/image.c Wed Feb 16 00:02:37 2022 +0100
2.3 @@ -60,13 +60,53 @@
2.4 return 0;
2.5 }
2.6
2.7 -/* Update the parent entry of a renamed directory. */
2.8 +/* Common parent entry state. */
2.9
2.10 struct _image_parent_entry_state
2.11 {
2.12 ext2_ino_t ino;
2.13 + int nonempty;
2.14 };
2.15
2.16 +/* Get entry details for a directory to be removed. */
2.17 +
2.18 +static int _image_get_parent_entry(struct ext2_dir_entry *dir_entry,
2.19 + int offset, int blocksize, char *buf,
2.20 + void *priv_data)
2.21 +{
2.22 + struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
2.23 +
2.24 + (void) offset; (void) blocksize; (void) buf;
2.25 +
2.26 + if (!strcmp(dir_entry->name, ".."))
2.27 + state->ino = dir_entry->inode;
2.28 + else if (strcmp(dir_entry->name, "."))
2.29 + state->nonempty = 1;
2.30 +
2.31 + return 0;
2.32 +}
2.33 +
2.34 +/* Test for objects in a directory. */
2.35 +
2.36 +static int _image_test_directory(struct ext2_dir_entry *dir_entry,
2.37 + int offset, int blocksize, char *buf,
2.38 + void *priv_data)
2.39 +{
2.40 + struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
2.41 +
2.42 + (void) offset; (void) blocksize; (void) buf;
2.43 +
2.44 + if (strcmp(dir_entry->name, ".") && strcmp(dir_entry->name, ".."))
2.45 + {
2.46 + state->nonempty = 1;
2.47 + return DIRENT_ABORT;
2.48 + }
2.49 +
2.50 + return 0;
2.51 +}
2.52 +
2.53 +/* Update the parent entry of a renamed directory. */
2.54 +
2.55 static int _image_update_parent_entry(struct ext2_dir_entry *dir_entry,
2.56 int offset, int blocksize, char *buf,
2.57 void *priv_data)
2.58 @@ -75,7 +115,7 @@
2.59
2.60 (void) offset; (void) blocksize; (void) buf;
2.61
2.62 - if (strcmp(dir_entry->name, "..") == 0)
2.63 + if (!strcmp(dir_entry->name, ".."))
2.64 {
2.65 dir_entry->inode = state->ino;
2.66 return DIRENT_CHANGED | DIRENT_ABORT;
2.67 @@ -363,13 +403,22 @@
2.68 errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino)
2.69 {
2.70 struct ext2_inode_large inode;
2.71 - errcode_t err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
2.72 - sizeof(inode));
2.73 + errcode_t retval;
2.74 +
2.75 + if (_image_isdir(fs, ino))
2.76 + {
2.77 + retval = image_remove_directory_test(fs, ino);
2.78 + if (retval)
2.79 + return retval;
2.80 + }
2.81 +
2.82 + retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
2.83 + sizeof(inode));
2.84
2.85 /* Handle invalid inodes, ignore unreferenced inodes. */
2.86
2.87 - if (err)
2.88 - return err;
2.89 + if (retval)
2.90 + return retval;
2.91
2.92 if (!inode.i_links_count)
2.93 return 0;
2.94 @@ -381,21 +430,28 @@
2.95
2.96 if (!inode.i_links_count)
2.97 {
2.98 - err = ext2fs_free_ext_attr(fs, ino, &inode);
2.99 + retval = image_remove_parent_decrement(fs, ino);
2.100 +
2.101 + if (retval)
2.102 + return retval;
2.103
2.104 - if (!err)
2.105 + /* NOTE: Update deletion time. */
2.106 +
2.107 + retval = ext2fs_free_ext_attr(fs, ino, &inode);
2.108 +
2.109 + if (!retval)
2.110 {
2.111 /* Deallocate blocks, if appropriate. ~0ULL as the end represents
2.112 truncation. */
2.113
2.114 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode))
2.115 {
2.116 - err = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL,
2.117 + retval = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL,
2.118 0, ~0ULL);
2.119
2.120 /* Update allocation statistics. */
2.121
2.122 - if (!err)
2.123 + if (!retval)
2.124 ext2fs_inode_alloc_stats2(fs, ino, -1,
2.125 LINUX_S_ISDIR(inode.i_mode));
2.126 }
2.127 @@ -420,6 +476,67 @@
2.128 return image_remove_by_inode(fs, ino);
2.129 }
2.130
2.131 +/* Test for an empty directory. */
2.132 +
2.133 +errcode_t image_remove_directory_test(ext2_filsys fs, ext2_ino_t ino)
2.134 +{
2.135 + /* Initialise the directory listing processing state. */
2.136 +
2.137 + struct _image_parent_entry_state state = {.nonempty = 0};
2.138 + errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
2.139 + _image_test_directory, &state);
2.140 +
2.141 + if (retval)
2.142 + return retval;
2.143 +
2.144 + /* NOTE: Need a proper error here. */
2.145 +
2.146 + return state.nonempty;
2.147 +}
2.148 +
2.149 +/* Find any parent reference and decrement the parent reference count. */
2.150 +
2.151 +errcode_t image_remove_parent_decrement(ext2_filsys fs, ext2_ino_t ino)
2.152 +{
2.153 + struct ext2_inode_large inode;
2.154 +
2.155 + /* Initialise the directory listing processing state. */
2.156 +
2.157 + struct _image_parent_entry_state state = {.ino = 0};
2.158 +
2.159 + /* A directory needs to be inspected for files and for the .. entry
2.160 + to be queried to obtain the parent directory. */
2.161 +
2.162 + errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
2.163 + _image_get_parent_entry, &state);
2.164 +
2.165 + if (retval)
2.166 + return retval;
2.167 +
2.168 + /* Reduce the reference count on the parent directory. */
2.169 +
2.170 + if (state.ino)
2.171 + {
2.172 + retval = ext2fs_read_inode_full(fs, state.ino,
2.173 + (struct ext2_inode *) &inode,
2.174 + sizeof(inode));
2.175 +
2.176 + if (retval)
2.177 + return retval;
2.178 +
2.179 + if (inode.i_links_count)
2.180 + inode.i_links_count--;
2.181 +
2.182 + /* NOTE: Update modification time. */
2.183 +
2.184 + retval = ext2fs_write_inode_full(fs, state.ino,
2.185 + (struct ext2_inode *) &inode,
2.186 + sizeof(inode));
2.187 + }
2.188 +
2.189 + return retval;
2.190 +}
2.191 +
2.192 /* Rename a file. */
2.193
2.194 errcode_t image_rename(ext2_filsys fs, ext2_ino_t source,
2.195 @@ -428,7 +545,11 @@
2.196 {
2.197 errcode_t retval;
2.198 struct ext2_inode source_inode, source_parent_inode, target_parent_inode;
2.199 - struct _image_parent_entry_state state = {target_parent};
2.200 +
2.201 + /* Initialise the directory listing processing state to refer to the target
2.202 + parent. */
2.203 +
2.204 + struct _image_parent_entry_state state = {.ino = target_parent};
2.205
2.206 /* NOTE: Should check for space. */
2.207