1cf8a2df26bd30b8170272cff40d185ce75709e5
[lwext4.git] / lwext4 / ext4_ialloc.c
1 /*
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  *
4  *
5  * HelenOS:
6  * Copyright (c) 2012 Martin Sucha
7  * Copyright (c) 2012 Frantisek Princ
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * - Redistributions of source code must retain the above copyright
15  *   notice, this list of conditions and the following disclaimer.
16  * - Redistributions in binary form must reproduce the above copyright
17  *   notice, this list of conditions and the following disclaimer in the
18  *   documentation and/or other materials provided with the distribution.
19  * - The name of the author may not be used to endorse or promote products
20  *   derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /** @addtogroup lwext4
35  * @{
36  */
37 /**
38  * @file  ext4_ialloc.c
39  * @brief Inode allocation procedures.
40  */
41
42 #include "ext4_config.h"
43 #include "ext4_types.h"
44 #include "ext4_ialloc.h"
45 #include "ext4_super.h"
46 #include "ext4_crc32c.h"
47 #include "ext4_fs.h"
48 #include "ext4_blockdev.h"
49 #include "ext4_block_group.h"
50 #include "ext4_bitmap.h"
51
52 /**@brief  Convert i-node number to relative index in block group.
53  * @param sb    Superblock
54  * @param inode I-node number to be converted
55  * @return Index of the i-node in the block group
56  */
57 static uint32_t ext4_ialloc_inode2index_in_group(struct ext4_sblock *sb,
58                                                  uint32_t inode)
59 {
60         uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
61         return (inode - 1) % inodes_per_group;
62 }
63
64 /**@brief Convert relative index of i-node to absolute i-node number.
65  * @param sb    Superblock
66  * @param index Index to be converted
67  * @return Absolute number of the i-node
68  *
69  */
70 static uint32_t ext4_ialloc_index_in_group2inode(struct ext4_sblock *sb,
71                                                  uint32_t index, uint32_t bgid)
72 {
73         uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
74         return bgid * inodes_per_group + (index + 1);
75 }
76
77 /**@brief Compute block group number from the i-node number.
78  * @param sb    Superblock
79  * @param inode I-node number to be found the block group for
80  * @return Block group number computed from i-node number
81  */
82 static uint32_t ext4_ialloc_get_bgid_of_inode(struct ext4_sblock *sb,
83                                               uint32_t inode)
84 {
85         uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
86         return (inode - 1) / inodes_per_group;
87 }
88
89 static uint32_t ext4_ialloc_bitmap_csum(struct ext4_sblock *sb,
90                                         void *bitmap)
91 {
92         uint32_t checksum = 0;
93         if (ext4_sb_has_feature_read_only(sb,
94                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
95                 uint32_t inodes_per_group =
96                         ext4_get32(sb, inodes_per_group);
97
98                 /* First calculate crc32 checksum against fs uuid */
99                 checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
100                 /* Then calculate crc32 checksum against inode bitmap */
101                 checksum = ext4_crc32c(checksum, bitmap,
102                                      (inodes_per_group + 7) / 8);
103         }
104         return checksum;
105 }
106
107 /*
108  * BIG FAT NOTES:
109  *       Currently we do not verify the checksum of bitmaps.
110  */
111
112 void ext4_ialloc_set_bitmap_csum(struct ext4_sblock *sb,
113                                  struct ext4_bgroup *bg,
114                                  void *bitmap)
115 {
116         int desc_size = ext4_sb_get_desc_size(sb);
117         uint32_t checksum = ext4_ialloc_bitmap_csum(sb, bitmap);
118         uint16_t lo_checksum = to_le16(checksum & 0xFFFF),
119                  hi_checksum = to_le16(checksum >> 16);
120         
121         /* See if we need to assign a 32bit checksum */
122         bg->inode_bitmap_csum_lo = lo_checksum;
123         if (desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE)
124                 bg->inode_bitmap_csum_hi = hi_checksum;
125
126 }
127
128 int ext4_ialloc_free_inode(struct ext4_fs *fs, uint32_t index, bool is_dir)
129 {
130         struct ext4_sblock *sb = &fs->sb;
131
132         /* Compute index of block group and load it */
133         uint32_t block_group = ext4_ialloc_get_bgid_of_inode(sb, index);
134
135         struct ext4_block_group_ref bg_ref;
136         int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
137         if (rc != EOK)
138                 return rc;
139
140         /* Load i-node bitmap */
141         uint32_t bitmap_block_addr =
142             ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
143
144         struct ext4_block bitmap_block;
145         rc = ext4_block_get(fs->bdev, &bitmap_block, bitmap_block_addr);
146         if (rc != EOK)
147                 return rc;
148
149         /* Free i-node in the bitmap */
150         uint32_t index_in_group = ext4_ialloc_inode2index_in_group(sb, index);
151         ext4_bmap_bit_clr(bitmap_block.data, index_in_group);
152         ext4_ialloc_set_bitmap_csum(sb, bg_ref.block_group,
153                                     bitmap_block.data);
154         bitmap_block.dirty = true;
155
156         /* Put back the block with bitmap */
157         rc = ext4_block_set(fs->bdev, &bitmap_block);
158         if (rc != EOK) {
159                 /* Error in saving bitmap */
160                 ext4_fs_put_block_group_ref(&bg_ref);
161                 return rc;
162         }
163
164         /* If released i-node is a directory, decrement used directories count
165          */
166         if (is_dir) {
167                 uint32_t bg_used_dirs =
168                     ext4_bg_get_used_dirs_count(bg_ref.block_group, sb);
169                 bg_used_dirs--;
170                 ext4_bg_set_used_dirs_count(bg_ref.block_group, sb,
171                                             bg_used_dirs);
172         }
173
174         /* Update block group free inodes count */
175         uint32_t free_inodes =
176             ext4_bg_get_free_inodes_count(bg_ref.block_group, sb);
177         free_inodes++;
178         ext4_bg_set_free_inodes_count(bg_ref.block_group, sb, free_inodes);
179
180         bg_ref.dirty = true;
181
182         /* Put back the modified block group */
183         rc = ext4_fs_put_block_group_ref(&bg_ref);
184         if (rc != EOK)
185                 return rc;
186
187         /* Update superblock free inodes count */
188         ext4_set32(sb, free_inodes_count,
189                    ext4_get32(sb, free_inodes_count) + 1);
190
191         return EOK;
192 }
193
194 int ext4_ialloc_alloc_inode(struct ext4_fs *fs, uint32_t *index, bool is_dir)
195 {
196         struct ext4_sblock *sb = &fs->sb;
197
198         uint32_t bgid = fs->last_inode_bg_id;
199         uint32_t bg_count = ext4_block_group_cnt(sb);
200         uint32_t sb_free_inodes = ext4_get32(sb, free_inodes_count);
201         bool rewind = false;
202
203         /* Try to find free i-node in all block groups */
204         while (bgid <= bg_count) {
205
206                 if (bgid == bg_count) {
207                         if (rewind)
208                                 break;
209                         bg_count = fs->last_inode_bg_id;
210                         bgid = 0;
211                         rewind = true;
212                         continue;
213                 }
214
215                 /* Load block group to check */
216                 struct ext4_block_group_ref bg_ref;
217                 int rc = ext4_fs_get_block_group_ref(fs, bgid, &bg_ref);
218                 if (rc != EOK)
219                         return rc;
220
221                 struct ext4_bgroup *bg = bg_ref.block_group;
222
223                 /* Read necessary values for algorithm */
224                 uint32_t free_inodes = ext4_bg_get_free_inodes_count(bg, sb);
225                 uint32_t used_dirs = ext4_bg_get_used_dirs_count(bg, sb);
226
227                 /* Check if this block group is good candidate for allocation */
228                 if (free_inodes > 0) {
229                         /* Load block with bitmap */
230                         uint32_t bitmap_block_addr =
231                             ext4_bg_get_inode_bitmap(bg_ref.block_group, sb);
232
233                         struct ext4_block bitmap_block;
234                         rc = ext4_block_get(fs->bdev, &bitmap_block,
235                                             bitmap_block_addr);
236                         if (rc != EOK) {
237                                 ext4_fs_put_block_group_ref(&bg_ref);
238                                 return rc;
239                         }
240
241                         /* Try to allocate i-node in the bitmap */
242                         uint32_t inodes_in_group =
243                             ext4_inodes_in_group_cnt(sb, bgid);
244                         uint32_t index_in_group;
245
246                         rc = ext4_bmap_bit_find_clr(bitmap_block.data, 0,
247                                                     inodes_in_group,
248                                                     &index_in_group);
249                         /* Block group has not any free i-node */
250                         if (rc == ENOSPC) {
251                                 rc = ext4_block_set(fs->bdev, &bitmap_block);
252                                 if (rc != EOK) {
253                                         ext4_fs_put_block_group_ref(&bg_ref);
254                                         return rc;
255                                 }
256
257                                 rc = ext4_fs_put_block_group_ref(&bg_ref);
258                                 if (rc != EOK)
259                                         return rc;
260
261                                 continue;
262                         }
263
264                         ext4_bmap_bit_set(bitmap_block.data, index_in_group);
265
266                         /* Free i-node found, save the bitmap */
267                         ext4_ialloc_set_bitmap_csum(sb, bg_ref.block_group,
268                                                     bitmap_block.data);
269                         bitmap_block.dirty = true;
270
271                         ext4_block_set(fs->bdev, &bitmap_block);
272                         if (rc != EOK) {
273                                 ext4_fs_put_block_group_ref(&bg_ref);
274                                 return rc;
275                         }
276
277                         /* Modify filesystem counters */
278                         free_inodes--;
279                         ext4_bg_set_free_inodes_count(bg, sb, free_inodes);
280
281                         /* Increment used directories counter */
282                         if (is_dir) {
283                                 used_dirs++;
284                                 ext4_bg_set_used_dirs_count(bg, sb, used_dirs);
285                         }
286
287                         /* Decrease unused inodes count */
288                         uint32_t unused =
289                             ext4_bg_get_itable_unused(bg, sb);
290
291                         uint32_t free = inodes_in_group - unused;
292
293                         if (index_in_group >= free) {
294                                 unused = inodes_in_group -
295                                          (index_in_group + 1);
296                                 ext4_bg_set_itable_unused(bg, sb,
297                                                           unused);
298                         }
299
300                         /* Save modified block group */
301                         bg_ref.dirty = true;
302
303                         rc = ext4_fs_put_block_group_ref(&bg_ref);
304                         if (rc != EOK)
305                                 return rc;
306
307                         /* Update superblock */
308                         sb_free_inodes--;
309                         ext4_set32(sb, free_inodes_count, sb_free_inodes);
310
311                         /* Compute the absolute i-nodex number */
312                         *index = ext4_ialloc_index_in_group2inode(
313                             sb, index_in_group, bgid);
314
315                         fs->last_inode_bg_id = bgid;
316
317                         return EOK;
318                 }
319
320                 /* Block group not modified, put it and jump to the next block
321                  * group */
322                 ext4_fs_put_block_group_ref(&bg_ref);
323                 if (rc != EOK)
324                         return rc;
325
326                 ++bgid;
327         }
328
329         return ENOSPC;
330 }
331
332 /**
333  * @}
334  */