yaffsfs.c: Fix NULL dereference in yaffs_unmount2_reldev()
[yaffs2.git] / yaffs_checkptrw.c
1 /*
2  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3  *
4  * Copyright (C) 2002-2018 Aleph One Ltd.
5  *
6  * Created by Charles Manning <charles@aleph1.co.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include "yaffs_checkptrw.h"
14 #include "yaffs_getblockinfo.h"
15 #include "yaffs_endian.h"
16
17 struct yaffs_checkpt_chunk_hdr {
18         int version;
19         int seq;
20         u32 sum;
21         u32 xor;
22 } ;
23
24
25 static int apply_chunk_offset(struct yaffs_dev *dev, int chunk)
26 {
27         return chunk - dev->chunk_offset;
28 }
29
30 static int apply_block_offset(struct yaffs_dev *dev, int block)
31 {
32         return block - dev->block_offset;
33 }
34
35
36 static void yaffs2_do_endian_hdr(struct yaffs_dev *dev,
37                                  struct yaffs_checkpt_chunk_hdr *hdr)
38 {
39         if (!dev->swap_endian)
40                 return;
41         hdr->version = swap_s32(hdr->version);
42         hdr->seq     = swap_s32(hdr->seq);
43         hdr->sum     = swap_u32(hdr->sum);
44         hdr->xor     = swap_u32(hdr->xor);
45 }
46
47 static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev)
48 {
49         struct yaffs_checkpt_chunk_hdr hdr;
50
51         hdr.version = YAFFS_CHECKPOINT_VERSION;
52         hdr.seq = dev->checkpt_page_seq;
53         hdr.sum = dev->checkpt_sum;
54         hdr.xor = dev->checkpt_xor;
55
56         dev->checkpt_byte_offs = sizeof(hdr);
57
58         yaffs2_do_endian_hdr(dev, &hdr);
59         memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr));
60 }
61
62 static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev)
63 {
64         struct yaffs_checkpt_chunk_hdr hdr;
65
66         memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr));
67         yaffs2_do_endian_hdr(dev, &hdr);
68
69         dev->checkpt_byte_offs = sizeof(hdr);
70
71         return hdr.version == YAFFS_CHECKPOINT_VERSION &&
72                 hdr.seq == dev->checkpt_page_seq &&
73                 hdr.sum == dev->checkpt_sum &&
74                 hdr.xor == dev->checkpt_xor;
75 }
76
77 static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev)
78 {
79         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
80
81         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
82                 "checkpt blocks_avail = %d", blocks_avail);
83
84         return (blocks_avail <= 0) ? 0 : 1;
85 }
86
87 static int yaffs_checkpt_erase(struct yaffs_dev *dev)
88 {
89         u32 i;
90
91         if (!dev->drv.drv_erase_fn)
92                 return 0;
93         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
94                 "checking blocks %d to %d",
95                 dev->internal_start_block, dev->internal_end_block);
96
97         for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
98                 struct yaffs_block_info *bi = yaffs_get_block_info(dev, i);
99                 int offset_i = apply_block_offset(dev, i);
100                 int result;
101
102                 if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) {
103                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
104                         "erasing checkpt block %d", i);
105
106                         dev->n_erasures++;
107
108                         result = dev->drv.drv_erase_fn(dev, offset_i);
109                         if(result) {
110                                 bi->block_state = YAFFS_BLOCK_STATE_EMPTY;
111                                 dev->n_erased_blocks++;
112                                 dev->n_free_chunks +=
113                                     dev->param.chunks_per_block;
114                         } else {
115                                 dev->drv.drv_mark_bad_fn(dev, offset_i);
116                                 bi->block_state = YAFFS_BLOCK_STATE_DEAD;
117                         }
118                 }
119         }
120
121         dev->blocks_in_checkpt = 0;
122
123         return 1;
124 }
125
126 static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev)
127 {
128         u32 i;
129         int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks;
130
131         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
132                 "allocating checkpt block: erased %d reserved %d avail %d next %d ",
133                 dev->n_erased_blocks, dev->param.n_reserved_blocks,
134                 blocks_avail, dev->checkpt_next_block);
135
136         if (dev->checkpt_next_block >= 0 &&
137             dev->checkpt_next_block <= (int)dev->internal_end_block &&
138             blocks_avail > 0) {
139
140                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
141                      i++) {
142                         struct yaffs_block_info *bi;
143
144                         bi = yaffs_get_block_info(dev, i);
145                         if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) {
146                                 dev->checkpt_next_block = i + 1;
147                                 dev->checkpt_cur_block = i;
148                                 yaffs_trace(YAFFS_TRACE_CHECKPOINT,
149                                         "allocating checkpt block %d", i);
150                                 return;
151                         }
152                 }
153         }
154         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks");
155
156         dev->checkpt_next_block = -1;
157         dev->checkpt_cur_block = -1;
158 }
159
160 static void yaffs2_checkpt_find_block(struct yaffs_dev *dev)
161 {
162         u32 i;
163         struct yaffs_ext_tags tags;
164
165         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
166                 "find next checkpt block: start:  blocks %d next %d",
167                 dev->blocks_in_checkpt, dev->checkpt_next_block);
168
169         if (dev->blocks_in_checkpt < dev->checkpt_max_blocks)
170                 for (i = dev->checkpt_next_block; i <= dev->internal_end_block;
171                      i++) {
172                         int chunk = i * dev->param.chunks_per_block;
173                         enum yaffs_block_state state;
174                         u32 seq;
175
176                         dev->tagger.read_chunk_tags_fn(dev,
177                                         apply_chunk_offset(dev, chunk),
178                                         NULL, &tags);
179                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
180                                 "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d",
181                                 i, (int) state,
182                                 tags.obj_id, tags.seq_number,
183                                 tags.ecc_result);
184
185                         if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
186                                 continue;
187
188                         dev->tagger.query_block_fn(dev,
189                                                 apply_block_offset(dev, i),
190                                                 &state, &seq);
191                         if (state == YAFFS_BLOCK_STATE_DEAD)
192                                 continue;
193
194                         /* Right kind of block */
195                         dev->checkpt_next_block = tags.obj_id;
196                         dev->checkpt_cur_block = i;
197                         dev->checkpt_block_list[dev->blocks_in_checkpt] = i;
198                         dev->blocks_in_checkpt++;
199                         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
200                                 "found checkpt block %d", i);
201                         return;
202                 }
203
204         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks");
205
206         dev->checkpt_next_block = -1;
207         dev->checkpt_cur_block = -1;
208 }
209
210 int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing)
211 {
212         u32 i;
213
214         dev->checkpt_open_write = writing;
215
216         /* Got the functions we need? */
217         if (!dev->tagger.write_chunk_tags_fn ||
218             !dev->tagger.read_chunk_tags_fn ||
219             !dev->drv.drv_erase_fn ||
220             !dev->drv.drv_mark_bad_fn)
221                 return 0;
222
223         if (writing && !yaffs2_checkpt_space_ok(dev))
224                 return 0;
225
226         if (!dev->checkpt_buffer)
227                 dev->checkpt_buffer =
228                     kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS);
229         if (!dev->checkpt_buffer)
230                 return 0;
231
232         dev->checkpt_page_seq = 0;
233         dev->checkpt_byte_count = 0;
234         dev->checkpt_sum = 0;
235         dev->checkpt_xor = 0;
236         dev->checkpt_cur_block = -1;
237         dev->checkpt_cur_chunk = -1;
238         dev->checkpt_next_block = dev->internal_start_block;
239
240         if (writing) {
241                 memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
242                 yaffs2_checkpt_init_chunk_hdr(dev);
243                 return yaffs_checkpt_erase(dev);
244         }
245
246         /* Opening for a read */
247         /* Set to a value that will kick off a read */
248         dev->checkpt_byte_offs = dev->data_bytes_per_chunk;
249         /* A checkpoint block list of 1 checkpoint block per 16 block is
250          * (hopefully) going to be way more than we need */
251         dev->blocks_in_checkpt = 0;
252         dev->checkpt_max_blocks =
253             (dev->internal_end_block - dev->internal_start_block) / 16 + 2;
254         if (!dev->checkpt_block_list)
255                 dev->checkpt_block_list =
256                       kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS);
257
258         if (!dev->checkpt_block_list)
259                 return 0;
260
261         for (i = 0; i < dev->checkpt_max_blocks; i++)
262                 dev->checkpt_block_list[i] = -1;
263
264         return 1;
265 }
266
267 int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum)
268 {
269         u32 composite_sum;
270
271         composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff);
272         *sum = composite_sum;
273         return 1;
274 }
275
276 static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev)
277 {
278         int chunk;
279         int offset_chunk;
280         struct yaffs_ext_tags tags;
281
282         if (dev->checkpt_cur_block < 0) {
283                 yaffs2_checkpt_find_erased_block(dev);
284                 dev->checkpt_cur_chunk = 0;
285         }
286
287         if (dev->checkpt_cur_block < 0)
288                 return 0;
289
290         tags.is_deleted = 0;
291         tags.obj_id = dev->checkpt_next_block;  /* Hint to next place to look */
292         tags.chunk_id = dev->checkpt_page_seq + 1;
293         tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA;
294         tags.n_bytes = dev->data_bytes_per_chunk;
295         if (dev->checkpt_cur_chunk == 0) {
296                 /* First chunk we write for the block? Set block state to
297                    checkpoint */
298                 struct yaffs_block_info *bi =
299                     yaffs_get_block_info(dev, dev->checkpt_cur_block);
300                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
301                 dev->blocks_in_checkpt++;
302         }
303
304         chunk =
305             dev->checkpt_cur_block * dev->param.chunks_per_block +
306             dev->checkpt_cur_chunk;
307
308         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
309                 "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d",
310                 chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk,
311                 tags.obj_id, tags.chunk_id);
312
313         offset_chunk = apply_chunk_offset(dev, chunk);
314
315         dev->n_page_writes++;
316
317         dev->tagger.write_chunk_tags_fn(dev, offset_chunk,
318                                        dev->checkpt_buffer, &tags);
319         dev->checkpt_page_seq++;
320         dev->checkpt_cur_chunk++;
321         if (dev->checkpt_cur_chunk >= (int)dev->param.chunks_per_block) {
322                 dev->checkpt_cur_chunk = 0;
323                 dev->checkpt_cur_block = -1;
324         }
325         memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk);
326
327         yaffs2_checkpt_init_chunk_hdr(dev);
328
329
330         return 1;
331 }
332
333 int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes)
334 {
335         int i = 0;
336         int ok = 1;
337         u8 *data_bytes = (u8 *) data;
338
339         if (!dev->checkpt_buffer)
340                 return 0;
341
342         if (!dev->checkpt_open_write)
343                 return -1;
344
345         while (i < n_bytes && ok) {
346                 dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes;
347                 dev->checkpt_sum += *data_bytes;
348                 dev->checkpt_xor ^= *data_bytes;
349
350                 dev->checkpt_byte_offs++;
351                 i++;
352                 data_bytes++;
353                 dev->checkpt_byte_count++;
354
355                 if (dev->checkpt_byte_offs < 0 ||
356                     dev->checkpt_byte_offs >= (int)dev->data_bytes_per_chunk)
357                         ok = yaffs2_checkpt_flush_buffer(dev);
358         }
359
360         return i;
361 }
362
363 int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes)
364 {
365         int i = 0;
366         struct yaffs_ext_tags tags;
367         int chunk;
368         int offset_chunk;
369         u8 *data_bytes = (u8 *) data;
370
371         if (!dev->checkpt_buffer)
372                 return 0;
373
374         if (dev->checkpt_open_write)
375                 return -1;
376
377         while (i < n_bytes) {
378
379                 if (dev->checkpt_byte_offs < 0 ||
380                     dev->checkpt_byte_offs >= (int)dev->data_bytes_per_chunk) {
381
382                         if (dev->checkpt_cur_block < 0) {
383                                 yaffs2_checkpt_find_block(dev);
384                                 dev->checkpt_cur_chunk = 0;
385                         }
386
387                         /* Bail out if we can't find a checpoint block */
388                         if (dev->checkpt_cur_block < 0)
389                                 break;
390
391                         chunk = dev->checkpt_cur_block *
392                             dev->param.chunks_per_block +
393                             dev->checkpt_cur_chunk;
394
395                         offset_chunk = apply_chunk_offset(dev, chunk);
396                         dev->n_page_reads++;
397
398                         /* Read in the next chunk */
399                         dev->tagger.read_chunk_tags_fn(dev,
400                                                 offset_chunk,
401                                                 dev->checkpt_buffer,
402                                                 &tags);
403
404                         /* Bail out if the chunk is corrupted. */
405                         if (tags.chunk_id != (u32)(dev->checkpt_page_seq + 1) ||
406                             tags.ecc_result > YAFFS_ECC_RESULT_FIXED ||
407                             tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA)
408                                 break;
409
410                         /* Bail out if it is not a checkpoint chunk. */
411                         if(!yaffs2_checkpt_check_chunk_hdr(dev))
412                                 break;
413
414                         dev->checkpt_page_seq++;
415                         dev->checkpt_cur_chunk++;
416
417                         if (dev->checkpt_cur_chunk >=
418                                         (int)dev->param.chunks_per_block)
419                                 dev->checkpt_cur_block = -1;
420
421                 }
422
423                 *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs];
424                 dev->checkpt_sum += *data_bytes;
425                 dev->checkpt_xor ^= *data_bytes;
426                 dev->checkpt_byte_offs++;
427                 i++;
428                 data_bytes++;
429                 dev->checkpt_byte_count++;
430         }
431
432         return i; /* Number of bytes read */
433 }
434
435 int yaffs_checkpt_close(struct yaffs_dev *dev)
436 {
437         u32 i;
438
439         if (dev->checkpt_open_write) {
440                 if (dev->checkpt_byte_offs !=
441                         sizeof(struct yaffs_checkpt_chunk_hdr))
442                         yaffs2_checkpt_flush_buffer(dev);
443         } else if (dev->checkpt_block_list) {
444                 for (i = 0;
445                      i < dev->blocks_in_checkpt &&
446                      dev->checkpt_block_list[i] >= 0; i++) {
447                         int blk = dev->checkpt_block_list[i];
448                         struct yaffs_block_info *bi = NULL;
449
450                         if ((int)dev->internal_start_block <= blk &&
451                             blk <= (int)dev->internal_end_block)
452                                 bi = yaffs_get_block_info(dev, blk);
453                         if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY)
454                                 bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT;
455                 }
456         }
457
458         dev->n_free_chunks -=
459                 dev->blocks_in_checkpt * dev->param.chunks_per_block;
460         dev->n_erased_blocks -= dev->blocks_in_checkpt;
461
462         yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d",
463                 dev->checkpt_byte_count);
464
465         if (dev->checkpt_buffer)
466                 return 1;
467         else
468                 return 0;
469 }
470
471 int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev)
472 {
473         /* Erase the checkpoint data */
474
475         yaffs_trace(YAFFS_TRACE_CHECKPOINT,
476                 "checkpoint invalidate of %d blocks",
477                 dev->blocks_in_checkpt);
478
479         return yaffs_checkpt_erase(dev);
480 }