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