blob: 84b74a9a4bb46b84d77595b228bb214ab089ad30 [file] [log] [blame]
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00001/*
2 * icount.c --- an efficient inode count abstraction
3 *
4 * Copyright (C) 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
Theodore Ts'o543547a2010-05-17 21:31:56 -04007 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00009 * %End-Header%
10 */
11
Theodore Ts'od1154eb2011-09-18 17:34:37 -040012#include "config.h"
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000013#if HAVE_UNISTD_H
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000014#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000015#endif
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000016#include <string.h>
17#include <stdio.h>
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -040018#include <sys/stat.h>
19#include <fcntl.h>
20#include <errno.h>
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000021
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000022#include "ext2_fs.h"
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000023#include "ext2fs.h"
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -040024#include "tdb.h"
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000025
26/*
27 * The data storage strategy used by icount relies on the observation
28 * that most inode counts are either zero (for non-allocated inodes),
29 * one (for most files), and only a few that are two or more
30 * (directories and files that are linked to more than one directory).
31 *
32 * Also, e2fsck tends to load the icount data sequentially.
33 *
34 * So, we use an inode bitmap to indicate which inodes have a count of
35 * one, and then use a sorted list to store the counts for inodes
36 * which are greater than one.
37 *
38 * We also use an optional bitmap to indicate which inodes are already
39 * in the sorted list, to speed up the use of this abstraction by
40 * e2fsck's pass 2. Pass 2 increments inode counts as it finds them,
41 * so this extra bitmap avoids searching the sorted list to see if a
42 * particular inode is on the sorted list already.
43 */
44
45struct ext2_icount_el {
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000046 ext2_ino_t ino;
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -040047 __u32 count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000048};
49
50struct ext2_icount {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000051 errcode_t magic;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000052 ext2fs_inode_bitmap single;
53 ext2fs_inode_bitmap multiple;
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000054 ext2_ino_t count;
55 ext2_ino_t size;
56 ext2_ino_t num_inodes;
Theodore Ts'o54434922003-12-07 01:28:50 -050057 ext2_ino_t cursor;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000058 struct ext2_icount_el *list;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -040059 struct ext2_icount_el *last_lookup;
60 char *tdb_fn;
61 TDB_CONTEXT *tdb;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000062};
63
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -040064/*
65 * We now use a 32-bit counter field because it doesn't cost us
66 * anything extra for the in-memory data structure, due to alignment
67 * padding. But there's no point changing the interface if most of
68 * the time we only care if the number is bigger than 65,000 or not.
69 * So use the following translation function to return a 16-bit count.
70 */
71#define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x))
72
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000073void ext2fs_free_icount(ext2_icount_t icount)
74{
75 if (!icount)
76 return;
77
78 icount->magic = 0;
79 if (icount->list)
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040080 ext2fs_free_mem(&icount->list);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000081 if (icount->single)
82 ext2fs_free_inode_bitmap(icount->single);
83 if (icount->multiple)
84 ext2fs_free_inode_bitmap(icount->multiple);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -040085 if (icount->tdb)
86 tdb_close(icount->tdb);
87 if (icount->tdb_fn) {
88 unlink(icount->tdb_fn);
89 free(icount->tdb_fn);
90 }
91
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -040092 ext2fs_free_mem(&icount);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000093}
94
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -040095static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret)
96{
97 ext2_icount_t icount;
98 errcode_t retval;
99
100 *ret = 0;
101
102 retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
103 if (retval)
104 return retval;
105 memset(icount, 0, sizeof(struct ext2_icount));
106
Lukas Czerner9288e3b2011-12-16 17:31:07 -0500107 retval = ext2fs_allocate_inode_bitmap(fs, "icount", &icount->single);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400108 if (retval)
109 goto errout;
110
111 if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
Lukas Czerner9288e3b2011-12-16 17:31:07 -0500112 retval = ext2fs_allocate_inode_bitmap(fs, "icount_inc",
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400113 &icount->multiple);
114 if (retval)
115 goto errout;
116 } else
117 icount->multiple = 0;
118
119 icount->magic = EXT2_ET_MAGIC_ICOUNT;
120 icount->num_inodes = fs->super->s_inodes_count;
121
122 *ret = icount;
123 return 0;
124
125errout:
126 ext2fs_free_icount(icount);
127 return(retval);
128}
129
130struct uuid {
131 __u32 time_low;
132 __u16 time_mid;
133 __u16 time_hi_and_version;
134 __u16 clock_seq;
135 __u8 node[6];
136};
137
138static void unpack_uuid(void *in, struct uuid *uu)
139{
140 __u8 *ptr = in;
141 __u32 tmp;
142
143 tmp = *ptr++;
144 tmp = (tmp << 8) | *ptr++;
145 tmp = (tmp << 8) | *ptr++;
146 tmp = (tmp << 8) | *ptr++;
147 uu->time_low = tmp;
148
149 tmp = *ptr++;
150 tmp = (tmp << 8) | *ptr++;
151 uu->time_mid = tmp;
152
153 tmp = *ptr++;
154 tmp = (tmp << 8) | *ptr++;
155 uu->time_hi_and_version = tmp;
156
157 tmp = *ptr++;
158 tmp = (tmp << 8) | *ptr++;
159 uu->clock_seq = tmp;
160
161 memcpy(uu->node, ptr, 6);
162}
163
164static void uuid_unparse(void *uu, char *out)
165{
166 struct uuid uuid;
167
168 unpack_uuid(uu, &uuid);
169 sprintf(out,
170 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
171 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
172 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
173 uuid.node[0], uuid.node[1], uuid.node[2],
174 uuid.node[3], uuid.node[4], uuid.node[5]);
175}
176
177errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
178 int flags, ext2_icount_t *ret)
179{
180 ext2_icount_t icount;
181 errcode_t retval;
182 char *fn, uuid[40];
Theodore Ts'o4e523bb2011-11-29 11:24:52 -0500183 ext2_ino_t num_inodes;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400184 int fd;
185
186 retval = alloc_icount(fs, flags, &icount);
187 if (retval)
188 return retval;
189
190 retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn);
191 if (retval)
192 goto errout;
193 uuid_unparse(fs->super->s_uuid, uuid);
194 sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid);
195 fd = mkstemp(fn);
196
Theodore Ts'o4e523bb2011-11-29 11:24:52 -0500197 /*
198 * This is an overestimate of the size that we will need; the
199 * ideal value is the number of used inodes with a count
200 * greater than 1. OTOH the times when we really need this is
201 * with the backup programs that use lots of hard links, in
202 * which case the number of inodes in use approaches the ideal
203 * value.
204 */
205 num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
206
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400207 icount->tdb_fn = fn;
Theodore Ts'o4e523bb2011-11-29 11:24:52 -0500208 icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400209 O_RDWR | O_CREAT | O_TRUNC, 0600);
210 if (icount->tdb) {
211 close(fd);
212 *ret = icount;
213 return 0;
214 }
215
216 retval = errno;
217 close(fd);
218
219errout:
220 ext2fs_free_icount(icount);
221 return(retval);
222}
223
Theodore Ts'o54434922003-12-07 01:28:50 -0500224errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
Theodore Ts'o521e3681997-04-29 17:48:10 +0000225 ext2_icount_t hint, ext2_icount_t *ret)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000226{
227 ext2_icount_t icount;
228 errcode_t retval;
229 size_t bytes;
Theodore Ts'o54434922003-12-07 01:28:50 -0500230 ext2_ino_t i;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000231
Theodore Ts'o521e3681997-04-29 17:48:10 +0000232 if (hint) {
233 EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
234 if (hint->size > size)
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000235 size = (size_t) hint->size;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000236 }
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400237
238 retval = alloc_icount(fs, flags, &icount);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000239 if (retval)
240 return retval;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000241
242 if (size) {
243 icount->size = size;
244 } else {
245 /*
246 * Figure out how many special case inode counts we will
247 * have. We know we will need one for each directory;
248 * we also need to reserve some extra room for file links
249 */
250 retval = ext2fs_get_num_dirs(fs, &icount->size);
251 if (retval)
252 goto errout;
253 icount->size += fs->super->s_inodes_count / 50;
254 }
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400255
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000256 bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000257#if 0
Eric Sandeend0ff90d2006-09-12 14:56:15 -0400258 printf("Icount allocated %u entries, %d bytes.\n",
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000259 icount->size, bytes);
260#endif
Theodore Ts'oee010792007-11-09 19:01:06 -0500261 retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
262 &icount->list);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000263 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000264 goto errout;
265 memset(icount->list, 0, bytes);
266
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000267 icount->count = 0;
268 icount->cursor = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000269
Theodore Ts'o521e3681997-04-29 17:48:10 +0000270 /*
271 * Populate the sorted list with those entries which were
272 * found in the hint icount (since those are ones which will
273 * likely need to be in the sorted list this time around).
274 */
275 if (hint) {
276 for (i=0; i < hint->count; i++)
277 icount->list[i].ino = hint->list[i].ino;
278 icount->count = hint->count;
279 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000280
Theodore Ts'o521e3681997-04-29 17:48:10 +0000281 *ret = icount;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000282 return 0;
283
284errout:
285 ext2fs_free_icount(icount);
286 return(retval);
287}
288
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400289errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
Theodore Ts'o54434922003-12-07 01:28:50 -0500290 unsigned int size,
Theodore Ts'o521e3681997-04-29 17:48:10 +0000291 ext2_icount_t *ret)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000292{
Theodore Ts'o521e3681997-04-29 17:48:10 +0000293 return ext2fs_create_icount2(fs, flags, size, 0, ret);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000294}
295
296/*
Theodore Ts'o521e3681997-04-29 17:48:10 +0000297 * insert_icount_el() --- Insert a new entry into the sorted list at a
298 * specified position.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000299 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000300static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000301 ext2_ino_t ino, int pos)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000302{
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000303 struct ext2_icount_el *el;
304 errcode_t retval;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400305 ext2_ino_t new_size = 0;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000306 int num;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000307
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400308 if (icount->last_lookup && icount->last_lookup->ino == ino)
309 return icount->last_lookup;
310
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000311 if (icount->count >= icount->size) {
312 if (icount->count) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000313 new_size = icount->list[(unsigned)icount->count-1].ino;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400314 new_size = (ext2_ino_t) (icount->count *
Theodore Ts'o30e50b72001-06-08 09:43:40 +0000315 ((float) icount->num_inodes / new_size));
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000316 }
317 if (new_size < (icount->size + 100))
318 new_size = icount->size + 100;
319#if 0
Eric Sandeend0ff90d2006-09-12 14:56:15 -0400320 printf("Reallocating icount %u entries...\n", new_size);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400321#endif
Theodore Ts'o76f875d1998-04-27 01:41:13 +0000322 retval = ext2fs_resize_mem((size_t) icount->size *
323 sizeof(struct ext2_icount_el),
324 (size_t) new_size *
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000325 sizeof(struct ext2_icount_el),
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400326 &icount->list);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000327 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000328 return 0;
329 icount->size = new_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000330 }
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000331 num = (int) icount->count - pos;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000332 if (num < 0)
333 return 0; /* should never happen */
334 if (num) {
335 memmove(&icount->list[pos+1], &icount->list[pos],
336 sizeof(struct ext2_icount_el) * num);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000337 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000338 icount->count++;
339 el = &icount->list[pos];
340 el->count = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000341 el->ino = ino;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400342 icount->last_lookup = el;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000343 return el;
344}
Theodore Ts'o521e3681997-04-29 17:48:10 +0000345
346/*
347 * get_icount_el() --- given an inode number, try to find icount
348 * information in the sorted list. If the create flag is set,
349 * and we can't find an entry, create one in the sorted list.
350 */
351static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000352 ext2_ino_t ino, int create)
Theodore Ts'o521e3681997-04-29 17:48:10 +0000353{
Theodore Ts'o521e3681997-04-29 17:48:10 +0000354 int low, high, mid;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000355
356 if (!icount || !icount->list)
357 return 0;
358
359 if (create && ((icount->count == 0) ||
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000360 (ino > icount->list[(unsigned)icount->count-1].ino))) {
361 return insert_icount_el(icount, ino, (unsigned) icount->count);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000362 }
363 if (icount->count == 0)
364 return 0;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400365
Theodore Ts'o521e3681997-04-29 17:48:10 +0000366 if (icount->cursor >= icount->count)
367 icount->cursor = 0;
368 if (ino == icount->list[icount->cursor].ino)
369 return &icount->list[icount->cursor++];
370#if 0
371 printf("Non-cursor get_icount_el: %u\n", ino);
372#endif
373 low = 0;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000374 high = (int) icount->count-1;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000375 while (low <= high) {
Theodore Ts'oa4aff9c2011-08-31 14:27:21 -0400376 mid = ((unsigned)low + (unsigned)high) >> 1;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000377 if (ino == icount->list[mid].ino) {
378 icount->cursor = mid+1;
379 return &icount->list[mid];
380 }
381 if (ino < icount->list[mid].ino)
382 high = mid-1;
383 else
384 low = mid+1;
385 }
386 /*
387 * If we need to create a new entry, it should be right at
388 * low (where high will be left at low-1).
389 */
390 if (create)
391 return insert_icount_el(icount, ino, low);
392 return 0;
393}
394
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400395static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400396 __u32 count)
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400397{
398 struct ext2_icount_el *el;
399 TDB_DATA key, data;
400
401 if (icount->tdb) {
402 key.dptr = (unsigned char *) &ino;
403 key.dsize = sizeof(ext2_ino_t);
404 data.dptr = (unsigned char *) &count;
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400405 data.dsize = sizeof(__u32);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400406 if (count) {
407 if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
408 return tdb_error(icount->tdb) +
409 EXT2_ET_TDB_SUCCESS;
410 } else {
411 if (tdb_delete(icount->tdb, key))
412 return tdb_error(icount->tdb) +
413 EXT2_ET_TDB_SUCCESS;
414 }
415 return 0;
416 }
417
418 el = get_icount_el(icount, ino, 1);
419 if (!el)
420 return EXT2_ET_NO_MEMORY;
421
422 el->count = count;
423 return 0;
424}
425
426static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400427 __u32 *count)
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400428{
429 struct ext2_icount_el *el;
430 TDB_DATA key, data;
431
432 if (icount->tdb) {
433 key.dptr = (unsigned char *) &ino;
434 key.dsize = sizeof(ext2_ino_t);
435
436 data = tdb_fetch(icount->tdb, key);
437 if (data.dptr == NULL) {
438 *count = 0;
439 return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
440 }
441
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400442 *count = *((__u32 *) data.dptr);
Theodore Ts'oa1f64272007-04-06 23:28:30 -0400443 free(data.dptr);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400444 return 0;
445 }
446 el = get_icount_el(icount, ino, 0);
447 if (!el) {
448 *count = 0;
449 return ENOENT;
450 }
451
452 *count = el->count;
453 return 0;
454}
455
Theodore Ts'o521e3681997-04-29 17:48:10 +0000456errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
457{
458 errcode_t ret = 0;
Theodore Ts'o54434922003-12-07 01:28:50 -0500459 unsigned int i;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000460 const char *bad = "bad icount";
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400461
Theodore Ts'o521e3681997-04-29 17:48:10 +0000462 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
463
464 if (icount->count > icount->size) {
465 fprintf(out, "%s: count > size\n", bad);
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000466 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000467 }
468 for (i=1; i < icount->count; i++) {
469 if (icount->list[i-1].ino >= icount->list[i].ino) {
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000470 fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
Theodore Ts'o521e3681997-04-29 17:48:10 +0000471 bad, i-1, icount->list[i-1].ino,
472 i, icount->list[i].ino);
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000473 ret = EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000474 }
475 }
476 return ret;
477}
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000478
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000479errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000480{
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400481 __u32 val;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000482 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
483
Theodore Ts'o521e3681997-04-29 17:48:10 +0000484 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000485 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000486
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400487 if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000488 *ret = 1;
489 return 0;
490 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000491 if (icount->multiple &&
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400492 !ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000493 *ret = 0;
494 return 0;
495 }
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400496 get_inode_count(icount, ino, &val);
497 *ret = icount_16_xlate(val);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000498 return 0;
499}
500
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000501errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000502 __u16 *ret)
503{
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400504 __u32 curr_value;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000505
506 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
507
Theodore Ts'o521e3681997-04-29 17:48:10 +0000508 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000509 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000510
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400511 if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000512 /*
513 * If the existing count is 1, then we know there is
Theodore Ts'o521e3681997-04-29 17:48:10 +0000514 * no entry in the list.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000515 */
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400516 if (set_inode_count(icount, ino, 2))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000517 return EXT2_ET_NO_MEMORY;
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400518 curr_value = 2;
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400519 ext2fs_unmark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000520 } else if (icount->multiple) {
521 /*
522 * The count is either zero or greater than 1; if the
523 * inode is set in icount->multiple, then there should
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400524 * be an entry in the list, so we need to fix it.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000525 */
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400526 if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400527 get_inode_count(icount, ino, &curr_value);
528 curr_value++;
529 if (set_inode_count(icount, ino, curr_value))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000530 return EXT2_ET_NO_MEMORY;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000531 } else {
532 /*
533 * The count was zero; mark the single bitmap
534 * and return.
535 */
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400536 ext2fs_mark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000537 if (ret)
538 *ret = 1;
539 return 0;
540 }
541 } else {
542 /*
543 * The count is either zero or greater than 1; try to
544 * find an entry in the list to determine which.
545 */
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400546 get_inode_count(icount, ino, &curr_value);
547 curr_value++;
548 if (set_inode_count(icount, ino, curr_value))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000549 return EXT2_ET_NO_MEMORY;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000550 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000551 if (icount->multiple)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400552 ext2fs_mark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000553 if (ret)
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400554 *ret = icount_16_xlate(curr_value);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000555 return 0;
556}
557
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000558errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000559 __u16 *ret)
560{
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400561 __u32 curr_value;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000562
Theodore Ts'o521e3681997-04-29 17:48:10 +0000563 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000564 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000565
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000566 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
567
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400568 if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
569 ext2fs_unmark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000570 if (icount->multiple)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400571 ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000572 else {
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400573 set_inode_count(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000574 }
575 if (ret)
576 *ret = 0;
577 return 0;
578 }
579
Theodore Ts'o521e3681997-04-29 17:48:10 +0000580 if (icount->multiple &&
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400581 !ext2fs_test_inode_bitmap2(icount->multiple, ino))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000582 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000583
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400584 get_inode_count(icount, ino, &curr_value);
585 if (!curr_value)
586 return EXT2_ET_INVALID_ARGUMENT;
587 curr_value--;
588 if (set_inode_count(icount, ino, curr_value))
589 return EXT2_ET_NO_MEMORY;
590
591 if (curr_value == 1)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400592 ext2fs_mark_inode_bitmap2(icount->single, ino);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400593 if ((curr_value == 0) && icount->multiple)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400594 ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000595
596 if (ret)
Theodore Ts'o60bbd1a2008-03-13 13:37:29 -0400597 *ret = icount_16_xlate(curr_value);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000598 return 0;
599}
600
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000601errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000602 __u16 count)
603{
Theodore Ts'o521e3681997-04-29 17:48:10 +0000604 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000605 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000606
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000607 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
608
609 if (count == 1) {
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400610 ext2fs_mark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000611 if (icount->multiple)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400612 ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000613 return 0;
614 }
615 if (count == 0) {
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400616 ext2fs_unmark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000617 if (icount->multiple) {
618 /*
619 * If the icount->multiple bitmap is enabled,
620 * we can just clear both bitmaps and we're done
621 */
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400622 ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400623 } else
624 set_inode_count(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000625 return 0;
626 }
627
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400628 if (set_inode_count(icount, ino, count))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000629 return EXT2_ET_NO_MEMORY;
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400630 ext2fs_unmark_inode_bitmap2(icount->single, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000631 if (icount->multiple)
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400632 ext2fs_mark_inode_bitmap2(icount->multiple, ino);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000633 return 0;
634}
635
Theodore Ts'o31dbecd2001-01-11 04:54:39 +0000636ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000637{
638 if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
639 return 0;
640
641 return icount->size;
642}
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400643
644#ifdef DEBUG
645
646ext2_filsys test_fs;
647ext2_icount_t icount;
648
649#define EXIT 0x00
650#define FETCH 0x01
651#define STORE 0x02
652#define INCREMENT 0x03
653#define DECREMENT 0x04
654
655struct test_program {
656 int cmd;
657 ext2_ino_t ino;
658 __u16 arg;
659 __u16 expected;
660};
661
662struct test_program prog[] = {
663 { STORE, 42, 42, 42 },
664 { STORE, 1, 1, 1 },
665 { STORE, 2, 2, 2 },
666 { STORE, 3, 3, 3 },
667 { STORE, 10, 1, 1 },
668 { STORE, 42, 0, 0 },
669 { INCREMENT, 5, 0, 1 },
670 { INCREMENT, 5, 0, 2 },
671 { INCREMENT, 5, 0, 3 },
672 { INCREMENT, 5, 0, 4 },
673 { DECREMENT, 5, 0, 3 },
674 { DECREMENT, 5, 0, 2 },
675 { DECREMENT, 5, 0, 1 },
676 { DECREMENT, 5, 0, 0 },
677 { FETCH, 10, 0, 1 },
678 { FETCH, 1, 0, 1 },
679 { FETCH, 2, 0, 2 },
680 { FETCH, 3, 0, 3 },
681 { INCREMENT, 1, 0, 2 },
682 { DECREMENT, 2, 0, 1 },
683 { DECREMENT, 2, 0, 0 },
684 { FETCH, 12, 0, 0 },
685 { EXIT, 0, 0, 0 }
686};
687
688struct test_program extended[] = {
689 { STORE, 1, 1, 1 },
690 { STORE, 2, 2, 2 },
691 { STORE, 3, 3, 3 },
692 { STORE, 4, 4, 4 },
693 { STORE, 5, 5, 5 },
694 { STORE, 6, 1, 1 },
695 { STORE, 7, 2, 2 },
696 { STORE, 8, 3, 3 },
697 { STORE, 9, 4, 4 },
698 { STORE, 10, 5, 5 },
699 { STORE, 11, 1, 1 },
700 { STORE, 12, 2, 2 },
701 { STORE, 13, 3, 3 },
702 { STORE, 14, 4, 4 },
703 { STORE, 15, 5, 5 },
704 { STORE, 16, 1, 1 },
705 { STORE, 17, 2, 2 },
706 { STORE, 18, 3, 3 },
707 { STORE, 19, 4, 4 },
708 { STORE, 20, 5, 5 },
709 { STORE, 21, 1, 1 },
710 { STORE, 22, 2, 2 },
711 { STORE, 23, 3, 3 },
712 { STORE, 24, 4, 4 },
713 { STORE, 25, 5, 5 },
714 { STORE, 26, 1, 1 },
715 { STORE, 27, 2, 2 },
716 { STORE, 28, 3, 3 },
717 { STORE, 29, 4, 4 },
718 { STORE, 30, 5, 5 },
719 { EXIT, 0, 0, 0 }
720};
721
722/*
723 * Setup the variables for doing the inode scan test.
724 */
725static void setup(void)
726{
727 errcode_t retval;
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400728 struct ext2_super_block param;
729
730 initialize_ext2_error_table();
731
732 memset(&param, 0, sizeof(param));
Valerie Aurora Henson4efbac62009-09-07 20:46:34 -0400733 ext2fs_blocks_count_set(&param, 12000);
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400734
Valerie Aurora Henson8f82ef92009-08-05 00:27:10 -0400735 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400736 test_io_manager, &test_fs);
737 if (retval) {
738 com_err("setup", retval,
739 "while initializing filesystem");
740 exit(1);
741 }
742 retval = ext2fs_allocate_tables(test_fs);
743 if (retval) {
744 com_err("setup", retval,
745 "while allocating tables for test filesystem");
746 exit(1);
747 }
748}
749
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400750int run_test(int flags, int size, char *dir, struct test_program *prog)
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400751{
752 errcode_t retval;
753 ext2_icount_t icount;
754 struct test_program *pc;
755 __u16 result;
756 int problem = 0;
757
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400758 if (dir) {
759 retval = ext2fs_create_icount_tdb(test_fs, dir,
760 flags, &icount);
761 if (retval) {
762 com_err("run_test", retval,
763 "while creating icount using tdb");
764 exit(1);
765 }
766 } else {
767 retval = ext2fs_create_icount2(test_fs, flags, size, 0,
768 &icount);
769 if (retval) {
770 com_err("run_test", retval, "while creating icount");
771 exit(1);
772 }
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400773 }
774 for (pc = prog; pc->cmd != EXIT; pc++) {
775 switch (pc->cmd) {
776 case FETCH:
777 printf("icount_fetch(%u) = ", pc->ino);
778 break;
779 case STORE:
780 retval = ext2fs_icount_store(icount, pc->ino, pc->arg);
781 if (retval) {
782 com_err("run_test", retval,
783 "while calling icount_store");
784 exit(1);
785 }
786 printf("icount_store(%u, %u) = ", pc->ino, pc->arg);
787 break;
788 case INCREMENT:
789 retval = ext2fs_icount_increment(icount, pc->ino, 0);
790 if (retval) {
791 com_err("run_test", retval,
792 "while calling icount_increment");
793 exit(1);
794 }
795 printf("icount_increment(%u) = ", pc->ino);
796 break;
797 case DECREMENT:
798 retval = ext2fs_icount_decrement(icount, pc->ino, 0);
799 if (retval) {
800 com_err("run_test", retval,
801 "while calling icount_decrement");
802 exit(1);
803 }
804 printf("icount_decrement(%u) = ", pc->ino);
805 break;
806 }
807 retval = ext2fs_icount_fetch(icount, pc->ino, &result);
808 if (retval) {
809 com_err("run_test", retval,
810 "while calling icount_fetch");
811 exit(1);
812 }
813 printf("%u (%s)\n", result, (result == pc->expected) ?
814 "OK" : "NOT OK");
815 if (result != pc->expected)
816 problem++;
817 }
818 printf("icount size is %u\n", ext2fs_get_icount_size(icount));
819 retval = ext2fs_icount_validate(icount, stdout);
820 if (retval) {
821 com_err("run_test", retval, "while calling icount_validate");
822 exit(1);
823 }
824 ext2fs_free_icount(icount);
825 return problem;
826}
827
828
829int main(int argc, char **argv)
830{
831 int failed = 0;
832
833 setup();
834 printf("Standard icount run:\n");
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400835 failed += run_test(0, 0, 0, prog);
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400836 printf("\nMultiple bitmap test:\n");
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400837 failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog);
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400838 printf("\nResizing icount:\n");
Theodore Ts'o1b9d8cb2007-04-06 14:30:39 -0400839 failed += run_test(0, 3, 0, extended);
840 printf("\nStandard icount run with tdb:\n");
841 failed += run_test(0, 0, ".", prog);
842 printf("\nMultiple bitmap test with tdb:\n");
843 failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog);
844 if (failed)
Theodore Ts'o1cb78d82007-04-06 08:52:49 -0400845 printf("FAILED!\n");
846 return failed;
847}
848#endif