1/* 2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. 3 * 4 * Copyright (C) 2002-2010 Aleph One Ltd. 5 * for Toby Churchill Ltd and Brightstar Engineering 6 * 7 * Created by Charles Manning <charles@aleph1.co.uk> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13#include "yportenv.h" 14#include "yaffs_trace.h" 15 16#include "yaffsinterface.h" 17#include "yaffs_guts.h" 18#include "yaffs_tagsvalidity.h" 19#include "yaffs_getblockinfo.h" 20 21#include "yaffs_tagscompat.h" 22 23#include "yaffs_nand.h" 24 25#include "yaffs_yaffs1.h" 26#include "yaffs_yaffs2.h" 27#include "yaffs_bitmap.h" 28#include "yaffs_verify.h" 29 30#include "yaffs_nand.h" 31#include "yaffs_packedtags2.h" 32 33#include "yaffs_nameval.h" 34#include "yaffs_allocator.h" 35 36/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ 37#define YAFFS_GC_GOOD_ENOUGH 2 38#define YAFFS_GC_PASSIVE_THRESHOLD 4 39 40#include "yaffs_ecc.h" 41 42 43 44/* Robustification (if it ever comes about...) */ 45static void yaffs_retire_block(yaffs_dev_t *dev, int flash_block); 46static void yaffs_handle_chunk_wr_error(yaffs_dev_t *dev, int nand_chunk, 47 int erasedOk); 48static void yaffs_handle_chunk_wr_ok(yaffs_dev_t *dev, int nand_chunk, 49 const __u8 *data, 50 const yaffs_ext_tags *tags); 51static void yaffs_handle_chunk_update(yaffs_dev_t *dev, int nand_chunk, 52 const yaffs_ext_tags *tags); 53 54/* Other local prototypes */ 55static void yaffs_update_parent(yaffs_obj_t *obj); 56static int yaffs_unlink_obj(yaffs_obj_t *obj); 57static int yaffs_obj_cache_dirty(yaffs_obj_t *obj); 58 59static int yaffs_write_new_chunk(yaffs_dev_t *dev, 60 const __u8 *buffer, 61 yaffs_ext_tags *tags, 62 int useReserve); 63 64 65static yaffs_obj_t *yaffs_new_obj(yaffs_dev_t *dev, int number, 66 yaffs_obj_type type); 67 68 69static int yaffs_apply_xattrib_mod(yaffs_obj_t *obj, char *buffer, yaffs_xattr_mod *xmod); 70 71static void yaffs_remove_obj_from_dir(yaffs_obj_t *obj); 72static int yaffs_check_structures(void); 73static int yaffs_generic_obj_del(yaffs_obj_t *in); 74 75static int yaffs_check_chunk_erased(struct yaffs_dev_s *dev, 76 int nand_chunk); 77 78static int yaffs_unlink_worker(yaffs_obj_t *obj); 79 80static int yaffs_tags_match(const yaffs_ext_tags *tags, int obj_id, 81 int chunkInObject); 82 83static int yaffs_alloc_chunk(yaffs_dev_t *dev, int useReserve, 84 yaffs_block_info_t **blockUsedPtr); 85 86static void yaffs_check_obj_details_loaded(yaffs_obj_t *in); 87 88static void yaffs_invalidate_whole_cache(yaffs_obj_t *in); 89static void yaffs_invalidate_chunk_cache(yaffs_obj_t *object, int chunk_id); 90 91static int yaffs_find_chunk_in_file(yaffs_obj_t *in, int inode_chunk, 92 yaffs_ext_tags *tags); 93 94static int yaffs_verify_chunk_written(yaffs_dev_t *dev, 95 int nand_chunk, 96 const __u8 *data, 97 yaffs_ext_tags *tags); 98 99 100static void yaffs_load_name_from_oh(yaffs_dev_t *dev,YCHAR *name, const YCHAR *ohName, int bufferSize); 101static void yaffs_load_oh_from_name(yaffs_dev_t *dev,YCHAR *ohName, const YCHAR *name); 102 103 104/* Function to calculate chunk and offset */ 105 106static void yaffs_addr_to_chunk(yaffs_dev_t *dev, loff_t addr, int *chunkOut, 107 __u32 *offsetOut) 108{ 109 int chunk; 110 __u32 offset; 111 112 chunk = (__u32)(addr >> dev->chunk_shift); 113 114 if (dev->chunk_div == 1) { 115 /* easy power of 2 case */ 116 offset = (__u32)(addr & dev->chunk_mask); 117 } else { 118 /* Non power-of-2 case */ 119 120 loff_t chunkBase; 121 122 chunk /= dev->chunk_div; 123 124 chunkBase = ((loff_t)chunk) * dev->data_bytes_per_chunk; 125 offset = (__u32)(addr - chunkBase); 126 } 127 128 *chunkOut = chunk; 129 *offsetOut = offset; 130} 131 132/* Function to return the number of shifts for a power of 2 greater than or 133 * equal to the given number 134 * Note we don't try to cater for all possible numbers and this does not have to 135 * be hellishly efficient. 136 */ 137 138static __u32 ShiftsGE(__u32 x) 139{ 140 int extraBits; 141 int nShifts; 142 143 nShifts = extraBits = 0; 144 145 while (x > 1) { 146 if (x & 1) 147 extraBits++; 148 x >>= 1; 149 nShifts++; 150 } 151 152 if (extraBits) 153 nShifts++; 154 155 return nShifts; 156} 157 158/* Function to return the number of shifts to get a 1 in bit 0 159 */ 160 161static __u32 Shifts(__u32 x) 162{ 163 __u32 nShifts; 164 165 nShifts = 0; 166 167 if (!x) 168 return 0; 169 170 while (!(x&1)) { 171 x >>= 1; 172 nShifts++; 173 } 174 175 return nShifts; 176} 177 178 179 180/* 181 * Temporary buffer manipulations. 182 */ 183 184static int yaffs_init_tmp_buffers(yaffs_dev_t *dev) 185{ 186 int i; 187 __u8 *buf = (__u8 *)1; 188 189 memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); 190 191 for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { 192 dev->temp_buffer[i].line = 0; /* not in use */ 193 dev->temp_buffer[i].buffer = buf = 194 YMALLOC_DMA(dev->param.total_bytes_per_chunk); 195 } 196 197 return buf ? YAFFS_OK : YAFFS_FAIL; 198} 199 200__u8 *yaffs_get_temp_buffer(yaffs_dev_t *dev, int line_no) 201{ 202 int i, j; 203 204 dev->temp_in_use++; 205 if (dev->temp_in_use > dev->max_temp) 206 dev->max_temp = dev->temp_in_use; 207 208 for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { 209 if (dev->temp_buffer[i].line == 0) { 210 dev->temp_buffer[i].line = line_no; 211 if ((i + 1) > dev->max_temp) { 212 dev->max_temp = i + 1; 213 for (j = 0; j <= i; j++) 214 dev->temp_buffer[j].max_line = 215 dev->temp_buffer[j].line; 216 } 217 218 return dev->temp_buffer[i].buffer; 219 } 220 } 221 222 T(YAFFS_TRACE_BUFFERS, 223 (TSTR("Out of temp buffers at line %d, other held by lines:"), 224 line_no)); 225 for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) 226 T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->temp_buffer[i].line)); 227 228 T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); 229 230 /* 231 * If we got here then we have to allocate an unmanaged one 232 * This is not good. 233 */ 234 235 dev->unmanaged_buffer_allocs++; 236 return YMALLOC(dev->data_bytes_per_chunk); 237 238} 239 240void yaffs_release_temp_buffer(yaffs_dev_t *dev, __u8 *buffer, 241 int line_no) 242{ 243 int i; 244 245 dev->temp_in_use--; 246 247 for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { 248 if (dev->temp_buffer[i].buffer == buffer) { 249 dev->temp_buffer[i].line = 0; 250 return; 251 } 252 } 253 254 if (buffer) { 255 /* assume it is an unmanaged one. */ 256 T(YAFFS_TRACE_BUFFERS, 257 (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), 258 line_no)); 259 YFREE(buffer); 260 dev->unmanaged_buffer_deallocs++; 261 } 262 263} 264 265/* 266 * Determine if we have a managed buffer. 267 */ 268int yaffs_is_managed_tmp_buffer(yaffs_dev_t *dev, const __u8 *buffer) 269{ 270 int i; 271 272 for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { 273 if (dev->temp_buffer[i].buffer == buffer) 274 return 1; 275 } 276 277 for (i = 0; i < dev->param.n_caches; i++) { 278 if (dev->cache[i].data == buffer) 279 return 1; 280 } 281 282 if (buffer == dev->checkpt_buffer) 283 return 1; 284 285 T(YAFFS_TRACE_ALWAYS, 286 (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); 287 return 0; 288} 289 290/* 291 * Verification code 292 */ 293 294 295 296 297/* 298 * Simple hash function. Needs to have a reasonable spread 299 */ 300 301static Y_INLINE int yaffs_hash_fn(int n) 302{ 303 n = abs(n); 304 return n % YAFFS_NOBJECT_BUCKETS; 305} 306 307/* 308 * Access functions to useful fake objects. 309 * Note that root might have a presence in NAND if permissions are set. 310 */ 311 312yaffs_obj_t *yaffs_root(yaffs_dev_t *dev) 313{ 314 return dev->root_dir; 315} 316 317yaffs_obj_t *yaffs_lost_n_found(yaffs_dev_t *dev) 318{ 319 return dev->lost_n_found; 320} 321 322 323/* 324 * Erased NAND checking functions 325 */ 326 327int yaffs_check_ff(__u8 *buffer, int n_bytes) 328{ 329 /* Horrible, slow implementation */ 330 while (n_bytes--) { 331 if (*buffer != 0xFF) 332 return 0; 333 buffer++; 334 } 335 return 1; 336} 337 338static int yaffs_check_chunk_erased(struct yaffs_dev_s *dev, 339 int nand_chunk) 340{ 341 int retval = YAFFS_OK; 342 __u8 *data = yaffs_get_temp_buffer(dev, __LINE__); 343 yaffs_ext_tags tags; 344 int result; 345 346 result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); 347 348 if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) 349 retval = YAFFS_FAIL; 350 351 if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || tags.chunk_used) { 352 T(YAFFS_TRACE_NANDACCESS, 353 (TSTR("Chunk %d not erased" TENDSTR), nand_chunk)); 354 retval = YAFFS_FAIL; 355 } 356 357 yaffs_release_temp_buffer(dev, data, __LINE__); 358 359 return retval; 360 361} 362 363 364static int yaffs_verify_chunk_written(yaffs_dev_t *dev, 365 int nand_chunk, 366 const __u8 *data, 367 yaffs_ext_tags *tags) 368{ 369 int retval = YAFFS_OK; 370 yaffs_ext_tags tempTags; 371 __u8 *buffer = yaffs_get_temp_buffer(dev,__LINE__); 372 int result; 373 374 result = yaffs_rd_chunk_tags_nand(dev,nand_chunk,buffer,&tempTags); 375 if(memcmp(buffer,data,dev->data_bytes_per_chunk) || 376 tempTags.obj_id != tags->obj_id || 377 tempTags.chunk_id != tags->chunk_id || 378 tempTags.n_bytes != tags->n_bytes) 379 retval = YAFFS_FAIL; 380 381 yaffs_release_temp_buffer(dev, buffer, __LINE__); 382 383 return retval; 384} 385 386static int yaffs_write_new_chunk(struct yaffs_dev_s *dev, 387 const __u8 *data, 388 yaffs_ext_tags *tags, 389 int useReserve) 390{ 391 int attempts = 0; 392 int writeOk = 0; 393 int chunk; 394 395 yaffs2_checkpt_invalidate(dev); 396 397 do { 398 yaffs_block_info_t *bi = 0; 399 int erasedOk = 0; 400 401 chunk = yaffs_alloc_chunk(dev, useReserve, &bi); 402 if (chunk < 0) { 403 /* no space */ 404 break; 405 } 406 407 /* First check this chunk is erased, if it needs 408 * checking. The checking policy (unless forced 409 * always on) is as follows: 410 * 411 * Check the first page we try to write in a block. 412 * If the check passes then we don't need to check any 413 * more. If the check fails, we check again... 414 * If the block has been erased, we don't need to check. 415 * 416 * However, if the block has been prioritised for gc, 417 * then we think there might be something odd about 418 * this block and stop using it. 419 * 420 * Rationale: We should only ever see chunks that have 421 * not been erased if there was a partially written 422 * chunk due to power loss. This checking policy should 423 * catch that case with very few checks and thus save a 424 * lot of checks that are most likely not needed. 425 * 426 * Mods to the above 427 * If an erase check fails or the write fails we skip the 428 * rest of the block. 429 */ 430 431 /* let's give it a try */ 432 attempts++; 433 434 if(dev->param.always_check_erased) 435 bi->skip_erased_check = 0; 436 437 if (!bi->skip_erased_check) { 438 erasedOk = yaffs_check_chunk_erased(dev, chunk); 439 if (erasedOk != YAFFS_OK) { 440 T(YAFFS_TRACE_ERROR, 441 (TSTR("**>> yaffs chunk %d was not erased" 442 TENDSTR), chunk)); 443 444 /* If not erased, delete this one, 445 * skip rest of block and 446 * try another chunk */ 447 yaffs_chunk_del(dev,chunk,1,__LINE__); 448 yaffs_skip_rest_of_block(dev); 449 continue; 450 } 451 } 452 453 writeOk = yaffs_wr_chunk_tags_nand(dev, chunk, 454 data, tags); 455 456 if(!bi->skip_erased_check) 457 writeOk = yaffs_verify_chunk_written(dev, chunk, data, tags); 458 459 if (writeOk != YAFFS_OK) { 460 /* Clean up aborted write, skip to next block and 461 * try another chunk */ 462 yaffs_handle_chunk_wr_error(dev, chunk, erasedOk); 463 continue; 464 } 465 466 bi->skip_erased_check = 1; 467 468 /* Copy the data into the robustification buffer */ 469 yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); 470 471 } while (writeOk != YAFFS_OK && 472 (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); 473 474 if (!writeOk) 475 chunk = -1; 476 477 if (attempts > 1) { 478 T(YAFFS_TRACE_ERROR, 479 (TSTR("**>> yaffs write required %d attempts" TENDSTR), 480 attempts)); 481 482 dev->n_retired_writes += (attempts - 1); 483 } 484 485 return chunk; 486} 487 488 489 490/* 491 * Block retiring for handling a broken block. 492 */ 493 494static void yaffs_retire_block(yaffs_dev_t *dev, int flash_block) 495{ 496 yaffs_block_info_t *bi = yaffs_get_block_info(dev, flash_block); 497 498 yaffs2_checkpt_invalidate(dev); 499 500 yaffs2_clear_oldest_dirty_seq(dev,bi); 501 502 if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { 503 if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { 504 T(YAFFS_TRACE_ALWAYS, (TSTR( 505 "yaffs: Failed to mark bad and erase block %d" 506 TENDSTR), flash_block)); 507 } else { 508 yaffs_ext_tags tags; 509 int chunk_id = flash_block * dev->param.chunks_per_block; 510 511 __u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); 512 513 memset(buffer, 0xff, dev->data_bytes_per_chunk); 514 yaffs_init_tags(&tags); 515 tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; 516 if (dev->param.write_chunk_tags_fn(dev, chunk_id - 517 dev->chunk_offset, buffer, &tags) != YAFFS_OK) 518 T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Failed to " 519 TCONT("write bad block marker to block %d") 520 TENDSTR), flash_block)); 521 522 yaffs_release_temp_buffer(dev, buffer, __LINE__); 523 } 524 } 525 526 bi->block_state = YAFFS_BLOCK_STATE_DEAD; 527 bi->gc_prioritise = 0; 528 bi->needs_retiring = 0; 529 530 dev->n_retired_blocks++; 531} 532 533/* 534 * Functions for robustisizing TODO 535 * 536 */ 537 538static void yaffs_handle_chunk_wr_ok(yaffs_dev_t *dev, int nand_chunk, 539 const __u8 *data, 540 const yaffs_ext_tags *tags) 541{ 542 dev=dev; 543 nand_chunk=nand_chunk; 544 data=data; 545 tags=tags; 546} 547 548static void yaffs_handle_chunk_update(yaffs_dev_t *dev, int nand_chunk, 549 const yaffs_ext_tags *tags) 550{ 551 dev=dev; 552 nand_chunk=nand_chunk; 553 tags=tags; 554} 555 556void yaffs_handle_chunk_error(yaffs_dev_t *dev, yaffs_block_info_t *bi) 557{ 558 if (!bi->gc_prioritise) { 559 bi->gc_prioritise = 1; 560 dev->has_pending_prioritised_gc = 1; 561 bi->chunk_error_strikes++; 562 563 if (bi->chunk_error_strikes > 3) { 564 bi->needs_retiring = 1; /* Too many stikes, so retire this */ 565 T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); 566 567 } 568 } 569} 570 571static void yaffs_handle_chunk_wr_error(yaffs_dev_t *dev, int nand_chunk, 572 int erasedOk) 573{ 574 int flash_block = nand_chunk / dev->param.chunks_per_block; 575 yaffs_block_info_t *bi = yaffs_get_block_info(dev, flash_block); 576 577 yaffs_handle_chunk_error(dev, bi); 578 579 if (erasedOk) { 580 /* Was an actual write failure, so mark the block for retirement */ 581 bi->needs_retiring = 1; 582 T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, 583 (TSTR("**>> Block %d needs retiring" TENDSTR), flash_block)); 584 } 585 586 /* Delete the chunk */ 587 yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); 588 yaffs_skip_rest_of_block(dev); 589} 590 591 592/*---------------- Name handling functions ------------*/ 593 594static __u16 yaffs_calc_name_sum(const YCHAR *name) 595{ 596 __u16 sum = 0; 597 __u16 i = 1; 598 599 const YUCHAR *bname = (const YUCHAR *) name; 600 if (bname) { 601 while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { 602 603#ifdef CONFIG_YAFFS_CASE_INSENSITIVE 604 sum += yaffs_toupper(*bname) * i; 605#else 606 sum += (*bname) * i; 607#endif 608 i++; 609 bname++; 610 } 611 } 612 return sum; 613} 614 615void yaffs_set_obj_name(yaffs_obj_t *obj, const YCHAR *name) 616{ 617#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM 618 memset(obj->short_name, 0, sizeof(YCHAR) * (YAFFS_SHORT_NAME_LENGTH+1)); 619 if (name && yaffs_strnlen(name,YAFFS_SHORT_NAME_LENGTH+1) <= YAFFS_SHORT_NAME_LENGTH) 620 yaffs_strcpy(obj->short_name, name); 621 else 622 obj->short_name[0] = _Y('\0'); 623#endif 624 obj->sum = yaffs_calc_name_sum(name); 625} 626 627void yaffs_set_obj_name_from_oh(yaffs_obj_t *obj, const yaffs_obj_header *oh) 628{ 629#ifdef CONFIG_YAFFS_AUTO_UNICODE 630 YCHAR tmpName[YAFFS_MAX_NAME_LENGTH+1]; 631 memset(tmpName,0,sizeof(tmpName)); 632 yaffs_load_name_from_oh(obj->my_dev,tmpName,oh->name,YAFFS_MAX_NAME_LENGTH+1); 633 yaffs_set_obj_name(obj,tmpName); 634#else 635 yaffs_set_obj_name(obj,oh->name); 636#endif 637} 638 639/*-------------------- TNODES ------------------- 640 641 * List of spare tnodes 642 * The list is hooked together using the first pointer 643 * in the tnode. 644 */ 645 646 647yaffs_tnode_t *yaffs_get_tnode(yaffs_dev_t *dev) 648{ 649 yaffs_tnode_t *tn = yaffs_alloc_raw_tnode(dev); 650 if (tn){ 651 memset(tn, 0, dev->tnode_size); 652 dev->n_tnodes++; 653 } 654 655 dev->checkpoint_blocks_required = 0; /* force recalculation*/ 656 657 return tn; 658} 659 660/* FreeTnode frees up a tnode and puts it back on the free list */ 661static void yaffs_free_tnode(yaffs_dev_t *dev, yaffs_tnode_t *tn) 662{ 663 yaffs_free_raw_tnode(dev,tn); 664 dev->n_tnodes--; 665 dev->checkpoint_blocks_required = 0; /* force recalculation*/ 666} 667 668static void yaffs_deinit_tnodes_and_objs(yaffs_dev_t *dev) 669{ 670 yaffs_deinit_raw_tnodes_and_objs(dev); 671 dev->n_obj = 0; 672 dev->n_tnodes = 0; 673} 674 675 676void yaffs_load_tnode_0(yaffs_dev_t *dev, yaffs_tnode_t *tn, unsigned pos, 677 unsigned val) 678{ 679 __u32 *map = (__u32 *)tn; 680 __u32 bitInMap; 681 __u32 bitInWord; 682 __u32 wordInMap; 683 __u32 mask; 684 685 pos &= YAFFS_TNODES_LEVEL0_MASK; 686 val >>= dev->chunk_grp_bits; 687 688 bitInMap = pos * dev->tnode_width; 689 wordInMap = bitInMap / 32; 690 bitInWord = bitInMap & (32 - 1); 691 692 mask = dev->tnode_mask << bitInWord; 693 694 map[wordInMap] &= ~mask; 695 map[wordInMap] |= (mask & (val << bitInWord)); 696 697 if (dev->tnode_width > (32 - bitInWord)) { 698 bitInWord = (32 - bitInWord); 699 wordInMap++;; 700 mask = dev->tnode_mask >> (/*dev->tnode_width -*/ bitInWord); 701 map[wordInMap] &= ~mask; 702 map[wordInMap] |= (mask & (val >> bitInWord)); 703 } 704} 705 706__u32 yaffs_get_group_base(yaffs_dev_t *dev, yaffs_tnode_t *tn, 707 unsigned pos) 708{ 709 __u32 *map = (__u32 *)tn; 710 __u32 bitInMap; 711 __u32 bitInWord; 712 __u32 wordInMap; 713 __u32 val; 714 715 pos &= YAFFS_TNODES_LEVEL0_MASK; 716 717 bitInMap = pos * dev->tnode_width; 718 wordInMap = bitInMap / 32; 719 bitInWord = bitInMap & (32 - 1); 720 721 val = map[wordInMap] >> bitInWord; 722 723 if (dev->tnode_width > (32 - bitInWord)) { 724 bitInWord = (32 - bitInWord); 725 wordInMap++;; 726 val |= (map[wordInMap] << bitInWord); 727 } 728 729 val &= dev->tnode_mask; 730 val <<= dev->chunk_grp_bits; 731 732 return val; 733} 734 735/* ------------------- End of individual tnode manipulation -----------------*/ 736 737/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ 738 * The look up tree is represented by the top tnode and the number of top_level 739 * in the tree. 0 means only the level 0 tnode is in the tree. 740 */ 741 742/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ 743yaffs_tnode_t *yaffs_find_tnode_0(yaffs_dev_t *dev, 744 yaffs_file_s *file_struct, 745 __u32 chunk_id) 746{ 747 yaffs_tnode_t *tn = file_struct->top; 748 __u32 i; 749 int requiredTallness; 750 int level = file_struct->top_level; 751 752 dev=dev; 753 754 /* Check sane level and chunk Id */ 755 if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) 756 return NULL; 757 758 if (chunk_id > YAFFS_MAX_CHUNK_ID) 759 return NULL; 760 761 /* First check we're tall enough (ie enough top_level) */ 762 763 i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; 764 requiredTallness = 0; 765 while (i) { 766 i >>= YAFFS_TNODES_INTERNAL_BITS; 767 requiredTallness++; 768 } 769 770 if (requiredTallness > file_struct->top_level) 771 return NULL; /* Not tall enough, so we can't find it */ 772 773 /* Traverse down to level 0 */ 774 while (level > 0 && tn) { 775 tn = tn->internal[(chunk_id >> 776 (YAFFS_TNODES_LEVEL0_BITS + 777 (level - 1) * 778 YAFFS_TNODES_INTERNAL_BITS)) & 779 YAFFS_TNODES_INTERNAL_MASK]; 780 level--; 781 } 782 783 return tn; 784} 785 786/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. 787 * This happens in two steps: 788 * 1. If the tree isn't tall enough, then make it taller. 789 * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. 790 * 791 * Used when modifying the tree. 792 * 793 * If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will 794 * be plugged into the ttree. 795 */ 796 797yaffs_tnode_t *yaffs_add_find_tnode_0(yaffs_dev_t *dev, 798 yaffs_file_s *file_struct, 799 __u32 chunk_id, 800 yaffs_tnode_t *passed_tn) 801{ 802 int requiredTallness; 803 int i; 804 int l; 805 yaffs_tnode_t *tn; 806 807 __u32 x; 808 809 810 /* Check sane level and page Id */ 811 if (file_struct->top_level < 0 || file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) 812 return NULL; 813 814 if (chunk_id > YAFFS_MAX_CHUNK_ID) 815 return NULL; 816 817 /* First check we're tall enough (ie enough top_level) */ 818 819 x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; 820 requiredTallness = 0; 821 while (x) { 822 x >>= YAFFS_TNODES_INTERNAL_BITS; 823 requiredTallness++; 824 } 825 826 827 if (requiredTallness > file_struct->top_level) { 828 /* Not tall enough, gotta make the tree taller */ 829 for (i = file_struct->top_level; i < requiredTallness; i++) { 830 831 tn = yaffs_get_tnode(dev); 832 833 if (tn) { 834 tn->internal[0] = file_struct->top; 835 file_struct->top = tn; 836 file_struct->top_level++; 837 } else { 838 T(YAFFS_TRACE_ERROR, 839 (TSTR("yaffs: no more tnodes" TENDSTR))); 840 return NULL; 841 } 842 } 843 } 844 845 /* Traverse down to level 0, adding anything we need */ 846 847 l = file_struct->top_level; 848 tn = file_struct->top; 849 850 if (l > 0) { 851 while (l > 0 && tn) { 852 x = (chunk_id >> 853 (YAFFS_TNODES_LEVEL0_BITS + 854 (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & 855 YAFFS_TNODES_INTERNAL_MASK; 856 857 858 if ((l > 1) && !tn->internal[x]) { 859 /* Add missing non-level-zero tnode */ 860 tn->internal[x] = yaffs_get_tnode(dev); 861 if(!tn->internal[x]) 862 return NULL; 863 } else if (l == 1) { 864 /* Looking from level 1 at level 0 */ 865 if (passed_tn) { 866 /* If we already have one, then release it.*/ 867 if (tn->internal[x]) 868 yaffs_free_tnode(dev, tn->internal[x]); 869 tn->internal[x] = passed_tn; 870 871 } else if (!tn->internal[x]) { 872 /* Don't have one, none passed in */ 873 tn->internal[x] = yaffs_get_tnode(dev); 874 if(!tn->internal[x]) 875 return NULL; 876 } 877 } 878 879 tn = tn->internal[x]; 880 l--; 881 } 882 } else { 883 /* top is level 0 */ 884 if (passed_tn) { 885 memcpy(tn, passed_tn, (dev->tnode_width * YAFFS_NTNODES_LEVEL0)/8); 886 yaffs_free_tnode(dev, passed_tn); 887 } 888 } 889 890 return tn; 891} 892 893static int yaffs_find_chunk_in_group(yaffs_dev_t *dev, int theChunk, 894 yaffs_ext_tags *tags, int obj_id, 895 int inode_chunk) 896{ 897 int j; 898 899 for (j = 0; theChunk && j < dev->chunk_grp_size; j++) { 900 if (yaffs_check_chunk_bit(dev, theChunk / dev->param.chunks_per_block, 901 theChunk % dev->param.chunks_per_block)) { 902 903 if(dev->chunk_grp_size == 1) 904 return theChunk; 905 else { 906 yaffs_rd_chunk_tags_nand(dev, theChunk, NULL, 907 tags); 908 if (yaffs_tags_match(tags, obj_id, inode_chunk)) { 909 /* found it; */ 910 return theChunk; 911 } 912 } 913 } 914 theChunk++; 915 } 916 return -1; 917} 918 919#if 0 920/* Experimental code not being used yet. Might speed up file deletion */ 921/* DeleteWorker scans backwards through the tnode tree and deletes all the 922 * chunks and tnodes in the file. 923 * Returns 1 if the tree was deleted. 924 * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. 925 */ 926 927static int yaffs_del_worker(yaffs_obj_t *in, yaffs_tnode_t *tn, __u32 level, 928 int chunk_offset, int *limit) 929{ 930 int i; 931 int inode_chunk; 932 int theChunk; 933 yaffs_ext_tags tags; 934 int foundChunk; 935 yaffs_dev_t *dev = in->my_dev; 936 937 int allDone = 1; 938 939 if (tn) { 940 if (level > 0) { 941 for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; 942 i--) { 943 if (tn->internal[i]) { 944 if (limit && (*limit) < 0) { 945 allDone = 0; 946 } else { 947 allDone = 948 yaffs_del_worker(in, 949 tn-> 950 internal 951 [i], 952 level - 953 1, 954 (chunk_offset 955 << 956 YAFFS_TNODES_INTERNAL_BITS) 957 + i, 958 limit); 959 } 960 if (allDone) { 961 yaffs_free_tnode(dev, 962 tn-> 963 internal[i]); 964 tn->internal[i] = NULL; 965 } 966 } 967 } 968 return (allDone) ? 1 : 0; 969 } else if (level == 0) { 970 int hitLimit = 0; 971 972 for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit; 973 i--) { 974 theChunk = yaffs_get_group_base(dev, tn, i); 975 if (theChunk) { 976 977 inode_chunk = (chunk_offset << 978 YAFFS_TNODES_LEVEL0_BITS) + i; 979 980 foundChunk = 981 yaffs_find_chunk_in_group(dev, 982 theChunk, 983 &tags, 984 in->obj_id, 985 inode_chunk); 986 987 if (foundChunk > 0) { 988 yaffs_chunk_del(dev, 989 foundChunk, 1, 990 __LINE__); 991 in->n_data_chunks--; 992 if (limit) { 993 *limit = *limit - 1; 994 if (*limit <= 0) 995 hitLimit = 1; 996 } 997 998 } 999 1000 yaffs_load_tnode_0(dev, tn, i, 0); 1001 } 1002 1003 } 1004 return (i < 0) ? 1 : 0; 1005 1006 } 1007 1008 } 1009 1010 return 1; 1011 1012} 1013 1014#endif 1015 1016static void yaffs_soft_del_chunk(yaffs_dev_t *dev, int chunk) 1017{ 1018 yaffs_block_info_t *theBlock; 1019 unsigned block_no; 1020 1021 T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk)); 1022 1023 block_no = chunk / dev->param.chunks_per_block; 1024 theBlock = yaffs_get_block_info(dev, block_no); 1025 if (theBlock) { 1026 theBlock->soft_del_pages++; 1027 dev->n_free_chunks++; 1028 yaffs2_update_oldest_dirty_seq(dev, block_no, theBlock); 1029 } 1030} 1031 1032/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. 1033 * All soft deleting does is increment the block's softdelete count and pulls the chunk out 1034 * of the tnode. 1035 * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. 1036 */ 1037 1038static int yaffs_soft_del_worker(yaffs_obj_t *in, yaffs_tnode_t *tn, 1039 __u32 level, int chunk_offset) 1040{ 1041 int i; 1042 int theChunk; 1043 int allDone = 1; 1044 yaffs_dev_t *dev = in->my_dev; 1045 1046 if (tn) { 1047 if (level > 0) { 1048 1049 for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; 1050 i--) { 1051 if (tn->internal[i]) { 1052 allDone = 1053 yaffs_soft_del_worker(in, 1054 tn-> 1055 internal[i], 1056 level - 1, 1057 (chunk_offset 1058 << 1059 YAFFS_TNODES_INTERNAL_BITS) 1060 + i); 1061 if (allDone) { 1062 yaffs_free_tnode(dev, 1063 tn-> 1064 internal[i]); 1065 tn->internal[i] = NULL; 1066 } else { 1067 /* Hoosterman... how could this happen? */ 1068 } 1069 } 1070 } 1071 return (allDone) ? 1 : 0; 1072 } else if (level == 0) { 1073 1074 for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { 1075 theChunk = yaffs_get_group_base(dev, tn, i); 1076 if (theChunk) { 1077 /* Note this does not find the real chunk, only the chunk group. 1078 * We make an assumption that a chunk group is not larger than 1079 * a block. 1080 */ 1081 yaffs_soft_del_chunk(dev, theChunk); 1082 yaffs_load_tnode_0(dev, tn, i, 0); 1083 } 1084 1085 } 1086 return 1; 1087 1088 } 1089 1090 } 1091 1092 return 1; 1093 1094} 1095 1096static void yaffs_soft_del_file(yaffs_obj_t *obj) 1097{ 1098 if (obj->deleted && 1099 obj->variant_type == YAFFS_OBJECT_TYPE_FILE && !obj->soft_del) { 1100 if (obj->n_data_chunks <= 0) { 1101 /* Empty file with no duplicate object headers, just delete it immediately */ 1102 yaffs_free_tnode(obj->my_dev, 1103 obj->variant.file_variant.top); 1104 obj->variant.file_variant.top = NULL; 1105 T(YAFFS_TRACE_TRACING, 1106 (TSTR("yaffs: Deleting empty file %d" TENDSTR), 1107 obj->obj_id)); 1108 yaffs_generic_obj_del(obj); 1109 } else { 1110 yaffs_soft_del_worker(obj, 1111 obj->variant.file_variant.top, 1112 obj->variant.file_variant. 1113 top_level, 0); 1114 obj->soft_del = 1; 1115 } 1116 } 1117} 1118 1119/* Pruning removes any part of the file structure tree that is beyond the 1120 * bounds of the file (ie that does not point to chunks). 1121 * 1122 * A file should only get pruned when its size is reduced. 1123 * 1124 * Before pruning, the chunks must be pulled from the tree and the 1125 * level 0 tnode entries must be zeroed out. 1126 * Could also use this for file deletion, but that's probably better handled 1127 * by a special case. 1128 * 1129 * This function is recursive. For levels > 0 the function is called again on 1130 * any sub-tree. For level == 0 we just check if the sub-tree has data. 1131 * If there is no data in a subtree then it is pruned. 1132 */ 1133 1134static yaffs_tnode_t *yaffs_prune_worker(yaffs_dev_t *dev, yaffs_tnode_t *tn, 1135 __u32 level, int del0) 1136{ 1137 int i; 1138 int hasData; 1139 1140 if (tn) { 1141 hasData = 0; 1142 1143 if(level > 0){ 1144 for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { 1145 if (tn->internal[i]) { 1146 tn->internal[i] = 1147 yaffs_prune_worker(dev, tn->internal[i], 1148 level - 1, 1149 (i == 0) ? del0 : 1); 1150 } 1151 1152 if (tn->internal[i]) 1153 hasData++; 1154 } 1155 } else { 1156 int tnode_size_u32 = dev->tnode_size/sizeof(__u32); 1157 __u32 *map = (__u32 *)tn; 1158 1159 for(i = 0; !hasData && i < tnode_size_u32; i++){ 1160 if(map[i]) 1161 hasData++; 1162 } 1163 } 1164 1165 if (hasData == 0 && del0) { 1166 /* Free and return NULL */ 1167 1168 yaffs_free_tnode(dev, tn); 1169 tn = NULL; 1170 } 1171 1172 } 1173 1174 return tn; 1175 1176} 1177 1178static int yaffs_prune_tree(yaffs_dev_t *dev, 1179 yaffs_file_s *file_struct) 1180{ 1181 int i; 1182 int hasData; 1183 int done = 0; 1184 yaffs_tnode_t *tn; 1185 1186 if (file_struct->top_level > 0) { 1187 file_struct->top = 1188 yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); 1189 1190 /* Now we have a tree with all the non-zero branches NULL but the height 1191 * is the same as it was. 1192 * Let's see if we can trim internal tnodes to shorten the tree. 1193 * We can do this if only the 0th element in the tnode is in use 1194 * (ie all the non-zero are NULL) 1195 */ 1196 1197 while (file_struct->top_level && !done) { 1198 tn = file_struct->top; 1199 1200 hasData = 0; 1201 for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { 1202 if (tn->internal[i]) 1203 hasData++; 1204 } 1205 1206 if (!hasData) { 1207 file_struct->top = tn->internal[0]; 1208 file_struct->top_level--; 1209 yaffs_free_tnode(dev, tn); 1210 } else { 1211 done = 1; 1212 } 1213 } 1214 } 1215 1216 return YAFFS_OK; 1217} 1218 1219/*-------------------- End of File Structure functions.-------------------*/ 1220 1221 1222/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ 1223static yaffs_obj_t *yaffs_alloc_empty_obj(yaffs_dev_t *dev) 1224{ 1225 yaffs_obj_t *obj = yaffs_alloc_raw_obj(dev); 1226 1227 if (obj) { 1228 dev->n_obj++; 1229 1230 /* Now sweeten it up... */ 1231 1232 memset(obj, 0, sizeof(yaffs_obj_t)); 1233 obj->being_created = 1; 1234 1235 obj->my_dev = dev; 1236 obj->hdr_chunk = 0; 1237 obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; 1238 YINIT_LIST_HEAD(&(obj->hard_links)); 1239 YINIT_LIST_HEAD(&(obj->hash_link)); 1240 YINIT_LIST_HEAD(&obj->siblings); 1241 1242 1243 /* Now make the directory sane */ 1244 if (dev->root_dir) { 1245 obj->parent = dev->root_dir; 1246 ylist_add(&(obj->siblings), &dev->root_dir->variant.dir_variant.children); 1247 } 1248 1249 /* Add it to the lost and found directory. 1250 * NB Can't put root or lostNFound in lostNFound so 1251 * check if lostNFound exists first 1252 */ 1253 if (dev->lost_n_found) 1254 yaffs_add_obj_to_dir(dev->lost_n_found, obj); 1255 1256 obj->being_created = 0; 1257 } 1258 1259 dev->checkpoint_blocks_required = 0; /* force recalculation*/ 1260 1261 return obj; 1262} 1263 1264static yaffs_obj_t *yaffs_create_fake_dir(yaffs_dev_t *dev, int number, 1265 __u32 mode) 1266{ 1267 1268 yaffs_obj_t *obj = 1269 yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); 1270 if (obj) { 1271 obj->fake = 1; /* it is fake so it might have no NAND presence... */ 1272 obj->rename_allowed = 0; /* ... and we're not allowed to rename it... */ 1273 obj->unlink_allowed = 0; /* ... or unlink it */ 1274 obj->deleted = 0; 1275 obj->unlinked = 0; 1276 obj->yst_mode = mode; 1277 obj->my_dev = dev; 1278 obj->hdr_chunk = 0; /* Not a valid chunk. */ 1279 } 1280 1281 return obj; 1282 1283} 1284 1285static void yaffs_unhash_obj(yaffs_obj_t *obj) 1286{ 1287 int bucket; 1288 yaffs_dev_t *dev = obj->my_dev; 1289 1290 /* If it is still linked into the bucket list, free from the list */ 1291 if (!ylist_empty(&obj->hash_link)) { 1292 ylist_del_init(&obj->hash_link); 1293 bucket = yaffs_hash_fn(obj->obj_id); 1294 dev->obj_bucket[bucket].count--; 1295 } 1296} 1297 1298/* FreeObject frees up a Object and puts it back on the free list */ 1299static void yaffs_free_obj(yaffs_obj_t *obj) 1300{ 1301 yaffs_dev_t *dev = obj->my_dev; 1302 1303 T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), obj, obj->my_inode)); 1304 1305 if (!obj) 1306 YBUG(); 1307 if (obj->parent) 1308 YBUG(); 1309 if (!ylist_empty(&obj->siblings)) 1310 YBUG(); 1311 1312 1313 if (obj->my_inode) { 1314 /* We're still hooked up to a cached inode. 1315 * Don't delete now, but mark for later deletion 1316 */ 1317 obj->defered_free = 1; 1318 return; 1319 } 1320 1321 yaffs_unhash_obj(obj); 1322 1323 yaffs_free_raw_obj(dev,obj); 1324 dev->n_obj--; 1325 dev->checkpoint_blocks_required = 0; /* force recalculation*/ 1326} 1327 1328 1329void yaffs_handle_defered_free(yaffs_obj_t *obj) 1330{ 1331 if (obj->defered_free) 1332 yaffs_free_obj(obj); 1333} 1334 1335static void yaffs_init_tnodes_and_objs(yaffs_dev_t *dev) 1336{ 1337 int i; 1338 1339 dev->n_obj = 0; 1340 dev->n_tnodes = 0; 1341 1342 yaffs_init_raw_tnodes_and_objs(dev); 1343 1344 for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { 1345 YINIT_LIST_HEAD(&dev->obj_bucket[i].list); 1346 dev->obj_bucket[i].count = 0; 1347 } 1348} 1349 1350static int yaffs_find_nice_bucket(yaffs_dev_t *dev) 1351{ 1352 int i; 1353 int l = 999; 1354 int lowest = 999999; 1355 1356 1357 /* Search for the shortest list or one that 1358 * isn't too long. 1359 */ 1360 1361 for (i = 0; i < 10 && lowest > 4; i++) { 1362 dev->bucket_finder++; 1363 dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; 1364 if (dev->obj_bucket[dev->bucket_finder].count < lowest) { 1365 lowest = dev->obj_bucket[dev->bucket_finder].count; 1366 l = dev->bucket_finder; 1367 } 1368 1369 } 1370 1371 return l; 1372} 1373 1374static int yaffs_new_obj_id(yaffs_dev_t *dev) 1375{ 1376 int bucket = yaffs_find_nice_bucket(dev); 1377 1378 /* Now find an object value that has not already been taken 1379 * by scanning the list. 1380 */ 1381 1382 int found = 0; 1383 struct ylist_head *i; 1384 1385 __u32 n = (__u32) bucket; 1386 1387 /* yaffs_check_obj_hash_sane(); */ 1388 1389 while (!found) { 1390 found = 1; 1391 n += YAFFS_NOBJECT_BUCKETS; 1392 if (1 || dev->obj_bucket[bucket].count > 0) { 1393 ylist_for_each(i, &dev->obj_bucket[bucket].list) { 1394 /* If there is already one in the list */ 1395 if (i && ylist_entry(i, yaffs_obj_t, 1396 hash_link)->obj_id == n) { 1397 found = 0; 1398 } 1399 } 1400 } 1401 } 1402 1403 return n; 1404} 1405 1406static void yaffs_hash_obj(yaffs_obj_t *in) 1407{ 1408 int bucket = yaffs_hash_fn(in->obj_id); 1409 yaffs_dev_t *dev = in->my_dev; 1410 1411 ylist_add(&in->hash_link, &dev->obj_bucket[bucket].list); 1412 dev->obj_bucket[bucket].count++; 1413} 1414 1415yaffs_obj_t *yaffs_find_by_number(yaffs_dev_t *dev, __u32 number) 1416{ 1417 int bucket = yaffs_hash_fn(number); 1418 struct ylist_head *i; 1419 yaffs_obj_t *in; 1420 1421 ylist_for_each(i, &dev->obj_bucket[bucket].list) { 1422 /* Look if it is in the list */ 1423 if (i) { 1424 in = ylist_entry(i, yaffs_obj_t, hash_link); 1425 if (in->obj_id == number) { 1426 1427 /* Don't tell the VFS about this one if it is defered free */ 1428 if (in->defered_free) 1429 return NULL; 1430 1431 return in; 1432 } 1433 } 1434 } 1435 1436 return NULL; 1437} 1438 1439yaffs_obj_t *yaffs_new_obj(yaffs_dev_t *dev, int number, 1440 yaffs_obj_type type) 1441{ 1442 yaffs_obj_t *theObject=NULL; 1443 yaffs_tnode_t *tn = NULL; 1444 1445 if (number < 0) 1446 number = yaffs_new_obj_id(dev); 1447 1448 if (type == YAFFS_OBJECT_TYPE_FILE) { 1449 tn = yaffs_get_tnode(dev); 1450 if (!tn) 1451 return NULL; 1452 } 1453 1454 theObject = yaffs_alloc_empty_obj(dev); 1455 if (!theObject){ 1456 if(tn) 1457 yaffs_free_tnode(dev,tn); 1458 return NULL; 1459 } 1460 1461 1462 if (theObject) { 1463 theObject->fake = 0; 1464 theObject->rename_allowed = 1; 1465 theObject->unlink_allowed = 1; 1466 theObject->obj_id = number; 1467 yaffs_hash_obj(theObject); 1468 theObject->variant_type = type; 1469#ifdef CONFIG_YAFFS_WINCE 1470 yfsd_win_file_time_now(theObject->win_atime); 1471 theObject->win_ctime[0] = theObject->win_mtime[0] = 1472 theObject->win_atime[0]; 1473 theObject->win_ctime[1] = theObject->win_mtime[1] = 1474 theObject->win_atime[1]; 1475 1476#else 1477 1478 theObject->yst_atime = theObject->yst_mtime = 1479 theObject->yst_ctime = Y_CURRENT_TIME; 1480#endif 1481 switch (type) { 1482 case YAFFS_OBJECT_TYPE_FILE: 1483 theObject->variant.file_variant.file_size = 0; 1484 theObject->variant.file_variant.scanned_size = 0; 1485 theObject->variant.file_variant.shrink_size = 0xFFFFFFFF; /* max __u32 */ 1486 theObject->variant.file_variant.top_level = 0; 1487 theObject->variant.file_variant.top = tn; 1488 break; 1489 case YAFFS_OBJECT_TYPE_DIRECTORY: 1490 YINIT_LIST_HEAD(&theObject->variant.dir_variant. 1491 children); 1492 YINIT_LIST_HEAD(&theObject->variant.dir_variant. 1493 dirty); 1494 break; 1495 case YAFFS_OBJECT_TYPE_SYMLINK: 1496 case YAFFS_OBJECT_TYPE_HARDLINK: 1497 case YAFFS_OBJECT_TYPE_SPECIAL: 1498 /* No action required */ 1499 break; 1500 case YAFFS_OBJECT_TYPE_UNKNOWN: 1501 /* todo this should not happen */ 1502 break; 1503 } 1504 } 1505 1506 return theObject; 1507} 1508 1509yaffs_obj_t *yaffs_find_or_create_by_number(yaffs_dev_t *dev, 1510 int number, 1511 yaffs_obj_type type) 1512{ 1513 yaffs_obj_t *theObject = NULL; 1514 1515 if (number > 0) 1516 theObject = yaffs_find_by_number(dev, number); 1517 1518 if (!theObject) 1519 theObject = yaffs_new_obj(dev, number, type); 1520 1521 return theObject; 1522 1523} 1524 1525 1526YCHAR *yaffs_clone_str(const YCHAR *str) 1527{ 1528 YCHAR *newStr = NULL; 1529 int len; 1530 1531 if (!str) 1532 str = _Y(""); 1533 1534 len = yaffs_strnlen(str,YAFFS_MAX_ALIAS_LENGTH); 1535 newStr = YMALLOC((len + 1) * sizeof(YCHAR)); 1536 if (newStr){ 1537 yaffs_strncpy(newStr, str,len); 1538 newStr[len] = 0; 1539 } 1540 return newStr; 1541 1542} 1543 1544/* 1545 * Mknod (create) a new object. 1546 * equiv_obj only has meaning for a hard link; 1547 * aliasString only has meaning for a symlink. 1548 * rdev only has meaning for devices (a subset of special objects) 1549 */ 1550 1551static yaffs_obj_t *yaffs_create_obj(yaffs_obj_type type, 1552 yaffs_obj_t *parent, 1553 const YCHAR *name, 1554 __u32 mode, 1555 __u32 uid, 1556 __u32 gid, 1557 yaffs_obj_t *equiv_obj, 1558 const YCHAR *aliasString, __u32 rdev) 1559{ 1560 yaffs_obj_t *in; 1561 YCHAR *str = NULL; 1562 1563 yaffs_dev_t *dev = parent->my_dev; 1564 1565 /* Check if the entry exists. If it does then fail the call since we don't want a dup.*/ 1566 if (yaffs_find_by_name(parent, name)) 1567 return NULL; 1568 1569 if (type == YAFFS_OBJECT_TYPE_SYMLINK) { 1570 str = yaffs_clone_str(aliasString); 1571 if (!str) 1572 return NULL; 1573 } 1574 1575 in = yaffs_new_obj(dev, -1, type); 1576 1577 if (!in){ 1578 if(str) 1579 YFREE(str); 1580 return NULL; 1581 } 1582 1583 1584 1585 1586 1587 if (in) { 1588 in->hdr_chunk = 0; 1589 in->valid = 1; 1590 in->variant_type = type; 1591 1592 in->yst_mode = mode; 1593 1594#ifdef CONFIG_YAFFS_WINCE 1595 yfsd_win_file_time_now(in->win_atime); 1596 in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0]; 1597 in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1]; 1598 1599#else 1600 in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME; 1601 1602 in->yst_rdev = rdev; 1603 in->yst_uid = uid; 1604 in->yst_gid = gid; 1605#endif 1606 in->n_data_chunks = 0; 1607 1608 yaffs_set_obj_name(in, name); 1609 in->dirty = 1; 1610 1611 yaffs_add_obj_to_dir(parent, in); 1612 1613 in->my_dev = parent->my_dev; 1614 1615 switch (type) { 1616 case YAFFS_OBJECT_TYPE_SYMLINK: 1617 in->variant.symlink_variant.alias = str; 1618 break; 1619 case YAFFS_OBJECT_TYPE_HARDLINK: 1620 in->variant.hardlink_variant.equiv_obj = 1621 equiv_obj; 1622 in->variant.hardlink_variant.equiv_id = 1623 equiv_obj->obj_id; 1624 ylist_add(&in->hard_links, &equiv_obj->hard_links); 1625 break; 1626 case YAFFS_OBJECT_TYPE_FILE: 1627 case YAFFS_OBJECT_TYPE_DIRECTORY: 1628 case YAFFS_OBJECT_TYPE_SPECIAL: 1629 case YAFFS_OBJECT_TYPE_UNKNOWN: 1630 /* do nothing */ 1631 break; 1632 } 1633 1634 if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { 1635 /* Could not create the object header, fail the creation */ 1636 yaffs_del_obj(in); 1637 in = NULL; 1638 } 1639 1640 yaffs_update_parent(parent); 1641 } 1642 1643 return in; 1644} 1645 1646yaffs_obj_t *yaffs_create_file(yaffs_obj_t *parent, const YCHAR *name, 1647 __u32 mode, __u32 uid, __u32 gid) 1648{ 1649 return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, 1650 uid, gid, NULL, NULL, 0); 1651} 1652 1653yaffs_obj_t *yaffs_create_dir(yaffs_obj_t *parent, const YCHAR *name, 1654 __u32 mode, __u32 uid, __u32 gid) 1655{ 1656 return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, 1657 mode, uid, gid, NULL, NULL, 0); 1658} 1659 1660yaffs_obj_t *yaffs_create_special(yaffs_obj_t *parent, const YCHAR *name, 1661 __u32 mode, __u32 uid, __u32 gid, __u32 rdev) 1662{ 1663 return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, 1664 uid, gid, NULL, NULL, rdev); 1665} 1666 1667yaffs_obj_t *yaffs_create_symlink(yaffs_obj_t *parent, const YCHAR *name, 1668 __u32 mode, __u32 uid, __u32 gid, 1669 const YCHAR *alias) 1670{ 1671 return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, 1672 uid, gid, NULL, alias, 0); 1673} 1674 1675/* yaffs_link_obj returns the object id of the equivalent object.*/ 1676yaffs_obj_t *yaffs_link_obj(yaffs_obj_t *parent, const YCHAR *name, 1677 yaffs_obj_t *equiv_obj) 1678{ 1679 /* Get the real object in case we were fed a hard link as an equivalent object */ 1680 equiv_obj = yaffs_get_equivalent_obj(equiv_obj); 1681 1682 if (yaffs_create_obj 1683 (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, 1684 equiv_obj, NULL, 0)) { 1685 return equiv_obj; 1686 } else { 1687 return NULL; 1688 } 1689 1690} 1691 1692static int yaffs_change_obj_name(yaffs_obj_t *obj, yaffs_obj_t *new_dir, 1693 const YCHAR *new_name, int force, int shadows) 1694{ 1695 int unlinkOp; 1696 int deleteOp; 1697 1698 yaffs_obj_t *existingTarget; 1699 1700 if (new_dir == NULL) 1701 new_dir = obj->parent; /* use the old directory */ 1702 1703 if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { 1704 T(YAFFS_TRACE_ALWAYS, 1705 (TSTR 1706 ("tragedy: yaffs_change_obj_name: new_dir is not a directory" 1707 TENDSTR))); 1708 YBUG(); 1709 } 1710 1711 /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ 1712 if (obj->my_dev->param.is_yaffs2) 1713 unlinkOp = (new_dir == obj->my_dev->unlinked_dir); 1714 else 1715 unlinkOp = (new_dir == obj->my_dev->unlinked_dir 1716 && obj->variant_type == YAFFS_OBJECT_TYPE_FILE); 1717 1718 deleteOp = (new_dir == obj->my_dev->del_dir); 1719 1720 existingTarget = yaffs_find_by_name(new_dir, new_name); 1721 1722 /* If the object is a file going into the unlinked directory, 1723 * then it is OK to just stuff it in since duplicate names are allowed. 1724 * else only proceed if the new name does not exist and if we're putting 1725 * it into a directory. 1726 */ 1727 if ((unlinkOp || 1728 deleteOp || 1729 force || 1730 (shadows > 0) || 1731 !existingTarget) && 1732 new_dir->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) { 1733 yaffs_set_obj_name(obj, new_name); 1734 obj->dirty = 1; 1735 1736 yaffs_add_obj_to_dir(new_dir, obj); 1737 1738 if (unlinkOp) 1739 obj->unlinked = 1; 1740 1741 /* If it is a deletion then we mark it as a shrink for gc purposes. */ 1742 if (yaffs_update_oh(obj, new_name, 0, deleteOp, shadows, NULL) >= 0) 1743 return YAFFS_OK; 1744 } 1745 1746 return YAFFS_FAIL; 1747} 1748 1749int yaffs_rename_obj(yaffs_obj_t *old_dir, const YCHAR *old_name, 1750 yaffs_obj_t *new_dir, const YCHAR *new_name) 1751{ 1752 yaffs_obj_t *obj = NULL; 1753 yaffs_obj_t *existingTarget = NULL; 1754 int force = 0; 1755 int result; 1756 yaffs_dev_t *dev; 1757 1758 1759 if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) 1760 YBUG(); 1761 if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) 1762 YBUG(); 1763 1764 dev = old_dir->my_dev; 1765 1766#ifdef CONFIG_YAFFS_CASE_INSENSITIVE 1767 /* Special case for case insemsitive systems (eg. WinCE). 1768 * While look-up is case insensitive, the name isn't. 1769 * Therefore we might want to change x.txt to X.txt 1770 */ 1771 if (old_dir == new_dir && yaffs_strcmp(old_name, new_name) == 0) 1772 force = 1; 1773#endif 1774 1775 if(yaffs_strnlen(new_name,YAFFS_MAX_NAME_LENGTH+1) > YAFFS_MAX_NAME_LENGTH) 1776 /* ENAMETOOLONG */ 1777 return YAFFS_FAIL; 1778 1779 obj = yaffs_find_by_name(old_dir, old_name); 1780 1781 if (obj && obj->rename_allowed) { 1782 1783 /* Now do the handling for an existing target, if there is one */ 1784 1785 existingTarget = yaffs_find_by_name(new_dir, new_name); 1786 if (existingTarget && 1787 existingTarget->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && 1788 !ylist_empty(&existingTarget->variant.dir_variant.children)) { 1789 /* There is a target that is a non-empty directory, so we fail */ 1790 return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */ 1791 } else if (existingTarget && existingTarget != obj) { 1792 /* Nuke the target first, using shadowing, 1793 * but only if it isn't the same object. 1794 * 1795 * Note we must disable gc otherwise it can mess up the shadowing. 1796 * 1797 */ 1798 dev->gc_disable=1; 1799 yaffs_change_obj_name(obj, new_dir, new_name, force, 1800 existingTarget->obj_id); 1801 existingTarget->is_shadowed = 1; 1802 yaffs_unlink_obj(existingTarget); 1803 dev->gc_disable=0; 1804 } 1805 1806 result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); 1807 1808 yaffs_update_parent(old_dir); 1809 if(new_dir != old_dir) 1810 yaffs_update_parent(new_dir); 1811 1812 return result; 1813 } 1814 return YAFFS_FAIL; 1815} 1816 1817/*------------------------- Block Management and Page Allocation ----------------*/ 1818 1819static int yaffs_init_blocks(yaffs_dev_t *dev) 1820{ 1821 int nBlocks = dev->internal_end_block - dev->internal_start_block + 1; 1822 1823 dev->block_info = NULL; 1824 dev->chunk_bits = NULL; 1825 1826 dev->alloc_block = -1; /* force it to get a new one */ 1827 1828 /* If the first allocation strategy fails, thry the alternate one */ 1829 dev->block_info = YMALLOC(nBlocks * sizeof(yaffs_block_info_t)); 1830 if (!dev->block_info) { 1831 dev->block_info = YMALLOC_ALT(nBlocks * sizeof(yaffs_block_info_t)); 1832 dev->block_info_alt = 1; 1833 } else 1834 dev->block_info_alt = 0; 1835 1836 if (dev->block_info) { 1837 /* Set up dynamic blockinfo stuff. */ 1838 dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; /* round up bytes */ 1839 dev->chunk_bits = YMALLOC(dev->chunk_bit_stride * nBlocks); 1840 if (!dev->chunk_bits) { 1841 dev->chunk_bits = YMALLOC_ALT(dev->chunk_bit_stride * nBlocks); 1842 dev->chunk_bits_alt = 1; 1843 } else 1844 dev->chunk_bits_alt = 0; 1845 } 1846 1847 if (dev->block_info && dev->chunk_bits) { 1848 memset(dev->block_info, 0, nBlocks * sizeof(yaffs_block_info_t)); 1849 memset(dev->chunk_bits, 0, dev->chunk_bit_stride * nBlocks); 1850 return YAFFS_OK; 1851 } 1852 1853 return YAFFS_FAIL; 1854} 1855 1856static void yaffs_deinit_blocks(yaffs_dev_t *dev) 1857{ 1858 if (dev->block_info_alt && dev->block_info) 1859 YFREE_ALT(dev->block_info); 1860 else if (dev->block_info) 1861 YFREE(dev->block_info); 1862 1863 dev->block_info_alt = 0; 1864 1865 dev->block_info = NULL; 1866 1867 if (dev->chunk_bits_alt && dev->chunk_bits) 1868 YFREE_ALT(dev->chunk_bits); 1869 else if (dev->chunk_bits) 1870 YFREE(dev->chunk_bits); 1871 dev->chunk_bits_alt = 0; 1872 dev->chunk_bits = NULL; 1873} 1874 1875void yaffs_block_became_dirty(yaffs_dev_t *dev, int block_no) 1876{ 1877 yaffs_block_info_t *bi = yaffs_get_block_info(dev, block_no); 1878 1879 int erasedOk = 0; 1880 1881 /* If the block is still healthy erase it and mark as clean. 1882 * If the block has had a data failure, then retire it. 1883 */ 1884 1885 T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, 1886 (TSTR("yaffs_block_became_dirty block %d state %d %s"TENDSTR), 1887 block_no, bi->block_state, (bi->needs_retiring) ? "needs retiring" : "")); 1888 1889 yaffs2_clear_oldest_dirty_seq(dev,bi); 1890 1891 bi->block_state = YAFFS_BLOCK_STATE_DIRTY; 1892 1893 /* If this is the block being garbage collected then stop gc'ing this block */ 1894 if(block_no == dev->gc_block) 1895 dev->gc_block = 0; 1896 1897 /* If this block is currently the best candidate for gc then drop as a candidate */ 1898 if(block_no == dev->gc_dirtiest){ 1899 dev->gc_dirtiest = 0; 1900 dev->gc_pages_in_use = 0; 1901 } 1902 1903 if (!bi->needs_retiring) { 1904 yaffs2_checkpt_invalidate(dev); 1905 erasedOk = yaffs_erase_block(dev, block_no); 1906 if (!erasedOk) { 1907 dev->n_erase_failures++; 1908 T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, 1909 (TSTR("**>> Erasure failed %d" TENDSTR), block_no)); 1910 } 1911 } 1912 1913 if (erasedOk && 1914 ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || !yaffs_skip_verification(dev))) { 1915 int i; 1916 for (i = 0; i < dev->param.chunks_per_block; i++) { 1917 if (!yaffs_check_chunk_erased 1918 (dev, block_no * dev->param.chunks_per_block + i)) { 1919 T(YAFFS_TRACE_ERROR, 1920 (TSTR 1921 (">>Block %d erasure supposedly OK, but chunk %d not erased" 1922 TENDSTR), block_no, i)); 1923 } 1924 } 1925 } 1926 1927 if (erasedOk) { 1928 /* Clean it up... */ 1929 bi->block_state = YAFFS_BLOCK_STATE_EMPTY; 1930 bi->seq_number = 0; 1931 dev->n_erased_blocks++; 1932 bi->pages_in_use = 0; 1933 bi->soft_del_pages = 0; 1934 bi->has_shrink_hdr = 0; 1935 bi->skip_erased_check = 1; /* This is clean, so no need to check */ 1936 bi->gc_prioritise = 0; 1937 yaffs_clear_chunk_bits(dev, block_no); 1938 1939 T(YAFFS_TRACE_ERASE, 1940 (TSTR("Erased block %d" TENDSTR), block_no)); 1941 } else { 1942 dev->n_free_chunks -= dev->param.chunks_per_block; /* We lost a block of free space */ 1943 1944 yaffs_retire_block(dev, block_no); 1945 T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, 1946 (TSTR("**>> Block %d retired" TENDSTR), block_no)); 1947 } 1948} 1949 1950static int yaffs_find_alloc_block(yaffs_dev_t *dev) 1951{ 1952 int i; 1953 1954 yaffs_block_info_t *bi; 1955 1956 if (dev->n_erased_blocks < 1) { 1957 /* Hoosterman we've got a problem. 1958 * Can't get space to gc 1959 */ 1960 T(YAFFS_TRACE_ERROR, 1961 (TSTR("yaffs tragedy: no more erased blocks" TENDSTR))); 1962 1963 return -1; 1964 } 1965 1966 /* Find an empty block. */ 1967 1968 for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { 1969 dev->alloc_block_finder++; 1970 if (dev->alloc_block_finder < dev->internal_start_block 1971 || dev->alloc_block_finder > dev->internal_end_block) { 1972 dev->alloc_block_finder = dev->internal_start_block; 1973 } 1974 1975 bi = yaffs_get_block_info(dev, dev->alloc_block_finder); 1976 1977 if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { 1978 bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; 1979 dev->seq_number++; 1980 bi->seq_number = dev->seq_number; 1981 dev->n_erased_blocks--; 1982 T(YAFFS_TRACE_ALLOCATE, 1983 (TSTR("Allocated block %d, seq %d, %d left" TENDSTR), 1984 dev->alloc_block_finder, dev->seq_number, 1985 dev->n_erased_blocks)); 1986 return dev->alloc_block_finder; 1987 } 1988 } 1989 1990 T(YAFFS_TRACE_ALWAYS, 1991 (TSTR 1992 ("yaffs tragedy: no more erased blocks, but there should have been %d" 1993 TENDSTR), dev->n_erased_blocks)); 1994 1995 return -1; 1996} 1997 1998 1999/* 2000 * Check if there's space to allocate... 2001 * Thinks.... do we need top make this ths same as yaffs_get_free_chunks()? 2002 */ 2003int yaffs_check_alloc_available(yaffs_dev_t *dev, int n_chunks) 2004{ 2005 int reservedChunks; 2006 int reservedBlocks = dev->param.n_reserved_blocks; 2007 int checkpointBlocks; 2008 2009 checkpointBlocks = yaffs_calc_checkpt_blocks_required(dev); 2010 2011 reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.chunks_per_block); 2012 2013 return (dev->n_free_chunks > (reservedChunks + n_chunks)); 2014} 2015 2016static int yaffs_alloc_chunk(yaffs_dev_t *dev, int useReserve, 2017 yaffs_block_info_t **blockUsedPtr) 2018{ 2019 int retVal; 2020 yaffs_block_info_t *bi; 2021 2022 if (dev->alloc_block < 0) { 2023 /* Get next block to allocate off */ 2024 dev->alloc_block = yaffs_find_alloc_block(dev); 2025 dev->alloc_page = 0; 2026 } 2027 2028 if (!useReserve && !yaffs_check_alloc_available(dev, 1)) { 2029 /* Not enough space to allocate unless we're allowed to use the reserve. */ 2030 return -1; 2031 } 2032 2033 if (dev->n_erased_blocks < dev->param.n_reserved_blocks 2034 && dev->alloc_page == 0) { 2035 T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR))); 2036 } 2037 2038 /* Next page please.... */ 2039 if (dev->alloc_block >= 0) { 2040 bi = yaffs_get_block_info(dev, dev->alloc_block); 2041 2042 retVal = (dev->alloc_block * dev->param.chunks_per_block) + 2043 dev->alloc_page; 2044 bi->pages_in_use++; 2045 yaffs_set_chunk_bit(dev, dev->alloc_block, 2046 dev->alloc_page); 2047 2048 dev->alloc_page++; 2049 2050 dev->n_free_chunks--; 2051 2052 /* If the block is full set the state to full */ 2053 if (dev->alloc_page >= dev->param.chunks_per_block) { 2054 bi->block_state = YAFFS_BLOCK_STATE_FULL; 2055 dev->alloc_block = -1; 2056 } 2057 2058 if (blockUsedPtr) 2059 *blockUsedPtr = bi; 2060 2061 return retVal; 2062 } 2063 2064 T(YAFFS_TRACE_ERROR, 2065 (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); 2066 2067 return -1; 2068} 2069 2070static int yaffs_get_erased_chunks(yaffs_dev_t *dev) 2071{ 2072 int n; 2073 2074 n = dev->n_erased_blocks * dev->param.chunks_per_block; 2075 2076 if (dev->alloc_block > 0) 2077 n += (dev->param.chunks_per_block - dev->alloc_page); 2078 2079 return n; 2080 2081} 2082 2083/* 2084 * yaffs_skip_rest_of_block() skips over the rest of the allocation block 2085 * if we don't want to write to it. 2086 */ 2087void yaffs_skip_rest_of_block(yaffs_dev_t *dev) 2088{ 2089 if(dev->alloc_block > 0){ 2090 yaffs_block_info_t *bi = yaffs_get_block_info(dev, dev->alloc_block); 2091 if(bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING){ 2092 bi->block_state = YAFFS_BLOCK_STATE_FULL; 2093 dev->alloc_block = -1; 2094 } 2095 } 2096} 2097 2098 2099static int yaffs_gc_block(yaffs_dev_t *dev, int block, 2100 int wholeBlock) 2101{ 2102 int oldChunk; 2103 int newChunk; 2104 int mark_flash; 2105 int retVal = YAFFS_OK; 2106 int i; 2107 int isCheckpointBlock; 2108 int matchingChunk; 2109 int maxCopies; 2110 2111 int chunksBefore = yaffs_get_erased_chunks(dev); 2112 int chunksAfter; 2113 2114 yaffs_ext_tags tags; 2115 2116 yaffs_block_info_t *bi = yaffs_get_block_info(dev, block); 2117 2118 yaffs_obj_t *object; 2119 2120 isCheckpointBlock = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); 2121 2122 2123 T(YAFFS_TRACE_TRACING, 2124 (TSTR("Collecting block %d, in use %d, shrink %d, wholeBlock %d" TENDSTR), 2125 block, 2126 bi->pages_in_use, 2127 bi->has_shrink_hdr, 2128 wholeBlock)); 2129 2130 /*yaffs_verify_free_chunks(dev); */ 2131 2132 if(bi->block_state == YAFFS_BLOCK_STATE_FULL) 2133 bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; 2134 2135 bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ 2136 2137 dev->gc_disable = 1; 2138 2139 if (isCheckpointBlock || 2140 !yaffs_still_some_chunks(dev, block)) { 2141 T(YAFFS_TRACE_TRACING, 2142 (TSTR 2143 ("Collecting block %d that has no chunks in use" TENDSTR), 2144 block)); 2145 yaffs_block_became_dirty(dev, block); 2146 } else { 2147 2148 __u8 *buffer = yaffs_get_temp_buffer(dev, __LINE__); 2149 2150 yaffs_verify_blk(dev, bi, block); 2151 2152 maxCopies = (wholeBlock) ? dev->param.chunks_per_block : 5; 2153 oldChunk = block * dev->param.chunks_per_block + dev->gc_chunk; 2154 2155 for (/* init already done */; 2156 retVal == YAFFS_OK && 2157 dev->gc_chunk < dev->param.chunks_per_block && 2158 (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && 2159 maxCopies > 0; 2160 dev->gc_chunk++, oldChunk++) { 2161 if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { 2162 2163 /* This page is in use and might need to be copied off */ 2164 2165 maxCopies--; 2166 2167 mark_flash = 1; 2168 2169 yaffs_init_tags(&tags); 2170 2171 yaffs_rd_chunk_tags_nand(dev, oldChunk, 2172 buffer, &tags); 2173 2174 object = 2175 yaffs_find_by_number(dev, 2176 tags.obj_id); 2177 2178 T(YAFFS_TRACE_GC_DETAIL, 2179 (TSTR 2180 ("Collecting chunk in block %d, %d %d %d " TENDSTR), 2181 dev->gc_chunk, tags.obj_id, tags.chunk_id, 2182 tags.n_bytes)); 2183 2184 if (object && !yaffs_skip_verification(dev)) { 2185 if (tags.chunk_id == 0) 2186 matchingChunk = object->hdr_chunk; 2187 else if (object->soft_del) 2188 matchingChunk = oldChunk; /* Defeat the test */ 2189 else 2190 matchingChunk = yaffs_find_chunk_in_file(object, tags.chunk_id, NULL); 2191 2192 if (oldChunk != matchingChunk) 2193 T(YAFFS_TRACE_ERROR, 2194 (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR), 2195 oldChunk, matchingChunk, tags.obj_id, tags.chunk_id)); 2196 2197 } 2198 2199 if (!object) { 2200 T(YAFFS_TRACE_ERROR, 2201 (TSTR 2202 ("page %d in gc has no object: %d %d %d " 2203 TENDSTR), oldChunk, 2204 tags.obj_id, tags.chunk_id, tags.n_bytes)); 2205 } 2206 2207 if (object && 2208 object->deleted && 2209 object->soft_del && 2210 tags.chunk_id != 0) { 2211 /* Data chunk in a soft deleted file, throw it away 2212 * It's a soft deleted data chunk, 2213 * No need to copy this, just forget about it and 2214 * fix up the object. 2215 */ 2216 2217 /* Free chunks already includes softdeleted chunks. 2218 * How ever this chunk is going to soon be really deleted 2219 * which will increment free chunks. 2220 * We have to decrement free chunks so this works out properly. 2221 */ 2222 dev->n_free_chunks--; 2223 bi->soft_del_pages--; 2224 2225 object->n_data_chunks--; 2226 2227 if (object->n_data_chunks <= 0) { 2228 /* remeber to clean up the object */ 2229 dev->gc_cleanup_list[dev->n_clean_ups] = 2230 tags.obj_id; 2231 dev->n_clean_ups++; 2232 } 2233 mark_flash = 0; 2234 } else if (0) { 2235 /* Todo object && object->deleted && object->n_data_chunks == 0 */ 2236 /* Deleted object header with no data chunks. 2237 * Can be discarded and the file deleted. 2238 */ 2239 object->hdr_chunk = 0; 2240 yaffs_free_tnode(object->my_dev, 2241 object->variant. 2242 file_variant.top); 2243 object->variant.file_variant.top = NULL; 2244 yaffs_generic_obj_del(object); 2245 2246 } else if (object) { 2247 /* It's either a data chunk in a live file or 2248 * an ObjectHeader, so we're interested in it. 2249 * NB Need to keep the ObjectHeaders of deleted files 2250 * until the whole file has been deleted off 2251 */ 2252 tags.serial_number++; 2253 2254 dev->n_gc_copies++; 2255 2256 if (tags.chunk_id == 0) { 2257 /* It is an object Id, 2258 * We need to nuke the shrinkheader flags first 2259 * Also need to clean up shadowing. 2260 * We no longer want the shrinkHeader flag since its work is done 2261 * and if it is left in place it will mess up scanning. 2262 */ 2263 2264 yaffs_obj_header *oh; 2265 oh = (yaffs_obj_header *)buffer; 2266 2267 oh->is_shrink = 0; 2268 tags.extra_is_shrink = 0; 2269 2270 oh->shadows_obj = 0; 2271 oh->inband_shadowed_obj_id = 0; 2272 tags.extra_shadows = 0; 2273 2274 /* Update file size */ 2275 if(object->variant_type == YAFFS_OBJECT_TYPE_FILE){ 2276 oh->file_size = object->variant.file_variant.file_size; 2277 tags.extra_length = oh->file_size; 2278 } 2279 2280 yaffs_verify_oh(object, oh, &tags, 1); 2281 newChunk = 2282 yaffs_write_new_chunk(dev,(__u8 *) oh, &tags, 1); 2283 } else 2284 newChunk = 2285 yaffs_write_new_chunk(dev, buffer, &tags, 1); 2286 2287 if (newChunk < 0) { 2288 retVal = YAFFS_FAIL; 2289 } else { 2290 2291 /* Ok, now fix up the Tnodes etc. */ 2292 2293 if (tags.chunk_id == 0) { 2294 /* It's a header */ 2295 object->hdr_chunk = newChunk; 2296 object->serial = tags.serial_number; 2297 } else { 2298 /* It's a data chunk */ 2299 int ok; 2300 ok = yaffs_put_chunk_in_file 2301 (object, 2302 tags.chunk_id, 2303 newChunk, 0); 2304 } 2305 } 2306 } 2307 2308 if (retVal == YAFFS_OK) 2309 yaffs_chunk_del(dev, oldChunk, mark_flash, __LINE__); 2310 2311 } 2312 } 2313 2314 yaffs_release_temp_buffer(dev, buffer, __LINE__); 2315 2316 2317 2318 } 2319 2320 yaffs_verify_collected_blk(dev, bi, block); 2321 2322 2323 2324 if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { 2325 /* 2326 * The gc did not complete. Set block state back to FULL 2327 * because checkpointing does not restore gc. 2328 */ 2329 bi->block_state = YAFFS_BLOCK_STATE_FULL; 2330 } else { 2331 /* The gc completed. */ 2332 /* Do any required cleanups */ 2333 for (i = 0; i < dev->n_clean_ups; i++) { 2334 /* Time to delete the file too */ 2335 object = 2336 yaffs_find_by_number(dev, 2337 dev->gc_cleanup_list[i]); 2338 if (object) { 2339 yaffs_free_tnode(dev, 2340 object->variant.file_variant. 2341 top); 2342 object->variant.file_variant.top = NULL; 2343 T(YAFFS_TRACE_GC, 2344 (TSTR 2345 ("yaffs: About to finally delete object %d" 2346 TENDSTR), object->obj_id)); 2347 yaffs_generic_obj_del(object); 2348 object->my_dev->n_deleted_files--; 2349 } 2350 2351 } 2352 2353 2354 chunksAfter = yaffs_get_erased_chunks(dev); 2355 if (chunksBefore >= chunksAfter) { 2356 T(YAFFS_TRACE_GC, 2357 (TSTR 2358 ("gc did not increase free chunks before %d after %d" 2359 TENDSTR), chunksBefore, chunksAfter)); 2360 } 2361 dev->gc_block = 0; 2362 dev->gc_chunk = 0; 2363 dev->n_clean_ups = 0; 2364 } 2365 2366 dev->gc_disable = 0; 2367 2368 return retVal; 2369} 2370 2371/* 2372 * FindBlockForgarbageCollection is used to select the dirtiest block (or close enough) 2373 * for garbage collection. 2374 */ 2375 2376static unsigned yaffs_find_gc_block(yaffs_dev_t *dev, 2377 int aggressive, 2378 int background) 2379{ 2380 int i; 2381 int iterations; 2382 unsigned selected = 0; 2383 int prioritised = 0; 2384 int prioritisedExists = 0; 2385 yaffs_block_info_t *bi; 2386 int threshold; 2387 2388 /* First let's see if we need to grab a prioritised block */ 2389 if (dev->has_pending_prioritised_gc && !aggressive) { 2390 dev->gc_dirtiest = 0; 2391 bi = dev->block_info; 2392 for (i = dev->internal_start_block; 2393 i <= dev->internal_end_block && !selected; 2394 i++) { 2395 2396 if (bi->gc_prioritise) { 2397 prioritisedExists = 1; 2398 if (bi->block_state == YAFFS_BLOCK_STATE_FULL && 2399 yaffs_block_ok_for_gc(dev, bi)) { 2400 selected = i; 2401 prioritised = 1; 2402 } 2403 } 2404 bi++; 2405 } 2406 2407 /* 2408 * If there is a prioritised block and none was selected then 2409 * this happened because there is at least one old dirty block gumming 2410 * up the works. Let's gc the oldest dirty block. 2411 */ 2412 2413 if(prioritisedExists && 2414 !selected && 2415 dev->oldest_dirty_block > 0) 2416 selected = dev->oldest_dirty_block; 2417 2418 if (!prioritisedExists) /* None found, so we can clear this */ 2419 dev->has_pending_prioritised_gc = 0; 2420 } 2421 2422 /* If we're doing aggressive GC then we are happy to take a less-dirty block, and 2423 * search harder. 2424 * else (we're doing a leasurely gc), then we only bother to do this if the 2425 * block has only a few pages in use. 2426 */ 2427 2428 if (!selected){ 2429 int pagesUsed; 2430 int nBlocks = dev->internal_end_block - dev->internal_start_block + 1; 2431 if (aggressive){ 2432 threshold = dev->param.chunks_per_block; 2433 iterations = nBlocks; 2434 } else { 2435 int maxThreshold; 2436 2437 if(background) 2438 maxThreshold = dev->param.chunks_per_block/2; 2439 else 2440 maxThreshold = dev->param.chunks_per_block/8; 2441 2442 if(maxThreshold < YAFFS_GC_PASSIVE_THRESHOLD) 2443 maxThreshold = YAFFS_GC_PASSIVE_THRESHOLD; 2444 2445 threshold = background ? 2446 (dev->gc_not_done + 2) * 2 : 0; 2447 if(threshold <YAFFS_GC_PASSIVE_THRESHOLD) 2448 threshold = YAFFS_GC_PASSIVE_THRESHOLD; 2449 if(threshold > maxThreshold) 2450 threshold = maxThreshold; 2451 2452 iterations = nBlocks / 16 + 1; 2453 if (iterations > 100) 2454 iterations = 100; 2455 } 2456 2457 for (i = 0; 2458 i < iterations && 2459 (dev->gc_dirtiest < 1 || 2460 dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); 2461 i++) { 2462 dev->gc_block_finder++; 2463 if (dev->gc_block_finder < dev->internal_start_block || 2464 dev->gc_block_finder > dev->internal_end_block) 2465 dev->gc_block_finder = dev->internal_start_block; 2466 2467 bi = yaffs_get_block_info(dev, dev->gc_block_finder); 2468 2469 pagesUsed = bi->pages_in_use - bi->soft_del_pages; 2470 2471 if (bi->block_state == YAFFS_BLOCK_STATE_FULL && 2472 pagesUsed < dev->param.chunks_per_block && 2473 (dev->gc_dirtiest < 1 || pagesUsed < dev->gc_pages_in_use) && 2474 yaffs_block_ok_for_gc(dev, bi)) { 2475 dev->gc_dirtiest = dev->gc_block_finder; 2476 dev->gc_pages_in_use = pagesUsed; 2477 } 2478 } 2479 2480 if(dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) 2481 selected = dev->gc_dirtiest; 2482 } 2483 2484 /* 2485 * If nothing has been selected for a while, try selecting the oldest dirty 2486 * because that's gumming up the works. 2487 */ 2488 2489 if(!selected && dev->param.is_yaffs2 && 2490 dev->gc_not_done >= ( background ? 10 : 20)){ 2491 yaffs2_find_oldest_dirty_seq(dev); 2492 if(dev->oldest_dirty_block > 0) { 2493 selected = dev->oldest_dirty_block; 2494 dev->gc_dirtiest = selected; 2495 dev->oldest_dirty_gc_count++; 2496 bi = yaffs_get_block_info(dev, selected); 2497 dev->gc_pages_in_use = bi->pages_in_use - bi->soft_del_pages; 2498 } else 2499 dev->gc_not_done = 0; 2500 } 2501 2502 if(selected){ 2503 T(YAFFS_TRACE_GC, 2504 (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), 2505 selected, 2506 dev->param.chunks_per_block - dev->gc_pages_in_use, 2507 prioritised)); 2508 2509 dev->n_gc_blocks++; 2510 if(background) 2511 dev->bg_gcs++; 2512 2513 dev->gc_dirtiest = 0; 2514 dev->gc_pages_in_use = 0; 2515 dev->gc_not_done = 0; 2516 if(dev->refresh_skip > 0) 2517 dev->refresh_skip--; 2518 } else{ 2519 dev->gc_not_done++; 2520 T(YAFFS_TRACE_GC, 2521 (TSTR("GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s" TENDSTR), 2522 dev->gc_block_finder, dev->gc_not_done, 2523 threshold, 2524 dev->gc_dirtiest, dev->gc_pages_in_use, 2525 dev->oldest_dirty_block, 2526 background ? " bg" : "")); 2527 } 2528 2529 return selected; 2530} 2531 2532/* New garbage collector 2533 * If we're very low on erased blocks then we do aggressive garbage collection 2534 * otherwise we do "leasurely" garbage collection. 2535 * Aggressive gc looks further (whole array) and will accept less dirty blocks. 2536 * Passive gc only inspects smaller areas and will only accept more dirty blocks. 2537 * 2538 * The idea is to help clear out space in a more spread-out manner. 2539 * Dunno if it really does anything useful. 2540 */ 2541static int yaffs_check_gc(yaffs_dev_t *dev, int background) 2542{ 2543 int aggressive = 0; 2544 int gcOk = YAFFS_OK; 2545 int maxTries = 0; 2546 int minErased; 2547 int erasedChunks; 2548 int checkpointBlockAdjust; 2549 2550 if(dev->param.gc_control && 2551 (dev->param.gc_control(dev) & 1) == 0) 2552 return YAFFS_OK; 2553 2554 if (dev->gc_disable) { 2555 /* Bail out so we don't get recursive gc */ 2556 return YAFFS_OK; 2557 } 2558 2559 /* This loop should pass the first time. 2560 * We'll only see looping here if the collection does not increase space. 2561 */ 2562 2563 do { 2564 maxTries++; 2565 2566 checkpointBlockAdjust = yaffs_calc_checkpt_blocks_required(dev); 2567 2568 minErased = dev->param.n_reserved_blocks + checkpointBlockAdjust + 1; 2569 erasedChunks = dev->n_erased_blocks * dev->param.chunks_per_block; 2570 2571 /* If we need a block soon then do aggressive gc.*/ 2572 if (dev->n_erased_blocks < minErased) 2573 aggressive = 1; 2574 else { 2575 if(!background && erasedChunks > (dev->n_free_chunks / 4)) 2576 break; 2577 2578 if(dev->gc_skip > 20) 2579 dev->gc_skip = 20; 2580 if(erasedChunks < dev->n_free_chunks/2 || 2581 dev->gc_skip < 1 || 2582 background) 2583 aggressive = 0; 2584 else { 2585 dev->gc_skip--; 2586 break; 2587 } 2588 } 2589 2590 dev->gc_skip = 5; 2591 2592 /* If we don't already have a block being gc'd then see if we should start another */ 2593 2594 if (dev->gc_block < 1 && !aggressive) { 2595 dev->gc_block = yaffs2_find_refresh_block(dev); 2596 dev->gc_chunk = 0; 2597 dev->n_clean_ups=0; 2598 } 2599 if (dev->gc_block < 1) { 2600 dev->gc_block = yaffs_find_gc_block(dev, aggressive, background); 2601 dev->gc_chunk = 0; 2602 dev->n_clean_ups=0; 2603 } 2604 2605 if (dev->gc_block > 0) { 2606 dev->all_gcs++; 2607 if (!aggressive) 2608 dev->passive_gc_count++; 2609 2610 T(YAFFS_TRACE_GC, 2611 (TSTR 2612 ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR), 2613 dev->n_erased_blocks, aggressive)); 2614 2615 gcOk = yaffs_gc_block(dev, dev->gc_block, aggressive); 2616 } 2617 2618 if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && dev->gc_block > 0) { 2619 T(YAFFS_TRACE_GC, 2620 (TSTR 2621 ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" 2622 TENDSTR), dev->n_erased_blocks, maxTries, dev->gc_block)); 2623 } 2624 } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && 2625 (dev->gc_block > 0) && 2626 (maxTries < 2)); 2627 2628 return aggressive ? gcOk : YAFFS_OK; 2629} 2630 2631/* 2632 * yaffs_bg_gc() 2633 * Garbage collects. Intended to be called from a background thread. 2634 * Returns non-zero if at least half the free chunks are erased. 2635 */ 2636int yaffs_bg_gc(yaffs_dev_t *dev, unsigned urgency) 2637{ 2638 int erasedChunks = dev->n_erased_blocks * dev->param.chunks_per_block; 2639 2640 T(YAFFS_TRACE_BACKGROUND, (TSTR("Background gc %u" TENDSTR),urgency)); 2641 2642 yaffs_check_gc(dev, 1); 2643 return erasedChunks > dev->n_free_chunks/2; 2644} 2645 2646/*------------------------- TAGS --------------------------------*/ 2647 2648static int yaffs_tags_match(const yaffs_ext_tags *tags, int obj_id, 2649 int chunkInObject) 2650{ 2651 return (tags->chunk_id == chunkInObject && 2652 tags->obj_id == obj_id && !tags->is_deleted) ? 1 : 0; 2653 2654} 2655 2656 2657/*-------------------- Data file manipulation -----------------*/ 2658 2659static int yaffs_find_chunk_in_file(yaffs_obj_t *in, int inode_chunk, 2660 yaffs_ext_tags *tags) 2661{ 2662 /*Get the Tnode, then get the level 0 offset chunk offset */ 2663 yaffs_tnode_t *tn; 2664 int theChunk = -1; 2665 yaffs_ext_tags localTags; 2666 int retVal = -1; 2667 2668 yaffs_dev_t *dev = in->my_dev; 2669 2670 if (!tags) { 2671 /* Passed a NULL, so use our own tags space */ 2672 tags = &localTags; 2673 } 2674 2675 tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); 2676 2677 if (tn) { 2678 theChunk = yaffs_get_group_base(dev, tn, inode_chunk); 2679 2680 retVal = 2681 yaffs_find_chunk_in_group(dev, theChunk, tags, in->obj_id, 2682 inode_chunk); 2683 } 2684 return retVal; 2685} 2686 2687static int yaffs_find_del_file_chunk(yaffs_obj_t *in, int inode_chunk, 2688 yaffs_ext_tags *tags) 2689{ 2690 /* Get the Tnode, then get the level 0 offset chunk offset */ 2691 yaffs_tnode_t *tn; 2692 int theChunk = -1; 2693 yaffs_ext_tags localTags; 2694 2695 yaffs_dev_t *dev = in->my_dev; 2696 int retVal = -1; 2697 2698 if (!tags) { 2699 /* Passed a NULL, so use our own tags space */ 2700 tags = &localTags; 2701 } 2702 2703 tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); 2704 2705 if (tn) { 2706 2707 theChunk = yaffs_get_group_base(dev, tn, inode_chunk); 2708 2709 retVal = 2710 yaffs_find_chunk_in_group(dev, theChunk, tags, in->obj_id, 2711 inode_chunk); 2712 2713 /* Delete the entry in the filestructure (if found) */ 2714 if (retVal != -1) 2715 yaffs_load_tnode_0(dev, tn, inode_chunk, 0); 2716 } 2717 2718 return retVal; 2719} 2720 2721 2722int yaffs_put_chunk_in_file(yaffs_obj_t *in, int inode_chunk, 2723 int nand_chunk, int in_scan) 2724{ 2725 /* NB in_scan is zero unless scanning. 2726 * For forward scanning, in_scan is > 0; 2727 * for backward scanning in_scan is < 0 2728 * 2729 * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. 2730 */ 2731 2732 yaffs_tnode_t *tn; 2733 yaffs_dev_t *dev = in->my_dev; 2734 int existingChunk; 2735 yaffs_ext_tags existingTags; 2736 yaffs_ext_tags newTags; 2737 unsigned existingSerial, newSerial; 2738 2739 if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { 2740 /* Just ignore an attempt at putting a chunk into a non-file during scanning 2741 * If it is not during Scanning then something went wrong! 2742 */ 2743 if (!in_scan) { 2744 T(YAFFS_TRACE_ERROR, 2745 (TSTR 2746 ("yaffs tragedy:attempt to put data chunk into a non-file" 2747 TENDSTR))); 2748 YBUG(); 2749 } 2750 2751 yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); 2752 return YAFFS_OK; 2753 } 2754 2755 tn = yaffs_add_find_tnode_0(dev, 2756 &in->variant.file_variant, 2757 inode_chunk, 2758 NULL); 2759 if (!tn) 2760 return YAFFS_FAIL; 2761 2762 if(!nand_chunk) 2763 /* Dummy insert, bail now */ 2764 return YAFFS_OK; 2765 2766 existingChunk = yaffs_get_group_base(dev, tn, inode_chunk); 2767 2768 if (in_scan != 0) { 2769 /* If we're scanning then we need to test for duplicates 2770 * NB This does not need to be efficient since it should only ever 2771 * happen when the power fails during a write, then only one 2772 * chunk should ever be affected. 2773 * 2774 * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO 2775 * Update: For backward scanning we don't need to re-read tags so this is quite cheap. 2776 */ 2777 2778 if (existingChunk > 0) { 2779 /* NB Right now existing chunk will not be real chunk_id if the chunk group size > 1 2780 * thus we have to do a FindChunkInFile to get the real chunk id. 2781 * 2782 * We have a duplicate now we need to decide which one to use: 2783 * 2784 * Backwards scanning YAFFS2: The old one is what we use, dump the new one. 2785 * Forward scanning YAFFS2: The new one is what we use, dump the old one. 2786 * YAFFS1: Get both sets of tags and compare serial numbers. 2787 */ 2788 2789 if (in_scan > 0) { 2790 /* Only do this for forward scanning */ 2791 yaffs_rd_chunk_tags_nand(dev, 2792 nand_chunk, 2793 NULL, &newTags); 2794 2795 /* Do a proper find */ 2796 existingChunk = 2797 yaffs_find_chunk_in_file(in, inode_chunk, 2798 &existingTags); 2799 } 2800 2801 if (existingChunk <= 0) { 2802 /*Hoosterman - how did this happen? */ 2803 2804 T(YAFFS_TRACE_ERROR, 2805 (TSTR 2806 ("yaffs tragedy: existing chunk < 0 in scan" 2807 TENDSTR))); 2808 2809 } 2810 2811 /* NB The deleted flags should be false, otherwise the chunks will 2812 * not be loaded during a scan 2813 */ 2814 2815 if (in_scan > 0) { 2816 newSerial = newTags.serial_number; 2817 existingSerial = existingTags.serial_number; 2818 } 2819 2820 if ((in_scan > 0) && 2821 (existingChunk <= 0 || 2822 ((existingSerial + 1) & 3) == newSerial)) { 2823 /* Forward scanning. 2824 * Use new 2825 * Delete the old one and drop through to update the tnode 2826 */ 2827 yaffs_chunk_del(dev, existingChunk, 1, 2828 __LINE__); 2829 } else { 2830 /* Backward scanning or we want to use the existing one 2831 * Use existing. 2832 * Delete the new one and return early so that the tnode isn't changed 2833 */ 2834 yaffs_chunk_del(dev, nand_chunk, 1, 2835 __LINE__); 2836 return YAFFS_OK; 2837 } 2838 } 2839 2840 } 2841 2842 if (existingChunk == 0) 2843 in->n_data_chunks++; 2844 2845 yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); 2846 2847 return YAFFS_OK; 2848} 2849 2850static int yaffs_rd_data_obj(yaffs_obj_t *in, int inode_chunk, 2851 __u8 *buffer) 2852{ 2853 int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); 2854 2855 if (nand_chunk >= 0) 2856 return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, 2857 buffer, NULL); 2858 else { 2859 T(YAFFS_TRACE_NANDACCESS, 2860 (TSTR("Chunk %d not found zero instead" TENDSTR), 2861 nand_chunk)); 2862 /* get sane (zero) data if you read a hole */ 2863 memset(buffer, 0, in->my_dev->data_bytes_per_chunk); 2864 return 0; 2865 } 2866 2867} 2868 2869void yaffs_chunk_del(yaffs_dev_t *dev, int chunk_id, int mark_flash, int lyn) 2870{ 2871 int block; 2872 int page; 2873 yaffs_ext_tags tags; 2874 yaffs_block_info_t *bi; 2875 2876 if (chunk_id <= 0) 2877 return; 2878 2879 dev->n_deletions++; 2880 block = chunk_id / dev->param.chunks_per_block; 2881 page = chunk_id % dev->param.chunks_per_block; 2882 2883 2884 if (!yaffs_check_chunk_bit(dev, block, page)) 2885 T(YAFFS_TRACE_VERIFY, 2886 (TSTR("Deleting invalid chunk %d"TENDSTR), 2887 chunk_id)); 2888 2889 bi = yaffs_get_block_info(dev, block); 2890 2891 yaffs2_update_oldest_dirty_seq(dev, block, bi); 2892 2893 T(YAFFS_TRACE_DELETION, 2894 (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunk_id)); 2895 2896 if (!dev->param.is_yaffs2 && mark_flash && 2897 bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { 2898 2899 yaffs_init_tags(&tags); 2900 2901 tags.is_deleted = 1; 2902 2903 yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); 2904 yaffs_handle_chunk_update(dev, chunk_id, &tags); 2905 } else { 2906 dev->n_unmarked_deletions++; 2907 } 2908 2909 /* Pull out of the management area. 2910 * If the whole block became dirty, this will kick off an erasure. 2911 */ 2912 if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || 2913 bi->block_state == YAFFS_BLOCK_STATE_FULL || 2914 bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || 2915 bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { 2916 dev->n_free_chunks++; 2917 2918 yaffs_clear_chunk_bit(dev, block, page); 2919 2920 bi->pages_in_use--; 2921 2922 if (bi->pages_in_use == 0 && 2923 !bi->has_shrink_hdr && 2924 bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && 2925 bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { 2926 yaffs_block_became_dirty(dev, block); 2927 } 2928 2929 } 2930 2931} 2932 2933static int yaffs_wr_data_obj(yaffs_obj_t *in, int inode_chunk, 2934 const __u8 *buffer, int n_bytes, 2935 int useReserve) 2936{ 2937 /* Find old chunk Need to do this to get serial number 2938 * Write new one and patch into tree. 2939 * Invalidate old tags. 2940 */ 2941 2942 int prevChunkId; 2943 yaffs_ext_tags prevTags; 2944 2945 int newChunkId; 2946 yaffs_ext_tags newTags; 2947 2948 yaffs_dev_t *dev = in->my_dev; 2949 2950 yaffs_check_gc(dev,0); 2951 2952 /* Get the previous chunk at this location in the file if it exists. 2953 * If it does not exist then put a zero into the tree. This creates 2954 * the tnode now, rather than later when it is harder to clean up. 2955 */ 2956 prevChunkId = yaffs_find_chunk_in_file(in, inode_chunk, &prevTags); 2957 if(prevChunkId < 1 && 2958 !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) 2959 return 0; 2960 2961 /* Set up new tags */ 2962 yaffs_init_tags(&newTags); 2963 2964 newTags.chunk_id = inode_chunk; 2965 newTags.obj_id = in->obj_id; 2966 newTags.serial_number = 2967 (prevChunkId > 0) ? prevTags.serial_number + 1 : 1; 2968 newTags.n_bytes = n_bytes; 2969 2970 if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { 2971 T(YAFFS_TRACE_ERROR, 2972 (TSTR("Writing %d bytes to chunk!!!!!!!!!" TENDSTR), n_bytes)); 2973 YBUG(); 2974 } 2975 2976 2977 newChunkId = 2978 yaffs_write_new_chunk(dev, buffer, &newTags, 2979 useReserve); 2980 2981 if (newChunkId > 0) { 2982 yaffs_put_chunk_in_file(in, inode_chunk, newChunkId, 0); 2983 2984 if (prevChunkId > 0) 2985 yaffs_chunk_del(dev, prevChunkId, 1, __LINE__); 2986 2987 yaffs_verify_file_sane(in); 2988 } 2989 return newChunkId; 2990 2991} 2992 2993/* UpdateObjectHeader updates the header on NAND for an object. 2994 * If name is not NULL, then that new name is used. 2995 */ 2996int yaffs_update_oh(yaffs_obj_t *in, const YCHAR *name, int force, 2997 int is_shrink, int shadows, yaffs_xattr_mod *xmod) 2998{ 2999 3000 yaffs_block_info_t *bi; 3001 3002 yaffs_dev_t *dev = in->my_dev; 3003 3004 int prevChunkId; 3005 int retVal = 0; 3006 int result = 0; 3007 3008 int newChunkId; 3009 yaffs_ext_tags newTags; 3010 yaffs_ext_tags oldTags; 3011 const YCHAR *alias = NULL; 3012 3013 __u8 *buffer = NULL; 3014 YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; 3015 3016 yaffs_obj_header *oh = NULL; 3017 3018 yaffs_strcpy(old_name, _Y("silly old name")); 3019 3020 3021 if (!in->fake || 3022 in == dev->root_dir || /* The root_dir should also be saved */ 3023 force || xmod) { 3024 3025 yaffs_check_gc(dev,0); 3026 yaffs_check_obj_details_loaded(in); 3027 3028 buffer = yaffs_get_temp_buffer(in->my_dev, __LINE__); 3029 oh = (yaffs_obj_header *) buffer; 3030 3031 prevChunkId = in->hdr_chunk; 3032 3033 if (prevChunkId > 0) { 3034 result = yaffs_rd_chunk_tags_nand(dev, prevChunkId, 3035 buffer, &oldTags); 3036 3037 yaffs_verify_oh(in, oh, &oldTags, 0); 3038 3039 memcpy(old_name, oh->name, sizeof(oh->name)); 3040 memset(buffer, 0xFF, sizeof(yaffs_obj_header)); 3041 } else 3042 memset(buffer, 0xFF, dev->data_bytes_per_chunk); 3043 3044 oh->type = in->variant_type; 3045 oh->yst_mode = in->yst_mode; 3046 oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; 3047 3048#ifdef CONFIG_YAFFS_WINCE 3049 oh->win_atime[0] = in->win_atime[0]; 3050 oh->win_ctime[0] = in->win_ctime[0]; 3051 oh->win_mtime[0] = in->win_mtime[0]; 3052 oh->win_atime[1] = in->win_atime[1]; 3053 oh->win_ctime[1] = in->win_ctime[1]; 3054 oh->win_mtime[1] = in->win_mtime[1]; 3055#else 3056 oh->yst_uid = in->yst_uid; 3057 oh->yst_gid = in->yst_gid; 3058 oh->yst_atime = in->yst_atime; 3059 oh->yst_mtime = in->yst_mtime; 3060 oh->yst_ctime = in->yst_ctime; 3061 oh->yst_rdev = in->yst_rdev; 3062#endif 3063 if (in->parent) 3064 oh->parent_obj_id = in->parent->obj_id; 3065 else 3066 oh->parent_obj_id = 0; 3067 3068 if (name && *name) { 3069 memset(oh->name, 0, sizeof(oh->name)); 3070 yaffs_load_oh_from_name(dev,oh->name,name); 3071 } else if (prevChunkId > 0) 3072 memcpy(oh->name, old_name, sizeof(oh->name)); 3073 else 3074 memset(oh->name, 0, sizeof(oh->name)); 3075 3076 oh->is_shrink = is_shrink; 3077 3078 switch (in->variant_type) { 3079 case YAFFS_OBJECT_TYPE_UNKNOWN: 3080 /* Should not happen */ 3081 break; 3082 case YAFFS_OBJECT_TYPE_FILE: 3083 oh->file_size = 3084 (oh->parent_obj_id == YAFFS_OBJECTID_DELETED 3085 || oh->parent_obj_id == 3086 YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant. 3087 file_variant.file_size; 3088 break; 3089 case YAFFS_OBJECT_TYPE_HARDLINK: 3090 oh->equiv_id = 3091 in->variant.hardlink_variant.equiv_id; 3092 break; 3093 case YAFFS_OBJECT_TYPE_SPECIAL: 3094 /* Do nothing */ 3095 break; 3096 case YAFFS_OBJECT_TYPE_DIRECTORY: 3097 /* Do nothing */ 3098 break; 3099 case YAFFS_OBJECT_TYPE_SYMLINK: 3100 alias = in->variant.symlink_variant.alias; 3101 if(!alias) 3102 alias = _Y("no alias"); 3103 yaffs_strncpy(oh->alias, 3104 alias, 3105 YAFFS_MAX_ALIAS_LENGTH); 3106 oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; 3107 break; 3108 } 3109 3110 /* process any xattrib modifications */ 3111 if(xmod) 3112 yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); 3113 3114 3115 /* Tags */ 3116 yaffs_init_tags(&newTags); 3117 in->serial++; 3118 newTags.chunk_id = 0; 3119 newTags.obj_id = in->obj_id; 3120 newTags.serial_number = in->serial; 3121 3122 /* Add extra info for file header */ 3123 3124 newTags.extra_available = 1; 3125 newTags.extra_parent_id = oh->parent_obj_id; 3126 newTags.extra_length = oh->file_size; 3127 newTags.extra_is_shrink = oh->is_shrink; 3128 newTags.extra_equiv_id = oh->equiv_id; 3129 newTags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; 3130 newTags.extra_obj_type = in->variant_type; 3131 3132 yaffs_verify_oh(in, oh, &newTags, 1); 3133 3134 /* Create new chunk in NAND */ 3135 newChunkId = 3136 yaffs_write_new_chunk(dev, buffer, &newTags, 3137 (prevChunkId > 0) ? 1 : 0); 3138 3139 if (newChunkId >= 0) { 3140 3141 in->hdr_chunk = newChunkId; 3142 3143 if (prevChunkId > 0) { 3144 yaffs_chunk_del(dev, prevChunkId, 1, 3145 __LINE__); 3146 } 3147 3148 if (!yaffs_obj_cache_dirty(in)) 3149 in->dirty = 0; 3150 3151 /* If this was a shrink, then mark the block that the chunk lives on */ 3152 if (is_shrink) { 3153 bi = yaffs_get_block_info(in->my_dev, 3154 newChunkId / in->my_dev->param.chunks_per_block); 3155 bi->has_shrink_hdr = 1; 3156 } 3157 3158 } 3159 3160 retVal = newChunkId; 3161 3162 } 3163 3164 if (buffer) 3165 yaffs_release_temp_buffer(dev, buffer, __LINE__); 3166 3167 return retVal; 3168} 3169 3170/*------------------------ Short Operations Cache ---------------------------------------- 3171 * In many situations where there is no high level buffering (eg WinCE) a lot of 3172 * reads might be short sequential reads, and a lot of writes may be short 3173 * sequential writes. eg. scanning/writing a jpeg file. 3174 * In these cases, a short read/write cache can provide a huge perfomance benefit 3175 * with dumb-as-a-rock code. 3176 * In Linux, the page cache provides read buffering aand the short op cache provides write 3177 * buffering. 3178 * 3179 * There are a limited number (~10) of cache chunks per device so that we don't 3180 * need a very intelligent search. 3181 */ 3182 3183static int yaffs_obj_cache_dirty(yaffs_obj_t *obj) 3184{ 3185 yaffs_dev_t *dev = obj->my_dev; 3186 int i; 3187 yaffs_cache_t *cache; 3188 int nCaches = obj->my_dev->param.n_caches; 3189 3190 for (i = 0; i < nCaches; i++) { 3191 cache = &dev->cache[i]; 3192 if (cache->object == obj && 3193 cache->dirty) 3194 return 1; 3195 } 3196 3197 return 0; 3198} 3199 3200 3201static void yaffs_flush_file_cache(yaffs_obj_t *obj) 3202{ 3203 yaffs_dev_t *dev = obj->my_dev; 3204 int lowest = -99; /* Stop compiler whining. */ 3205 int i; 3206 yaffs_cache_t *cache; 3207 int chunkWritten = 0; 3208 int nCaches = obj->my_dev->param.n_caches; 3209 3210 if (nCaches > 0) { 3211 do { 3212 cache = NULL; 3213 3214 /* Find the dirty cache for this object with the lowest chunk id. */ 3215 for (i = 0; i < nCaches; i++) { 3216 if (dev->cache[i].object == obj && 3217 dev->cache[i].dirty) { 3218 if (!cache 3219 || dev->cache[i].chunk_id < 3220 lowest) { 3221 cache = &dev->cache[i]; 3222 lowest = cache->chunk_id; 3223 } 3224 } 3225 } 3226 3227 if (cache && !cache->locked) { 3228 /* Write it out and free it up */ 3229 3230 chunkWritten = 3231 yaffs_wr_data_obj(cache->object, 3232 cache->chunk_id, 3233 cache->data, 3234 cache->n_bytes, 3235 1); 3236 cache->dirty = 0; 3237 cache->object = NULL; 3238 } 3239 3240 } while (cache && chunkWritten > 0); 3241 3242 if (cache) { 3243 /* Hoosterman, disk full while writing cache out. */ 3244 T(YAFFS_TRACE_ERROR, 3245 (TSTR("yaffs tragedy: no space during cache write" TENDSTR))); 3246 3247 } 3248 } 3249 3250} 3251 3252/*yaffs_flush_whole_cache(dev) 3253 * 3254 * 3255 */ 3256 3257void yaffs_flush_whole_cache(yaffs_dev_t *dev) 3258{ 3259 yaffs_obj_t *obj; 3260 int nCaches = dev->param.n_caches; 3261 int i; 3262 3263 /* Find a dirty object in the cache and flush it... 3264 * until there are no further dirty objects. 3265 */ 3266 do { 3267 obj = NULL; 3268 for (i = 0; i < nCaches && !obj; i++) { 3269 if (dev->cache[i].object && 3270 dev->cache[i].dirty) 3271 obj = dev->cache[i].object; 3272 3273 } 3274 if (obj) 3275 yaffs_flush_file_cache(obj); 3276 3277 } while (obj); 3278 3279} 3280 3281 3282/* Grab us a cache chunk for use. 3283 * First look for an empty one. 3284 * Then look for the least recently used non-dirty one. 3285 * Then look for the least recently used dirty one...., flush and look again. 3286 */ 3287static yaffs_cache_t *yaffs_grab_chunk_worker(yaffs_dev_t *dev) 3288{ 3289 int i; 3290 3291 if (dev->param.n_caches > 0) { 3292 for (i = 0; i < dev->param.n_caches; i++) { 3293 if (!dev->cache[i].object) 3294 return &dev->cache[i]; 3295 } 3296 } 3297 3298 return NULL; 3299} 3300 3301static yaffs_cache_t *yaffs_grab_chunk_cache(yaffs_dev_t *dev) 3302{ 3303 yaffs_cache_t *cache; 3304 yaffs_obj_t *theObj; 3305 int usage; 3306 int i; 3307 int pushout; 3308 3309 if (dev->param.n_caches > 0) { 3310 /* Try find a non-dirty one... */ 3311 3312 cache = yaffs_grab_chunk_worker(dev); 3313 3314 if (!cache) { 3315 /* They were all dirty, find the last recently used object and flush 3316 * its cache, then find again. 3317 * NB what's here is not very accurate, we actually flush the object 3318 * the last recently used page. 3319 */ 3320 3321 /* With locking we can't assume we can use entry zero */ 3322 3323 theObj = NULL; 3324 usage = -1; 3325 cache = NULL; 3326 pushout = -1; 3327 3328 for (i = 0; i < dev->param.n_caches; i++) { 3329 if (dev->cache[i].object && 3330 !dev->cache[i].locked && 3331 (dev->cache[i].last_use < usage || !cache)) { 3332 usage = dev->cache[i].last_use; 3333 theObj = dev->cache[i].object; 3334 cache = &dev->cache[i]; 3335 pushout = i; 3336 } 3337 } 3338 3339 if (!cache || cache->dirty) { 3340 /* Flush and try again */ 3341 yaffs_flush_file_cache(theObj); 3342 cache = yaffs_grab_chunk_worker(dev); 3343 } 3344 3345 } 3346 return cache; 3347 } else 3348 return NULL; 3349 3350} 3351 3352/* Find a cached chunk */ 3353static yaffs_cache_t *yaffs_find_chunk_cache(const yaffs_obj_t *obj, 3354 int chunk_id) 3355{ 3356 yaffs_dev_t *dev = obj->my_dev; 3357 int i; 3358 if (dev->param.n_caches > 0) { 3359 for (i = 0; i < dev->param.n_caches; i++) { 3360 if (dev->cache[i].object == obj && 3361 dev->cache[i].chunk_id == chunk_id) { 3362 dev->cache_hits++; 3363 3364 return &dev->cache[i]; 3365 } 3366 } 3367 } 3368 return NULL; 3369} 3370 3371/* Mark the chunk for the least recently used algorithym */ 3372static void yaffs_use_cache(yaffs_dev_t *dev, yaffs_cache_t *cache, 3373 int isAWrite) 3374{ 3375 3376 if (dev->param.n_caches > 0) { 3377 if (dev->cache_last_use < 0 || dev->cache_last_use > 100000000) { 3378 /* Reset the cache usages */ 3379 int i; 3380 for (i = 1; i < dev->param.n_caches; i++) 3381 dev->cache[i].last_use = 0; 3382 3383 dev->cache_last_use = 0; 3384 } 3385 3386 dev->cache_last_use++; 3387 3388 cache->last_use = dev->cache_last_use; 3389 3390 if (isAWrite) 3391 cache->dirty = 1; 3392 } 3393} 3394 3395/* Invalidate a single cache page. 3396 * Do this when a whole page gets written, 3397 * ie the short cache for this page is no longer valid. 3398 */ 3399static void yaffs_invalidate_chunk_cache(yaffs_obj_t *object, int chunk_id) 3400{ 3401 if (object->my_dev->param.n_caches > 0) { 3402 yaffs_cache_t *cache = yaffs_find_chunk_cache(object, chunk_id); 3403 3404 if (cache) 3405 cache->object = NULL; 3406 } 3407} 3408 3409/* Invalidate all the cache pages associated with this object 3410 * Do this whenever ther file is deleted or resized. 3411 */ 3412static void yaffs_invalidate_whole_cache(yaffs_obj_t *in) 3413{ 3414 int i; 3415 yaffs_dev_t *dev = in->my_dev; 3416 3417 if (dev->param.n_caches > 0) { 3418 /* Invalidate it. */ 3419 for (i = 0; i < dev->param.n_caches; i++) { 3420 if (dev->cache[i].object == in) 3421 dev->cache[i].object = NULL; 3422 } 3423 } 3424} 3425 3426 3427/*--------------------- File read/write ------------------------ 3428 * Read and write have very similar structures. 3429 * In general the read/write has three parts to it 3430 * An incomplete chunk to start with (if the read/write is not chunk-aligned) 3431 * Some complete chunks 3432 * An incomplete chunk to end off with 3433 * 3434 * Curve-balls: the first chunk might also be the last chunk. 3435 */ 3436 3437int yaffs_file_rd(yaffs_obj_t *in, __u8 *buffer, loff_t offset, 3438 int n_bytes) 3439{ 3440 3441 int chunk; 3442 __u32 start; 3443 int nToCopy; 3444 int n = n_bytes; 3445 int nDone = 0; 3446 yaffs_cache_t *cache; 3447 3448 yaffs_dev_t *dev; 3449 3450 dev = in->my_dev; 3451 3452 while (n > 0) { 3453 /* chunk = offset / dev->data_bytes_per_chunk + 1; */ 3454 /* start = offset % dev->data_bytes_per_chunk; */ 3455 yaffs_addr_to_chunk(dev, offset, &chunk, &start); 3456 chunk++; 3457 3458 /* OK now check for the curveball where the start and end are in 3459 * the same chunk. 3460 */ 3461 if ((start + n) < dev->data_bytes_per_chunk) 3462 nToCopy = n; 3463 else 3464 nToCopy = dev->data_bytes_per_chunk - start; 3465 3466 cache = yaffs_find_chunk_cache(in, chunk); 3467 3468 /* If the chunk is already in the cache or it is less than a whole chunk 3469 * or we're using inband tags then use the cache (if there is caching) 3470 * else bypass the cache. 3471 */ 3472 if (cache || nToCopy != dev->data_bytes_per_chunk || dev->param.inband_tags) { 3473 if (dev->param.n_caches > 0) { 3474 3475 /* If we can't find the data in the cache, then load it up. */ 3476 3477 if (!cache) { 3478 cache = yaffs_grab_chunk_cache(in->my_dev); 3479 cache->object = in; 3480 cache->chunk_id = chunk; 3481 cache->dirty = 0; 3482 cache->locked = 0; 3483 yaffs_rd_data_obj(in, chunk, 3484 cache-> 3485 data); 3486 cache->n_bytes = 0; 3487 } 3488 3489 yaffs_use_cache(dev, cache, 0); 3490 3491 cache->locked = 1; 3492 3493 3494 memcpy(buffer, &cache->data[start], nToCopy); 3495 3496 cache->locked = 0; 3497 } else { 3498 /* Read into the local buffer then copy..*/ 3499 3500 __u8 *localBuffer = 3501 yaffs_get_temp_buffer(dev, __LINE__); 3502 yaffs_rd_data_obj(in, chunk, 3503 localBuffer); 3504 3505 memcpy(buffer, &localBuffer[start], nToCopy); 3506 3507 3508 yaffs_release_temp_buffer(dev, localBuffer, 3509 __LINE__); 3510 } 3511 3512 } else { 3513 3514 /* A full chunk. Read directly into the supplied buffer. */ 3515 yaffs_rd_data_obj(in, chunk, buffer); 3516 3517 } 3518 3519 n -= nToCopy; 3520 offset += nToCopy; 3521 buffer += nToCopy; 3522 nDone += nToCopy; 3523 3524 } 3525 3526 return nDone; 3527} 3528 3529int yaffs_do_file_wr(yaffs_obj_t *in, const __u8 *buffer, loff_t offset, 3530 int n_bytes, int write_trhrough) 3531{ 3532 3533 int chunk; 3534 __u32 start; 3535 int nToCopy; 3536 int n = n_bytes; 3537 int nDone = 0; 3538 int nToWriteBack; 3539 int startOfWrite = offset; 3540 int chunkWritten = 0; 3541 __u32 n_bytesRead; 3542 __u32 chunkStart; 3543 3544 yaffs_dev_t *dev; 3545 3546 dev = in->my_dev; 3547 3548 while (n > 0 && chunkWritten >= 0) { 3549 yaffs_addr_to_chunk(dev, offset, &chunk, &start); 3550 3551 if (chunk * dev->data_bytes_per_chunk + start != offset || 3552 start >= dev->data_bytes_per_chunk) { 3553 T(YAFFS_TRACE_ERROR, ( 3554 TSTR("AddrToChunk of offset %d gives chunk %d start %d" 3555 TENDSTR), 3556 (int)offset, chunk, start)); 3557 } 3558 chunk++; /* File pos to chunk in file offset */ 3559 3560 /* OK now check for the curveball where the start and end are in 3561 * the same chunk. 3562 */ 3563 3564 if ((start + n) < dev->data_bytes_per_chunk) { 3565 nToCopy = n; 3566 3567 /* Now folks, to calculate how many bytes to write back.... 3568 * If we're overwriting and not writing to then end of file then 3569 * we need to write back as much as was there before. 3570 */ 3571 3572 chunkStart = ((chunk - 1) * dev->data_bytes_per_chunk); 3573 3574 if (chunkStart > in->variant.file_variant.file_size) 3575 n_bytesRead = 0; /* Past end of file */ 3576 else 3577 n_bytesRead = in->variant.file_variant.file_size - chunkStart; 3578 3579 if (n_bytesRead > dev->data_bytes_per_chunk) 3580 n_bytesRead = dev->data_bytes_per_chunk; 3581 3582 nToWriteBack = 3583 (n_bytesRead > 3584 (start + n)) ? n_bytesRead : (start + n); 3585 3586 if (nToWriteBack < 0 || nToWriteBack > dev->data_bytes_per_chunk) 3587 YBUG(); 3588 3589 } else { 3590 nToCopy = dev->data_bytes_per_chunk - start; 3591 nToWriteBack = dev->data_bytes_per_chunk; 3592 } 3593 3594 if (nToCopy != dev->data_bytes_per_chunk || dev->param.inband_tags) { 3595 /* An incomplete start or end chunk (or maybe both start and end chunk), 3596 * or we're using inband tags, so we want to use the cache buffers. 3597 */ 3598 if (dev->param.n_caches > 0) { 3599 yaffs_cache_t *cache; 3600 /* If we can't find the data in the cache, then load the cache */ 3601 cache = yaffs_find_chunk_cache(in, chunk); 3602 3603 if (!cache 3604 && yaffs_check_alloc_available(dev, 1)) { 3605 cache = yaffs_grab_chunk_cache(dev); 3606 cache->object = in; 3607 cache->chunk_id = chunk; 3608 cache->dirty = 0; 3609 cache->locked = 0; 3610 yaffs_rd_data_obj(in, chunk, 3611 cache->data); 3612 } else if (cache && 3613 !cache->dirty && 3614 !yaffs_check_alloc_available(dev, 1)) { 3615 /* Drop the cache if it was a read cache item and 3616 * no space check has been made for it. 3617 */ 3618 cache = NULL; 3619 } 3620 3621 if (cache) { 3622 yaffs_use_cache(dev, cache, 1); 3623 cache->locked = 1; 3624 3625 3626 memcpy(&cache->data[start], buffer, 3627 nToCopy); 3628 3629 3630 cache->locked = 0; 3631 cache->n_bytes = nToWriteBack; 3632 3633 if (write_trhrough) { 3634 chunkWritten = 3635 yaffs_wr_data_obj 3636 (cache->object, 3637 cache->chunk_id, 3638 cache->data, cache->n_bytes, 3639 1); 3640 cache->dirty = 0; 3641 } 3642 3643 } else { 3644 chunkWritten = -1; /* fail the write */ 3645 } 3646 } else { 3647 /* An incomplete start or end chunk (or maybe both start and end chunk) 3648 * Read into the local buffer then copy, then copy over and write back. 3649 */ 3650 3651 __u8 *localBuffer = 3652 yaffs_get_temp_buffer(dev, __LINE__); 3653 3654 yaffs_rd_data_obj(in, chunk, 3655 localBuffer); 3656 3657 3658 3659 memcpy(&localBuffer[start], buffer, nToCopy); 3660 3661 chunkWritten = 3662 yaffs_wr_data_obj(in, chunk, 3663 localBuffer, 3664 nToWriteBack, 3665 0); 3666 3667 yaffs_release_temp_buffer(dev, localBuffer, 3668 __LINE__); 3669 3670 } 3671 3672 } else { 3673 /* A full chunk. Write directly from the supplied buffer. */ 3674 3675 3676 3677 chunkWritten = 3678 yaffs_wr_data_obj(in, chunk, buffer, 3679 dev->data_bytes_per_chunk, 3680 0); 3681 3682 /* Since we've overwritten the cached data, we better invalidate it. */ 3683 yaffs_invalidate_chunk_cache(in, chunk); 3684 } 3685 3686 if (chunkWritten >= 0) { 3687 n -= nToCopy; 3688 offset += nToCopy; 3689 buffer += nToCopy; 3690 nDone += nToCopy; 3691 } 3692 3693 } 3694 3695 /* Update file object */ 3696 3697 if ((startOfWrite + nDone) > in->variant.file_variant.file_size) 3698 in->variant.file_variant.file_size = (startOfWrite + nDone); 3699 3700 in->dirty = 1; 3701 3702 return nDone; 3703} 3704 3705int yaffs_wr_file(yaffs_obj_t *in, const __u8 *buffer, loff_t offset, 3706 int n_bytes, int write_trhrough) 3707{ 3708 yaffs2_handle_hole(in,offset); 3709 return yaffs_do_file_wr(in,buffer,offset,n_bytes,write_trhrough); 3710} 3711 3712 3713 3714/* ---------------------- File resizing stuff ------------------ */ 3715 3716static void yaffs_prune_chunks(yaffs_obj_t *in, int new_size) 3717{ 3718 3719 yaffs_dev_t *dev = in->my_dev; 3720 int oldFileSize = in->variant.file_variant.file_size; 3721 3722 int lastDel = 1 + (oldFileSize - 1) / dev->data_bytes_per_chunk; 3723 3724 int startDel = 1 + (new_size + dev->data_bytes_per_chunk - 1) / 3725 dev->data_bytes_per_chunk; 3726 int i; 3727 int chunk_id; 3728 3729 /* Delete backwards so that we don't end up with holes if 3730 * power is lost part-way through the operation. 3731 */ 3732 for (i = lastDel; i >= startDel; i--) { 3733 /* NB this could be optimised somewhat, 3734 * eg. could retrieve the tags and write them without 3735 * using yaffs_chunk_del 3736 */ 3737 3738 chunk_id = yaffs_find_del_file_chunk(in, i, NULL); 3739 if (chunk_id > 0) { 3740 if (chunk_id < 3741 (dev->internal_start_block * dev->param.chunks_per_block) 3742 || chunk_id >= 3743 ((dev->internal_end_block + 3744 1) * dev->param.chunks_per_block)) { 3745 T(YAFFS_TRACE_ALWAYS, 3746 (TSTR("Found daft chunk_id %d for %d" TENDSTR), 3747 chunk_id, i)); 3748 } else { 3749 in->n_data_chunks--; 3750 yaffs_chunk_del(dev, chunk_id, 1, __LINE__); 3751 } 3752 } 3753 } 3754 3755} 3756 3757 3758void yaffs_resize_file_down( yaffs_obj_t *obj, loff_t new_size) 3759{ 3760 int newFullChunks; 3761 __u32 new_sizeOfPartialChunk; 3762 yaffs_dev_t *dev = obj->my_dev; 3763 3764 yaffs_addr_to_chunk(dev, new_size, &newFullChunks, &new_sizeOfPartialChunk); 3765 3766 yaffs_prune_chunks(obj, new_size); 3767 3768 if (new_sizeOfPartialChunk != 0) { 3769 int lastChunk = 1 + newFullChunks; 3770 __u8 *localBuffer = yaffs_get_temp_buffer(dev, __LINE__); 3771 3772 /* Got to read and rewrite the last chunk with its new size and zero pad */ 3773 yaffs_rd_data_obj(obj, lastChunk, localBuffer); 3774 memset(localBuffer + new_sizeOfPartialChunk, 0, 3775 dev->data_bytes_per_chunk - new_sizeOfPartialChunk); 3776 3777 yaffs_wr_data_obj(obj, lastChunk, localBuffer, 3778 new_sizeOfPartialChunk, 1); 3779 3780 yaffs_release_temp_buffer(dev, localBuffer, __LINE__); 3781 } 3782 3783 obj->variant.file_variant.file_size = new_size; 3784 3785 yaffs_prune_tree(dev, &obj->variant.file_variant); 3786} 3787 3788 3789int yaffs_resize_file(yaffs_obj_t *in, loff_t new_size) 3790{ 3791 yaffs_dev_t *dev = in->my_dev; 3792 int oldFileSize = in->variant.file_variant.file_size; 3793 3794 yaffs_flush_file_cache(in); 3795 yaffs_invalidate_whole_cache(in); 3796 3797 yaffs_check_gc(dev,0); 3798 3799 if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) 3800 return YAFFS_FAIL; 3801 3802 if (new_size == oldFileSize) 3803 return YAFFS_OK; 3804 3805 if(new_size > oldFileSize){ 3806 yaffs2_handle_hole(in,new_size); 3807 in->variant.file_variant.file_size = new_size; 3808 } else { 3809 /* new_size < oldFileSize */ 3810 yaffs_resize_file_down(in, new_size); 3811 } 3812 3813 /* Write a new object header to reflect the resize. 3814 * show we've shrunk the file, if need be 3815 * Do this only if the file is not in the deleted directories 3816 * and is not shadowed. 3817 */ 3818 if (in->parent && 3819 !in->is_shadowed && 3820 in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && 3821 in->parent->obj_id != YAFFS_OBJECTID_DELETED) 3822 yaffs_update_oh(in, NULL, 0, 0, 0, NULL); 3823 3824 3825 return YAFFS_OK; 3826} 3827 3828loff_t yaffs_get_file_size(yaffs_obj_t *obj) 3829{ 3830 YCHAR *alias = NULL; 3831 obj = yaffs_get_equivalent_obj(obj); 3832 3833 switch (obj->variant_type) { 3834 case YAFFS_OBJECT_TYPE_FILE: 3835 return obj->variant.file_variant.file_size; 3836 case YAFFS_OBJECT_TYPE_SYMLINK: 3837 alias = obj->variant.symlink_variant.alias; 3838 if(!alias) 3839 return 0; 3840 return yaffs_strnlen(alias,YAFFS_MAX_ALIAS_LENGTH); 3841 default: 3842 return 0; 3843 } 3844} 3845 3846 3847 3848int yaffs_flush_file(yaffs_obj_t *in, int update_time, int data_sync) 3849{ 3850 int retVal; 3851 if (in->dirty) { 3852 yaffs_flush_file_cache(in); 3853 if(data_sync) /* Only sync data */ 3854 retVal=YAFFS_OK; 3855 else { 3856 if (update_time) { 3857#ifdef CONFIG_YAFFS_WINCE 3858 yfsd_win_file_time_now(in->win_mtime); 3859#else 3860 3861 in->yst_mtime = Y_CURRENT_TIME; 3862 3863#endif 3864 } 3865 3866 retVal = (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 3867 0) ? YAFFS_OK : YAFFS_FAIL; 3868 } 3869 } else { 3870 retVal = YAFFS_OK; 3871 } 3872 3873 return retVal; 3874 3875} 3876 3877static int yaffs_generic_obj_del(yaffs_obj_t *in) 3878{ 3879 3880 /* First off, invalidate the file's data in the cache, without flushing. */ 3881 yaffs_invalidate_whole_cache(in); 3882 3883 if (in->my_dev->param.is_yaffs2 && (in->parent != in->my_dev->del_dir)) { 3884 /* Move to the unlinked directory so we have a record that it was deleted. */ 3885 yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, 0); 3886 3887 } 3888 3889 yaffs_remove_obj_from_dir(in); 3890 yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); 3891 in->hdr_chunk = 0; 3892 3893 yaffs_free_obj(in); 3894 return YAFFS_OK; 3895 3896} 3897 3898/* yaffs_del_file deletes the whole file data 3899 * and the inode associated with the file. 3900 * It does not delete the links associated with the file. 3901 */ 3902static int yaffs_unlink_file_if_needed(yaffs_obj_t *in) 3903{ 3904 3905 int retVal; 3906 int immediateDeletion = 0; 3907 yaffs_dev_t *dev = in->my_dev; 3908 3909 if (!in->my_inode) 3910 immediateDeletion = 1; 3911 3912 if (immediateDeletion) { 3913 retVal = 3914 yaffs_change_obj_name(in, in->my_dev->del_dir, 3915 _Y("deleted"), 0, 0); 3916 T(YAFFS_TRACE_TRACING, 3917 (TSTR("yaffs: immediate deletion of file %d" TENDSTR), 3918 in->obj_id)); 3919 in->deleted = 1; 3920 in->my_dev->n_deleted_files++; 3921 if (dev->param.disable_soft_del || dev->param.is_yaffs2) 3922 yaffs_resize_file(in, 0); 3923 yaffs_soft_del_file(in); 3924 } else { 3925 retVal = 3926 yaffs_change_obj_name(in, in->my_dev->unlinked_dir, 3927 _Y("unlinked"), 0, 0); 3928 } 3929 3930 3931 return retVal; 3932} 3933 3934int yaffs_del_file(yaffs_obj_t *in) 3935{ 3936 int retVal = YAFFS_OK; 3937 int deleted; /* Need to cache value on stack if in is freed */ 3938 yaffs_dev_t *dev = in->my_dev; 3939 3940 if (dev->param.disable_soft_del || dev->param.is_yaffs2) 3941 yaffs_resize_file(in, 0); 3942 3943 if (in->n_data_chunks > 0) { 3944 /* Use soft deletion if there is data in the file. 3945 * That won't be the case if it has been resized to zero. 3946 */ 3947 if (!in->unlinked) 3948 retVal = yaffs_unlink_file_if_needed(in); 3949 3950 deleted = in->deleted; 3951 3952 if (retVal == YAFFS_OK && in->unlinked && !in->deleted) { 3953 in->deleted = 1; 3954 deleted = 1; 3955 in->my_dev->n_deleted_files++; 3956 yaffs_soft_del_file(in); 3957 } 3958 return deleted ? YAFFS_OK : YAFFS_FAIL; 3959 } else { 3960 /* The file has no data chunks so we toss it immediately */ 3961 yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); 3962 in->variant.file_variant.top = NULL; 3963 yaffs_generic_obj_del(in); 3964 3965 return YAFFS_OK; 3966 } 3967} 3968 3969static int yaffs_is_non_empty_dir(yaffs_obj_t *obj) 3970{ 3971 return (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && 3972 !(ylist_empty(&obj->variant.dir_variant.children)); 3973} 3974 3975static int yaffs_del_dir(yaffs_obj_t *obj) 3976{ 3977 /* First check that the directory is empty. */ 3978 if (yaffs_is_non_empty_dir(obj)) 3979 return YAFFS_FAIL; 3980 3981 return yaffs_generic_obj_del(obj); 3982} 3983 3984static int yaffs_del_symlink(yaffs_obj_t *in) 3985{ 3986 if(in->variant.symlink_variant.alias) 3987 YFREE(in->variant.symlink_variant.alias); 3988 in->variant.symlink_variant.alias=NULL; 3989 3990 return yaffs_generic_obj_del(in); 3991} 3992 3993static int yaffs_del_link(yaffs_obj_t *in) 3994{ 3995 /* remove this hardlink from the list assocaited with the equivalent 3996 * object 3997 */ 3998 ylist_del_init(&in->hard_links); 3999 return yaffs_generic_obj_del(in); 4000} 4001 4002int yaffs_del_obj(yaffs_obj_t *obj) 4003{ 4004int retVal = -1; 4005 switch (obj->variant_type) { 4006 case YAFFS_OBJECT_TYPE_FILE: 4007 retVal = yaffs_del_file(obj); 4008 break; 4009 case YAFFS_OBJECT_TYPE_DIRECTORY: 4010 if(!ylist_empty(&obj->variant.dir_variant.dirty)){ 4011 T(YAFFS_TRACE_BACKGROUND, (TSTR("Remove object %d from dirty directories" TENDSTR),obj->obj_id)); 4012 ylist_del_init(&obj->variant.dir_variant.dirty); 4013 } 4014 return yaffs_del_dir(obj); 4015 break; 4016 case YAFFS_OBJECT_TYPE_SYMLINK: 4017 retVal = yaffs_del_symlink(obj); 4018 break; 4019 case YAFFS_OBJECT_TYPE_HARDLINK: 4020 retVal = yaffs_del_link(obj); 4021 break; 4022 case YAFFS_OBJECT_TYPE_SPECIAL: 4023 retVal = yaffs_generic_obj_del(obj); 4024 break; 4025 case YAFFS_OBJECT_TYPE_UNKNOWN: 4026 retVal = 0; 4027 break; /* should not happen. */ 4028 } 4029 4030 return retVal; 4031} 4032 4033static int yaffs_unlink_worker(yaffs_obj_t *obj) 4034{ 4035 4036 int immediateDeletion = 0; 4037 4038 if (!obj->my_inode) 4039 immediateDeletion = 1; 4040 4041 if(obj) 4042 yaffs_update_parent(obj->parent); 4043 4044 if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { 4045 return yaffs_del_link(obj); 4046 } else if (!ylist_empty(&obj->hard_links)) { 4047 /* Curve ball: We're unlinking an object that has a hardlink. 4048 * 4049 * This problem arises because we are not strictly following 4050 * The Linux link/inode model. 4051 * 4052 * We can't really delete the object. 4053 * Instead, we do the following: 4054 * - Select a hardlink. 4055 * - Unhook it from the hard links 4056 * - Move it from its parent directory (so that the rename can work) 4057 * - Rename the object to the hardlink's name. 4058 * - Delete the hardlink 4059 */ 4060 4061 yaffs_obj_t *hl; 4062 yaffs_obj_t *parent; 4063 int retVal; 4064 YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; 4065 4066 hl = ylist_entry(obj->hard_links.next, yaffs_obj_t, hard_links); 4067 4068 yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); 4069 parent = hl->parent; 4070 4071 ylist_del_init(&hl->hard_links); 4072 4073 yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); 4074 4075 retVal = yaffs_change_obj_name(obj,parent, name, 0, 0); 4076 4077 if (retVal == YAFFS_OK) 4078 retVal = yaffs_generic_obj_del(hl); 4079 4080 return retVal; 4081 4082 } else if (immediateDeletion) { 4083 switch (obj->variant_type) { 4084 case YAFFS_OBJECT_TYPE_FILE: 4085 return yaffs_del_file(obj); 4086 break; 4087 case YAFFS_OBJECT_TYPE_DIRECTORY: 4088 ylist_del_init(&obj->variant.dir_variant.dirty); 4089 return yaffs_del_dir(obj); 4090 break; 4091 case YAFFS_OBJECT_TYPE_SYMLINK: 4092 return yaffs_del_symlink(obj); 4093 break; 4094 case YAFFS_OBJECT_TYPE_SPECIAL: 4095 return yaffs_generic_obj_del(obj); 4096 break; 4097 case YAFFS_OBJECT_TYPE_HARDLINK: 4098 case YAFFS_OBJECT_TYPE_UNKNOWN: 4099 default: 4100 return YAFFS_FAIL; 4101 } 4102 } else if(yaffs_is_non_empty_dir(obj)) 4103 return YAFFS_FAIL; 4104 else 4105 return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, 4106 _Y("unlinked"), 0, 0); 4107} 4108 4109 4110static int yaffs_unlink_obj(yaffs_obj_t *obj) 4111{ 4112 4113 if (obj && obj->unlink_allowed) 4114 return yaffs_unlink_worker(obj); 4115 4116 return YAFFS_FAIL; 4117 4118} 4119int yaffs_unlinker(yaffs_obj_t *dir, const YCHAR *name) 4120{ 4121 yaffs_obj_t *obj; 4122 4123 obj = yaffs_find_by_name(dir, name); 4124 return yaffs_unlink_obj(obj); 4125} 4126 4127/*----------------------- Initialisation Scanning ---------------------- */ 4128 4129void yaffs_handle_shadowed_obj(yaffs_dev_t *dev, int obj_id, 4130 int backward_scanning) 4131{ 4132 yaffs_obj_t *obj; 4133 4134 if (!backward_scanning) { 4135 /* Handle YAFFS1 forward scanning case 4136 * For YAFFS1 we always do the deletion 4137 */ 4138 4139 } else { 4140 /* Handle YAFFS2 case (backward scanning) 4141 * If the shadowed object exists then ignore. 4142 */ 4143 obj = yaffs_find_by_number(dev, obj_id); 4144 if(obj) 4145 return; 4146 } 4147 4148 /* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. 4149 * We put it in unlinked dir to be cleaned up after the scanning 4150 */ 4151 obj = 4152 yaffs_find_or_create_by_number(dev, obj_id, 4153 YAFFS_OBJECT_TYPE_FILE); 4154 if (!obj) 4155 return; 4156 obj->is_shadowed = 1; 4157 yaffs_add_obj_to_dir(dev->unlinked_dir, obj); 4158 obj->variant.file_variant.shrink_size = 0; 4159 obj->valid = 1; /* So that we don't read any other info for this file */ 4160 4161} 4162 4163 4164void yaffs_link_fixup(yaffs_dev_t *dev, yaffs_obj_t *hard_list) 4165{ 4166 yaffs_obj_t *hl; 4167 yaffs_obj_t *in; 4168 4169 while (hard_list) { 4170 hl = hard_list; 4171 hard_list = (yaffs_obj_t *) (hard_list->hard_links.next); 4172 4173 in = yaffs_find_by_number(dev, 4174 hl->variant.hardlink_variant. 4175 equiv_id); 4176 4177 if (in) { 4178 /* Add the hardlink pointers */ 4179 hl->variant.hardlink_variant.equiv_obj = in; 4180 ylist_add(&hl->hard_links, &in->hard_links); 4181 } else { 4182 /* Todo Need to report/handle this better. 4183 * Got a problem... hardlink to a non-existant object 4184 */ 4185 hl->variant.hardlink_variant.equiv_obj = NULL; 4186 YINIT_LIST_HEAD(&hl->hard_links); 4187 4188 } 4189 } 4190} 4191 4192 4193static void yaffs_strip_deleted_objs(yaffs_dev_t *dev) 4194{ 4195 /* 4196 * Sort out state of unlinked and deleted objects after scanning. 4197 */ 4198 struct ylist_head *i; 4199 struct ylist_head *n; 4200 yaffs_obj_t *l; 4201 4202 if (dev->read_only) 4203 return; 4204 4205 /* Soft delete all the unlinked files */ 4206 ylist_for_each_safe(i, n, 4207 &dev->unlinked_dir->variant.dir_variant.children) { 4208 if (i) { 4209 l = ylist_entry(i, yaffs_obj_t, siblings); 4210 yaffs_del_obj(l); 4211 } 4212 } 4213 4214 ylist_for_each_safe(i, n, 4215 &dev->del_dir->variant.dir_variant.children) { 4216 if (i) { 4217 l = ylist_entry(i, yaffs_obj_t, siblings); 4218 yaffs_del_obj(l); 4219 } 4220 } 4221 4222} 4223 4224/* 4225 * This code iterates through all the objects making sure that they are rooted. 4226 * Any unrooted objects are re-rooted in lost+found. 4227 * An object needs to be in one of: 4228 * - Directly under deleted, unlinked 4229 * - Directly or indirectly under root. 4230 * 4231 * Note: 4232 * This code assumes that we don't ever change the current relationships between 4233 * directories: 4234 * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL 4235 * lostNfound->parent == root_dir 4236 * 4237 * This fixes the problem where directories might have inadvertently been deleted 4238 * leaving the object "hanging" without being rooted in the directory tree. 4239 */ 4240 4241static int yaffs_has_null_parent(yaffs_dev_t *dev, yaffs_obj_t *obj) 4242{ 4243 return (obj == dev->del_dir || 4244 obj == dev->unlinked_dir|| 4245 obj == dev->root_dir); 4246} 4247 4248static void yaffs_fix_hanging_objs(yaffs_dev_t *dev) 4249{ 4250 yaffs_obj_t *obj; 4251 yaffs_obj_t *parent; 4252 int i; 4253 struct ylist_head *lh; 4254 struct ylist_head *n; 4255 int depthLimit; 4256 int hanging; 4257 4258 if (dev->read_only) 4259 return; 4260 4261 /* Iterate through the objects in each hash entry, 4262 * looking at each object. 4263 * Make sure it is rooted. 4264 */ 4265 4266 for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { 4267 ylist_for_each_safe(lh, n, &dev->obj_bucket[i].list) { 4268 if (lh) { 4269 obj = ylist_entry(lh, yaffs_obj_t, hash_link); 4270 parent= obj->parent; 4271 4272 if(yaffs_has_null_parent(dev,obj)){ 4273 /* These directories are not hanging */ 4274 hanging = 0; 4275 } 4276 else if(!parent || parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) 4277 hanging = 1; 4278 else if(yaffs_has_null_parent(dev,parent)) 4279 hanging = 0; 4280 else { 4281 /* 4282 * Need to follow the parent chain to see if it is hanging. 4283 */ 4284 hanging = 0; 4285 depthLimit=100; 4286 4287 while(parent != dev->root_dir && 4288 parent->parent && 4289 parent->parent->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && 4290 depthLimit > 0){ 4291 parent = parent->parent; 4292 depthLimit--; 4293 } 4294 if(parent != dev->root_dir) 4295 hanging = 1; 4296 } 4297 if(hanging){ 4298 T(YAFFS_TRACE_SCAN, 4299 (TSTR("Hanging object %d moved to lost and found" TENDSTR), 4300 obj->obj_id)); 4301 yaffs_add_obj_to_dir(dev->lost_n_found,obj); 4302 } 4303 } 4304 } 4305 } 4306} 4307 4308 4309/* 4310 * Delete directory contents for cleaning up lost and found. 4311 */ 4312static void yaffs_del_dir_contents(yaffs_obj_t *dir) 4313{ 4314 yaffs_obj_t *obj; 4315 struct ylist_head *lh; 4316 struct ylist_head *n; 4317 4318 if(dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) 4319 YBUG(); 4320 4321 ylist_for_each_safe(lh, n, &dir->variant.dir_variant.children) { 4322 if (lh) { 4323 obj = ylist_entry(lh, yaffs_obj_t, siblings); 4324 if(obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) 4325 yaffs_del_dir_contents(obj); 4326 4327 T(YAFFS_TRACE_SCAN, 4328 (TSTR("Deleting lost_found object %d" TENDSTR), 4329 obj->obj_id)); 4330 4331 /* Need to use UnlinkObject since Delete would not handle 4332 * hardlinked objects correctly. 4333 */ 4334 yaffs_unlink_obj(obj); 4335 } 4336 } 4337 4338} 4339 4340static void yaffs_empty_l_n_f(yaffs_dev_t *dev) 4341{ 4342 yaffs_del_dir_contents(dev->lost_n_found); 4343} 4344 4345static void yaffs_check_obj_details_loaded(yaffs_obj_t *in) 4346{ 4347 __u8 *chunkData; 4348 yaffs_obj_header *oh; 4349 yaffs_dev_t *dev; 4350 yaffs_ext_tags tags; 4351 int result; 4352 int alloc_failed = 0; 4353 4354 if (!in) 4355 return; 4356 4357 dev = in->my_dev; 4358 4359#if 0 4360 T(YAFFS_TRACE_SCAN, (TSTR("details for object %d %s loaded" TENDSTR), 4361 in->obj_id, 4362 in->lazy_loaded ? "not yet" : "already")); 4363#endif 4364 4365 if (in->lazy_loaded && in->hdr_chunk > 0) { 4366 in->lazy_loaded = 0; 4367 chunkData = yaffs_get_temp_buffer(dev, __LINE__); 4368 4369 result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, chunkData, &tags); 4370 oh = (yaffs_obj_header *) chunkData; 4371 4372 in->yst_mode = oh->yst_mode; 4373#ifdef CONFIG_YAFFS_WINCE 4374 in->win_atime[0] = oh->win_atime[0]; 4375 in->win_ctime[0] = oh->win_ctime[0]; 4376 in->win_mtime[0] = oh->win_mtime[0]; 4377 in->win_atime[1] = oh->win_atime[1]; 4378 in->win_ctime[1] = oh->win_ctime[1]; 4379 in->win_mtime[1] = oh->win_mtime[1]; 4380#else 4381 in->yst_uid = oh->yst_uid; 4382 in->yst_gid = oh->yst_gid; 4383 in->yst_atime = oh->yst_atime; 4384 in->yst_mtime = oh->yst_mtime; 4385 in->yst_ctime = oh->yst_ctime; 4386 in->yst_rdev = oh->yst_rdev; 4387 4388#endif 4389 yaffs_set_obj_name_from_oh(in, oh); 4390 4391 if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { 4392 in->variant.symlink_variant.alias = 4393 yaffs_clone_str(oh->alias); 4394 if (!in->variant.symlink_variant.alias) 4395 alloc_failed = 1; /* Not returned to caller */ 4396 } 4397 4398 yaffs_release_temp_buffer(dev, chunkData, __LINE__); 4399 } 4400} 4401 4402/*------------------------------ Directory Functions ----------------------------- */ 4403 4404/* 4405 *yaffs_update_parent() handles fixing a directories mtime and ctime when a new 4406 * link (ie. name) is created or deleted in the directory. 4407 * 4408 * ie. 4409 * create dir/a : update dir's mtime/ctime 4410 * rm dir/a: update dir's mtime/ctime 4411 * modify dir/a: don't update dir's mtimme/ctime 4412 * 4413 * This can be handled immediately or defered. Defering helps reduce the number 4414 * of updates when many files in a directory are changed within a brief period. 4415 * 4416 * If the directory updating is defered then yaffs_update_dirty_dirs must be 4417 * called periodically. 4418 */ 4419 4420static void yaffs_update_parent(yaffs_obj_t *obj) 4421{ 4422 yaffs_dev_t *dev; 4423 if(!obj) 4424 return; 4425#ifndef CONFIG_YAFFS_WINCE 4426 4427 dev = obj->my_dev; 4428 obj->dirty = 1; 4429 obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME; 4430 if(dev->param.defered_dir_update){ 4431 struct ylist_head *link = &obj->variant.dir_variant.dirty; 4432 4433 if(ylist_empty(link)){ 4434 ylist_add(link,&dev->dirty_dirs); 4435 T(YAFFS_TRACE_BACKGROUND, (TSTR("Added object %d to dirty directories" TENDSTR),obj->obj_id)); 4436 } 4437 4438 } else 4439 yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); 4440#endif 4441} 4442 4443void yaffs_update_dirty_dirs(yaffs_dev_t *dev) 4444{ 4445 struct ylist_head *link; 4446 yaffs_obj_t *obj; 4447 yaffs_dir_s *dS; 4448 yaffs_obj_variant *oV; 4449 4450 T(YAFFS_TRACE_BACKGROUND, (TSTR("Update dirty directories" TENDSTR))); 4451 4452 while(!ylist_empty(&dev->dirty_dirs)){ 4453 link = dev->dirty_dirs.next; 4454 ylist_del_init(link); 4455 4456 dS=ylist_entry(link,yaffs_dir_s,dirty); 4457 oV = ylist_entry(dS,yaffs_obj_variant,dir_variant); 4458 obj = ylist_entry(oV,yaffs_obj_t,variant); 4459 4460 T(YAFFS_TRACE_BACKGROUND, (TSTR("Update directory %d" TENDSTR), obj->obj_id)); 4461 4462 if(obj->dirty) 4463 yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); 4464 } 4465} 4466 4467static void yaffs_remove_obj_from_dir(yaffs_obj_t *obj) 4468{ 4469 yaffs_dev_t *dev = obj->my_dev; 4470 yaffs_obj_t *parent; 4471 4472 yaffs_verify_obj_in_dir(obj); 4473 parent = obj->parent; 4474 4475 yaffs_verify_dir(parent); 4476 4477 if (dev && dev->param.remove_obj_fn) 4478 dev->param.remove_obj_fn(obj); 4479 4480 4481 ylist_del_init(&obj->siblings); 4482 obj->parent = NULL; 4483 4484 yaffs_verify_dir(parent); 4485} 4486 4487void yaffs_add_obj_to_dir(yaffs_obj_t *directory, 4488 yaffs_obj_t *obj) 4489{ 4490 if (!directory) { 4491 T(YAFFS_TRACE_ALWAYS, 4492 (TSTR 4493 ("tragedy: Trying to add an object to a null pointer directory" 4494 TENDSTR))); 4495 YBUG(); 4496 return; 4497 } 4498 if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { 4499 T(YAFFS_TRACE_ALWAYS, 4500 (TSTR 4501 ("tragedy: Trying to add an object to a non-directory" 4502 TENDSTR))); 4503 YBUG(); 4504 } 4505 4506 if (obj->siblings.prev == NULL) { 4507 /* Not initialised */ 4508 YBUG(); 4509 } 4510 4511 4512 yaffs_verify_dir(directory); 4513 4514 yaffs_remove_obj_from_dir(obj); 4515 4516 4517 /* Now add it */ 4518 ylist_add(&obj->siblings, &directory->variant.dir_variant.children); 4519 obj->parent = directory; 4520 4521 if (directory == obj->my_dev->unlinked_dir 4522 || directory == obj->my_dev->del_dir) { 4523 obj->unlinked = 1; 4524 obj->my_dev->n_unlinked_files++; 4525 obj->rename_allowed = 0; 4526 } 4527 4528 yaffs_verify_dir(directory); 4529 yaffs_verify_obj_in_dir(obj); 4530} 4531 4532yaffs_obj_t *yaffs_find_by_name(yaffs_obj_t *directory, 4533 const YCHAR *name) 4534{ 4535 int sum; 4536 4537 struct ylist_head *i; 4538 YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; 4539 4540 yaffs_obj_t *l; 4541 4542 if (!name) 4543 return NULL; 4544 4545 if (!directory) { 4546 T(YAFFS_TRACE_ALWAYS, 4547 (TSTR 4548 ("tragedy: yaffs_find_by_name: null pointer directory" 4549 TENDSTR))); 4550 YBUG(); 4551 return NULL; 4552 } 4553 if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { 4554 T(YAFFS_TRACE_ALWAYS, 4555 (TSTR 4556 ("tragedy: yaffs_find_by_name: non-directory" TENDSTR))); 4557 YBUG(); 4558 } 4559 4560 sum = yaffs_calc_name_sum(name); 4561 4562 ylist_for_each(i, &directory->variant.dir_variant.children) { 4563 if (i) { 4564 l = ylist_entry(i, yaffs_obj_t, siblings); 4565 4566 if (l->parent != directory) 4567 YBUG(); 4568 4569 yaffs_check_obj_details_loaded(l); 4570 4571 /* Special case for lost-n-found */ 4572 if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { 4573 if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) 4574 return l; 4575 } else if (yaffs_sum_cmp(l->sum, sum) || l->hdr_chunk <= 0) { 4576 /* LostnFound chunk called Objxxx 4577 * Do a real check 4578 */ 4579 yaffs_get_obj_name(l, buffer, 4580 YAFFS_MAX_NAME_LENGTH + 1); 4581 if (yaffs_strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH) == 0) 4582 return l; 4583 } 4584 } 4585 } 4586 4587 return NULL; 4588} 4589 4590 4591#if 0 4592int yaffs_ApplyToDirectoryChildren(yaffs_obj_t *the_dir, 4593 int (*fn) (yaffs_obj_t *)) 4594{ 4595 struct ylist_head *i; 4596 yaffs_obj_t *l; 4597 4598 if (!the_dir) { 4599 T(YAFFS_TRACE_ALWAYS, 4600 (TSTR 4601 ("tragedy: yaffs_find_by_name: null pointer directory" 4602 TENDSTR))); 4603 YBUG(); 4604 return YAFFS_FAIL; 4605 } 4606 if (the_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { 4607 T(YAFFS_TRACE_ALWAYS, 4608 (TSTR 4609 ("tragedy: yaffs_find_by_name: non-directory" TENDSTR))); 4610 YBUG(); 4611 return YAFFS_FAIL; 4612 } 4613 4614 ylist_for_each(i, &the_dir->variant.dir_variant.children) { 4615 if (i) { 4616 l = ylist_entry(i, yaffs_obj_t, siblings); 4617 if (l && !fn(l)) 4618 return YAFFS_FAIL; 4619 } 4620 } 4621 4622 return YAFFS_OK; 4623 4624} 4625#endif 4626 4627/* GetEquivalentObject dereferences any hard links to get to the 4628 * actual object. 4629 */ 4630 4631yaffs_obj_t *yaffs_get_equivalent_obj(yaffs_obj_t *obj) 4632{ 4633 if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { 4634 /* We want the object id of the equivalent object, not this one */ 4635 obj = obj->variant.hardlink_variant.equiv_obj; 4636 yaffs_check_obj_details_loaded(obj); 4637 } 4638 return obj; 4639} 4640 4641/* 4642 * A note or two on object names. 4643 * * If the object name is missing, we then make one up in the form objnnn 4644 * 4645 * * ASCII names are stored in the object header's name field from byte zero 4646 * * Unicode names are historically stored starting from byte zero. 4647 * 4648 * Then there are automatic Unicode names... 4649 * The purpose of these is to save names in a way that can be read as 4650 * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII 4651 * system to share files. 4652 * 4653 * These automatic unicode are stored slightly differently... 4654 * - If the name can fit in the ASCII character space then they are saved as 4655 * ascii names as per above. 4656 * - If the name needs Unicode then the name is saved in Unicode 4657 * starting at oh->name[1]. 4658 4659 */ 4660static void yaffs_fix_null_name(yaffs_obj_t * obj,YCHAR * name, int buffer_size) 4661{ 4662 /* Create an object name if we could not find one. */ 4663 if(yaffs_strnlen(name,YAFFS_MAX_NAME_LENGTH) == 0){ 4664 YCHAR locName[20]; 4665 YCHAR numString[20]; 4666 YCHAR *x = &numString[19]; 4667 unsigned v = obj->obj_id; 4668 numString[19] = 0; 4669 while(v>0){ 4670 x--; 4671 *x = '0' + (v % 10); 4672 v /= 10; 4673 } 4674 /* make up a name */ 4675 yaffs_strcpy(locName, YAFFS_LOSTNFOUND_PREFIX); 4676 yaffs_strcat(locName,x); 4677 yaffs_strncpy(name, locName, buffer_size - 1); 4678 } 4679} 4680 4681static void yaffs_load_name_from_oh(yaffs_dev_t *dev,YCHAR *name, const YCHAR *ohName, int bufferSize) 4682{ 4683#ifdef CONFIG_YAFFS_AUTO_UNICODE 4684 if(dev->param.auto_unicode){ 4685 if(*ohName){ 4686 /* It is an ASCII name, so do an ASCII to unicode conversion */ 4687 const char *asciiOhName = (const char *)ohName; 4688 int n = bufferSize - 1; 4689 while(n > 0 && *asciiOhName){ 4690 *name = *asciiOhName; 4691 name++; 4692 asciiOhName++; 4693 n--; 4694 } 4695 } else 4696 yaffs_strncpy(name,ohName+1, bufferSize -1); 4697 } else 4698#endif 4699 yaffs_strncpy(name, ohName, bufferSize - 1); 4700} 4701 4702 4703static void yaffs_load_oh_from_name(yaffs_dev_t *dev, YCHAR *ohName, const YCHAR *name) 4704{ 4705#ifdef CONFIG_YAFFS_AUTO_UNICODE 4706 4707 int isAscii; 4708 YCHAR *w; 4709 4710 if(dev->param.auto_unicode){ 4711 4712 isAscii = 1; 4713 w = name; 4714 4715 /* Figure out if the name will fit in ascii character set */ 4716 while(isAscii && *w){ 4717 if((*w) & 0xff00) 4718 isAscii = 0; 4719 w++; 4720 } 4721 4722 if(isAscii){ 4723 /* It is an ASCII name, so do a unicode to ascii conversion */ 4724 char *asciiOhName = (char *)ohName; 4725 int n = YAFFS_MAX_NAME_LENGTH - 1; 4726 while(n > 0 && *name){ 4727 *asciiOhName= *name; 4728 name++; 4729 asciiOhName++; 4730 n--; 4731 } 4732 } else{ 4733 /* It is a unicode name, so save starting at the second YCHAR */ 4734 *ohName = 0; 4735 yaffs_strncpy(ohName+1,name, YAFFS_MAX_NAME_LENGTH -2); 4736 } 4737 } 4738 else 4739#endif 4740 yaffs_strncpy(ohName,name, YAFFS_MAX_NAME_LENGTH - 1); 4741 4742} 4743 4744int yaffs_get_obj_name(yaffs_obj_t * obj, YCHAR * name, int buffer_size) 4745{ 4746 memset(name, 0, buffer_size * sizeof(YCHAR)); 4747 4748 yaffs_check_obj_details_loaded(obj); 4749 4750 if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { 4751 yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); 4752 } 4753#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM 4754 else if (obj->short_name[0]) { 4755 yaffs_strcpy(name, obj->short_name); 4756 } 4757#endif 4758 else if(obj->hdr_chunk > 0) { 4759 int result; 4760 __u8 *buffer = yaffs_get_temp_buffer(obj->my_dev, __LINE__); 4761 4762 yaffs_obj_header *oh = (yaffs_obj_header *) buffer; 4763 4764 memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); 4765 4766 if (obj->hdr_chunk > 0) { 4767 result = yaffs_rd_chunk_tags_nand(obj->my_dev, 4768 obj->hdr_chunk, buffer, 4769 NULL); 4770 } 4771 yaffs_load_name_from_oh(obj->my_dev,name,oh->name,buffer_size); 4772 4773 yaffs_release_temp_buffer(obj->my_dev, buffer, __LINE__); 4774 } 4775 4776 yaffs_fix_null_name(obj,name,buffer_size); 4777 4778 return yaffs_strnlen(name,YAFFS_MAX_NAME_LENGTH); 4779} 4780 4781 4782int yaffs_get_obj_length(yaffs_obj_t *obj) 4783{ 4784 /* Dereference any hard linking */ 4785 obj = yaffs_get_equivalent_obj(obj); 4786 4787 if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) 4788 return obj->variant.file_variant.file_size; 4789 if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK){ 4790 if(!obj->variant.symlink_variant.alias) 4791 return 0; 4792 return yaffs_strnlen(obj->variant.symlink_variant.alias,YAFFS_MAX_ALIAS_LENGTH); 4793 } else { 4794 /* Only a directory should drop through to here */ 4795 return obj->my_dev->data_bytes_per_chunk; 4796 } 4797} 4798 4799int yaffs_get_obj_link_count(yaffs_obj_t *obj) 4800{ 4801 int count = 0; 4802 struct ylist_head *i; 4803 4804 if (!obj->unlinked) 4805 count++; /* the object itself */ 4806 4807 ylist_for_each(i, &obj->hard_links) 4808 count++; /* add the hard links; */ 4809 4810 return count; 4811} 4812 4813int yaffs_get_obj_inode(yaffs_obj_t *obj) 4814{ 4815 obj = yaffs_get_equivalent_obj(obj); 4816 4817 return obj->obj_id; 4818} 4819 4820unsigned yaffs_get_obj_type(yaffs_obj_t *obj) 4821{ 4822 obj = yaffs_get_equivalent_obj(obj); 4823 4824 switch (obj->variant_type) { 4825 case YAFFS_OBJECT_TYPE_FILE: 4826 return DT_REG; 4827 break; 4828 case YAFFS_OBJECT_TYPE_DIRECTORY: 4829 return DT_DIR; 4830 break; 4831 case YAFFS_OBJECT_TYPE_SYMLINK: 4832 return DT_LNK; 4833 break; 4834 case YAFFS_OBJECT_TYPE_HARDLINK: 4835 return DT_REG; 4836 break; 4837 case YAFFS_OBJECT_TYPE_SPECIAL: 4838 if (S_ISFIFO(obj->yst_mode)) 4839 return DT_FIFO; 4840 if (S_ISCHR(obj->yst_mode)) 4841 return DT_CHR; 4842 if (S_ISBLK(obj->yst_mode)) 4843 return DT_BLK; 4844 if (S_ISSOCK(obj->yst_mode)) 4845 return DT_SOCK; 4846 default: 4847 return DT_REG; 4848 break; 4849 } 4850} 4851 4852YCHAR *yaffs_get_symlink_alias(yaffs_obj_t *obj) 4853{ 4854 obj = yaffs_get_equivalent_obj(obj); 4855 if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) 4856 return yaffs_clone_str(obj->variant.symlink_variant.alias); 4857 else 4858 return yaffs_clone_str(_Y("")); 4859} 4860 4861#ifndef CONFIG_YAFFS_WINCE 4862 4863int yaffs_set_attribs(yaffs_obj_t *obj, struct iattr *attr) 4864{ 4865 unsigned int valid = attr->ia_valid; 4866 4867 if (valid & ATTR_MODE) 4868 obj->yst_mode = attr->ia_mode; 4869 if (valid & ATTR_UID) 4870 obj->yst_uid = attr->ia_uid; 4871 if (valid & ATTR_GID) 4872 obj->yst_gid = attr->ia_gid; 4873 4874 if (valid & ATTR_ATIME) 4875 obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); 4876 if (valid & ATTR_CTIME) 4877 obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); 4878 if (valid & ATTR_MTIME) 4879 obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); 4880 4881 if (valid & ATTR_SIZE) 4882 yaffs_resize_file(obj, attr->ia_size); 4883 4884 yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); 4885 4886 return YAFFS_OK; 4887 4888} 4889int yaffs_get_attribs(yaffs_obj_t *obj, struct iattr *attr) 4890{ 4891 unsigned int valid = 0; 4892 4893 attr->ia_mode = obj->yst_mode; 4894 valid |= ATTR_MODE; 4895 attr->ia_uid = obj->yst_uid; 4896 valid |= ATTR_UID; 4897 attr->ia_gid = obj->yst_gid; 4898 valid |= ATTR_GID; 4899 4900 Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; 4901 valid |= ATTR_ATIME; 4902 Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; 4903 valid |= ATTR_CTIME; 4904 Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; 4905 valid |= ATTR_MTIME; 4906 4907 attr->ia_size = yaffs_get_file_size(obj); 4908 valid |= ATTR_SIZE; 4909 4910 attr->ia_valid = valid; 4911 4912 return YAFFS_OK; 4913} 4914 4915#endif 4916 4917 4918static int yaffs_do_xattrib_mod(yaffs_obj_t *obj, int set, const YCHAR *name, const void *value, int size, int flags) 4919{ 4920 yaffs_xattr_mod xmod; 4921 4922 int result; 4923 4924 xmod.set = set; 4925 xmod.name = name; 4926 xmod.data = value; 4927 xmod.size = size; 4928 xmod.flags = flags; 4929 xmod.result = -ENOSPC; 4930 4931 result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); 4932 4933 if(result > 0) 4934 return xmod.result; 4935 else 4936 return -ENOSPC; 4937} 4938 4939static int yaffs_apply_xattrib_mod(yaffs_obj_t *obj, char *buffer, yaffs_xattr_mod *xmod) 4940{ 4941 int retval = 0; 4942 int x_offs = sizeof(yaffs_obj_header); 4943 yaffs_dev_t *dev = obj->my_dev; 4944 int x_size = dev->data_bytes_per_chunk - sizeof(yaffs_obj_header); 4945 4946 char * x_buffer = buffer + x_offs; 4947 4948 if(xmod->set) 4949 retval = nval_set(x_buffer, x_size, xmod->name, xmod->data, xmod->size, xmod->flags); 4950 else 4951 retval = nval_del(x_buffer, x_size, xmod->name); 4952 4953 obj->has_xattr = nval_hasvalues(x_buffer, x_size); 4954 obj->xattr_known = 1; 4955 4956 xmod->result = retval; 4957 4958 return retval; 4959} 4960 4961static int yaffs_do_xattrib_fetch(yaffs_obj_t *obj, const YCHAR *name, void *value, int size) 4962{ 4963 char *buffer = NULL; 4964 int result; 4965 yaffs_ext_tags tags; 4966 yaffs_dev_t *dev = obj->my_dev; 4967 int x_offs = sizeof(yaffs_obj_header); 4968 int x_size = dev->data_bytes_per_chunk - sizeof(yaffs_obj_header); 4969 4970 char * x_buffer; 4971 4972 int retval = 0; 4973 4974 if(obj->hdr_chunk < 1) 4975 return -ENODATA; 4976 4977 /* If we know that the object has no xattribs then don't do all the 4978 * reading and parsing. 4979 */ 4980 if(obj->xattr_known && !obj->has_xattr){ 4981 if(name) 4982 return -ENODATA; 4983 else 4984 return 0; 4985 } 4986 4987 buffer = (char *) yaffs_get_temp_buffer(dev, __LINE__); 4988 if(!buffer) 4989 return -ENOMEM; 4990 4991 result = yaffs_rd_chunk_tags_nand(dev,obj->hdr_chunk, (__u8 *)buffer, &tags); 4992 4993 if(result != YAFFS_OK) 4994 retval = -ENOENT; 4995 else{ 4996 x_buffer = buffer + x_offs; 4997 4998 if (!obj->xattr_known){ 4999 obj->has_xattr = nval_hasvalues(x_buffer, x_size); 5000 obj->xattr_known = 1; 5001 } 5002 5003 if(name) 5004 retval = nval_get(x_buffer, x_size, name, value, size); 5005 else 5006 retval = nval_list(x_buffer, x_size, value,size); 5007 } 5008 yaffs_release_temp_buffer(dev,(__u8 *)buffer,__LINE__); 5009 return retval; 5010} 5011 5012int yaffs_set_xattrib(yaffs_obj_t *obj, const YCHAR *name, const void * value, int size, int flags) 5013{ 5014 return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); 5015} 5016 5017int yaffs_remove_xattrib(yaffs_obj_t *obj, const YCHAR *name) 5018{ 5019 return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); 5020} 5021 5022int yaffs_get_xattrib(yaffs_obj_t *obj, const YCHAR *name, void *value, int size) 5023{ 5024 return yaffs_do_xattrib_fetch(obj, name, value, size); 5025} 5026 5027int yaffs_list_xattrib(yaffs_obj_t *obj, char *buffer, int size) 5028{ 5029 return yaffs_do_xattrib_fetch(obj, NULL, buffer,size); 5030} 5031 5032 5033 5034#if 0 5035int yaffs_dump_obj(yaffs_obj_t *obj) 5036{ 5037 YCHAR name[257]; 5038 5039 yaffs_get_obj_name(obj, name, YAFFS_MAX_NAME_LENGTH + 1); 5040 5041 T(YAFFS_TRACE_ALWAYS, 5042 (TSTR 5043 ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d" 5044 " chunk %d type %d size %d\n" 5045 TENDSTR), obj->obj_id, yaffs_get_obj_inode(obj), name, 5046 obj->dirty, obj->valid, obj->serial, obj->sum, obj->hdr_chunk, 5047 yaffs_get_obj_type(obj), yaffs_get_obj_length(obj))); 5048 5049 return YAFFS_OK; 5050} 5051#endif 5052 5053/*---------------------------- Initialisation code -------------------------------------- */ 5054 5055static int yaffs_cehck_dev_fns(const yaffs_dev_t *dev) 5056{ 5057 5058 /* Common functions, gotta have */ 5059 if (!dev->param.erase_fn || !dev->param.initialise_flash_fn) 5060 return 0; 5061 5062#ifdef CONFIG_YAFFS_YAFFS2 5063 5064 /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ 5065 if (dev->param.write_chunk_tags_fn && 5066 dev->param.read_chunk_tags_fn && 5067 !dev->param.write_chunk_fn && 5068 !dev->param.read_chunk_fn && 5069 dev->param.bad_block_fn && 5070 dev->param.query_block_fn) 5071 return 1; 5072#endif 5073 5074 /* Can use the "spare" style interface for yaffs1 */ 5075 if (!dev->param.is_yaffs2 && 5076 !dev->param.write_chunk_tags_fn && 5077 !dev->param.read_chunk_tags_fn && 5078 dev->param.write_chunk_fn && 5079 dev->param.read_chunk_fn && 5080 !dev->param.bad_block_fn && 5081 !dev->param.query_block_fn) 5082 return 1; 5083 5084 return 0; /* bad */ 5085} 5086 5087 5088static int yaffs_create_initial_dir(yaffs_dev_t *dev) 5089{ 5090 /* Initialise the unlinked, deleted, root and lost and found directories */ 5091 5092 dev->lost_n_found = dev->root_dir = NULL; 5093 dev->unlinked_dir = dev->del_dir = NULL; 5094 5095 dev->unlinked_dir = 5096 yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); 5097 5098 dev->del_dir = 5099 yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); 5100 5101 dev->root_dir = 5102 yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, 5103 YAFFS_ROOT_MODE | S_IFDIR); 5104 dev->lost_n_found = 5105 yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, 5106 YAFFS_LOSTNFOUND_MODE | S_IFDIR); 5107 5108 if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir && dev->del_dir) { 5109 yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); 5110 return YAFFS_OK; 5111 } 5112 5113 return YAFFS_FAIL; 5114} 5115 5116int yaffs_guts_initialise(yaffs_dev_t *dev) 5117{ 5118 int init_failed = 0; 5119 unsigned x; 5120 int bits; 5121 5122 T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_guts_initialise()" TENDSTR))); 5123 5124 /* Check stuff that must be set */ 5125 5126 if (!dev) { 5127 T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR))); 5128 return YAFFS_FAIL; 5129 } 5130 5131 dev->internal_start_block = dev->param.start_block; 5132 dev->internal_end_block = dev->param.end_block; 5133 dev->block_offset = 0; 5134 dev->chunk_offset = 0; 5135 dev->n_free_chunks = 0; 5136 5137 dev->gc_block = 0; 5138 5139 if (dev->param.start_block == 0) { 5140 dev->internal_start_block = dev->param.start_block + 1; 5141 dev->internal_end_block = dev->param.end_block + 1; 5142 dev->block_offset = 1; 5143 dev->chunk_offset = dev->param.chunks_per_block; 5144 } 5145 5146 /* Check geometry parameters. */ 5147 5148 if ((!dev->param.inband_tags && dev->param.is_yaffs2 && dev->param.total_bytes_per_chunk < 1024) || 5149 (!dev->param.is_yaffs2 && dev->param.total_bytes_per_chunk < 512) || 5150 (dev->param.inband_tags && !dev->param.is_yaffs2) || 5151 dev->param.chunks_per_block < 2 || 5152 dev->param.n_reserved_blocks < 2 || 5153 dev->internal_start_block <= 0 || 5154 dev->internal_end_block <= 0 || 5155 dev->internal_end_block <= (dev->internal_start_block + dev->param.n_reserved_blocks + 2)) { /* otherwise it is too small */ 5156 T(YAFFS_TRACE_ALWAYS, 5157 (TSTR 5158 ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d " 5159 TENDSTR), dev->param.total_bytes_per_chunk, dev->param.is_yaffs2 ? "2" : "", dev->param.inband_tags)); 5160 return YAFFS_FAIL; 5161 } 5162 5163 if (yaffs_init_nand(dev) != YAFFS_OK) { 5164 T(YAFFS_TRACE_ALWAYS, 5165 (TSTR("yaffs: InitialiseNAND failed" TENDSTR))); 5166 return YAFFS_FAIL; 5167 } 5168 5169 /* Sort out space for inband tags, if required */ 5170 if (dev->param.inband_tags) 5171 dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk - sizeof(yaffs_PackedTags2TagsPart); 5172 else 5173 dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; 5174 5175 /* Got the right mix of functions? */ 5176 if (!yaffs_cehck_dev_fns(dev)) { 5177 /* Function missing */ 5178 T(YAFFS_TRACE_ALWAYS, 5179 (TSTR 5180 ("yaffs: device function(s) missing or wrong\n" TENDSTR))); 5181 5182 return YAFFS_FAIL; 5183 } 5184 5185 /* This is really a compilation check. */ 5186 if (!yaffs_check_structures()) { 5187 T(YAFFS_TRACE_ALWAYS, 5188 (TSTR("yaffs_check_structures failed\n" TENDSTR))); 5189 return YAFFS_FAIL; 5190 } 5191 5192 if (dev->is_mounted) { 5193 T(YAFFS_TRACE_ALWAYS, 5194 (TSTR("yaffs: device already mounted\n" TENDSTR))); 5195 return YAFFS_FAIL; 5196 } 5197 5198 /* Finished with most checks. One or two more checks happen later on too. */ 5199 5200 dev->is_mounted = 1; 5201 5202 /* OK now calculate a few things for the device */ 5203 5204 /* 5205 * Calculate all the chunk size manipulation numbers: 5206 */ 5207 x = dev->data_bytes_per_chunk; 5208 /* We always use dev->chunk_shift and dev->chunk_div */ 5209 dev->chunk_shift = Shifts(x); 5210 x >>= dev->chunk_shift; 5211 dev->chunk_div = x; 5212 /* We only use chunk mask if chunk_div is 1 */ 5213 dev->chunk_mask = (1<<dev->chunk_shift) - 1; 5214 5215 /* 5216 * Calculate chunk_grp_bits. 5217 * We need to find the next power of 2 > than internal_end_block 5218 */ 5219 5220 x = dev->param.chunks_per_block * (dev->internal_end_block + 1); 5221 5222 bits = ShiftsGE(x); 5223 5224 /* Set up tnode width if wide tnodes are enabled. */ 5225 if (!dev->param.wide_tnodes_disabled) { 5226 /* bits must be even so that we end up with 32-bit words */ 5227 if (bits & 1) 5228 bits++; 5229 if (bits < 16) 5230 dev->tnode_width = 16; 5231 else 5232 dev->tnode_width = bits; 5233 } else 5234 dev->tnode_width = 16; 5235 5236 dev->tnode_mask = (1<<dev->tnode_width)-1; 5237 5238 /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), 5239 * so if the bitwidth of the 5240 * chunk range we're using is greater than 16 we need 5241 * to figure out chunk shift and chunk_grp_size 5242 */ 5243 5244 if (bits <= dev->tnode_width) 5245 dev->chunk_grp_bits = 0; 5246 else 5247 dev->chunk_grp_bits = bits - dev->tnode_width; 5248 5249 dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0)/8; 5250 if(dev->tnode_size < sizeof(yaffs_tnode_t)) 5251 dev->tnode_size = sizeof(yaffs_tnode_t); 5252 5253 dev->chunk_grp_size = 1 << dev->chunk_grp_bits; 5254 5255 if (dev->param.chunks_per_block < dev->chunk_grp_size) { 5256 /* We have a problem because the soft delete won't work if 5257 * the chunk group size > chunks per block. 5258 * This can be remedied by using larger "virtual blocks". 5259 */ 5260 T(YAFFS_TRACE_ALWAYS, 5261 (TSTR("yaffs: chunk group too large\n" TENDSTR))); 5262 5263 return YAFFS_FAIL; 5264 } 5265 5266 /* OK, we've finished verifying the device, lets continue with initialisation */ 5267 5268 /* More device initialisation */ 5269 dev->all_gcs = 0; 5270 dev->passive_gc_count = 0; 5271 dev->oldest_dirty_gc_count = 0; 5272 dev->bg_gcs = 0; 5273 dev->gc_block_finder = 0; 5274 dev->buffered_block = -1; 5275 dev->doing_buffered_block_rewrite = 0; 5276 dev->n_deleted_files = 0; 5277 dev->n_bg_deletions = 0; 5278 dev->n_unlinked_files = 0; 5279 dev->n_ecc_fixed = 0; 5280 dev->n_ecc_unfixed = 0; 5281 dev->n_tags_ecc_fixed = 0; 5282 dev->n_tags_ecc_unfixed = 0; 5283 dev->n_erase_failures = 0; 5284 dev->n_erased_blocks = 0; 5285 dev->gc_disable= 0; 5286 dev->has_pending_prioritised_gc = 1; /* Assume the worst for now, will get fixed on first GC */ 5287 YINIT_LIST_HEAD(&dev->dirty_dirs); 5288 dev->oldest_dirty_seq = 0; 5289 dev->oldest_dirty_block = 0; 5290 5291 /* Initialise temporary buffers and caches. */ 5292 if (!yaffs_init_tmp_buffers(dev)) 5293 init_failed = 1; 5294 5295 dev->cache = NULL; 5296 dev->gc_cleanup_list = NULL; 5297 5298 5299 if (!init_failed && 5300 dev->param.n_caches > 0) { 5301 int i; 5302 void *buf; 5303 int cacheBytes = dev->param.n_caches * sizeof(yaffs_cache_t); 5304 5305 if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) 5306 dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; 5307 5308 dev->cache = YMALLOC(cacheBytes); 5309 5310 buf = (__u8 *) dev->cache; 5311 5312 if (dev->cache) 5313 memset(dev->cache, 0, cacheBytes); 5314 5315 for (i = 0; i < dev->param.n_caches && buf; i++) { 5316 dev->cache[i].object = NULL; 5317 dev->cache[i].last_use = 0; 5318 dev->cache[i].dirty = 0; 5319 dev->cache[i].data = buf = YMALLOC_DMA(dev->param.total_bytes_per_chunk); 5320 } 5321 if (!buf) 5322 init_failed = 1; 5323 5324 dev->cache_last_use = 0; 5325 } 5326 5327 dev->cache_hits = 0; 5328 5329 if (!init_failed) { 5330 dev->gc_cleanup_list = YMALLOC(dev->param.chunks_per_block * sizeof(__u32)); 5331 if (!dev->gc_cleanup_list) 5332 init_failed = 1; 5333 } 5334 5335 if (dev->param.is_yaffs2) 5336 dev->param.use_header_file_size = 1; 5337 5338 if (!init_failed && !yaffs_init_blocks(dev)) 5339 init_failed = 1; 5340 5341 yaffs_init_tnodes_and_objs(dev); 5342 5343 if (!init_failed && !yaffs_create_initial_dir(dev)) 5344 init_failed = 1; 5345 5346 5347 if (!init_failed) { 5348 /* Now scan the flash. */ 5349 if (dev->param.is_yaffs2) { 5350 if (yaffs2_checkpt_restore(dev)) { 5351 yaffs_check_obj_details_loaded(dev->root_dir); 5352 T(YAFFS_TRACE_ALWAYS, 5353 (TSTR("yaffs: restored from checkpoint" TENDSTR))); 5354 } else { 5355 5356 /* Clean up the mess caused by an aborted checkpoint load 5357 * and scan backwards. 5358 */ 5359 yaffs_deinit_blocks(dev); 5360 5361 yaffs_deinit_tnodes_and_objs(dev); 5362 5363 dev->n_erased_blocks = 0; 5364 dev->n_free_chunks = 0; 5365 dev->alloc_block = -1; 5366 dev->alloc_page = -1; 5367 dev->n_deleted_files = 0; 5368 dev->n_unlinked_files = 0; 5369 dev->n_bg_deletions = 0; 5370 5371 if (!init_failed && !yaffs_init_blocks(dev)) 5372 init_failed = 1; 5373 5374 yaffs_init_tnodes_and_objs(dev); 5375 5376 if (!init_failed && !yaffs_create_initial_dir(dev)) 5377 init_failed = 1; 5378 5379 if (!init_failed && !yaffs2_scan_backwards(dev)) 5380 init_failed = 1; 5381 } 5382 } else if (!yaffs1_scan(dev)) 5383 init_failed = 1; 5384 5385 yaffs_strip_deleted_objs(dev); 5386 yaffs_fix_hanging_objs(dev); 5387 if(dev->param.empty_lost_n_found) 5388 yaffs_empty_l_n_f(dev); 5389 } 5390 5391 if (init_failed) { 5392 /* Clean up the mess */ 5393 T(YAFFS_TRACE_TRACING, 5394 (TSTR("yaffs: yaffs_guts_initialise() aborted.\n" TENDSTR))); 5395 5396 yaffs_deinitialise(dev); 5397 return YAFFS_FAIL; 5398 } 5399 5400 /* Zero out stats */ 5401 dev->n_page_reads = 0; 5402 dev->n_page_writes = 0; 5403 dev->n_erasures = 0; 5404 dev->n_gc_copies = 0; 5405 dev->n_retired_writes = 0; 5406 5407 dev->n_retired_blocks = 0; 5408 5409 yaffs_verify_free_chunks(dev); 5410 yaffs_verify_blocks(dev); 5411 5412 /* Clean up any aborted checkpoint data */ 5413 if(!dev->is_checkpointed && dev->blocks_in_checkpt > 0) 5414 yaffs2_checkpt_invalidate(dev); 5415 5416 T(YAFFS_TRACE_TRACING, 5417 (TSTR("yaffs: yaffs_guts_initialise() done.\n" TENDSTR))); 5418 return YAFFS_OK; 5419 5420} 5421 5422void yaffs_deinitialise(yaffs_dev_t *dev) 5423{ 5424 if (dev->is_mounted) { 5425 int i; 5426 5427 yaffs_deinit_blocks(dev); 5428 yaffs_deinit_tnodes_and_objs(dev); 5429 if (dev->param.n_caches > 0 && 5430 dev->cache) { 5431 5432 for (i = 0; i < dev->param.n_caches; i++) { 5433 if (dev->cache[i].data) 5434 YFREE(dev->cache[i].data); 5435 dev->cache[i].data = NULL; 5436 } 5437 5438 YFREE(dev->cache); 5439 dev->cache = NULL; 5440 } 5441 5442 YFREE(dev->gc_cleanup_list); 5443 5444 for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) 5445 YFREE(dev->temp_buffer[i].buffer); 5446 5447 dev->is_mounted = 0; 5448 5449 if (dev->param.deinitialise_flash_fn) 5450 dev->param.deinitialise_flash_fn(dev); 5451 } 5452} 5453 5454int yaffs_count_free_chunks(yaffs_dev_t *dev) 5455{ 5456 int nFree=0; 5457 int b; 5458 5459 yaffs_block_info_t *blk; 5460 5461 blk = dev->block_info; 5462 for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { 5463 switch (blk->block_state) { 5464 case YAFFS_BLOCK_STATE_EMPTY: 5465 case YAFFS_BLOCK_STATE_ALLOCATING: 5466 case YAFFS_BLOCK_STATE_COLLECTING: 5467 case YAFFS_BLOCK_STATE_FULL: 5468 nFree += 5469 (dev->param.chunks_per_block - blk->pages_in_use + 5470 blk->soft_del_pages); 5471 break; 5472 default: 5473 break; 5474 } 5475 blk++; 5476 } 5477 5478 return nFree; 5479} 5480 5481int yaffs_get_n_free_chunks(yaffs_dev_t *dev) 5482{ 5483 /* This is what we report to the outside world */ 5484 5485 int nFree; 5486 int nDirtyCacheChunks; 5487 int blocksForCheckpoint; 5488 int i; 5489 5490#if 1 5491 nFree = dev->n_free_chunks; 5492#else 5493 nFree = yaffs_count_free_chunks(dev); 5494#endif 5495 5496 nFree += dev->n_deleted_files; 5497 5498 /* Now count the number of dirty chunks in the cache and subtract those */ 5499 5500 for (nDirtyCacheChunks = 0, i = 0; i < dev->param.n_caches; i++) { 5501 if (dev->cache[i].dirty) 5502 nDirtyCacheChunks++; 5503 } 5504 5505 nFree -= nDirtyCacheChunks; 5506 5507 nFree -= ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); 5508 5509 /* Now we figure out how much to reserve for the checkpoint and report that... */ 5510 blocksForCheckpoint = yaffs_calc_checkpt_blocks_required(dev); 5511 5512 nFree -= (blocksForCheckpoint * dev->param.chunks_per_block); 5513 5514 if (nFree < 0) 5515 nFree = 0; 5516 5517 return nFree; 5518 5519} 5520 5521 5522/*---------------------------------------- YAFFS test code ----------------------*/ 5523 5524#define yaffs_check_struct(structure, syze, name) \ 5525 do { \ 5526 if (sizeof(structure) != syze) { \ 5527 T(YAFFS_TRACE_ALWAYS, (TSTR("%s should be %d but is %d\n" TENDSTR),\ 5528 name, syze, (int) sizeof(structure))); \ 5529 return YAFFS_FAIL; \ 5530 } \ 5531 } while (0) 5532 5533static int yaffs_check_structures(void) 5534{ 5535/* yaffs_check_struct(yaffs_tags_t,8,"yaffs_tags_t"); */ 5536/* yaffs_check_struct(yaffs_tags_union_t,8,"yaffs_tags_union_t"); */ 5537/* yaffs_check_struct(yaffs_spare,16,"yaffs_spare"); */ 5538/* yaffs_check_struct(yaffs_tnode_t, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_tnode_t"); */ 5539 5540#ifndef CONFIG_YAFFS_WINCE 5541 yaffs_check_struct(yaffs_obj_header, 512, "yaffs_obj_header"); 5542#endif 5543 return YAFFS_OK; 5544} 5545