2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 * for Toby Churchill Ltd and Brightstar Engineering
7 * Created by Charles Manning <charles@aleph1.co.uk>
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.
14 #include "yaffs_yaffs1.h"
16 #include "yaffs_trace.h"
17 #include "yaffs_bitmap.h"
18 #include "yaffs_getblockinfo.h"
19 #include "yaffs_nand.h"
20 #include "yaffs_attribs.h"
22 int yaffs1_scan(struct yaffs_dev
*dev
)
24 struct yaffs_ext_tags tags
;
30 enum yaffs_block_state state
;
32 struct yaffs_block_info
*bi
;
34 struct yaffs_obj_hdr
*oh
;
36 struct yaffs_obj
*parent
;
38 struct yaffs_shadow_fixer
*shadow_fixers
= NULL
;
41 yaffs_trace(YAFFS_TRACE_SCAN
,
42 "yaffs1_scan starts intstartblk %d intendblk %d...",
43 dev
->internal_start_block
, dev
->internal_end_block
);
45 chunk_data
= yaffs_get_temp_buffer(dev
);
47 dev
->seq_number
= YAFFS_LOWEST_SEQUENCE_NUMBER
;
49 /* Scan all the blocks to determine their state */
51 for (blk
= dev
->internal_start_block
; blk
<= dev
->internal_end_block
;
53 yaffs_clear_chunk_bits(dev
, blk
);
55 bi
->soft_del_pages
= 0;
57 yaffs_query_init_block_state(dev
, blk
, &state
, &seq_number
);
59 bi
->block_state
= state
;
60 bi
->seq_number
= seq_number
;
62 if (bi
->seq_number
== YAFFS_SEQUENCE_BAD_BLOCK
)
63 bi
->block_state
= state
= YAFFS_BLOCK_STATE_DEAD
;
65 yaffs_trace(YAFFS_TRACE_SCAN_DEBUG
,
66 "Block scanning block %d state %d seq %d",
67 blk
, state
, seq_number
);
69 if (state
== YAFFS_BLOCK_STATE_DEAD
) {
70 yaffs_trace(YAFFS_TRACE_BAD_BLOCKS
,
71 "block %d is bad", blk
);
72 } else if (state
== YAFFS_BLOCK_STATE_EMPTY
) {
73 yaffs_trace(YAFFS_TRACE_SCAN_DEBUG
, "Block empty ");
74 dev
->n_erased_blocks
++;
75 dev
->n_free_chunks
+= dev
->param
.chunks_per_block
;
80 /* For each block.... */
81 for (blk
= dev
->internal_start_block
;
82 !alloc_failed
&& blk
<= dev
->internal_end_block
; blk
++) {
86 bi
= yaffs_get_block_info(dev
, blk
);
87 state
= bi
->block_state
;
91 /* For each chunk in each block that needs scanning.... */
93 !alloc_failed
&& c
< dev
->param
.chunks_per_block
&&
94 state
== YAFFS_BLOCK_STATE_NEEDS_SCAN
; c
++) {
95 /* Read the tags and decide what to do */
96 chunk
= blk
* dev
->param
.chunks_per_block
+ c
;
98 result
= yaffs_rd_chunk_tags_nand(dev
, chunk
, NULL
,
101 /* Let's have a good look at this chunk... */
103 if (tags
.ecc_result
== YAFFS_ECC_RESULT_UNFIXED
||
109 dev
->n_free_chunks
++;
110 } else if (!tags
.chunk_used
) {
111 /* An unassigned chunk in the block
112 * This means that either the block is empty or
113 * this is the one being allocated from
117 /* We're looking at the first chunk in
118 *the block so the block is unused */
119 state
= YAFFS_BLOCK_STATE_EMPTY
;
120 dev
->n_erased_blocks
++;
122 /* this is the block being allocated */
123 yaffs_trace(YAFFS_TRACE_SCAN
,
124 " Allocating from %d %d",
126 state
= YAFFS_BLOCK_STATE_ALLOCATING
;
127 dev
->alloc_block
= blk
;
129 dev
->alloc_block_finder
= blk
;
133 dev
->n_free_chunks
+=
134 (dev
->param
.chunks_per_block
- c
);
135 } else if (tags
.chunk_id
> 0) {
136 /* chunk_id > 0 so it is a data chunk... */
139 yaffs_set_chunk_bit(dev
, blk
, c
);
142 in
= yaffs_find_or_create_by_number(dev
,
144 YAFFS_OBJECT_TYPE_FILE
);
145 /* PutChunkIntoFile checks for a clash
146 * (two data chunks with the same chunk_id).
153 if (!yaffs_put_chunk_in_file
154 (in
, tags
.chunk_id
, chunk
, 1))
159 (tags
.chunk_id
- 1) *
160 dev
->data_bytes_per_chunk
+
164 YAFFS_OBJECT_TYPE_FILE
&&
165 in
->variant
.file_variant
.scanned_size
<
167 in
->variant
.file_variant
.scanned_size
=
169 if (!dev
->param
.use_header_file_size
) {
171 file_variant
.file_size
=
173 file_variant
.scanned_size
;
178 /* chunk_id == 0, so it is an ObjectHeader.
181 yaffs_set_chunk_bit(dev
, blk
, c
);
184 result
= yaffs_rd_chunk_tags_nand(dev
, chunk
,
188 oh
= (struct yaffs_obj_hdr
*)chunk_data
;
190 in
= yaffs_find_by_number(dev
, tags
.obj_id
);
191 if (in
&& in
->variant_type
!= oh
->type
) {
192 /* This should not happen, but somehow
193 * Wev'e ended up with an obj_id that
194 * has been reused but not yet deleted,
195 * and worse still it has changed type.
196 * Delete the old object.
203 in
= yaffs_find_or_create_by_number(dev
,
210 if (in
&& oh
->shadows_obj
> 0) {
212 struct yaffs_shadow_fixer
*fixer
;
215 (struct yaffs_shadow_fixer
),
218 fixer
->next
= shadow_fixers
;
219 shadow_fixers
= fixer
;
220 fixer
->obj_id
= tags
.obj_id
;
223 yaffs_trace(YAFFS_TRACE_SCAN
,
224 " Shadow fixer: %d shadows %d",
232 if (in
&& in
->valid
) {
233 /* We have already filled this one.
234 * We have a duplicate and need to
237 unsigned existing_serial
= in
->serial
;
238 unsigned new_serial
=
241 if (((existing_serial
+ 1) & 3) ==
243 /* Use new one - destroy the
250 /* Use existing - destroy
252 yaffs_chunk_del(dev
, chunk
, 1,
257 if (in
&& !in
->valid
&&
258 (tags
.obj_id
== YAFFS_OBJECTID_ROOT
||
260 YAFFS_OBJECTID_LOSTNFOUND
)) {
261 /* We only load some info, don't fiddle
262 * with directory structure */
264 in
->variant_type
= oh
->type
;
266 in
->yst_mode
= oh
->yst_mode
;
267 yaffs_load_attribs(in
, oh
);
268 in
->hdr_chunk
= chunk
;
269 in
->serial
= tags
.serial_number
;
271 } else if (in
&& !in
->valid
) {
272 /* we need to load this info */
275 in
->variant_type
= oh
->type
;
277 in
->yst_mode
= oh
->yst_mode
;
278 yaffs_load_attribs(in
, oh
);
279 in
->hdr_chunk
= chunk
;
280 in
->serial
= tags
.serial_number
;
282 yaffs_set_obj_name_from_oh(in
, oh
);
285 /* directory stuff...
290 yaffs_find_or_create_by_number
291 (dev
, oh
->parent_obj_id
,
292 YAFFS_OBJECT_TYPE_DIRECTORY
);
295 if (parent
&& parent
->variant_type
==
296 YAFFS_OBJECT_TYPE_UNKNOWN
) {
297 /* Set up as a directory */
298 parent
->variant_type
=
299 YAFFS_OBJECT_TYPE_DIRECTORY
;
300 INIT_LIST_HEAD(&parent
->
303 } else if (!parent
||
304 parent
->variant_type
!=
305 YAFFS_OBJECT_TYPE_DIRECTORY
) {
306 /* Hoosterman, a problem....
307 * We're trying to use a
308 * non-directory as a directory
311 yaffs_trace(YAFFS_TRACE_ERROR
,
312 "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
314 parent
= dev
->lost_n_found
;
317 yaffs_add_obj_to_dir(parent
, in
);
319 switch (in
->variant_type
) {
320 case YAFFS_OBJECT_TYPE_UNKNOWN
:
321 /* Todo got a problem */
323 case YAFFS_OBJECT_TYPE_FILE
:
325 use_header_file_size
)
327 file_variant
.file_size
328 = yaffs_oh_to_size(oh
);
330 case YAFFS_OBJECT_TYPE_HARDLINK
:
332 hardlink_variant
.equiv_id
=
334 list_add(&in
->hard_links
,
337 case YAFFS_OBJECT_TYPE_DIRECTORY
:
340 case YAFFS_OBJECT_TYPE_SPECIAL
:
343 case YAFFS_OBJECT_TYPE_SYMLINK
:
344 in
->variant
.symlink_variant
.
346 yaffs_clone_str(oh
->alias
);
348 symlink_variant
.alias
)
356 if (state
== YAFFS_BLOCK_STATE_NEEDS_SCAN
) {
357 /* If we got this far while scanning,
358 * then the block is fully allocated. */
359 state
= YAFFS_BLOCK_STATE_FULL
;
362 if (state
== YAFFS_BLOCK_STATE_ALLOCATING
) {
363 /* If the block was partially allocated then
364 * treat it as fully allocated. */
365 state
= YAFFS_BLOCK_STATE_FULL
;
366 dev
->alloc_block
= -1;
369 bi
->block_state
= state
;
371 /* Now let's see if it was dirty */
372 if (bi
->pages_in_use
== 0 &&
373 !bi
->has_shrink_hdr
&&
374 bi
->block_state
== YAFFS_BLOCK_STATE_FULL
)
375 yaffs_block_became_dirty(dev
, blk
);
378 /* Ok, we've done all the scanning.
379 * Fix up the hard link chains.
380 * We should now have scanned all the objects, now it's time to add
384 yaffs_link_fixup(dev
, &hard_list
);
387 * Fix up any shadowed objects.
388 * There should not be more than one of these.
391 struct yaffs_shadow_fixer
*fixer
;
392 struct yaffs_obj
*obj
;
394 while (shadow_fixers
) {
395 fixer
= shadow_fixers
;
396 shadow_fixers
= fixer
->next
;
397 /* Complete the rename transaction by deleting the
398 * shadowed object then setting the object header
401 obj
= yaffs_find_by_number(dev
, fixer
->shadowed_id
);
405 obj
= yaffs_find_by_number(dev
, fixer
->obj_id
);
408 yaffs_update_oh(obj
, NULL
, 1, 0, 0, NULL
);
414 yaffs_release_temp_buffer(dev
, chunk_data
);
419 yaffs_trace(YAFFS_TRACE_SCAN
, "yaffs1_scan ends");