1 /* 2 * ea_refcount.c 3 * 4 * Copyright (C) 2001 Theodore Ts'o. This file may be 5 * redistributed under the terms of the GNU Public License. 6 */ 7 8 #if HAVE_UNISTD_H 9 #include <unistd.h> 10 #endif 11 #include <string.h> 12 #include <stdio.h> 13 14 #ifdef TEST_PROGRAM 15 #undef ENABLE_NLS 16 #endif 17 #include "e2fsck.h" 18 19 /* 20 * The strategy we use for keeping track of EA refcounts is as 21 * follows. We keep a sorted array of first EA blocks and its 22 * reference counts. Once the refcount has dropped to zero, it is 23 * removed from the array to save memory space. Once the EA block is 24 * checked, its bit is set in the block_ea_map bitmap. 25 */ 26 struct ea_refcount_el { 27 blk_t ea_blk; 28 int ea_count; 29 }; 30 31 struct ea_refcount { 32 blk_t count; 33 blk_t size; 34 blk_t cursor; 35 struct ea_refcount_el *list; 36 }; 37 38 void ea_refcount_free(ext2_refcount_t refcount) 39 { 40 if (!refcount) 41 return; 42 43 if (refcount->list) 44 ext2fs_free_mem(&refcount->list); 45 ext2fs_free_mem(&refcount); 46 } 47 48 errcode_t ea_refcount_create(int size, ext2_refcount_t *ret) 49 { 50 ext2_refcount_t refcount; 51 errcode_t retval; 52 size_t bytes; 53 54 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount); 55 if (retval) 56 return retval; 57 memset(refcount, 0, sizeof(struct ea_refcount)); 58 59 if (!size) 60 size = 500; 61 refcount->size = size; 62 bytes = (size_t) (size * sizeof(struct ea_refcount_el)); 63 #ifdef DEBUG 64 printf("Refcount allocated %d entries, %d bytes.\n", 65 refcount->size, bytes); 66 #endif 67 retval = ext2fs_get_mem(bytes, &refcount->list); 68 if (retval) 69 goto errout; 70 memset(refcount->list, 0, bytes); 71 72 refcount->count = 0; 73 refcount->cursor = 0; 74 75 *ret = refcount; 76 return 0; 77 78 errout: 79 ea_refcount_free(refcount); 80 return(retval); 81 } 82 83 /* 84 * collapse_refcount() --- go through the refcount array, and get rid 85 * of any count == zero entries 86 */ 87 static void refcount_collapse(ext2_refcount_t refcount) 88 { 89 unsigned int i, j; 90 struct ea_refcount_el *list; 91 92 list = refcount->list; 93 for (i = 0, j = 0; i < refcount->count; i++) { 94 if (list[i].ea_count) { 95 if (i != j) 96 list[j] = list[i]; 97 j++; 98 } 99 } 100 #if defined(DEBUG) || defined(TEST_PROGRAM) 101 printf("Refcount_collapse: size was %d, now %d\n", 102 refcount->count, j); 103 #endif 104 refcount->count = j; 105 } 106 107 108 /* 109 * insert_refcount_el() --- Insert a new entry into the sorted list at a 110 * specified position. 111 */ 112 static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount, 113 blk_t blk, int pos) 114 { 115 struct ea_refcount_el *el; 116 errcode_t retval; 117 blk_t new_size = 0; 118 int num; 119 120 if (refcount->count >= refcount->size) { 121 new_size = refcount->size + 100; 122 #ifdef DEBUG 123 printf("Reallocating refcount %d entries...\n", new_size); 124 #endif 125 retval = ext2fs_resize_mem((size_t) refcount->size * 126 sizeof(struct ea_refcount_el), 127 (size_t) new_size * 128 sizeof(struct ea_refcount_el), 129 &refcount->list); 130 if (retval) 131 return 0; 132 refcount->size = new_size; 133 } 134 num = (int) refcount->count - pos; 135 if (num < 0) 136 return 0; /* should never happen */ 137 if (num) { 138 memmove(&refcount->list[pos+1], &refcount->list[pos], 139 sizeof(struct ea_refcount_el) * num); 140 } 141 refcount->count++; 142 el = &refcount->list[pos]; 143 el->ea_count = 0; 144 el->ea_blk = blk; 145 return el; 146 } 147 148 149 /* 150 * get_refcount_el() --- given an block number, try to find refcount 151 * information in the sorted list. If the create flag is set, 152 * and we can't find an entry, create one in the sorted list. 153 */ 154 static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount, 155 blk_t blk, int create) 156 { 157 float range; 158 int low, high, mid; 159 blk_t lowval, highval; 160 161 if (!refcount || !refcount->list) 162 return 0; 163 retry: 164 low = 0; 165 high = (int) refcount->count-1; 166 if (create && ((refcount->count == 0) || 167 (blk > refcount->list[high].ea_blk))) { 168 if (refcount->count >= refcount->size) 169 refcount_collapse(refcount); 170 171 return insert_refcount_el(refcount, blk, 172 (unsigned) refcount->count); 173 } 174 if (refcount->count == 0) 175 return 0; 176 177 if (refcount->cursor >= refcount->count) 178 refcount->cursor = 0; 179 if (blk == refcount->list[refcount->cursor].ea_blk) 180 return &refcount->list[refcount->cursor++]; 181 #ifdef DEBUG 182 printf("Non-cursor get_refcount_el: %u\n", blk); 183 #endif 184 while (low <= high) { 185 #if 0 186 mid = (low+high)/2; 187 #else 188 if (low == high) 189 mid = low; 190 else { 191 /* Interpolate for efficiency */ 192 lowval = refcount->list[low].ea_blk; 193 highval = refcount->list[high].ea_blk; 194 195 if (blk < lowval) 196 range = 0; 197 else if (blk > highval) 198 range = 1; 199 else { 200 range = ((float) (blk - lowval)) / 201 (highval - lowval); 202 if (range > 0.9) 203 range = 0.9; 204 if (range < 0.1) 205 range = 0.1; 206 } 207 mid = low + ((int) (range * (high-low))); 208 } 209 #endif 210 if (blk == refcount->list[mid].ea_blk) { 211 refcount->cursor = mid+1; 212 return &refcount->list[mid]; 213 } 214 if (blk < refcount->list[mid].ea_blk) 215 high = mid-1; 216 else 217 low = mid+1; 218 } 219 /* 220 * If we need to create a new entry, it should be right at 221 * low (where high will be left at low-1). 222 */ 223 if (create) { 224 if (refcount->count >= refcount->size) { 225 refcount_collapse(refcount); 226 if (refcount->count < refcount->size) 227 goto retry; 228 } 229 return insert_refcount_el(refcount, blk, low); 230 } 231 return 0; 232 } 233 234 errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk, 235 int *ret) 236 { 237 struct ea_refcount_el *el; 238 239 el = get_refcount_el(refcount, blk, 0); 240 if (!el) { 241 *ret = 0; 242 return 0; 243 } 244 *ret = el->ea_count; 245 return 0; 246 } 247 248 errcode_t ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret) 249 { 250 struct ea_refcount_el *el; 251 252 el = get_refcount_el(refcount, blk, 1); 253 if (!el) 254 return EXT2_ET_NO_MEMORY; 255 el->ea_count++; 256 257 if (ret) 258 *ret = el->ea_count; 259 return 0; 260 } 261 262 errcode_t ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret) 263 { 264 struct ea_refcount_el *el; 265 266 el = get_refcount_el(refcount, blk, 0); 267 if (!el || el->ea_count == 0) 268 return EXT2_ET_INVALID_ARGUMENT; 269 270 el->ea_count--; 271 272 if (ret) 273 *ret = el->ea_count; 274 return 0; 275 } 276 277 errcode_t ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count) 278 { 279 struct ea_refcount_el *el; 280 281 /* 282 * Get the refcount element 283 */ 284 el = get_refcount_el(refcount, blk, count ? 1 : 0); 285 if (!el) 286 return count ? EXT2_ET_NO_MEMORY : 0; 287 el->ea_count = count; 288 return 0; 289 } 290 291 blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount) 292 { 293 if (!refcount) 294 return 0; 295 296 return refcount->size; 297 } 298 299 void ea_refcount_intr_begin(ext2_refcount_t refcount) 300 { 301 refcount->cursor = 0; 302 } 303 304 305 blk_t ea_refcount_intr_next(ext2_refcount_t refcount, 306 int *ret) 307 { 308 struct ea_refcount_el *list; 309 310 while (1) { 311 if (refcount->cursor >= refcount->count) 312 return 0; 313 list = refcount->list; 314 if (list[refcount->cursor].ea_count) { 315 if (ret) 316 *ret = list[refcount->cursor].ea_count; 317 return list[refcount->cursor++].ea_blk; 318 } 319 refcount->cursor++; 320 } 321 } 322 323 324 #ifdef TEST_PROGRAM 325 326 errcode_t ea_refcount_validate(ext2_refcount_t refcount, FILE *out) 327 { 328 errcode_t ret = 0; 329 int i; 330 const char *bad = "bad refcount"; 331 332 if (refcount->count > refcount->size) { 333 fprintf(out, "%s: count > size\n", bad); 334 return EXT2_ET_INVALID_ARGUMENT; 335 } 336 for (i=1; i < refcount->count; i++) { 337 if (refcount->list[i-1].ea_blk >= refcount->list[i].ea_blk) { 338 fprintf(out, "%s: list[%d].blk=%u, list[%d].blk=%u\n", 339 bad, i-1, refcount->list[i-1].ea_blk, 340 i, refcount->list[i].ea_blk); 341 ret = EXT2_ET_INVALID_ARGUMENT; 342 } 343 } 344 return ret; 345 } 346 347 #define BCODE_END 0 348 #define BCODE_CREATE 1 349 #define BCODE_FREE 2 350 #define BCODE_STORE 3 351 #define BCODE_INCR 4 352 #define BCODE_DECR 5 353 #define BCODE_FETCH 6 354 #define BCODE_VALIDATE 7 355 #define BCODE_LIST 8 356 #define BCODE_COLLAPSE 9 357 358 int bcode_program[] = { 359 BCODE_CREATE, 5, 360 BCODE_STORE, 3, 3, 361 BCODE_STORE, 4, 4, 362 BCODE_STORE, 1, 1, 363 BCODE_STORE, 8, 8, 364 BCODE_STORE, 2, 2, 365 BCODE_STORE, 4, 0, 366 BCODE_STORE, 2, 0, 367 BCODE_STORE, 6, 6, 368 BCODE_VALIDATE, 369 BCODE_STORE, 4, 4, 370 BCODE_STORE, 2, 2, 371 BCODE_FETCH, 1, 372 BCODE_FETCH, 2, 373 BCODE_INCR, 3, 374 BCODE_INCR, 3, 375 BCODE_DECR, 4, 376 BCODE_STORE, 4, 4, 377 BCODE_VALIDATE, 378 BCODE_STORE, 20, 20, 379 BCODE_STORE, 40, 40, 380 BCODE_STORE, 30, 30, 381 BCODE_STORE, 10, 10, 382 BCODE_DECR, 30, 383 BCODE_FETCH, 30, 384 BCODE_DECR, 2, 385 BCODE_DECR, 2, 386 BCODE_COLLAPSE, 387 BCODE_LIST, 388 BCODE_VALIDATE, 389 BCODE_FREE, 390 BCODE_END 391 }; 392 393 int main(int argc, char **argv) 394 { 395 int i = 0; 396 ext2_refcount_t refcount; 397 int size, arg; 398 blk_t blk; 399 errcode_t retval; 400 401 while (1) { 402 switch (bcode_program[i++]) { 403 case BCODE_END: 404 exit(0); 405 case BCODE_CREATE: 406 size = bcode_program[i++]; 407 retval = ea_refcount_create(size, &refcount); 408 if (retval) { 409 com_err("ea_refcount_create", 410 retval, ""); 411 exit(1); 412 } else 413 printf("Creating refcount with size %d\n", 414 size); 415 break; 416 case BCODE_FREE: 417 ea_refcount_free(refcount); 418 refcount = 0; 419 printf("Freeing refcount\n"); 420 break; 421 case BCODE_STORE: 422 blk = (blk_t) bcode_program[i++]; 423 arg = bcode_program[i++]; 424 retval = ea_refcount_store(refcount, blk, arg); 425 printf("Storing blk %u with value %d\n", blk, arg); 426 if (retval) 427 com_err("ea_refcount_store", retval, ""); 428 break; 429 case BCODE_FETCH: 430 blk = (blk_t) bcode_program[i++]; 431 retval = ea_refcount_fetch(refcount, blk, &arg); 432 if (retval) 433 com_err("ea_refcount_fetch", retval, ""); 434 else 435 printf("bcode_fetch(%u) returns %d\n", 436 blk, arg); 437 break; 438 case BCODE_INCR: 439 blk = (blk_t) bcode_program[i++]; 440 retval = ea_refcount_increment(refcount, blk, 441 &arg); 442 if (retval) 443 com_err("ea_refcount_increment", retval, 444 ""); 445 else 446 printf("bcode_increment(%u) returns %d\n", 447 blk, arg); 448 break; 449 case BCODE_DECR: 450 blk = (blk_t) bcode_program[i++]; 451 retval = ea_refcount_decrement(refcount, blk, 452 &arg); 453 if (retval) 454 com_err("ea_refcount_decrement", retval, 455 "while decrementing blk %u", blk); 456 else 457 printf("bcode_decrement(%u) returns %d\n", 458 blk, arg); 459 break; 460 case BCODE_VALIDATE: 461 retval = ea_refcount_validate(refcount, stderr); 462 if (retval) 463 com_err("ea_refcount_validate", 464 retval, ""); 465 else 466 printf("Refcount validation OK.\n"); 467 break; 468 case BCODE_LIST: 469 ea_refcount_intr_begin(refcount); 470 while (1) { 471 blk = ea_refcount_intr_next(refcount, 472 &arg); 473 if (!blk) 474 break; 475 printf("\tblk=%u, count=%d\n", blk, 476 arg); 477 } 478 break; 479 case BCODE_COLLAPSE: 480 refcount_collapse(refcount); 481 break; 482 } 483 484 } 485 } 486 487 #endif 488