1.1 --- a/libe2access/lib/src/image.c Tue Feb 15 22:01:34 2022 +0100
1.2 +++ b/libe2access/lib/src/image.c Wed Feb 16 00:02:37 2022 +0100
1.3 @@ -60,13 +60,53 @@
1.4 return 0;
1.5 }
1.6
1.7 -/* Update the parent entry of a renamed directory. */
1.8 +/* Common parent entry state. */
1.9
1.10 struct _image_parent_entry_state
1.11 {
1.12 ext2_ino_t ino;
1.13 + int nonempty;
1.14 };
1.15
1.16 +/* Get entry details for a directory to be removed. */
1.17 +
1.18 +static int _image_get_parent_entry(struct ext2_dir_entry *dir_entry,
1.19 + int offset, int blocksize, char *buf,
1.20 + void *priv_data)
1.21 +{
1.22 + struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
1.23 +
1.24 + (void) offset; (void) blocksize; (void) buf;
1.25 +
1.26 + if (!strcmp(dir_entry->name, ".."))
1.27 + state->ino = dir_entry->inode;
1.28 + else if (strcmp(dir_entry->name, "."))
1.29 + state->nonempty = 1;
1.30 +
1.31 + return 0;
1.32 +}
1.33 +
1.34 +/* Test for objects in a directory. */
1.35 +
1.36 +static int _image_test_directory(struct ext2_dir_entry *dir_entry,
1.37 + int offset, int blocksize, char *buf,
1.38 + void *priv_data)
1.39 +{
1.40 + struct _image_parent_entry_state *state = (struct _image_parent_entry_state *) priv_data;
1.41 +
1.42 + (void) offset; (void) blocksize; (void) buf;
1.43 +
1.44 + if (strcmp(dir_entry->name, ".") && strcmp(dir_entry->name, ".."))
1.45 + {
1.46 + state->nonempty = 1;
1.47 + return DIRENT_ABORT;
1.48 + }
1.49 +
1.50 + return 0;
1.51 +}
1.52 +
1.53 +/* Update the parent entry of a renamed directory. */
1.54 +
1.55 static int _image_update_parent_entry(struct ext2_dir_entry *dir_entry,
1.56 int offset, int blocksize, char *buf,
1.57 void *priv_data)
1.58 @@ -75,7 +115,7 @@
1.59
1.60 (void) offset; (void) blocksize; (void) buf;
1.61
1.62 - if (strcmp(dir_entry->name, "..") == 0)
1.63 + if (!strcmp(dir_entry->name, ".."))
1.64 {
1.65 dir_entry->inode = state->ino;
1.66 return DIRENT_CHANGED | DIRENT_ABORT;
1.67 @@ -363,13 +403,22 @@
1.68 errcode_t image_remove_by_inode(ext2_filsys fs, ext2_ino_t ino)
1.69 {
1.70 struct ext2_inode_large inode;
1.71 - errcode_t err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
1.72 - sizeof(inode));
1.73 + errcode_t retval;
1.74 +
1.75 + if (_image_isdir(fs, ino))
1.76 + {
1.77 + retval = image_remove_directory_test(fs, ino);
1.78 + if (retval)
1.79 + return retval;
1.80 + }
1.81 +
1.82 + retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *) &inode,
1.83 + sizeof(inode));
1.84
1.85 /* Handle invalid inodes, ignore unreferenced inodes. */
1.86
1.87 - if (err)
1.88 - return err;
1.89 + if (retval)
1.90 + return retval;
1.91
1.92 if (!inode.i_links_count)
1.93 return 0;
1.94 @@ -381,21 +430,28 @@
1.95
1.96 if (!inode.i_links_count)
1.97 {
1.98 - err = ext2fs_free_ext_attr(fs, ino, &inode);
1.99 + retval = image_remove_parent_decrement(fs, ino);
1.100 +
1.101 + if (retval)
1.102 + return retval;
1.103
1.104 - if (!err)
1.105 + /* NOTE: Update deletion time. */
1.106 +
1.107 + retval = ext2fs_free_ext_attr(fs, ino, &inode);
1.108 +
1.109 + if (!retval)
1.110 {
1.111 /* Deallocate blocks, if appropriate. ~0ULL as the end represents
1.112 truncation. */
1.113
1.114 if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *) &inode))
1.115 {
1.116 - err = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL,
1.117 + retval = ext2fs_punch(fs, ino, (struct ext2_inode *) &inode, NULL,
1.118 0, ~0ULL);
1.119
1.120 /* Update allocation statistics. */
1.121
1.122 - if (!err)
1.123 + if (!retval)
1.124 ext2fs_inode_alloc_stats2(fs, ino, -1,
1.125 LINUX_S_ISDIR(inode.i_mode));
1.126 }
1.127 @@ -420,6 +476,67 @@
1.128 return image_remove_by_inode(fs, ino);
1.129 }
1.130
1.131 +/* Test for an empty directory. */
1.132 +
1.133 +errcode_t image_remove_directory_test(ext2_filsys fs, ext2_ino_t ino)
1.134 +{
1.135 + /* Initialise the directory listing processing state. */
1.136 +
1.137 + struct _image_parent_entry_state state = {.nonempty = 0};
1.138 + errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
1.139 + _image_test_directory, &state);
1.140 +
1.141 + if (retval)
1.142 + return retval;
1.143 +
1.144 + /* NOTE: Need a proper error here. */
1.145 +
1.146 + return state.nonempty;
1.147 +}
1.148 +
1.149 +/* Find any parent reference and decrement the parent reference count. */
1.150 +
1.151 +errcode_t image_remove_parent_decrement(ext2_filsys fs, ext2_ino_t ino)
1.152 +{
1.153 + struct ext2_inode_large inode;
1.154 +
1.155 + /* Initialise the directory listing processing state. */
1.156 +
1.157 + struct _image_parent_entry_state state = {.ino = 0};
1.158 +
1.159 + /* A directory needs to be inspected for files and for the .. entry
1.160 + to be queried to obtain the parent directory. */
1.161 +
1.162 + errcode_t retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
1.163 + _image_get_parent_entry, &state);
1.164 +
1.165 + if (retval)
1.166 + return retval;
1.167 +
1.168 + /* Reduce the reference count on the parent directory. */
1.169 +
1.170 + if (state.ino)
1.171 + {
1.172 + retval = ext2fs_read_inode_full(fs, state.ino,
1.173 + (struct ext2_inode *) &inode,
1.174 + sizeof(inode));
1.175 +
1.176 + if (retval)
1.177 + return retval;
1.178 +
1.179 + if (inode.i_links_count)
1.180 + inode.i_links_count--;
1.181 +
1.182 + /* NOTE: Update modification time. */
1.183 +
1.184 + retval = ext2fs_write_inode_full(fs, state.ino,
1.185 + (struct ext2_inode *) &inode,
1.186 + sizeof(inode));
1.187 + }
1.188 +
1.189 + return retval;
1.190 +}
1.191 +
1.192 /* Rename a file. */
1.193
1.194 errcode_t image_rename(ext2_filsys fs, ext2_ino_t source,
1.195 @@ -428,7 +545,11 @@
1.196 {
1.197 errcode_t retval;
1.198 struct ext2_inode source_inode, source_parent_inode, target_parent_inode;
1.199 - struct _image_parent_entry_state state = {target_parent};
1.200 +
1.201 + /* Initialise the directory listing processing state to refer to the target
1.202 + parent. */
1.203 +
1.204 + struct _image_parent_entry_state state = {.ino = target_parent};
1.205
1.206 /* NOTE: Should check for space. */
1.207