]>
Commit | Line | Data |
---|---|---|
753ac610 CM |
1 | /* |
2 | * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. | |
3 | * | |
4 | * Copyright (C) 2002-2011 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 | ||
14 | #include "yaffs_yaffs1.h" | |
15 | #include "yportenv.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" | |
21 | ||
22 | int yaffs1_scan(struct yaffs_dev *dev) | |
23 | { | |
24 | struct yaffs_ext_tags tags; | |
25 | int blk; | |
753ac610 CM |
26 | int chunk; |
27 | int c; | |
28 | int deleted; | |
29 | enum yaffs_block_state state; | |
30 | LIST_HEAD(hard_list); | |
31 | struct yaffs_block_info *bi; | |
32 | u32 seq_number; | |
33 | struct yaffs_obj_hdr *oh; | |
34 | struct yaffs_obj *in; | |
35 | struct yaffs_obj *parent; | |
36 | int alloc_failed = 0; | |
37 | struct yaffs_shadow_fixer *shadow_fixers = NULL; | |
38 | u8 *chunk_data; | |
39 | ||
40 | yaffs_trace(YAFFS_TRACE_SCAN, | |
41 | "yaffs1_scan starts intstartblk %d intendblk %d...", | |
42 | dev->internal_start_block, dev->internal_end_block); | |
43 | ||
44 | chunk_data = yaffs_get_temp_buffer(dev); | |
45 | ||
46 | dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; | |
47 | ||
48 | /* Scan all the blocks to determine their state */ | |
49 | bi = dev->block_info; | |
50 | for (blk = dev->internal_start_block; blk <= dev->internal_end_block; | |
51 | blk++) { | |
52 | yaffs_clear_chunk_bits(dev, blk); | |
53 | bi->pages_in_use = 0; | |
54 | bi->soft_del_pages = 0; | |
55 | ||
56 | yaffs_query_init_block_state(dev, blk, &state, &seq_number); | |
57 | ||
58 | bi->block_state = state; | |
59 | bi->seq_number = seq_number; | |
60 | ||
61 | if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) | |
62 | bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; | |
63 | ||
64 | yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, | |
65 | "Block scanning block %d state %d seq %d", | |
66 | blk, state, seq_number); | |
67 | ||
68 | if (state == YAFFS_BLOCK_STATE_DEAD) { | |
69 | yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, | |
70 | "block %d is bad", blk); | |
71 | } else if (state == YAFFS_BLOCK_STATE_EMPTY) { | |
72 | yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); | |
73 | dev->n_erased_blocks++; | |
74 | dev->n_free_chunks += dev->param.chunks_per_block; | |
75 | } | |
76 | bi++; | |
77 | } | |
78 | ||
79 | /* For each block.... */ | |
80 | for (blk = dev->internal_start_block; | |
81 | !alloc_failed && blk <= dev->internal_end_block; blk++) { | |
82 | ||
83 | cond_resched(); | |
84 | ||
85 | bi = yaffs_get_block_info(dev, blk); | |
86 | state = bi->block_state; | |
87 | ||
88 | deleted = 0; | |
89 | ||
90 | /* For each chunk in each block that needs scanning.... */ | |
91 | for (c = 0; | |
92 | !alloc_failed && c < dev->param.chunks_per_block && | |
93 | state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { | |
94 | /* Read the tags and decide what to do */ | |
95 | chunk = blk * dev->param.chunks_per_block + c; | |
96 | ||
8cc64baf | 97 | yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); |
753ac610 CM |
98 | |
99 | /* Let's have a good look at this chunk... */ | |
100 | ||
101 | if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || | |
102 | tags.is_deleted) { | |
103 | /* YAFFS1 only... | |
104 | * A deleted chunk | |
105 | */ | |
106 | deleted++; | |
107 | dev->n_free_chunks++; | |
108 | } else if (!tags.chunk_used) { | |
109 | /* An unassigned chunk in the block | |
110 | * This means that either the block is empty or | |
111 | * this is the one being allocated from | |
112 | */ | |
113 | ||
114 | if (c == 0) { | |
115 | /* We're looking at the first chunk in | |
116 | *the block so the block is unused */ | |
117 | state = YAFFS_BLOCK_STATE_EMPTY; | |
118 | dev->n_erased_blocks++; | |
119 | } else { | |
120 | /* this is the block being allocated */ | |
121 | yaffs_trace(YAFFS_TRACE_SCAN, | |
122 | " Allocating from %d %d", | |
123 | blk, c); | |
124 | state = YAFFS_BLOCK_STATE_ALLOCATING; | |
125 | dev->alloc_block = blk; | |
126 | dev->alloc_page = c; | |
127 | dev->alloc_block_finder = blk; | |
128 | ||
129 | } | |
130 | ||
131 | dev->n_free_chunks += | |
132 | (dev->param.chunks_per_block - c); | |
133 | } else if (tags.chunk_id > 0) { | |
134 | /* chunk_id > 0 so it is a data chunk... */ | |
135 | unsigned int endpos; | |
136 | ||
137 | yaffs_set_chunk_bit(dev, blk, c); | |
138 | bi->pages_in_use++; | |
139 | ||
140 | in = yaffs_find_or_create_by_number(dev, | |
141 | tags.obj_id, | |
142 | YAFFS_OBJECT_TYPE_FILE); | |
143 | /* PutChunkIntoFile checks for a clash | |
144 | * (two data chunks with the same chunk_id). | |
145 | */ | |
146 | ||
147 | if (!in) | |
148 | alloc_failed = 1; | |
149 | ||
150 | if (in) { | |
151 | if (!yaffs_put_chunk_in_file | |
152 | (in, tags.chunk_id, chunk, 1)) | |
153 | alloc_failed = 1; | |
154 | } | |
155 | ||
156 | endpos = | |
157 | (tags.chunk_id - 1) * | |
158 | dev->data_bytes_per_chunk + | |
159 | tags.n_bytes; | |
160 | if (in && | |
161 | in->variant_type == | |
162 | YAFFS_OBJECT_TYPE_FILE && | |
163 | in->variant.file_variant.scanned_size < | |
164 | endpos) { | |
165 | in->variant.file_variant.scanned_size = | |
166 | endpos; | |
167 | if (!dev->param.use_header_file_size) { | |
168 | in->variant. | |
169 | file_variant.file_size = | |
170 | in->variant. | |
171 | file_variant.scanned_size; | |
172 | } | |
173 | ||
174 | } | |
175 | } else { | |
176 | /* chunk_id == 0, so it is an ObjectHeader. | |
177 | * Make the object | |
178 | */ | |
179 | yaffs_set_chunk_bit(dev, blk, c); | |
180 | bi->pages_in_use++; | |
181 | ||
8cc64baf AG |
182 | yaffs_rd_chunk_tags_nand(dev, chunk, |
183 | chunk_data, NULL); | |
753ac610 CM |
184 | |
185 | oh = (struct yaffs_obj_hdr *)chunk_data; | |
186 | ||
187 | in = yaffs_find_by_number(dev, tags.obj_id); | |
188 | if (in && in->variant_type != oh->type) { | |
189 | /* This should not happen, but somehow | |
190 | * Wev'e ended up with an obj_id that | |
191 | * has been reused but not yet deleted, | |
192 | * and worse still it has changed type. | |
193 | * Delete the old object. | |
194 | */ | |
195 | ||
196 | yaffs_del_obj(in); | |
197 | in = NULL; | |
198 | } | |
199 | ||
200 | in = yaffs_find_or_create_by_number(dev, | |
201 | tags.obj_id, | |
202 | oh->type); | |
203 | ||
204 | if (!in) | |
205 | alloc_failed = 1; | |
206 | ||
207 | if (in && oh->shadows_obj > 0) { | |
208 | ||
209 | struct yaffs_shadow_fixer *fixer; | |
210 | fixer = | |
211 | kmalloc(sizeof | |
212 | (struct yaffs_shadow_fixer), | |
213 | GFP_NOFS); | |
214 | if (fixer) { | |
215 | fixer->next = shadow_fixers; | |
216 | shadow_fixers = fixer; | |
217 | fixer->obj_id = tags.obj_id; | |
218 | fixer->shadowed_id = | |
219 | oh->shadows_obj; | |
220 | yaffs_trace(YAFFS_TRACE_SCAN, | |
221 | " Shadow fixer: %d shadows %d", | |
222 | fixer->obj_id, | |
223 | fixer->shadowed_id); | |
224 | ||
225 | } | |
226 | ||
227 | } | |
228 | ||
229 | if (in && in->valid) { | |
230 | /* We have already filled this one. | |
231 | * We have a duplicate and need to | |
232 | * resolve it. */ | |
233 | ||
234 | unsigned existing_serial = in->serial; | |
235 | unsigned new_serial = | |
236 | tags.serial_number; | |
237 | ||
238 | if (((existing_serial + 1) & 3) == | |
239 | new_serial) { | |
240 | /* Use new one - destroy the | |
241 | * exisiting one */ | |
242 | yaffs_chunk_del(dev, | |
243 | in->hdr_chunk, | |
244 | 1, __LINE__); | |
245 | in->valid = 0; | |
246 | } else { | |
247 | /* Use existing - destroy | |
248 | * this one. */ | |
249 | yaffs_chunk_del(dev, chunk, 1, | |
250 | __LINE__); | |
251 | } | |
252 | } | |
253 | ||
254 | if (in && !in->valid && | |
255 | (tags.obj_id == YAFFS_OBJECTID_ROOT || | |
256 | tags.obj_id == | |
257 | YAFFS_OBJECTID_LOSTNFOUND)) { | |
258 | /* We only load some info, don't fiddle | |
259 | * with directory structure */ | |
260 | in->valid = 1; | |
261 | in->variant_type = oh->type; | |
262 | ||
263 | in->yst_mode = oh->yst_mode; | |
264 | yaffs_load_attribs(in, oh); | |
265 | in->hdr_chunk = chunk; | |
266 | in->serial = tags.serial_number; | |
267 | ||
268 | } else if (in && !in->valid) { | |
269 | /* we need to load this info */ | |
270 | ||
271 | in->valid = 1; | |
272 | in->variant_type = oh->type; | |
273 | ||
274 | in->yst_mode = oh->yst_mode; | |
275 | yaffs_load_attribs(in, oh); | |
276 | in->hdr_chunk = chunk; | |
277 | in->serial = tags.serial_number; | |
278 | ||
279 | yaffs_set_obj_name_from_oh(in, oh); | |
280 | in->dirty = 0; | |
281 | ||
282 | /* directory stuff... | |
283 | * hook up to parent | |
284 | */ | |
285 | ||
286 | parent = | |
287 | yaffs_find_or_create_by_number | |
288 | (dev, oh->parent_obj_id, | |
289 | YAFFS_OBJECT_TYPE_DIRECTORY); | |
290 | if (!parent) | |
291 | alloc_failed = 1; | |
292 | if (parent && parent->variant_type == | |
293 | YAFFS_OBJECT_TYPE_UNKNOWN) { | |
294 | /* Set up as a directory */ | |
295 | parent->variant_type = | |
296 | YAFFS_OBJECT_TYPE_DIRECTORY; | |
297 | INIT_LIST_HEAD(&parent-> | |
298 | variant.dir_variant. | |
299 | children); | |
300 | } else if (!parent || | |
301 | parent->variant_type != | |
302 | YAFFS_OBJECT_TYPE_DIRECTORY) { | |
303 | /* Hoosterman, a problem.... | |
304 | * We're trying to use a | |
305 | * non-directory as a directory | |
306 | */ | |
307 | ||
308 | yaffs_trace(YAFFS_TRACE_ERROR, | |
309 | "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." | |
310 | ); | |
311 | parent = dev->lost_n_found; | |
312 | } | |
313 | ||
314 | yaffs_add_obj_to_dir(parent, in); | |
315 | ||
316 | switch (in->variant_type) { | |
317 | case YAFFS_OBJECT_TYPE_UNKNOWN: | |
318 | /* Todo got a problem */ | |
319 | break; | |
320 | case YAFFS_OBJECT_TYPE_FILE: | |
321 | if (dev->param. | |
322 | use_header_file_size) | |
323 | in->variant. | |
324 | file_variant.file_size | |
325 | = yaffs_oh_to_size(oh); | |
326 | break; | |
327 | case YAFFS_OBJECT_TYPE_HARDLINK: | |
328 | in->variant. | |
329 | hardlink_variant.equiv_id = | |
330 | oh->equiv_id; | |
331 | list_add(&in->hard_links, | |
332 | &hard_list); | |
333 | break; | |
334 | case YAFFS_OBJECT_TYPE_DIRECTORY: | |
335 | /* Do nothing */ | |
336 | break; | |
337 | case YAFFS_OBJECT_TYPE_SPECIAL: | |
338 | /* Do nothing */ | |
339 | break; | |
340 | case YAFFS_OBJECT_TYPE_SYMLINK: | |
341 | in->variant.symlink_variant. | |
342 | alias = | |
343 | yaffs_clone_str(oh->alias); | |
344 | if (!in->variant. | |
345 | symlink_variant.alias) | |
346 | alloc_failed = 1; | |
347 | break; | |
348 | } | |
349 | } | |
350 | } | |
351 | } | |
352 | ||
353 | if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { | |
354 | /* If we got this far while scanning, | |
355 | * then the block is fully allocated. */ | |
356 | state = YAFFS_BLOCK_STATE_FULL; | |
357 | } | |
358 | ||
359 | if (state == YAFFS_BLOCK_STATE_ALLOCATING) { | |
360 | /* If the block was partially allocated then | |
361 | * treat it as fully allocated. */ | |
362 | state = YAFFS_BLOCK_STATE_FULL; | |
363 | dev->alloc_block = -1; | |
364 | } | |
365 | ||
366 | bi->block_state = state; | |
367 | ||
368 | /* Now let's see if it was dirty */ | |
369 | if (bi->pages_in_use == 0 && | |
370 | !bi->has_shrink_hdr && | |
371 | bi->block_state == YAFFS_BLOCK_STATE_FULL) | |
372 | yaffs_block_became_dirty(dev, blk); | |
373 | } | |
374 | ||
375 | /* Ok, we've done all the scanning. | |
376 | * Fix up the hard link chains. | |
377 | * We should now have scanned all the objects, now it's time to add | |
378 | * these hardlinks. | |
379 | */ | |
380 | ||
381 | yaffs_link_fixup(dev, &hard_list); | |
382 | ||
383 | /* | |
384 | * Fix up any shadowed objects. | |
385 | * There should not be more than one of these. | |
386 | */ | |
387 | { | |
388 | struct yaffs_shadow_fixer *fixer; | |
389 | struct yaffs_obj *obj; | |
390 | ||
391 | while (shadow_fixers) { | |
392 | fixer = shadow_fixers; | |
393 | shadow_fixers = fixer->next; | |
394 | /* Complete the rename transaction by deleting the | |
395 | * shadowed object then setting the object header | |
396 | to unshadowed. | |
397 | */ | |
398 | obj = yaffs_find_by_number(dev, fixer->shadowed_id); | |
399 | if (obj) | |
400 | yaffs_del_obj(obj); | |
401 | ||
402 | obj = yaffs_find_by_number(dev, fixer->obj_id); | |
403 | ||
404 | if (obj) | |
405 | yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); | |
406 | ||
407 | kfree(fixer); | |
408 | } | |
409 | } | |
410 | ||
411 | yaffs_release_temp_buffer(dev, chunk_data); | |
412 | ||
413 | if (alloc_failed) | |
414 | return YAFFS_FAIL; | |
415 | ||
416 | yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); | |
417 | ||
418 | return YAFFS_OK; | |
419 | } |