xref: /OK3568_Linux_fs/u-boot/fs/yaffs2/yaffs_yaffs2.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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