1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2002-2011 Aleph One Ltd.
5*4882a593Smuzhiyun * for Toby Churchill Ltd and Brightstar Engineering
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Created by Charles Manning <charles@aleph1.co.uk>
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This program is free software; you can redistribute it and/or modify
10*4882a593Smuzhiyun * it under the terms of the GNU General Public License version 2 as
11*4882a593Smuzhiyun * published by the Free Software Foundation.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include "yaffs_guts.h"
15*4882a593Smuzhiyun #include "yaffs_trace.h"
16*4882a593Smuzhiyun #include "yaffs_yaffs2.h"
17*4882a593Smuzhiyun #include "yaffs_checkptrw.h"
18*4882a593Smuzhiyun #include "yaffs_bitmap.h"
19*4882a593Smuzhiyun #include "yaffs_nand.h"
20*4882a593Smuzhiyun #include "yaffs_getblockinfo.h"
21*4882a593Smuzhiyun #include "yaffs_verify.h"
22*4882a593Smuzhiyun #include "yaffs_attribs.h"
23*4882a593Smuzhiyun #include "yaffs_summary.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /*
26*4882a593Smuzhiyun * Checkpoints are really no benefit on very small partitions.
27*4882a593Smuzhiyun *
28*4882a593Smuzhiyun * To save space on small partitions don't bother with checkpoints unless
29*4882a593Smuzhiyun * the partition is at least this big.
30*4882a593Smuzhiyun */
31*4882a593Smuzhiyun #define YAFFS_CHECKPOINT_MIN_BLOCKS 60
32*4882a593Smuzhiyun #define YAFFS_SMALL_HOLE_THRESHOLD 4
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /*
35*4882a593Smuzhiyun * Oldest Dirty Sequence Number handling.
36*4882a593Smuzhiyun */
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun /* yaffs_calc_oldest_dirty_seq()
39*4882a593Smuzhiyun * yaffs2_find_oldest_dirty_seq()
40*4882a593Smuzhiyun * Calculate the oldest dirty sequence number if we don't know it.
41*4882a593Smuzhiyun */
yaffs_calc_oldest_dirty_seq(struct yaffs_dev * dev)42*4882a593Smuzhiyun void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun int i;
45*4882a593Smuzhiyun unsigned seq;
46*4882a593Smuzhiyun unsigned block_no = 0;
47*4882a593Smuzhiyun struct yaffs_block_info *b;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
50*4882a593Smuzhiyun return;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun /* Find the oldest dirty sequence number. */
53*4882a593Smuzhiyun seq = dev->seq_number + 1;
54*4882a593Smuzhiyun b = dev->block_info;
55*4882a593Smuzhiyun for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
56*4882a593Smuzhiyun if (b->block_state == YAFFS_BLOCK_STATE_FULL &&
57*4882a593Smuzhiyun (b->pages_in_use - b->soft_del_pages) <
58*4882a593Smuzhiyun dev->param.chunks_per_block &&
59*4882a593Smuzhiyun b->seq_number < seq) {
60*4882a593Smuzhiyun seq = b->seq_number;
61*4882a593Smuzhiyun block_no = i;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun b++;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun if (block_no) {
67*4882a593Smuzhiyun dev->oldest_dirty_seq = seq;
68*4882a593Smuzhiyun dev->oldest_dirty_block = block_no;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
yaffs2_find_oldest_dirty_seq(struct yaffs_dev * dev)72*4882a593Smuzhiyun void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
75*4882a593Smuzhiyun return;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun if (!dev->oldest_dirty_seq)
78*4882a593Smuzhiyun yaffs_calc_oldest_dirty_seq(dev);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun * yaffs_clear_oldest_dirty_seq()
83*4882a593Smuzhiyun * Called when a block is erased or marked bad. (ie. when its seq_number
84*4882a593Smuzhiyun * becomes invalid). If the value matches the oldest then we clear
85*4882a593Smuzhiyun * dev->oldest_dirty_seq to force its recomputation.
86*4882a593Smuzhiyun */
yaffs2_clear_oldest_dirty_seq(struct yaffs_dev * dev,struct yaffs_block_info * bi)87*4882a593Smuzhiyun void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev,
88*4882a593Smuzhiyun struct yaffs_block_info *bi)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
92*4882a593Smuzhiyun return;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if (!bi || bi->seq_number == dev->oldest_dirty_seq) {
95*4882a593Smuzhiyun dev->oldest_dirty_seq = 0;
96*4882a593Smuzhiyun dev->oldest_dirty_block = 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun /*
101*4882a593Smuzhiyun * yaffs2_update_oldest_dirty_seq()
102*4882a593Smuzhiyun * Update the oldest dirty sequence number whenever we dirty a block.
103*4882a593Smuzhiyun * Only do this if the oldest_dirty_seq is actually being tracked.
104*4882a593Smuzhiyun */
yaffs2_update_oldest_dirty_seq(struct yaffs_dev * dev,unsigned block_no,struct yaffs_block_info * bi)105*4882a593Smuzhiyun void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no,
106*4882a593Smuzhiyun struct yaffs_block_info *bi)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
109*4882a593Smuzhiyun return;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (dev->oldest_dirty_seq) {
112*4882a593Smuzhiyun if (dev->oldest_dirty_seq > bi->seq_number) {
113*4882a593Smuzhiyun dev->oldest_dirty_seq = bi->seq_number;
114*4882a593Smuzhiyun dev->oldest_dirty_block = block_no;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
yaffs_block_ok_for_gc(struct yaffs_dev * dev,struct yaffs_block_info * bi)119*4882a593Smuzhiyun int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
123*4882a593Smuzhiyun return 1; /* disqualification only applies to yaffs2. */
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun if (!bi->has_shrink_hdr)
126*4882a593Smuzhiyun return 1; /* can gc */
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun yaffs2_find_oldest_dirty_seq(dev);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /* Can't do gc of this block if there are any blocks older than this
131*4882a593Smuzhiyun * one that have discarded pages.
132*4882a593Smuzhiyun */
133*4882a593Smuzhiyun return (bi->seq_number <= dev->oldest_dirty_seq);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun /*
137*4882a593Smuzhiyun * yaffs2_find_refresh_block()
138*4882a593Smuzhiyun * periodically finds the oldest full block by sequence number for refreshing.
139*4882a593Smuzhiyun * Only for yaffs2.
140*4882a593Smuzhiyun */
yaffs2_find_refresh_block(struct yaffs_dev * dev)141*4882a593Smuzhiyun u32 yaffs2_find_refresh_block(struct yaffs_dev *dev)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun u32 b;
144*4882a593Smuzhiyun u32 oldest = 0;
145*4882a593Smuzhiyun u32 oldest_seq = 0;
146*4882a593Smuzhiyun struct yaffs_block_info *bi;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
149*4882a593Smuzhiyun return oldest;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun * If refresh period < 10 then refreshing is disabled.
153*4882a593Smuzhiyun */
154*4882a593Smuzhiyun if (dev->param.refresh_period < 10)
155*4882a593Smuzhiyun return oldest;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun /*
158*4882a593Smuzhiyun * Fix broken values.
159*4882a593Smuzhiyun */
160*4882a593Smuzhiyun if (dev->refresh_skip > dev->param.refresh_period)
161*4882a593Smuzhiyun dev->refresh_skip = dev->param.refresh_period;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (dev->refresh_skip > 0)
164*4882a593Smuzhiyun return oldest;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun /*
167*4882a593Smuzhiyun * Refresh skip is now zero.
168*4882a593Smuzhiyun * We'll do a refresh this time around....
169*4882a593Smuzhiyun * Update the refresh skip and find the oldest block.
170*4882a593Smuzhiyun */
171*4882a593Smuzhiyun dev->refresh_skip = dev->param.refresh_period;
172*4882a593Smuzhiyun dev->refresh_count++;
173*4882a593Smuzhiyun bi = dev->block_info;
174*4882a593Smuzhiyun for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) {
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (bi->block_state == YAFFS_BLOCK_STATE_FULL) {
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun if (oldest < 1 || bi->seq_number < oldest_seq) {
179*4882a593Smuzhiyun oldest = b;
180*4882a593Smuzhiyun oldest_seq = bi->seq_number;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun bi++;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun if (oldest > 0) {
187*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_GC,
188*4882a593Smuzhiyun "GC refresh count %d selected block %d with seq_number %d",
189*4882a593Smuzhiyun dev->refresh_count, oldest, oldest_seq);
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun return oldest;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun
yaffs2_checkpt_required(struct yaffs_dev * dev)195*4882a593Smuzhiyun int yaffs2_checkpt_required(struct yaffs_dev *dev)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun int nblocks;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
200*4882a593Smuzhiyun return 0;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun nblocks = dev->internal_end_block - dev->internal_start_block + 1;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun return !dev->param.skip_checkpt_wr &&
205*4882a593Smuzhiyun !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
yaffs_calc_checkpt_blocks_required(struct yaffs_dev * dev)208*4882a593Smuzhiyun int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun int retval;
211*4882a593Smuzhiyun int n_bytes = 0;
212*4882a593Smuzhiyun int n_blocks;
213*4882a593Smuzhiyun int dev_blocks;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
216*4882a593Smuzhiyun return 0;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) {
219*4882a593Smuzhiyun /* Not a valid value so recalculate */
220*4882a593Smuzhiyun dev_blocks = dev->param.end_block - dev->param.start_block + 1;
221*4882a593Smuzhiyun n_bytes += sizeof(struct yaffs_checkpt_validity);
222*4882a593Smuzhiyun n_bytes += sizeof(struct yaffs_checkpt_dev);
223*4882a593Smuzhiyun n_bytes += dev_blocks * sizeof(struct yaffs_block_info);
224*4882a593Smuzhiyun n_bytes += dev_blocks * dev->chunk_bit_stride;
225*4882a593Smuzhiyun n_bytes +=
226*4882a593Smuzhiyun (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) *
227*4882a593Smuzhiyun dev->n_obj;
228*4882a593Smuzhiyun n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes;
229*4882a593Smuzhiyun n_bytes += sizeof(struct yaffs_checkpt_validity);
230*4882a593Smuzhiyun n_bytes += sizeof(u32); /* checksum */
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun /* Round up and add 2 blocks to allow for some bad blocks,
233*4882a593Smuzhiyun * so add 3 */
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun n_blocks =
236*4882a593Smuzhiyun (n_bytes /
237*4882a593Smuzhiyun (dev->data_bytes_per_chunk *
238*4882a593Smuzhiyun dev->param.chunks_per_block)) + 3;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun dev->checkpoint_blocks_required = n_blocks;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt;
244*4882a593Smuzhiyun if (retval < 0)
245*4882a593Smuzhiyun retval = 0;
246*4882a593Smuzhiyun return retval;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun /*--------------------- Checkpointing --------------------*/
250*4882a593Smuzhiyun
yaffs2_wr_checkpt_validity_marker(struct yaffs_dev * dev,int head)251*4882a593Smuzhiyun static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun struct yaffs_checkpt_validity cp;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun memset(&cp, 0, sizeof(cp));
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun cp.struct_type = sizeof(cp);
258*4882a593Smuzhiyun cp.magic = YAFFS_MAGIC;
259*4882a593Smuzhiyun cp.version = YAFFS_CHECKPOINT_VERSION;
260*4882a593Smuzhiyun cp.head = (head) ? 1 : 0;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
yaffs2_rd_checkpt_validity_marker(struct yaffs_dev * dev,int head)265*4882a593Smuzhiyun static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun struct yaffs_checkpt_validity cp;
268*4882a593Smuzhiyun int ok;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun if (ok)
273*4882a593Smuzhiyun ok = (cp.struct_type == sizeof(cp)) &&
274*4882a593Smuzhiyun (cp.magic == YAFFS_MAGIC) &&
275*4882a593Smuzhiyun (cp.version == YAFFS_CHECKPOINT_VERSION) &&
276*4882a593Smuzhiyun (cp.head == ((head) ? 1 : 0));
277*4882a593Smuzhiyun return ok ? 1 : 0;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev * cp,struct yaffs_dev * dev)280*4882a593Smuzhiyun static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp,
281*4882a593Smuzhiyun struct yaffs_dev *dev)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun cp->n_erased_blocks = dev->n_erased_blocks;
284*4882a593Smuzhiyun cp->alloc_block = dev->alloc_block;
285*4882a593Smuzhiyun cp->alloc_page = dev->alloc_page;
286*4882a593Smuzhiyun cp->n_free_chunks = dev->n_free_chunks;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun cp->n_deleted_files = dev->n_deleted_files;
289*4882a593Smuzhiyun cp->n_unlinked_files = dev->n_unlinked_files;
290*4882a593Smuzhiyun cp->n_bg_deletions = dev->n_bg_deletions;
291*4882a593Smuzhiyun cp->seq_number = dev->seq_number;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
yaffs_checkpt_dev_to_dev(struct yaffs_dev * dev,struct yaffs_checkpt_dev * cp)295*4882a593Smuzhiyun static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev,
296*4882a593Smuzhiyun struct yaffs_checkpt_dev *cp)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun dev->n_erased_blocks = cp->n_erased_blocks;
299*4882a593Smuzhiyun dev->alloc_block = cp->alloc_block;
300*4882a593Smuzhiyun dev->alloc_page = cp->alloc_page;
301*4882a593Smuzhiyun dev->n_free_chunks = cp->n_free_chunks;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun dev->n_deleted_files = cp->n_deleted_files;
304*4882a593Smuzhiyun dev->n_unlinked_files = cp->n_unlinked_files;
305*4882a593Smuzhiyun dev->n_bg_deletions = cp->n_bg_deletions;
306*4882a593Smuzhiyun dev->seq_number = cp->seq_number;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
yaffs2_wr_checkpt_dev(struct yaffs_dev * dev)309*4882a593Smuzhiyun static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun struct yaffs_checkpt_dev cp;
312*4882a593Smuzhiyun u32 n_bytes;
313*4882a593Smuzhiyun u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
314*4882a593Smuzhiyun int ok;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun /* Write device runtime values */
317*4882a593Smuzhiyun yaffs2_dev_to_checkpt_dev(&cp, dev);
318*4882a593Smuzhiyun cp.struct_type = sizeof(cp);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
321*4882a593Smuzhiyun if (!ok)
322*4882a593Smuzhiyun return 0;
323*4882a593Smuzhiyun
324*4882a593Smuzhiyun /* Write block info */
325*4882a593Smuzhiyun n_bytes = n_blocks * sizeof(struct yaffs_block_info);
326*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes);
327*4882a593Smuzhiyun if (!ok)
328*4882a593Smuzhiyun return 0;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun /* Write chunk bits */
331*4882a593Smuzhiyun n_bytes = n_blocks * dev->chunk_bit_stride;
332*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes);
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun return ok ? 1 : 0;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
yaffs2_rd_checkpt_dev(struct yaffs_dev * dev)337*4882a593Smuzhiyun static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun struct yaffs_checkpt_dev cp;
340*4882a593Smuzhiyun u32 n_bytes;
341*4882a593Smuzhiyun u32 n_blocks =
342*4882a593Smuzhiyun (dev->internal_end_block - dev->internal_start_block + 1);
343*4882a593Smuzhiyun int ok;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
346*4882a593Smuzhiyun if (!ok)
347*4882a593Smuzhiyun return 0;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun if (cp.struct_type != sizeof(cp))
350*4882a593Smuzhiyun return 0;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun yaffs_checkpt_dev_to_dev(dev, &cp);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun n_bytes = n_blocks * sizeof(struct yaffs_block_info);
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes);
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun if (!ok)
359*4882a593Smuzhiyun return 0;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun n_bytes = n_blocks * dev->chunk_bit_stride;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun return ok ? 1 : 0;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun
yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj * cp,struct yaffs_obj * obj)368*4882a593Smuzhiyun static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp,
369*4882a593Smuzhiyun struct yaffs_obj *obj)
370*4882a593Smuzhiyun {
371*4882a593Smuzhiyun cp->obj_id = obj->obj_id;
372*4882a593Smuzhiyun cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0;
373*4882a593Smuzhiyun cp->hdr_chunk = obj->hdr_chunk;
374*4882a593Smuzhiyun cp->variant_type = obj->variant_type;
375*4882a593Smuzhiyun cp->deleted = obj->deleted;
376*4882a593Smuzhiyun cp->soft_del = obj->soft_del;
377*4882a593Smuzhiyun cp->unlinked = obj->unlinked;
378*4882a593Smuzhiyun cp->fake = obj->fake;
379*4882a593Smuzhiyun cp->rename_allowed = obj->rename_allowed;
380*4882a593Smuzhiyun cp->unlink_allowed = obj->unlink_allowed;
381*4882a593Smuzhiyun cp->serial = obj->serial;
382*4882a593Smuzhiyun cp->n_data_chunks = obj->n_data_chunks;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
385*4882a593Smuzhiyun cp->size_or_equiv_obj = obj->variant.file_variant.file_size;
386*4882a593Smuzhiyun else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
387*4882a593Smuzhiyun cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
yaffs2_checkpt_obj_to_obj(struct yaffs_obj * obj,struct yaffs_checkpt_obj * cp)390*4882a593Smuzhiyun static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj,
391*4882a593Smuzhiyun struct yaffs_checkpt_obj *cp)
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun struct yaffs_obj *parent;
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun if (obj->variant_type != cp->variant_type) {
396*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_ERROR,
397*4882a593Smuzhiyun "Checkpoint read object %d type %d chunk %d does not match existing object type %d",
398*4882a593Smuzhiyun cp->obj_id, cp->variant_type, cp->hdr_chunk,
399*4882a593Smuzhiyun obj->variant_type);
400*4882a593Smuzhiyun return 0;
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun obj->obj_id = cp->obj_id;
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun if (cp->parent_id)
406*4882a593Smuzhiyun parent = yaffs_find_or_create_by_number(obj->my_dev,
407*4882a593Smuzhiyun cp->parent_id,
408*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_DIRECTORY);
409*4882a593Smuzhiyun else
410*4882a593Smuzhiyun parent = NULL;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun if (parent) {
413*4882a593Smuzhiyun if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) {
414*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_ALWAYS,
415*4882a593Smuzhiyun "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory",
416*4882a593Smuzhiyun cp->obj_id, cp->parent_id,
417*4882a593Smuzhiyun cp->variant_type, cp->hdr_chunk,
418*4882a593Smuzhiyun parent->variant_type);
419*4882a593Smuzhiyun return 0;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun yaffs_add_obj_to_dir(parent, obj);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun obj->hdr_chunk = cp->hdr_chunk;
425*4882a593Smuzhiyun obj->variant_type = cp->variant_type;
426*4882a593Smuzhiyun obj->deleted = cp->deleted;
427*4882a593Smuzhiyun obj->soft_del = cp->soft_del;
428*4882a593Smuzhiyun obj->unlinked = cp->unlinked;
429*4882a593Smuzhiyun obj->fake = cp->fake;
430*4882a593Smuzhiyun obj->rename_allowed = cp->rename_allowed;
431*4882a593Smuzhiyun obj->unlink_allowed = cp->unlink_allowed;
432*4882a593Smuzhiyun obj->serial = cp->serial;
433*4882a593Smuzhiyun obj->n_data_chunks = cp->n_data_chunks;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE)
436*4882a593Smuzhiyun obj->variant.file_variant.file_size = cp->size_or_equiv_obj;
437*4882a593Smuzhiyun else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK)
438*4882a593Smuzhiyun obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun if (obj->hdr_chunk > 0)
441*4882a593Smuzhiyun obj->lazy_loaded = 1;
442*4882a593Smuzhiyun return 1;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun
yaffs2_checkpt_tnode_worker(struct yaffs_obj * in,struct yaffs_tnode * tn,u32 level,int chunk_offset)445*4882a593Smuzhiyun static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in,
446*4882a593Smuzhiyun struct yaffs_tnode *tn, u32 level,
447*4882a593Smuzhiyun int chunk_offset)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun int i;
450*4882a593Smuzhiyun struct yaffs_dev *dev = in->my_dev;
451*4882a593Smuzhiyun int ok = 1;
452*4882a593Smuzhiyun u32 base_offset;
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun if (!tn)
455*4882a593Smuzhiyun return 1;
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun if (level > 0) {
458*4882a593Smuzhiyun for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) {
459*4882a593Smuzhiyun if (!tn->internal[i])
460*4882a593Smuzhiyun continue;
461*4882a593Smuzhiyun ok = yaffs2_checkpt_tnode_worker(in,
462*4882a593Smuzhiyun tn->internal[i],
463*4882a593Smuzhiyun level - 1,
464*4882a593Smuzhiyun (chunk_offset <<
465*4882a593Smuzhiyun YAFFS_TNODES_INTERNAL_BITS) + i);
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun return ok;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun /* Level 0 tnode */
471*4882a593Smuzhiyun base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS;
472*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) ==
473*4882a593Smuzhiyun sizeof(base_offset));
474*4882a593Smuzhiyun if (ok)
475*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) ==
476*4882a593Smuzhiyun dev->tnode_size);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun return ok;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun
yaffs2_wr_checkpt_tnodes(struct yaffs_obj * obj)481*4882a593Smuzhiyun static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun u32 end_marker = ~0;
484*4882a593Smuzhiyun int ok = 1;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
487*4882a593Smuzhiyun return ok;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun ok = yaffs2_checkpt_tnode_worker(obj,
490*4882a593Smuzhiyun obj->variant.file_variant.top,
491*4882a593Smuzhiyun obj->variant.file_variant.
492*4882a593Smuzhiyun top_level, 0);
493*4882a593Smuzhiyun if (ok)
494*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker,
495*4882a593Smuzhiyun sizeof(end_marker)) == sizeof(end_marker));
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun return ok ? 1 : 0;
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
yaffs2_rd_checkpt_tnodes(struct yaffs_obj * obj)500*4882a593Smuzhiyun static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun u32 base_chunk;
503*4882a593Smuzhiyun int ok = 1;
504*4882a593Smuzhiyun struct yaffs_dev *dev = obj->my_dev;
505*4882a593Smuzhiyun struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant;
506*4882a593Smuzhiyun struct yaffs_tnode *tn;
507*4882a593Smuzhiyun int nread = 0;
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) ==
510*4882a593Smuzhiyun sizeof(base_chunk));
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun while (ok && (~base_chunk)) {
513*4882a593Smuzhiyun nread++;
514*4882a593Smuzhiyun /* Read level 0 tnode */
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun tn = yaffs_get_tnode(dev);
517*4882a593Smuzhiyun if (tn)
518*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) ==
519*4882a593Smuzhiyun dev->tnode_size);
520*4882a593Smuzhiyun else
521*4882a593Smuzhiyun ok = 0;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun if (tn && ok)
524*4882a593Smuzhiyun ok = yaffs_add_find_tnode_0(dev,
525*4882a593Smuzhiyun file_stuct_ptr,
526*4882a593Smuzhiyun base_chunk, tn) ? 1 : 0;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun if (ok)
529*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd
530*4882a593Smuzhiyun (dev, &base_chunk,
531*4882a593Smuzhiyun sizeof(base_chunk)) == sizeof(base_chunk));
532*4882a593Smuzhiyun }
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
535*4882a593Smuzhiyun "Checkpoint read tnodes %d records, last %d. ok %d",
536*4882a593Smuzhiyun nread, base_chunk, ok);
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun return ok ? 1 : 0;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun
yaffs2_wr_checkpt_objs(struct yaffs_dev * dev)541*4882a593Smuzhiyun static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev)
542*4882a593Smuzhiyun {
543*4882a593Smuzhiyun struct yaffs_obj *obj;
544*4882a593Smuzhiyun struct yaffs_checkpt_obj cp;
545*4882a593Smuzhiyun int i;
546*4882a593Smuzhiyun int ok = 1;
547*4882a593Smuzhiyun struct list_head *lh;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun /* Iterate through the objects in each hash entry,
550*4882a593Smuzhiyun * dumping them to the checkpointing stream.
551*4882a593Smuzhiyun */
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) {
554*4882a593Smuzhiyun list_for_each(lh, &dev->obj_bucket[i].list) {
555*4882a593Smuzhiyun obj = list_entry(lh, struct yaffs_obj, hash_link);
556*4882a593Smuzhiyun if (!obj->defered_free) {
557*4882a593Smuzhiyun yaffs2_obj_checkpt_obj(&cp, obj);
558*4882a593Smuzhiyun cp.struct_type = sizeof(cp);
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
561*4882a593Smuzhiyun "Checkpoint write object %d parent %d type %d chunk %d obj addr %p",
562*4882a593Smuzhiyun cp.obj_id, cp.parent_id,
563*4882a593Smuzhiyun cp.variant_type, cp.hdr_chunk, obj);
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, &cp,
566*4882a593Smuzhiyun sizeof(cp)) == sizeof(cp));
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun if (ok &&
569*4882a593Smuzhiyun obj->variant_type ==
570*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_FILE)
571*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_tnodes(obj);
572*4882a593Smuzhiyun }
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun /* Dump end of list */
577*4882a593Smuzhiyun memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj));
578*4882a593Smuzhiyun cp.struct_type = sizeof(cp);
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun if (ok)
581*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp));
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun return ok ? 1 : 0;
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun
yaffs2_rd_checkpt_objs(struct yaffs_dev * dev)586*4882a593Smuzhiyun static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun struct yaffs_obj *obj;
589*4882a593Smuzhiyun struct yaffs_checkpt_obj cp;
590*4882a593Smuzhiyun int ok = 1;
591*4882a593Smuzhiyun int done = 0;
592*4882a593Smuzhiyun LIST_HEAD(hard_list);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun while (ok && !done) {
596*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp));
597*4882a593Smuzhiyun if (cp.struct_type != sizeof(cp)) {
598*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
599*4882a593Smuzhiyun "struct size %d instead of %d ok %d",
600*4882a593Smuzhiyun cp.struct_type, (int)sizeof(cp), ok);
601*4882a593Smuzhiyun ok = 0;
602*4882a593Smuzhiyun }
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
605*4882a593Smuzhiyun "Checkpoint read object %d parent %d type %d chunk %d ",
606*4882a593Smuzhiyun cp.obj_id, cp.parent_id, cp.variant_type,
607*4882a593Smuzhiyun cp.hdr_chunk);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun if (ok && cp.obj_id == ~0) {
610*4882a593Smuzhiyun done = 1;
611*4882a593Smuzhiyun } else if (ok) {
612*4882a593Smuzhiyun obj =
613*4882a593Smuzhiyun yaffs_find_or_create_by_number(dev, cp.obj_id,
614*4882a593Smuzhiyun cp.variant_type);
615*4882a593Smuzhiyun if (obj) {
616*4882a593Smuzhiyun ok = yaffs2_checkpt_obj_to_obj(obj, &cp);
617*4882a593Smuzhiyun if (!ok)
618*4882a593Smuzhiyun break;
619*4882a593Smuzhiyun if (obj->variant_type ==
620*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_FILE) {
621*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_tnodes(obj);
622*4882a593Smuzhiyun } else if (obj->variant_type ==
623*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_HARDLINK) {
624*4882a593Smuzhiyun list_add(&obj->hard_links, &hard_list);
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun } else {
627*4882a593Smuzhiyun ok = 0;
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun if (ok)
633*4882a593Smuzhiyun yaffs_link_fixup(dev, &hard_list);
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun return ok ? 1 : 0;
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun
yaffs2_wr_checkpt_sum(struct yaffs_dev * dev)638*4882a593Smuzhiyun static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev)
639*4882a593Smuzhiyun {
640*4882a593Smuzhiyun u32 checkpt_sum;
641*4882a593Smuzhiyun int ok;
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun yaffs2_get_checkpt_sum(dev, &checkpt_sum);
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) ==
646*4882a593Smuzhiyun sizeof(checkpt_sum));
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun if (!ok)
649*4882a593Smuzhiyun return 0;
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun return 1;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun
yaffs2_rd_checkpt_sum(struct yaffs_dev * dev)654*4882a593Smuzhiyun static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun u32 checkpt_sum0;
657*4882a593Smuzhiyun u32 checkpt_sum1;
658*4882a593Smuzhiyun int ok;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun yaffs2_get_checkpt_sum(dev, &checkpt_sum0);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) ==
663*4882a593Smuzhiyun sizeof(checkpt_sum1));
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun if (!ok)
666*4882a593Smuzhiyun return 0;
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun if (checkpt_sum0 != checkpt_sum1)
669*4882a593Smuzhiyun return 0;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun return 1;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
yaffs2_wr_checkpt_data(struct yaffs_dev * dev)674*4882a593Smuzhiyun static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun int ok = 1;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun if (!yaffs2_checkpt_required(dev)) {
679*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
680*4882a593Smuzhiyun "skipping checkpoint write");
681*4882a593Smuzhiyun ok = 0;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun
684*4882a593Smuzhiyun if (ok)
685*4882a593Smuzhiyun ok = yaffs2_checkpt_open(dev, 1);
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun if (ok) {
688*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
689*4882a593Smuzhiyun "write checkpoint validity");
690*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_validity_marker(dev, 1);
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun if (ok) {
693*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
694*4882a593Smuzhiyun "write checkpoint device");
695*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_dev(dev);
696*4882a593Smuzhiyun }
697*4882a593Smuzhiyun if (ok) {
698*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
699*4882a593Smuzhiyun "write checkpoint objects");
700*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_objs(dev);
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun if (ok) {
703*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
704*4882a593Smuzhiyun "write checkpoint validity");
705*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_validity_marker(dev, 0);
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun if (ok)
709*4882a593Smuzhiyun ok = yaffs2_wr_checkpt_sum(dev);
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun if (!yaffs_checkpt_close(dev))
712*4882a593Smuzhiyun ok = 0;
713*4882a593Smuzhiyun
714*4882a593Smuzhiyun if (ok)
715*4882a593Smuzhiyun dev->is_checkpointed = 1;
716*4882a593Smuzhiyun else
717*4882a593Smuzhiyun dev->is_checkpointed = 0;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun return dev->is_checkpointed;
720*4882a593Smuzhiyun }
721*4882a593Smuzhiyun
yaffs2_rd_checkpt_data(struct yaffs_dev * dev)722*4882a593Smuzhiyun static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev)
723*4882a593Smuzhiyun {
724*4882a593Smuzhiyun int ok = 1;
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
727*4882a593Smuzhiyun ok = 0;
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun if (ok && dev->param.skip_checkpt_rd) {
730*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
731*4882a593Smuzhiyun "skipping checkpoint read");
732*4882a593Smuzhiyun ok = 0;
733*4882a593Smuzhiyun }
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun if (ok)
736*4882a593Smuzhiyun ok = yaffs2_checkpt_open(dev, 0); /* open for read */
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun if (ok) {
739*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
740*4882a593Smuzhiyun "read checkpoint validity");
741*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_validity_marker(dev, 1);
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun if (ok) {
744*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
745*4882a593Smuzhiyun "read checkpoint device");
746*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_dev(dev);
747*4882a593Smuzhiyun }
748*4882a593Smuzhiyun if (ok) {
749*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
750*4882a593Smuzhiyun "read checkpoint objects");
751*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_objs(dev);
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun if (ok) {
754*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
755*4882a593Smuzhiyun "read checkpoint validity");
756*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_validity_marker(dev, 0);
757*4882a593Smuzhiyun }
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun if (ok) {
760*4882a593Smuzhiyun ok = yaffs2_rd_checkpt_sum(dev);
761*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
762*4882a593Smuzhiyun "read checkpoint checksum %d", ok);
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun if (!yaffs_checkpt_close(dev))
766*4882a593Smuzhiyun ok = 0;
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun if (ok)
769*4882a593Smuzhiyun dev->is_checkpointed = 1;
770*4882a593Smuzhiyun else
771*4882a593Smuzhiyun dev->is_checkpointed = 0;
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun return ok ? 1 : 0;
774*4882a593Smuzhiyun }
775*4882a593Smuzhiyun
yaffs2_checkpt_invalidate(struct yaffs_dev * dev)776*4882a593Smuzhiyun void yaffs2_checkpt_invalidate(struct yaffs_dev *dev)
777*4882a593Smuzhiyun {
778*4882a593Smuzhiyun if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) {
779*4882a593Smuzhiyun dev->is_checkpointed = 0;
780*4882a593Smuzhiyun yaffs2_checkpt_invalidate_stream(dev);
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun if (dev->param.sb_dirty_fn)
783*4882a593Smuzhiyun dev->param.sb_dirty_fn(dev);
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun
yaffs_checkpoint_save(struct yaffs_dev * dev)786*4882a593Smuzhiyun int yaffs_checkpoint_save(struct yaffs_dev *dev)
787*4882a593Smuzhiyun {
788*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
789*4882a593Smuzhiyun "save entry: is_checkpointed %d",
790*4882a593Smuzhiyun dev->is_checkpointed);
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun yaffs_verify_objects(dev);
793*4882a593Smuzhiyun yaffs_verify_blocks(dev);
794*4882a593Smuzhiyun yaffs_verify_free_chunks(dev);
795*4882a593Smuzhiyun
796*4882a593Smuzhiyun if (!dev->is_checkpointed) {
797*4882a593Smuzhiyun yaffs2_checkpt_invalidate(dev);
798*4882a593Smuzhiyun yaffs2_wr_checkpt_data(dev);
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT,
802*4882a593Smuzhiyun "save exit: is_checkpointed %d",
803*4882a593Smuzhiyun dev->is_checkpointed);
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun return dev->is_checkpointed;
806*4882a593Smuzhiyun }
807*4882a593Smuzhiyun
yaffs2_checkpt_restore(struct yaffs_dev * dev)808*4882a593Smuzhiyun int yaffs2_checkpt_restore(struct yaffs_dev *dev)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun int retval;
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
813*4882a593Smuzhiyun "restore entry: is_checkpointed %d",
814*4882a593Smuzhiyun dev->is_checkpointed);
815*4882a593Smuzhiyun
816*4882a593Smuzhiyun retval = yaffs2_rd_checkpt_data(dev);
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun if (dev->is_checkpointed) {
819*4882a593Smuzhiyun yaffs_verify_objects(dev);
820*4882a593Smuzhiyun yaffs_verify_blocks(dev);
821*4882a593Smuzhiyun yaffs_verify_free_chunks(dev);
822*4882a593Smuzhiyun }
823*4882a593Smuzhiyun
824*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_CHECKPOINT,
825*4882a593Smuzhiyun "restore exit: is_checkpointed %d",
826*4882a593Smuzhiyun dev->is_checkpointed);
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun return retval;
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun
yaffs2_handle_hole(struct yaffs_obj * obj,loff_t new_size)831*4882a593Smuzhiyun int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size)
832*4882a593Smuzhiyun {
833*4882a593Smuzhiyun /* if new_size > old_file_size.
834*4882a593Smuzhiyun * We're going to be writing a hole.
835*4882a593Smuzhiyun * If the hole is small then write zeros otherwise write a start
836*4882a593Smuzhiyun * of hole marker.
837*4882a593Smuzhiyun */
838*4882a593Smuzhiyun loff_t old_file_size;
839*4882a593Smuzhiyun loff_t increase;
840*4882a593Smuzhiyun int small_hole;
841*4882a593Smuzhiyun int result = YAFFS_OK;
842*4882a593Smuzhiyun struct yaffs_dev *dev = NULL;
843*4882a593Smuzhiyun u8 *local_buffer = NULL;
844*4882a593Smuzhiyun int small_increase_ok = 0;
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun if (!obj)
847*4882a593Smuzhiyun return YAFFS_FAIL;
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE)
850*4882a593Smuzhiyun return YAFFS_FAIL;
851*4882a593Smuzhiyun
852*4882a593Smuzhiyun dev = obj->my_dev;
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun /* Bail out if not yaffs2 mode */
855*4882a593Smuzhiyun if (!dev->param.is_yaffs2)
856*4882a593Smuzhiyun return YAFFS_OK;
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun old_file_size = obj->variant.file_variant.file_size;
859*4882a593Smuzhiyun
860*4882a593Smuzhiyun if (new_size <= old_file_size)
861*4882a593Smuzhiyun return YAFFS_OK;
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun increase = new_size - old_file_size;
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk &&
866*4882a593Smuzhiyun yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
867*4882a593Smuzhiyun small_hole = 1;
868*4882a593Smuzhiyun else
869*4882a593Smuzhiyun small_hole = 0;
870*4882a593Smuzhiyun
871*4882a593Smuzhiyun if (small_hole)
872*4882a593Smuzhiyun local_buffer = yaffs_get_temp_buffer(dev);
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun if (local_buffer) {
875*4882a593Smuzhiyun /* fill hole with zero bytes */
876*4882a593Smuzhiyun loff_t pos = old_file_size;
877*4882a593Smuzhiyun int this_write;
878*4882a593Smuzhiyun int written;
879*4882a593Smuzhiyun memset(local_buffer, 0, dev->data_bytes_per_chunk);
880*4882a593Smuzhiyun small_increase_ok = 1;
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun while (increase > 0 && small_increase_ok) {
883*4882a593Smuzhiyun this_write = increase;
884*4882a593Smuzhiyun if (this_write > dev->data_bytes_per_chunk)
885*4882a593Smuzhiyun this_write = dev->data_bytes_per_chunk;
886*4882a593Smuzhiyun written =
887*4882a593Smuzhiyun yaffs_do_file_wr(obj, local_buffer, pos, this_write,
888*4882a593Smuzhiyun 0);
889*4882a593Smuzhiyun if (written == this_write) {
890*4882a593Smuzhiyun pos += this_write;
891*4882a593Smuzhiyun increase -= this_write;
892*4882a593Smuzhiyun } else {
893*4882a593Smuzhiyun small_increase_ok = 0;
894*4882a593Smuzhiyun }
895*4882a593Smuzhiyun }
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun yaffs_release_temp_buffer(dev, local_buffer);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun /* If out of space then reverse any chunks we've added */
900*4882a593Smuzhiyun if (!small_increase_ok)
901*4882a593Smuzhiyun yaffs_resize_file_down(obj, old_file_size);
902*4882a593Smuzhiyun }
903*4882a593Smuzhiyun
904*4882a593Smuzhiyun if (!small_increase_ok &&
905*4882a593Smuzhiyun obj->parent &&
906*4882a593Smuzhiyun obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED &&
907*4882a593Smuzhiyun obj->parent->obj_id != YAFFS_OBJECTID_DELETED) {
908*4882a593Smuzhiyun /* Write a hole start header with the old file size */
909*4882a593Smuzhiyun yaffs_update_oh(obj, NULL, 0, 1, 0, NULL);
910*4882a593Smuzhiyun }
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun return result;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun
915*4882a593Smuzhiyun struct yaffs_block_index {
916*4882a593Smuzhiyun int seq;
917*4882a593Smuzhiyun int block;
918*4882a593Smuzhiyun };
919*4882a593Smuzhiyun
yaffs2_ybicmp(const void * a,const void * b)920*4882a593Smuzhiyun static int yaffs2_ybicmp(const void *a, const void *b)
921*4882a593Smuzhiyun {
922*4882a593Smuzhiyun int aseq = ((struct yaffs_block_index *)a)->seq;
923*4882a593Smuzhiyun int bseq = ((struct yaffs_block_index *)b)->seq;
924*4882a593Smuzhiyun int ablock = ((struct yaffs_block_index *)a)->block;
925*4882a593Smuzhiyun int bblock = ((struct yaffs_block_index *)b)->block;
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun if (aseq == bseq)
928*4882a593Smuzhiyun return ablock - bblock;
929*4882a593Smuzhiyun
930*4882a593Smuzhiyun return aseq - bseq;
931*4882a593Smuzhiyun }
932*4882a593Smuzhiyun
yaffs2_scan_chunk(struct yaffs_dev * dev,struct yaffs_block_info * bi,int blk,int chunk_in_block,int * found_chunks,u8 * chunk_data,struct list_head * hard_list,int summary_available)933*4882a593Smuzhiyun static inline int yaffs2_scan_chunk(struct yaffs_dev *dev,
934*4882a593Smuzhiyun struct yaffs_block_info *bi,
935*4882a593Smuzhiyun int blk, int chunk_in_block,
936*4882a593Smuzhiyun int *found_chunks,
937*4882a593Smuzhiyun u8 *chunk_data,
938*4882a593Smuzhiyun struct list_head *hard_list,
939*4882a593Smuzhiyun int summary_available)
940*4882a593Smuzhiyun {
941*4882a593Smuzhiyun struct yaffs_obj_hdr *oh;
942*4882a593Smuzhiyun struct yaffs_obj *in;
943*4882a593Smuzhiyun struct yaffs_obj *parent;
944*4882a593Smuzhiyun int equiv_id;
945*4882a593Smuzhiyun loff_t file_size;
946*4882a593Smuzhiyun int is_shrink;
947*4882a593Smuzhiyun int is_unlinked;
948*4882a593Smuzhiyun struct yaffs_ext_tags tags;
949*4882a593Smuzhiyun int alloc_failed = 0;
950*4882a593Smuzhiyun int chunk = blk * dev->param.chunks_per_block + chunk_in_block;
951*4882a593Smuzhiyun struct yaffs_file_var *file_var;
952*4882a593Smuzhiyun struct yaffs_hardlink_var *hl_var;
953*4882a593Smuzhiyun struct yaffs_symlink_var *sl_var;
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun if (summary_available) {
956*4882a593Smuzhiyun yaffs_summary_fetch(dev, &tags, chunk_in_block);
957*4882a593Smuzhiyun tags.seq_number = bi->seq_number;
958*4882a593Smuzhiyun }
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun if (!summary_available || tags.obj_id == 0) {
961*4882a593Smuzhiyun yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags);
962*4882a593Smuzhiyun dev->tags_used++;
963*4882a593Smuzhiyun } else {
964*4882a593Smuzhiyun dev->summary_used++;
965*4882a593Smuzhiyun }
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun /* Let's have a good look at this chunk... */
968*4882a593Smuzhiyun
969*4882a593Smuzhiyun if (!tags.chunk_used) {
970*4882a593Smuzhiyun /* An unassigned chunk in the block.
971*4882a593Smuzhiyun * If there are used chunks after this one, then
972*4882a593Smuzhiyun * it is a chunk that was skipped due to failing
973*4882a593Smuzhiyun * the erased check. Just skip it so that it can
974*4882a593Smuzhiyun * be deleted.
975*4882a593Smuzhiyun * But, more typically, We get here when this is
976*4882a593Smuzhiyun * an unallocated chunk and his means that
977*4882a593Smuzhiyun * either the block is empty or this is the one
978*4882a593Smuzhiyun * being allocated from
979*4882a593Smuzhiyun */
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun if (*found_chunks) {
982*4882a593Smuzhiyun /* This is a chunk that was skipped due
983*4882a593Smuzhiyun * to failing the erased check */
984*4882a593Smuzhiyun } else if (chunk_in_block == 0) {
985*4882a593Smuzhiyun /* We're looking at the first chunk in
986*4882a593Smuzhiyun * the block so the block is unused */
987*4882a593Smuzhiyun bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
988*4882a593Smuzhiyun dev->n_erased_blocks++;
989*4882a593Smuzhiyun } else {
990*4882a593Smuzhiyun if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
991*4882a593Smuzhiyun bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) {
992*4882a593Smuzhiyun if (dev->seq_number == bi->seq_number) {
993*4882a593Smuzhiyun /* Allocating from this block*/
994*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
995*4882a593Smuzhiyun " Allocating from %d %d",
996*4882a593Smuzhiyun blk, chunk_in_block);
997*4882a593Smuzhiyun
998*4882a593Smuzhiyun bi->block_state =
999*4882a593Smuzhiyun YAFFS_BLOCK_STATE_ALLOCATING;
1000*4882a593Smuzhiyun dev->alloc_block = blk;
1001*4882a593Smuzhiyun dev->alloc_page = chunk_in_block;
1002*4882a593Smuzhiyun dev->alloc_block_finder = blk;
1003*4882a593Smuzhiyun } else {
1004*4882a593Smuzhiyun /* This is a partially written block
1005*4882a593Smuzhiyun * that is not the current
1006*4882a593Smuzhiyun * allocation block.
1007*4882a593Smuzhiyun */
1008*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1009*4882a593Smuzhiyun "Partially written block %d detected. gc will fix this.",
1010*4882a593Smuzhiyun blk);
1011*4882a593Smuzhiyun }
1012*4882a593Smuzhiyun }
1013*4882a593Smuzhiyun }
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun dev->n_free_chunks++;
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun } else if (tags.ecc_result ==
1018*4882a593Smuzhiyun YAFFS_ECC_RESULT_UNFIXED) {
1019*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1020*4882a593Smuzhiyun " Unfixed ECC in chunk(%d:%d), chunk ignored",
1021*4882a593Smuzhiyun blk, chunk_in_block);
1022*4882a593Smuzhiyun dev->n_free_chunks++;
1023*4882a593Smuzhiyun } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID ||
1024*4882a593Smuzhiyun tags.chunk_id > YAFFS_MAX_CHUNK_ID ||
1025*4882a593Smuzhiyun tags.obj_id == YAFFS_OBJECTID_SUMMARY ||
1026*4882a593Smuzhiyun (tags.chunk_id > 0 &&
1027*4882a593Smuzhiyun tags.n_bytes > dev->data_bytes_per_chunk) ||
1028*4882a593Smuzhiyun tags.seq_number != bi->seq_number) {
1029*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1030*4882a593Smuzhiyun "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored",
1031*4882a593Smuzhiyun blk, chunk_in_block, tags.obj_id,
1032*4882a593Smuzhiyun tags.chunk_id, tags.n_bytes);
1033*4882a593Smuzhiyun dev->n_free_chunks++;
1034*4882a593Smuzhiyun } else if (tags.chunk_id > 0) {
1035*4882a593Smuzhiyun /* chunk_id > 0 so it is a data chunk... */
1036*4882a593Smuzhiyun loff_t endpos;
1037*4882a593Smuzhiyun loff_t chunk_base = (tags.chunk_id - 1) *
1038*4882a593Smuzhiyun dev->data_bytes_per_chunk;
1039*4882a593Smuzhiyun
1040*4882a593Smuzhiyun *found_chunks = 1;
1041*4882a593Smuzhiyun
1042*4882a593Smuzhiyun yaffs_set_chunk_bit(dev, blk, chunk_in_block);
1043*4882a593Smuzhiyun bi->pages_in_use++;
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun in = yaffs_find_or_create_by_number(dev,
1046*4882a593Smuzhiyun tags.obj_id,
1047*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_FILE);
1048*4882a593Smuzhiyun if (!in)
1049*4882a593Smuzhiyun /* Out of memory */
1050*4882a593Smuzhiyun alloc_failed = 1;
1051*4882a593Smuzhiyun
1052*4882a593Smuzhiyun if (in &&
1053*4882a593Smuzhiyun in->variant_type == YAFFS_OBJECT_TYPE_FILE &&
1054*4882a593Smuzhiyun chunk_base < in->variant.file_variant.shrink_size) {
1055*4882a593Smuzhiyun /* This has not been invalidated by
1056*4882a593Smuzhiyun * a resize */
1057*4882a593Smuzhiyun if (!yaffs_put_chunk_in_file(in, tags.chunk_id,
1058*4882a593Smuzhiyun chunk, -1))
1059*4882a593Smuzhiyun alloc_failed = 1;
1060*4882a593Smuzhiyun
1061*4882a593Smuzhiyun /* File size is calculated by looking at
1062*4882a593Smuzhiyun * the data chunks if we have not
1063*4882a593Smuzhiyun * seen an object header yet.
1064*4882a593Smuzhiyun * Stop this practice once we find an
1065*4882a593Smuzhiyun * object header.
1066*4882a593Smuzhiyun */
1067*4882a593Smuzhiyun endpos = chunk_base + tags.n_bytes;
1068*4882a593Smuzhiyun
1069*4882a593Smuzhiyun if (!in->valid &&
1070*4882a593Smuzhiyun in->variant.file_variant.scanned_size < endpos) {
1071*4882a593Smuzhiyun in->variant.file_variant.
1072*4882a593Smuzhiyun scanned_size = endpos;
1073*4882a593Smuzhiyun in->variant.file_variant.
1074*4882a593Smuzhiyun file_size = endpos;
1075*4882a593Smuzhiyun }
1076*4882a593Smuzhiyun } else if (in) {
1077*4882a593Smuzhiyun /* This chunk has been invalidated by a
1078*4882a593Smuzhiyun * resize, or a past file deletion
1079*4882a593Smuzhiyun * so delete the chunk*/
1080*4882a593Smuzhiyun yaffs_chunk_del(dev, chunk, 1, __LINE__);
1081*4882a593Smuzhiyun }
1082*4882a593Smuzhiyun } else {
1083*4882a593Smuzhiyun /* chunk_id == 0, so it is an ObjectHeader.
1084*4882a593Smuzhiyun * Thus, we read in the object header and make
1085*4882a593Smuzhiyun * the object
1086*4882a593Smuzhiyun */
1087*4882a593Smuzhiyun *found_chunks = 1;
1088*4882a593Smuzhiyun
1089*4882a593Smuzhiyun yaffs_set_chunk_bit(dev, blk, chunk_in_block);
1090*4882a593Smuzhiyun bi->pages_in_use++;
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun oh = NULL;
1093*4882a593Smuzhiyun in = NULL;
1094*4882a593Smuzhiyun
1095*4882a593Smuzhiyun if (tags.extra_available) {
1096*4882a593Smuzhiyun in = yaffs_find_or_create_by_number(dev,
1097*4882a593Smuzhiyun tags.obj_id,
1098*4882a593Smuzhiyun tags.extra_obj_type);
1099*4882a593Smuzhiyun if (!in)
1100*4882a593Smuzhiyun alloc_failed = 1;
1101*4882a593Smuzhiyun }
1102*4882a593Smuzhiyun
1103*4882a593Smuzhiyun if (!in ||
1104*4882a593Smuzhiyun (!in->valid && dev->param.disable_lazy_load) ||
1105*4882a593Smuzhiyun tags.extra_shadows ||
1106*4882a593Smuzhiyun (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT ||
1107*4882a593Smuzhiyun tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) {
1108*4882a593Smuzhiyun
1109*4882a593Smuzhiyun /* If we don't have valid info then we
1110*4882a593Smuzhiyun * need to read the chunk
1111*4882a593Smuzhiyun * TODO In future we can probably defer
1112*4882a593Smuzhiyun * reading the chunk and living with
1113*4882a593Smuzhiyun * invalid data until needed.
1114*4882a593Smuzhiyun */
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun yaffs_rd_chunk_tags_nand(dev, chunk, chunk_data, NULL);
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun oh = (struct yaffs_obj_hdr *)chunk_data;
1119*4882a593Smuzhiyun
1120*4882a593Smuzhiyun if (dev->param.inband_tags) {
1121*4882a593Smuzhiyun /* Fix up the header if they got
1122*4882a593Smuzhiyun * corrupted by inband tags */
1123*4882a593Smuzhiyun oh->shadows_obj =
1124*4882a593Smuzhiyun oh->inband_shadowed_obj_id;
1125*4882a593Smuzhiyun oh->is_shrink =
1126*4882a593Smuzhiyun oh->inband_is_shrink;
1127*4882a593Smuzhiyun }
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun if (!in) {
1130*4882a593Smuzhiyun in = yaffs_find_or_create_by_number(dev,
1131*4882a593Smuzhiyun tags.obj_id, oh->type);
1132*4882a593Smuzhiyun if (!in)
1133*4882a593Smuzhiyun alloc_failed = 1;
1134*4882a593Smuzhiyun }
1135*4882a593Smuzhiyun }
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun if (!in) {
1138*4882a593Smuzhiyun /* TODO Hoosterman we have a problem! */
1139*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_ERROR,
1140*4882a593Smuzhiyun "yaffs tragedy: Could not make object for object %d at chunk %d during scan",
1141*4882a593Smuzhiyun tags.obj_id, chunk);
1142*4882a593Smuzhiyun return YAFFS_FAIL;
1143*4882a593Smuzhiyun }
1144*4882a593Smuzhiyun
1145*4882a593Smuzhiyun if (in->valid) {
1146*4882a593Smuzhiyun /* We have already filled this one.
1147*4882a593Smuzhiyun * We have a duplicate that will be
1148*4882a593Smuzhiyun * discarded, but we first have to suck
1149*4882a593Smuzhiyun * out resize info if it is a file.
1150*4882a593Smuzhiyun */
1151*4882a593Smuzhiyun if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) &&
1152*4882a593Smuzhiyun ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) ||
1153*4882a593Smuzhiyun (tags.extra_available &&
1154*4882a593Smuzhiyun tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE)
1155*4882a593Smuzhiyun )) {
1156*4882a593Smuzhiyun loff_t this_size = (oh) ?
1157*4882a593Smuzhiyun yaffs_oh_to_size(oh) :
1158*4882a593Smuzhiyun tags.extra_file_size;
1159*4882a593Smuzhiyun u32 parent_obj_id = (oh) ?
1160*4882a593Smuzhiyun oh->parent_obj_id :
1161*4882a593Smuzhiyun tags.extra_parent_id;
1162*4882a593Smuzhiyun
1163*4882a593Smuzhiyun is_shrink = (oh) ?
1164*4882a593Smuzhiyun oh->is_shrink :
1165*4882a593Smuzhiyun tags.extra_is_shrink;
1166*4882a593Smuzhiyun
1167*4882a593Smuzhiyun /* If it is deleted (unlinked
1168*4882a593Smuzhiyun * at start also means deleted)
1169*4882a593Smuzhiyun * we treat the file size as
1170*4882a593Smuzhiyun * being zeroed at this point.
1171*4882a593Smuzhiyun */
1172*4882a593Smuzhiyun if (parent_obj_id == YAFFS_OBJECTID_DELETED ||
1173*4882a593Smuzhiyun parent_obj_id == YAFFS_OBJECTID_UNLINKED) {
1174*4882a593Smuzhiyun this_size = 0;
1175*4882a593Smuzhiyun is_shrink = 1;
1176*4882a593Smuzhiyun }
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun if (is_shrink &&
1179*4882a593Smuzhiyun in->variant.file_variant.shrink_size >
1180*4882a593Smuzhiyun this_size)
1181*4882a593Smuzhiyun in->variant.file_variant.shrink_size =
1182*4882a593Smuzhiyun this_size;
1183*4882a593Smuzhiyun
1184*4882a593Smuzhiyun if (is_shrink)
1185*4882a593Smuzhiyun bi->has_shrink_hdr = 1;
1186*4882a593Smuzhiyun }
1187*4882a593Smuzhiyun /* Use existing - destroy this one. */
1188*4882a593Smuzhiyun yaffs_chunk_del(dev, chunk, 1, __LINE__);
1189*4882a593Smuzhiyun }
1190*4882a593Smuzhiyun
1191*4882a593Smuzhiyun if (!in->valid && in->variant_type !=
1192*4882a593Smuzhiyun (oh ? oh->type : tags.extra_obj_type))
1193*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_ERROR,
1194*4882a593Smuzhiyun "yaffs tragedy: Bad object type, %d != %d, for object %d at chunk %d during scan",
1195*4882a593Smuzhiyun oh ? oh->type : tags.extra_obj_type,
1196*4882a593Smuzhiyun in->variant_type, tags.obj_id,
1197*4882a593Smuzhiyun chunk);
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun if (!in->valid &&
1200*4882a593Smuzhiyun (tags.obj_id == YAFFS_OBJECTID_ROOT ||
1201*4882a593Smuzhiyun tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) {
1202*4882a593Smuzhiyun /* We only load some info, don't fiddle
1203*4882a593Smuzhiyun * with directory structure */
1204*4882a593Smuzhiyun in->valid = 1;
1205*4882a593Smuzhiyun
1206*4882a593Smuzhiyun if (oh) {
1207*4882a593Smuzhiyun in->yst_mode = oh->yst_mode;
1208*4882a593Smuzhiyun yaffs_load_attribs(in, oh);
1209*4882a593Smuzhiyun in->lazy_loaded = 0;
1210*4882a593Smuzhiyun } else {
1211*4882a593Smuzhiyun in->lazy_loaded = 1;
1212*4882a593Smuzhiyun }
1213*4882a593Smuzhiyun in->hdr_chunk = chunk;
1214*4882a593Smuzhiyun
1215*4882a593Smuzhiyun } else if (!in->valid) {
1216*4882a593Smuzhiyun /* we need to load this info */
1217*4882a593Smuzhiyun in->valid = 1;
1218*4882a593Smuzhiyun in->hdr_chunk = chunk;
1219*4882a593Smuzhiyun if (oh) {
1220*4882a593Smuzhiyun in->variant_type = oh->type;
1221*4882a593Smuzhiyun in->yst_mode = oh->yst_mode;
1222*4882a593Smuzhiyun yaffs_load_attribs(in, oh);
1223*4882a593Smuzhiyun
1224*4882a593Smuzhiyun if (oh->shadows_obj > 0)
1225*4882a593Smuzhiyun yaffs_handle_shadowed_obj(dev,
1226*4882a593Smuzhiyun oh->shadows_obj, 1);
1227*4882a593Smuzhiyun
1228*4882a593Smuzhiyun yaffs_set_obj_name_from_oh(in, oh);
1229*4882a593Smuzhiyun parent = yaffs_find_or_create_by_number(dev,
1230*4882a593Smuzhiyun oh->parent_obj_id,
1231*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_DIRECTORY);
1232*4882a593Smuzhiyun file_size = yaffs_oh_to_size(oh);
1233*4882a593Smuzhiyun is_shrink = oh->is_shrink;
1234*4882a593Smuzhiyun equiv_id = oh->equiv_id;
1235*4882a593Smuzhiyun } else {
1236*4882a593Smuzhiyun in->variant_type = tags.extra_obj_type;
1237*4882a593Smuzhiyun parent = yaffs_find_or_create_by_number(dev,
1238*4882a593Smuzhiyun tags.extra_parent_id,
1239*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_DIRECTORY);
1240*4882a593Smuzhiyun file_size = tags.extra_file_size;
1241*4882a593Smuzhiyun is_shrink = tags.extra_is_shrink;
1242*4882a593Smuzhiyun equiv_id = tags.extra_equiv_id;
1243*4882a593Smuzhiyun in->lazy_loaded = 1;
1244*4882a593Smuzhiyun }
1245*4882a593Smuzhiyun in->dirty = 0;
1246*4882a593Smuzhiyun
1247*4882a593Smuzhiyun if (!parent)
1248*4882a593Smuzhiyun alloc_failed = 1;
1249*4882a593Smuzhiyun
1250*4882a593Smuzhiyun /* directory stuff...
1251*4882a593Smuzhiyun * hook up to parent
1252*4882a593Smuzhiyun */
1253*4882a593Smuzhiyun
1254*4882a593Smuzhiyun if (parent &&
1255*4882a593Smuzhiyun parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) {
1256*4882a593Smuzhiyun /* Set up as a directory */
1257*4882a593Smuzhiyun parent->variant_type =
1258*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_DIRECTORY;
1259*4882a593Smuzhiyun INIT_LIST_HEAD(&parent->
1260*4882a593Smuzhiyun variant.dir_variant.children);
1261*4882a593Smuzhiyun } else if (!parent ||
1262*4882a593Smuzhiyun parent->variant_type !=
1263*4882a593Smuzhiyun YAFFS_OBJECT_TYPE_DIRECTORY) {
1264*4882a593Smuzhiyun /* Hoosterman, another problem....
1265*4882a593Smuzhiyun * Trying to use a non-directory as a directory
1266*4882a593Smuzhiyun */
1267*4882a593Smuzhiyun
1268*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_ERROR,
1269*4882a593Smuzhiyun "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found."
1270*4882a593Smuzhiyun );
1271*4882a593Smuzhiyun parent = dev->lost_n_found;
1272*4882a593Smuzhiyun }
1273*4882a593Smuzhiyun yaffs_add_obj_to_dir(parent, in);
1274*4882a593Smuzhiyun
1275*4882a593Smuzhiyun is_unlinked = (parent == dev->del_dir) ||
1276*4882a593Smuzhiyun (parent == dev->unlinked_dir);
1277*4882a593Smuzhiyun
1278*4882a593Smuzhiyun if (is_shrink)
1279*4882a593Smuzhiyun /* Mark the block */
1280*4882a593Smuzhiyun bi->has_shrink_hdr = 1;
1281*4882a593Smuzhiyun
1282*4882a593Smuzhiyun /* Note re hardlinks.
1283*4882a593Smuzhiyun * Since we might scan a hardlink before its equivalent
1284*4882a593Smuzhiyun * object is scanned we put them all in a list.
1285*4882a593Smuzhiyun * After scanning is complete, we should have all the
1286*4882a593Smuzhiyun * objects, so we run through this list and fix up all
1287*4882a593Smuzhiyun * the chains.
1288*4882a593Smuzhiyun */
1289*4882a593Smuzhiyun
1290*4882a593Smuzhiyun switch (in->variant_type) {
1291*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_UNKNOWN:
1292*4882a593Smuzhiyun /* Todo got a problem */
1293*4882a593Smuzhiyun break;
1294*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_FILE:
1295*4882a593Smuzhiyun file_var = &in->variant.file_variant;
1296*4882a593Smuzhiyun if (file_var->scanned_size < file_size) {
1297*4882a593Smuzhiyun /* This covers the case where the file
1298*4882a593Smuzhiyun * size is greater than the data held.
1299*4882a593Smuzhiyun * This will happen if the file is
1300*4882a593Smuzhiyun * resized to be larger than its
1301*4882a593Smuzhiyun * current data extents.
1302*4882a593Smuzhiyun */
1303*4882a593Smuzhiyun file_var->file_size = file_size;
1304*4882a593Smuzhiyun file_var->scanned_size = file_size;
1305*4882a593Smuzhiyun }
1306*4882a593Smuzhiyun
1307*4882a593Smuzhiyun if (file_var->shrink_size > file_size)
1308*4882a593Smuzhiyun file_var->shrink_size = file_size;
1309*4882a593Smuzhiyun
1310*4882a593Smuzhiyun break;
1311*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_HARDLINK:
1312*4882a593Smuzhiyun hl_var = &in->variant.hardlink_variant;
1313*4882a593Smuzhiyun if (!is_unlinked) {
1314*4882a593Smuzhiyun hl_var->equiv_id = equiv_id;
1315*4882a593Smuzhiyun list_add(&in->hard_links, hard_list);
1316*4882a593Smuzhiyun }
1317*4882a593Smuzhiyun break;
1318*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_DIRECTORY:
1319*4882a593Smuzhiyun /* Do nothing */
1320*4882a593Smuzhiyun break;
1321*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_SPECIAL:
1322*4882a593Smuzhiyun /* Do nothing */
1323*4882a593Smuzhiyun break;
1324*4882a593Smuzhiyun case YAFFS_OBJECT_TYPE_SYMLINK:
1325*4882a593Smuzhiyun sl_var = &in->variant.symlink_variant;
1326*4882a593Smuzhiyun if (oh) {
1327*4882a593Smuzhiyun sl_var->alias =
1328*4882a593Smuzhiyun yaffs_clone_str(oh->alias);
1329*4882a593Smuzhiyun if (!sl_var->alias)
1330*4882a593Smuzhiyun alloc_failed = 1;
1331*4882a593Smuzhiyun }
1332*4882a593Smuzhiyun break;
1333*4882a593Smuzhiyun }
1334*4882a593Smuzhiyun }
1335*4882a593Smuzhiyun }
1336*4882a593Smuzhiyun return alloc_failed ? YAFFS_FAIL : YAFFS_OK;
1337*4882a593Smuzhiyun }
1338*4882a593Smuzhiyun
yaffs2_scan_backwards(struct yaffs_dev * dev)1339*4882a593Smuzhiyun int yaffs2_scan_backwards(struct yaffs_dev *dev)
1340*4882a593Smuzhiyun {
1341*4882a593Smuzhiyun int blk;
1342*4882a593Smuzhiyun int block_iter;
1343*4882a593Smuzhiyun int start_iter;
1344*4882a593Smuzhiyun int end_iter;
1345*4882a593Smuzhiyun int n_to_scan = 0;
1346*4882a593Smuzhiyun enum yaffs_block_state state;
1347*4882a593Smuzhiyun int c;
1348*4882a593Smuzhiyun LIST_HEAD(hard_list);
1349*4882a593Smuzhiyun struct yaffs_block_info *bi;
1350*4882a593Smuzhiyun u32 seq_number;
1351*4882a593Smuzhiyun int n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
1352*4882a593Smuzhiyun u8 *chunk_data;
1353*4882a593Smuzhiyun int found_chunks;
1354*4882a593Smuzhiyun int alloc_failed = 0;
1355*4882a593Smuzhiyun struct yaffs_block_index *block_index = NULL;
1356*4882a593Smuzhiyun int alt_block_index = 0;
1357*4882a593Smuzhiyun int summary_available;
1358*4882a593Smuzhiyun
1359*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1360*4882a593Smuzhiyun "yaffs2_scan_backwards starts intstartblk %d intendblk %d...",
1361*4882a593Smuzhiyun dev->internal_start_block, dev->internal_end_block);
1362*4882a593Smuzhiyun
1363*4882a593Smuzhiyun dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun block_index =
1366*4882a593Smuzhiyun kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS);
1367*4882a593Smuzhiyun
1368*4882a593Smuzhiyun if (!block_index) {
1369*4882a593Smuzhiyun block_index =
1370*4882a593Smuzhiyun vmalloc(n_blocks * sizeof(struct yaffs_block_index));
1371*4882a593Smuzhiyun alt_block_index = 1;
1372*4882a593Smuzhiyun }
1373*4882a593Smuzhiyun
1374*4882a593Smuzhiyun if (!block_index) {
1375*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1376*4882a593Smuzhiyun "yaffs2_scan_backwards() could not allocate block index!"
1377*4882a593Smuzhiyun );
1378*4882a593Smuzhiyun return YAFFS_FAIL;
1379*4882a593Smuzhiyun }
1380*4882a593Smuzhiyun
1381*4882a593Smuzhiyun dev->blocks_in_checkpt = 0;
1382*4882a593Smuzhiyun
1383*4882a593Smuzhiyun chunk_data = yaffs_get_temp_buffer(dev);
1384*4882a593Smuzhiyun
1385*4882a593Smuzhiyun /* Scan all the blocks to determine their state */
1386*4882a593Smuzhiyun bi = dev->block_info;
1387*4882a593Smuzhiyun for (blk = dev->internal_start_block; blk <= dev->internal_end_block;
1388*4882a593Smuzhiyun blk++) {
1389*4882a593Smuzhiyun yaffs_clear_chunk_bits(dev, blk);
1390*4882a593Smuzhiyun bi->pages_in_use = 0;
1391*4882a593Smuzhiyun bi->soft_del_pages = 0;
1392*4882a593Smuzhiyun
1393*4882a593Smuzhiyun yaffs_query_init_block_state(dev, blk, &state, &seq_number);
1394*4882a593Smuzhiyun
1395*4882a593Smuzhiyun bi->block_state = state;
1396*4882a593Smuzhiyun bi->seq_number = seq_number;
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA)
1399*4882a593Smuzhiyun bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
1400*4882a593Smuzhiyun if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK)
1401*4882a593Smuzhiyun bi->block_state = YAFFS_BLOCK_STATE_DEAD;
1402*4882a593Smuzhiyun
1403*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN_DEBUG,
1404*4882a593Smuzhiyun "Block scanning block %d state %d seq %d",
1405*4882a593Smuzhiyun blk, bi->block_state, seq_number);
1406*4882a593Smuzhiyun
1407*4882a593Smuzhiyun if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
1408*4882a593Smuzhiyun dev->blocks_in_checkpt++;
1409*4882a593Smuzhiyun
1410*4882a593Smuzhiyun } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) {
1411*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
1412*4882a593Smuzhiyun "block %d is bad", blk);
1413*4882a593Smuzhiyun } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
1414*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty ");
1415*4882a593Smuzhiyun dev->n_erased_blocks++;
1416*4882a593Smuzhiyun dev->n_free_chunks += dev->param.chunks_per_block;
1417*4882a593Smuzhiyun } else if (bi->block_state ==
1418*4882a593Smuzhiyun YAFFS_BLOCK_STATE_NEEDS_SCAN) {
1419*4882a593Smuzhiyun /* Determine the highest sequence number */
1420*4882a593Smuzhiyun if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
1421*4882a593Smuzhiyun seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) {
1422*4882a593Smuzhiyun block_index[n_to_scan].seq = seq_number;
1423*4882a593Smuzhiyun block_index[n_to_scan].block = blk;
1424*4882a593Smuzhiyun n_to_scan++;
1425*4882a593Smuzhiyun if (seq_number >= dev->seq_number)
1426*4882a593Smuzhiyun dev->seq_number = seq_number;
1427*4882a593Smuzhiyun } else {
1428*4882a593Smuzhiyun /* TODO: Nasty sequence number! */
1429*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN,
1430*4882a593Smuzhiyun "Block scanning block %d has bad sequence number %d",
1431*4882a593Smuzhiyun blk, seq_number);
1432*4882a593Smuzhiyun }
1433*4882a593Smuzhiyun }
1434*4882a593Smuzhiyun bi++;
1435*4882a593Smuzhiyun }
1436*4882a593Smuzhiyun
1437*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN, "%d blocks to be sorted...", n_to_scan);
1438*4882a593Smuzhiyun
1439*4882a593Smuzhiyun cond_resched();
1440*4882a593Smuzhiyun
1441*4882a593Smuzhiyun /* Sort the blocks by sequence number */
1442*4882a593Smuzhiyun sort(block_index, n_to_scan, sizeof(struct yaffs_block_index),
1443*4882a593Smuzhiyun yaffs2_ybicmp, NULL);
1444*4882a593Smuzhiyun
1445*4882a593Smuzhiyun cond_resched();
1446*4882a593Smuzhiyun
1447*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN, "...done");
1448*4882a593Smuzhiyun
1449*4882a593Smuzhiyun /* Now scan the blocks looking at the data. */
1450*4882a593Smuzhiyun start_iter = 0;
1451*4882a593Smuzhiyun end_iter = n_to_scan - 1;
1452*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan);
1453*4882a593Smuzhiyun
1454*4882a593Smuzhiyun /* For each block.... backwards */
1455*4882a593Smuzhiyun for (block_iter = end_iter;
1456*4882a593Smuzhiyun !alloc_failed && block_iter >= start_iter;
1457*4882a593Smuzhiyun block_iter--) {
1458*4882a593Smuzhiyun /* Cooperative multitasking! This loop can run for so
1459*4882a593Smuzhiyun long that watchdog timers expire. */
1460*4882a593Smuzhiyun cond_resched();
1461*4882a593Smuzhiyun
1462*4882a593Smuzhiyun /* get the block to scan in the correct order */
1463*4882a593Smuzhiyun blk = block_index[block_iter].block;
1464*4882a593Smuzhiyun bi = yaffs_get_block_info(dev, blk);
1465*4882a593Smuzhiyun
1466*4882a593Smuzhiyun summary_available = yaffs_summary_read(dev, dev->sum_tags, blk);
1467*4882a593Smuzhiyun
1468*4882a593Smuzhiyun /* For each chunk in each block that needs scanning.... */
1469*4882a593Smuzhiyun found_chunks = 0;
1470*4882a593Smuzhiyun if (summary_available)
1471*4882a593Smuzhiyun c = dev->chunks_per_summary - 1;
1472*4882a593Smuzhiyun else
1473*4882a593Smuzhiyun c = dev->param.chunks_per_block - 1;
1474*4882a593Smuzhiyun
1475*4882a593Smuzhiyun for (/* c is already initialised */;
1476*4882a593Smuzhiyun !alloc_failed && c >= 0 &&
1477*4882a593Smuzhiyun (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
1478*4882a593Smuzhiyun bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING);
1479*4882a593Smuzhiyun c--) {
1480*4882a593Smuzhiyun /* Scan backwards...
1481*4882a593Smuzhiyun * Read the tags and decide what to do
1482*4882a593Smuzhiyun */
1483*4882a593Smuzhiyun if (yaffs2_scan_chunk(dev, bi, blk, c,
1484*4882a593Smuzhiyun &found_chunks, chunk_data,
1485*4882a593Smuzhiyun &hard_list, summary_available) ==
1486*4882a593Smuzhiyun YAFFS_FAIL)
1487*4882a593Smuzhiyun alloc_failed = 1;
1488*4882a593Smuzhiyun }
1489*4882a593Smuzhiyun
1490*4882a593Smuzhiyun if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) {
1491*4882a593Smuzhiyun /* If we got this far while scanning, then the block
1492*4882a593Smuzhiyun * is fully allocated. */
1493*4882a593Smuzhiyun bi->block_state = YAFFS_BLOCK_STATE_FULL;
1494*4882a593Smuzhiyun }
1495*4882a593Smuzhiyun
1496*4882a593Smuzhiyun /* Now let's see if it was dirty */
1497*4882a593Smuzhiyun if (bi->pages_in_use == 0 &&
1498*4882a593Smuzhiyun !bi->has_shrink_hdr &&
1499*4882a593Smuzhiyun bi->block_state == YAFFS_BLOCK_STATE_FULL) {
1500*4882a593Smuzhiyun yaffs_block_became_dirty(dev, blk);
1501*4882a593Smuzhiyun }
1502*4882a593Smuzhiyun }
1503*4882a593Smuzhiyun
1504*4882a593Smuzhiyun yaffs_skip_rest_of_block(dev);
1505*4882a593Smuzhiyun
1506*4882a593Smuzhiyun if (alt_block_index)
1507*4882a593Smuzhiyun vfree(block_index);
1508*4882a593Smuzhiyun else
1509*4882a593Smuzhiyun kfree(block_index);
1510*4882a593Smuzhiyun
1511*4882a593Smuzhiyun /* Ok, we've done all the scanning.
1512*4882a593Smuzhiyun * Fix up the hard link chains.
1513*4882a593Smuzhiyun * We have scanned all the objects, now it's time to add these
1514*4882a593Smuzhiyun * hardlinks.
1515*4882a593Smuzhiyun */
1516*4882a593Smuzhiyun yaffs_link_fixup(dev, &hard_list);
1517*4882a593Smuzhiyun
1518*4882a593Smuzhiyun yaffs_release_temp_buffer(dev, chunk_data);
1519*4882a593Smuzhiyun
1520*4882a593Smuzhiyun if (alloc_failed)
1521*4882a593Smuzhiyun return YAFFS_FAIL;
1522*4882a593Smuzhiyun
1523*4882a593Smuzhiyun yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends");
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun return YAFFS_OK;
1526*4882a593Smuzhiyun }
1527