1*0a6a1f1dSLionel Sambuc /* $NetBSD: chfs_scan.c,v 1.6 2015/02/07 04:19:52 christos Exp $ */
2d65f6f70SBen Gras
3d65f6f70SBen Gras /*-
4d65f6f70SBen Gras * Copyright (c) 2010 Department of Software Engineering,
5d65f6f70SBen Gras * University of Szeged, Hungary
6d65f6f70SBen Gras * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
7d65f6f70SBen Gras * All rights reserved.
8d65f6f70SBen Gras *
9d65f6f70SBen Gras * This code is derived from software contributed to The NetBSD Foundation
10d65f6f70SBen Gras * by the Department of Software Engineering, University of Szeged, Hungary
11d65f6f70SBen Gras *
12d65f6f70SBen Gras * Redistribution and use in source and binary forms, with or without
13d65f6f70SBen Gras * modification, are permitted provided that the following conditions
14d65f6f70SBen Gras * are met:
15d65f6f70SBen Gras * 1. Redistributions of source code must retain the above copyright
16d65f6f70SBen Gras * notice, this list of conditions and the following disclaimer.
17d65f6f70SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
18d65f6f70SBen Gras * notice, this list of conditions and the following disclaimer in the
19d65f6f70SBen Gras * documentation and/or other materials provided with the distribution.
20d65f6f70SBen Gras *
21d65f6f70SBen Gras * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22d65f6f70SBen Gras * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23d65f6f70SBen Gras * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24d65f6f70SBen Gras * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25d65f6f70SBen Gras * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26d65f6f70SBen Gras * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27d65f6f70SBen Gras * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28d65f6f70SBen Gras * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29d65f6f70SBen Gras * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30d65f6f70SBen Gras * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31d65f6f70SBen Gras * SUCH DAMAGE.
32d65f6f70SBen Gras */
33d65f6f70SBen Gras
34d65f6f70SBen Gras #include "chfs.h"
35d65f6f70SBen Gras
3684d9c625SLionel Sambuc /*
37d65f6f70SBen Gras * chfs_scan_make_vnode_cache - makes a new vnode cache during scan
38d65f6f70SBen Gras * This function returns a vnode cache belonging to @vno.
39d65f6f70SBen Gras */
40d65f6f70SBen Gras struct chfs_vnode_cache *
chfs_scan_make_vnode_cache(struct chfs_mount * chmp,ino_t vno)41d65f6f70SBen Gras chfs_scan_make_vnode_cache(struct chfs_mount *chmp, ino_t vno)
42d65f6f70SBen Gras {
43d65f6f70SBen Gras struct chfs_vnode_cache *vc;
44d65f6f70SBen Gras
45d65f6f70SBen Gras KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
46d65f6f70SBen Gras
4784d9c625SLionel Sambuc /* vnode cache already exists */
48d65f6f70SBen Gras vc = chfs_vnode_cache_get(chmp, vno);
49d65f6f70SBen Gras if (vc) {
50d65f6f70SBen Gras return vc;
51d65f6f70SBen Gras }
52d65f6f70SBen Gras
5384d9c625SLionel Sambuc /* update max vnode number if needed */
54d65f6f70SBen Gras if (vno > chmp->chm_max_vno) {
55d65f6f70SBen Gras chmp->chm_max_vno = vno;
56d65f6f70SBen Gras }
57d65f6f70SBen Gras
5884d9c625SLionel Sambuc /* create new vnode cache */
59d65f6f70SBen Gras vc = chfs_vnode_cache_alloc(vno);
60d65f6f70SBen Gras
61d65f6f70SBen Gras chfs_vnode_cache_add(chmp, vc);
62d65f6f70SBen Gras
63d65f6f70SBen Gras if (vno == CHFS_ROOTINO) {
64d65f6f70SBen Gras vc->nlink = 2;
65d65f6f70SBen Gras vc->pvno = CHFS_ROOTINO;
6684d9c625SLionel Sambuc vc->state = VNO_STATE_CHECKEDABSENT;
67d65f6f70SBen Gras }
68d65f6f70SBen Gras
69d65f6f70SBen Gras return vc;
70d65f6f70SBen Gras }
71d65f6f70SBen Gras
7284d9c625SLionel Sambuc /*
73d65f6f70SBen Gras * chfs_scan_check_node_hdr - checks node magic and crc
74d65f6f70SBen Gras * Returns 0 if everything is OK, error code otherwise.
75d65f6f70SBen Gras */
76d65f6f70SBen Gras int
chfs_scan_check_node_hdr(struct chfs_flash_node_hdr * nhdr)77d65f6f70SBen Gras chfs_scan_check_node_hdr(struct chfs_flash_node_hdr *nhdr)
78d65f6f70SBen Gras {
79d65f6f70SBen Gras uint16_t magic;
80d65f6f70SBen Gras uint32_t crc, hdr_crc;
81d65f6f70SBen Gras
82d65f6f70SBen Gras magic = le16toh(nhdr->magic);
83d65f6f70SBen Gras
84d65f6f70SBen Gras if (magic != CHFS_FS_MAGIC_BITMASK) {
85d65f6f70SBen Gras dbg("bad magic\n");
86d65f6f70SBen Gras return CHFS_NODE_BADMAGIC;
87d65f6f70SBen Gras }
88d65f6f70SBen Gras
89d65f6f70SBen Gras hdr_crc = le32toh(nhdr->hdr_crc);
90d65f6f70SBen Gras crc = crc32(0, (uint8_t *)nhdr, CHFS_NODE_HDR_SIZE - 4);
91d65f6f70SBen Gras
92d65f6f70SBen Gras if (crc != hdr_crc) {
93d65f6f70SBen Gras dbg("bad crc\n");
94d65f6f70SBen Gras return CHFS_NODE_BADCRC;
95d65f6f70SBen Gras }
96d65f6f70SBen Gras
97d65f6f70SBen Gras return CHFS_NODE_OK;
98d65f6f70SBen Gras }
99d65f6f70SBen Gras
10084d9c625SLionel Sambuc /* chfs_scan_check_vnode - check vnode crc and add it to vnode cache */
101d65f6f70SBen Gras int
chfs_scan_check_vnode(struct chfs_mount * chmp,struct chfs_eraseblock * cheb,void * buf,off_t ofs)102d65f6f70SBen Gras chfs_scan_check_vnode(struct chfs_mount *chmp,
103d65f6f70SBen Gras struct chfs_eraseblock *cheb, void *buf, off_t ofs)
104d65f6f70SBen Gras {
105d65f6f70SBen Gras KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
106d65f6f70SBen Gras struct chfs_vnode_cache *vc;
107d65f6f70SBen Gras struct chfs_flash_vnode *vnode = buf;
108d65f6f70SBen Gras struct chfs_node_ref *nref;
109d65f6f70SBen Gras int err;
110d65f6f70SBen Gras uint32_t crc;
111d65f6f70SBen Gras ino_t vno;
112d65f6f70SBen Gras
113d65f6f70SBen Gras crc = crc32(0, (uint8_t *)vnode,
114d65f6f70SBen Gras sizeof(struct chfs_flash_vnode) - 4);
115d65f6f70SBen Gras
11684d9c625SLionel Sambuc /* check node crc */
117d65f6f70SBen Gras if (crc != le32toh(vnode->node_crc)) {
118d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp,
119d65f6f70SBen Gras cheb, le32toh(vnode->length));
120d65f6f70SBen Gras if (err) {
121d65f6f70SBen Gras return err;
122d65f6f70SBen Gras }
123d65f6f70SBen Gras
124d65f6f70SBen Gras return CHFS_NODE_BADCRC;
125d65f6f70SBen Gras }
126d65f6f70SBen Gras
127d65f6f70SBen Gras vno = le64toh(vnode->vno);
128d65f6f70SBen Gras
12984d9c625SLionel Sambuc /* find the corresponding vnode cache */
130d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_vnocache);
131d65f6f70SBen Gras vc = chfs_vnode_cache_get(chmp, vno);
132d65f6f70SBen Gras if (!vc) {
133d65f6f70SBen Gras vc = chfs_scan_make_vnode_cache(chmp, vno);
134d65f6f70SBen Gras if (!vc) {
135d65f6f70SBen Gras mutex_exit(&chmp->chm_lock_vnocache);
136d65f6f70SBen Gras return ENOMEM;
137d65f6f70SBen Gras }
138d65f6f70SBen Gras }
139d65f6f70SBen Gras
140d65f6f70SBen Gras nref = chfs_alloc_node_ref(cheb);
141d65f6f70SBen Gras
142d65f6f70SBen Gras nref->nref_offset = ofs;
143d65f6f70SBen Gras
144d65f6f70SBen Gras KASSERT(nref->nref_lnr == cheb->lnr);
145d65f6f70SBen Gras
14684d9c625SLionel Sambuc /* check version of vnode */
147d65f6f70SBen Gras if ((struct chfs_vnode_cache *)vc->v != vc) {
148d65f6f70SBen Gras if (le64toh(vnode->version) > *vc->vno_version) {
149d65f6f70SBen Gras *vc->vno_version = le64toh(vnode->version);
150d65f6f70SBen Gras chfs_add_vnode_ref_to_vc(chmp, vc, nref);
151d65f6f70SBen Gras } else {
152d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb,
153d65f6f70SBen Gras sizeof(struct chfs_flash_vnode));
154d65f6f70SBen Gras return CHFS_NODE_OK;
155d65f6f70SBen Gras }
156d65f6f70SBen Gras } else {
157d65f6f70SBen Gras vc->vno_version = kmem_alloc(sizeof(uint64_t), KM_SLEEP);
158d65f6f70SBen Gras if (!vc->vno_version)
159d65f6f70SBen Gras return ENOMEM;
160d65f6f70SBen Gras *vc->vno_version = le64toh(vnode->version);
161d65f6f70SBen Gras chfs_add_vnode_ref_to_vc(chmp, vc, nref);
162d65f6f70SBen Gras }
16384d9c625SLionel Sambuc mutex_exit(&chmp->chm_lock_vnocache);
164d65f6f70SBen Gras
16584d9c625SLionel Sambuc /* update sizes */
166d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_sizes);
167d65f6f70SBen Gras chfs_change_size_free(chmp, cheb, -le32toh(vnode->length));
168d65f6f70SBen Gras chfs_change_size_used(chmp, cheb, le32toh(vnode->length));
169d65f6f70SBen Gras mutex_exit(&chmp->chm_lock_sizes);
170d65f6f70SBen Gras
171d65f6f70SBen Gras KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
172d65f6f70SBen Gras
173d65f6f70SBen Gras KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
174d65f6f70SBen Gras
175d65f6f70SBen Gras return CHFS_NODE_OK;
176d65f6f70SBen Gras }
177d65f6f70SBen Gras
17884d9c625SLionel Sambuc /* chfs_scan_mark_dirent_obsolete - marks a directory entry "obsolete" */
179d65f6f70SBen Gras int
chfs_scan_mark_dirent_obsolete(struct chfs_mount * chmp,struct chfs_vnode_cache * vc,struct chfs_dirent * fd)180d65f6f70SBen Gras chfs_scan_mark_dirent_obsolete(struct chfs_mount *chmp,
181d65f6f70SBen Gras struct chfs_vnode_cache *vc, struct chfs_dirent *fd)
182d65f6f70SBen Gras {
183d65f6f70SBen Gras struct chfs_eraseblock *cheb;
184d65f6f70SBen Gras struct chfs_node_ref *prev, *nref;
185d65f6f70SBen Gras
186d65f6f70SBen Gras nref = fd->nref;
187d65f6f70SBen Gras cheb = &chmp->chm_blocks[fd->nref->nref_lnr];
188d65f6f70SBen Gras
18984d9c625SLionel Sambuc /* remove dirent's node ref from vnode cache */
190d65f6f70SBen Gras prev = vc->dirents;
191d65f6f70SBen Gras if (prev && prev == nref) {
192d65f6f70SBen Gras vc->dirents = prev->nref_next;
193d65f6f70SBen Gras } else if (prev && prev != (void *)vc) {
19484d9c625SLionel Sambuc while (prev->nref_next && prev->nref_next != (void *)vc) {
195d65f6f70SBen Gras if (prev->nref_next == nref) {
196d65f6f70SBen Gras prev->nref_next = nref->nref_next;
19784d9c625SLionel Sambuc break;
19884d9c625SLionel Sambuc }
19984d9c625SLionel Sambuc prev = prev->nref_next;
200d65f6f70SBen Gras }
201d65f6f70SBen Gras }
202d65f6f70SBen Gras
203d65f6f70SBen Gras KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size +
204d65f6f70SBen Gras cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
205d65f6f70SBen Gras
206d65f6f70SBen Gras return 0;
207d65f6f70SBen Gras }
208d65f6f70SBen Gras
20984d9c625SLionel Sambuc /* chfs_add_fd_to_list - adds a directory entry to its parent's vnode cache */
210d65f6f70SBen Gras void
chfs_add_fd_to_list(struct chfs_mount * chmp,struct chfs_dirent * new,struct chfs_vnode_cache * pvc)211d65f6f70SBen Gras chfs_add_fd_to_list(struct chfs_mount *chmp,
212d65f6f70SBen Gras struct chfs_dirent *new, struct chfs_vnode_cache *pvc)
213d65f6f70SBen Gras {
214d65f6f70SBen Gras KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
215d65f6f70SBen Gras int size;
216d65f6f70SBen Gras struct chfs_eraseblock *cheb, *oldcheb;
217d65f6f70SBen Gras struct chfs_dirent *fd, *tmpfd;
218d65f6f70SBen Gras
219d65f6f70SBen Gras dbg("adding fd to list: %s\n", new->name);
220d65f6f70SBen Gras
22184d9c625SLionel Sambuc /* update highest version if needed */
222d65f6f70SBen Gras if ((new->version > pvc->highest_version))
223d65f6f70SBen Gras pvc->highest_version = new->version;
224d65f6f70SBen Gras
225d65f6f70SBen Gras size = CHFS_PAD(sizeof(struct chfs_flash_dirent_node) +
226d65f6f70SBen Gras new->nsize);
227d65f6f70SBen Gras cheb = &chmp->chm_blocks[new->nref->nref_lnr];
228d65f6f70SBen Gras
229d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_sizes);
230d65f6f70SBen Gras TAILQ_FOREACH_SAFE(fd, &pvc->scan_dirents, fds, tmpfd) {
231d65f6f70SBen Gras if (fd->nhash > new->nhash) {
232d65f6f70SBen Gras /* insert new before fd */
233d65f6f70SBen Gras TAILQ_INSERT_BEFORE(fd, new, fds);
234d65f6f70SBen Gras goto out;
235d65f6f70SBen Gras } else if (fd->nhash == new->nhash &&
236d65f6f70SBen Gras !strcmp(fd->name, new->name)) {
237d65f6f70SBen Gras if (new->version > fd->version) {
238d65f6f70SBen Gras /* replace fd with new */
239d65f6f70SBen Gras TAILQ_INSERT_BEFORE(fd, new, fds);
240d65f6f70SBen Gras chfs_change_size_free(chmp, cheb, -size);
241d65f6f70SBen Gras chfs_change_size_used(chmp, cheb, size);
242d65f6f70SBen Gras
243d65f6f70SBen Gras TAILQ_REMOVE(&pvc->scan_dirents, fd, fds);
244d65f6f70SBen Gras if (fd->nref) {
245d65f6f70SBen Gras size = CHFS_PAD(sizeof(struct chfs_flash_dirent_node) + fd->nsize);
246d65f6f70SBen Gras chfs_scan_mark_dirent_obsolete(chmp, pvc, fd);
247d65f6f70SBen Gras oldcheb = &chmp->chm_blocks[fd->nref->nref_lnr];
248d65f6f70SBen Gras chfs_change_size_used(chmp, oldcheb, -size);
249d65f6f70SBen Gras chfs_change_size_dirty(chmp, oldcheb, size);
250d65f6f70SBen Gras }
251d65f6f70SBen Gras chfs_free_dirent(fd);
252d65f6f70SBen Gras } else {
25384d9c625SLionel Sambuc /* new dirent is older */
254d65f6f70SBen Gras chfs_scan_mark_dirent_obsolete(chmp, pvc, new);
255d65f6f70SBen Gras chfs_change_size_free(chmp, cheb, -size);
256d65f6f70SBen Gras chfs_change_size_dirty(chmp, cheb, size);
257d65f6f70SBen Gras chfs_free_dirent(new);
258d65f6f70SBen Gras }
259d65f6f70SBen Gras mutex_exit(&chmp->chm_lock_sizes);
260d65f6f70SBen Gras return;
261d65f6f70SBen Gras }
262d65f6f70SBen Gras }
263d65f6f70SBen Gras /* if we couldnt fit it elsewhere, lets add to the end */
264d65f6f70SBen Gras TAILQ_INSERT_TAIL(&pvc->scan_dirents, new, fds);
265d65f6f70SBen Gras
266d65f6f70SBen Gras out:
26784d9c625SLionel Sambuc /* update sizes */
268d65f6f70SBen Gras chfs_change_size_free(chmp, cheb, -size);
269d65f6f70SBen Gras chfs_change_size_used(chmp, cheb, size);
270d65f6f70SBen Gras mutex_exit(&chmp->chm_lock_sizes);
271d65f6f70SBen Gras
272d65f6f70SBen Gras KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
273d65f6f70SBen Gras
274d65f6f70SBen Gras KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
275d65f6f70SBen Gras }
27684d9c625SLionel Sambuc
27784d9c625SLionel Sambuc /* chfs_scan_check_dirent_node - check vnode crc and add to vnode cache */
278d65f6f70SBen Gras int
chfs_scan_check_dirent_node(struct chfs_mount * chmp,struct chfs_eraseblock * cheb,void * buf,off_t ofs)279d65f6f70SBen Gras chfs_scan_check_dirent_node(struct chfs_mount *chmp,
280d65f6f70SBen Gras struct chfs_eraseblock *cheb, void *buf, off_t ofs)
281d65f6f70SBen Gras {
282d65f6f70SBen Gras int err, namelen;
283d65f6f70SBen Gras uint32_t crc;
284d65f6f70SBen Gras struct chfs_dirent *fd;
28584d9c625SLionel Sambuc struct chfs_vnode_cache *parentvc;
286d65f6f70SBen Gras struct chfs_flash_dirent_node *dirent = buf;
287d65f6f70SBen Gras
28884d9c625SLionel Sambuc /* check crc */
289d65f6f70SBen Gras crc = crc32(0, (uint8_t *)dirent, sizeof(*dirent) - 4);
290d65f6f70SBen Gras if (crc != le32toh(dirent->node_crc)) {
291d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb, le32toh(dirent->length));
292d65f6f70SBen Gras if (err)
293d65f6f70SBen Gras return err;
294d65f6f70SBen Gras return CHFS_NODE_BADCRC;
295d65f6f70SBen Gras }
29684d9c625SLionel Sambuc
29784d9c625SLionel Sambuc /* allocate space for name */
298d65f6f70SBen Gras namelen = dirent->nsize;
299d65f6f70SBen Gras
300d65f6f70SBen Gras fd = chfs_alloc_dirent(namelen + 1);
301d65f6f70SBen Gras if (!fd)
302d65f6f70SBen Gras return ENOMEM;
303d65f6f70SBen Gras
30484d9c625SLionel Sambuc /* allocate an nref */
305d65f6f70SBen Gras fd->nref = chfs_alloc_node_ref(cheb);
306d65f6f70SBen Gras if (!fd->nref)
307d65f6f70SBen Gras return ENOMEM;
308d65f6f70SBen Gras
309d65f6f70SBen Gras KASSERT(fd->nref->nref_lnr == cheb->lnr);
310d65f6f70SBen Gras
311d65f6f70SBen Gras memcpy(&fd->name, dirent->name, namelen);
312d65f6f70SBen Gras fd->nsize = namelen;
313d65f6f70SBen Gras fd->name[namelen] = 0;
314d65f6f70SBen Gras crc = crc32(0, fd->name, dirent->nsize);
315d65f6f70SBen Gras if (crc != le32toh(dirent->name_crc)) {
316d65f6f70SBen Gras chfs_err("Directory entry's name has bad crc: read: 0x%x, "
317d65f6f70SBen Gras "calculated: 0x%x\n", le32toh(dirent->name_crc), crc);
318d65f6f70SBen Gras chfs_free_dirent(fd);
319d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb, le32toh(dirent->length));
320d65f6f70SBen Gras if (err)
321d65f6f70SBen Gras return err;
322d65f6f70SBen Gras return CHFS_NODE_BADNAMECRC;
323d65f6f70SBen Gras }
324d65f6f70SBen Gras
32584d9c625SLionel Sambuc /* check vnode_cache of parent node */
326d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_vnocache);
32784d9c625SLionel Sambuc parentvc = chfs_scan_make_vnode_cache(chmp, le64toh(dirent->pvno));
32884d9c625SLionel Sambuc if (!parentvc) {
329d65f6f70SBen Gras chfs_free_dirent(fd);
330d65f6f70SBen Gras return ENOMEM;
331d65f6f70SBen Gras }
332d65f6f70SBen Gras
333d65f6f70SBen Gras fd->nref->nref_offset = ofs;
334d65f6f70SBen Gras
33584d9c625SLionel Sambuc dbg("add dirent to #%llu\n", (unsigned long long)parentvc->vno);
33684d9c625SLionel Sambuc chfs_add_node_to_list(chmp, parentvc, fd->nref, &parentvc->dirents);
33784d9c625SLionel Sambuc mutex_exit(&chmp->chm_lock_vnocache);
338d65f6f70SBen Gras
339d65f6f70SBen Gras fd->vno = le64toh(dirent->vno);
340d65f6f70SBen Gras fd->version = le64toh(dirent->version);
341d65f6f70SBen Gras fd->nhash = hash32_buf(fd->name, namelen, HASH32_BUF_INIT);
342d65f6f70SBen Gras fd->type = dirent->dtype;
343d65f6f70SBen Gras
34484d9c625SLionel Sambuc chfs_add_fd_to_list(chmp, fd, parentvc);
345d65f6f70SBen Gras
346d65f6f70SBen Gras return CHFS_NODE_OK;
347d65f6f70SBen Gras }
348d65f6f70SBen Gras
34984d9c625SLionel Sambuc /* chfs_scan_check_data_node - check vnode crc and add to vnode cache */
350d65f6f70SBen Gras int
chfs_scan_check_data_node(struct chfs_mount * chmp,struct chfs_eraseblock * cheb,void * buf,off_t ofs)351d65f6f70SBen Gras chfs_scan_check_data_node(struct chfs_mount *chmp,
352d65f6f70SBen Gras struct chfs_eraseblock *cheb, void *buf, off_t ofs)
353d65f6f70SBen Gras {
354d65f6f70SBen Gras KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
355d65f6f70SBen Gras int err;
356d65f6f70SBen Gras uint32_t crc, vno;
357d65f6f70SBen Gras struct chfs_node_ref *nref;
358d65f6f70SBen Gras struct chfs_vnode_cache *vc;
359d65f6f70SBen Gras struct chfs_flash_data_node *dnode = buf;
360d65f6f70SBen Gras
36184d9c625SLionel Sambuc /* check crc */
362d65f6f70SBen Gras crc = crc32(0, (uint8_t *)dnode, sizeof(struct chfs_flash_data_node) - 4);
363d65f6f70SBen Gras if (crc != le32toh(dnode->node_crc)) {
364d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb, le32toh(dnode->length));
365d65f6f70SBen Gras if (err)
366d65f6f70SBen Gras return err;
367d65f6f70SBen Gras return CHFS_NODE_BADCRC;
368d65f6f70SBen Gras }
36984d9c625SLionel Sambuc /*
370d65f6f70SBen Gras * Don't check data nodes crc and version here, it will be done in
371d65f6f70SBen Gras * the background GC thread.
372d65f6f70SBen Gras */
373d65f6f70SBen Gras nref = chfs_alloc_node_ref(cheb);
374d65f6f70SBen Gras if (!nref)
375d65f6f70SBen Gras return ENOMEM;
376d65f6f70SBen Gras
37784d9c625SLionel Sambuc nref->nref_offset = CHFS_GET_OFS(ofs) | CHFS_UNCHECKED_NODE_MASK;
378d65f6f70SBen Gras
379d65f6f70SBen Gras KASSERT(nref->nref_lnr == cheb->lnr);
380d65f6f70SBen Gras
381d65f6f70SBen Gras vno = le64toh(dnode->vno);
382d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_vnocache);
383d65f6f70SBen Gras vc = chfs_vnode_cache_get(chmp, vno);
384d65f6f70SBen Gras if (!vc) {
385d65f6f70SBen Gras vc = chfs_scan_make_vnode_cache(chmp, vno);
386d65f6f70SBen Gras if (!vc)
387d65f6f70SBen Gras return ENOMEM;
388d65f6f70SBen Gras }
389d65f6f70SBen Gras chfs_add_node_to_list(chmp, vc, nref, &vc->dnode);
39084d9c625SLionel Sambuc mutex_exit(&chmp->chm_lock_vnocache);
391d65f6f70SBen Gras
392d65f6f70SBen Gras dbg("chmpfree: %u, chebfree: %u, dnode: %u\n", chmp->chm_free_size, cheb->free_size, dnode->length);
393d65f6f70SBen Gras
39484d9c625SLionel Sambuc /* update sizes */
395d65f6f70SBen Gras mutex_enter(&chmp->chm_lock_sizes);
396d65f6f70SBen Gras chfs_change_size_free(chmp, cheb, -dnode->length);
397d65f6f70SBen Gras chfs_change_size_unchecked(chmp, cheb, dnode->length);
398d65f6f70SBen Gras mutex_exit(&chmp->chm_lock_sizes);
399d65f6f70SBen Gras return CHFS_NODE_OK;
400d65f6f70SBen Gras }
401d65f6f70SBen Gras
40284d9c625SLionel Sambuc /* chfs_scan_classify_cheb - determine eraseblock's state */
403d65f6f70SBen Gras int
chfs_scan_classify_cheb(struct chfs_mount * chmp,struct chfs_eraseblock * cheb)404d65f6f70SBen Gras chfs_scan_classify_cheb(struct chfs_mount *chmp,
405d65f6f70SBen Gras struct chfs_eraseblock *cheb)
406d65f6f70SBen Gras {
407d65f6f70SBen Gras if (cheb->free_size == chmp->chm_ebh->eb_size)
408d65f6f70SBen Gras return CHFS_BLK_STATE_FREE;
409d65f6f70SBen Gras else if (cheb->dirty_size < MAX_DIRTY_TO_CLEAN)
410d65f6f70SBen Gras return CHFS_BLK_STATE_CLEAN;
411d65f6f70SBen Gras else if (cheb->used_size || cheb->unchecked_size)
412d65f6f70SBen Gras return CHFS_BLK_STATE_PARTDIRTY;
413d65f6f70SBen Gras else
414d65f6f70SBen Gras return CHFS_BLK_STATE_ALLDIRTY;
415d65f6f70SBen Gras }
416d65f6f70SBen Gras
417d65f6f70SBen Gras
41884d9c625SLionel Sambuc /*
419d65f6f70SBen Gras * chfs_scan_eraseblock - scans an eraseblock and looking for nodes
420d65f6f70SBen Gras *
421d65f6f70SBen Gras * This function scans a whole eraseblock, checks the nodes on it and add them
422d65f6f70SBen Gras * to the vnode cache.
423d65f6f70SBen Gras * Returns eraseblock state on success, error code if fails.
424d65f6f70SBen Gras */
425d65f6f70SBen Gras int
chfs_scan_eraseblock(struct chfs_mount * chmp,struct chfs_eraseblock * cheb)426d65f6f70SBen Gras chfs_scan_eraseblock(struct chfs_mount *chmp,
42784d9c625SLionel Sambuc struct chfs_eraseblock *cheb)
42884d9c625SLionel Sambuc {
429d65f6f70SBen Gras int err;
430d65f6f70SBen Gras size_t len, retlen;
431d65f6f70SBen Gras off_t ofs = 0;
432d65f6f70SBen Gras int lnr = cheb->lnr;
433d65f6f70SBen Gras u_char *buf;
434d65f6f70SBen Gras struct chfs_flash_node_hdr *nhdr;
435d65f6f70SBen Gras int read_free = 0;
436d65f6f70SBen Gras struct chfs_node_ref *nref;
437d65f6f70SBen Gras
438d65f6f70SBen Gras dbg("scanning eraseblock content: %d free_size: %d\n", cheb->lnr, cheb->free_size);
439d65f6f70SBen Gras dbg("scanned physical block: %d\n", chmp->chm_ebh->lmap[lnr]);
440d65f6f70SBen Gras buf = kmem_alloc(CHFS_MAX_NODE_SIZE, KM_SLEEP);
441d65f6f70SBen Gras
442d65f6f70SBen Gras while((ofs + CHFS_NODE_HDR_SIZE) < chmp->chm_ebh->eb_size) {
443d65f6f70SBen Gras memset(buf, 0 , CHFS_MAX_NODE_SIZE);
444d65f6f70SBen Gras err = chfs_read_leb(chmp,
445d65f6f70SBen Gras lnr, buf, ofs, CHFS_NODE_HDR_SIZE, &retlen);
446*0a6a1f1dSLionel Sambuc if (err)
447*0a6a1f1dSLionel Sambuc goto err_return;
448d65f6f70SBen Gras
449d65f6f70SBen Gras if (retlen != CHFS_NODE_HDR_SIZE) {
450d65f6f70SBen Gras chfs_err("Error reading node header: "
451d65f6f70SBen Gras "read: %zu instead of: %zu\n",
452d65f6f70SBen Gras CHFS_NODE_HDR_SIZE, retlen);
453*0a6a1f1dSLionel Sambuc err = EIO;
454*0a6a1f1dSLionel Sambuc goto err_return;
455d65f6f70SBen Gras }
456d65f6f70SBen Gras
457d65f6f70SBen Gras /* first we check if the buffer we read is full with 0xff, if yes maybe
458d65f6f70SBen Gras * the blocks remaining area is free. We increase read_free and if it
459d65f6f70SBen Gras * reaches MAX_READ_FREE we stop reading the block */
460d65f6f70SBen Gras if (check_pattern(buf, 0xff, 0, CHFS_NODE_HDR_SIZE)) {
461d65f6f70SBen Gras read_free += CHFS_NODE_HDR_SIZE;
462d65f6f70SBen Gras if (read_free >= MAX_READ_FREE(chmp)) {
463d65f6f70SBen Gras dbg("rest of the block is free. Size: %d\n", cheb->free_size);
464*0a6a1f1dSLionel Sambuc kmem_free(buf, CHFS_MAX_NODE_SIZE);
465d65f6f70SBen Gras return chfs_scan_classify_cheb(chmp, cheb);
466d65f6f70SBen Gras }
467d65f6f70SBen Gras ofs += CHFS_NODE_HDR_SIZE;
468d65f6f70SBen Gras continue;
469d65f6f70SBen Gras } else {
470d65f6f70SBen Gras chfs_update_eb_dirty(chmp, cheb, read_free);
471d65f6f70SBen Gras read_free = 0;
472d65f6f70SBen Gras }
473d65f6f70SBen Gras
474d65f6f70SBen Gras nhdr = (struct chfs_flash_node_hdr *)buf;
475d65f6f70SBen Gras
476d65f6f70SBen Gras err = chfs_scan_check_node_hdr(nhdr);
477d65f6f70SBen Gras if (err) {
478d65f6f70SBen Gras dbg("node hdr error\n");
479d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb, 4);
480*0a6a1f1dSLionel Sambuc if (err)
481*0a6a1f1dSLionel Sambuc goto err_return;
482d65f6f70SBen Gras
483d65f6f70SBen Gras ofs += 4;
484d65f6f70SBen Gras continue;
485d65f6f70SBen Gras }
486d65f6f70SBen Gras ofs += CHFS_NODE_HDR_SIZE;
487d65f6f70SBen Gras if (ofs > chmp->chm_ebh->eb_size) {
488d65f6f70SBen Gras chfs_err("Second part of node is on the next eraseblock.\n");
489*0a6a1f1dSLionel Sambuc err = EIO;
490*0a6a1f1dSLionel Sambuc goto err_return;
491d65f6f70SBen Gras }
492d65f6f70SBen Gras switch (le16toh(nhdr->type)) {
493d65f6f70SBen Gras case CHFS_NODETYPE_VNODE:
49484d9c625SLionel Sambuc /* vnode information */
49584d9c625SLionel Sambuc /* read up the node */
496d65f6f70SBen Gras len = le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
497d65f6f70SBen Gras err = chfs_read_leb(chmp,
498d65f6f70SBen Gras lnr, buf + CHFS_NODE_HDR_SIZE,
499d65f6f70SBen Gras ofs, len, &retlen);
500*0a6a1f1dSLionel Sambuc if (err)
501*0a6a1f1dSLionel Sambuc goto err_return;
502d65f6f70SBen Gras
503d65f6f70SBen Gras if (retlen != len) {
504d65f6f70SBen Gras chfs_err("Error reading vnode: read: %zu instead of: %zu\n",
505d65f6f70SBen Gras len, retlen);
506*0a6a1f1dSLionel Sambuc err = EIO;
507*0a6a1f1dSLionel Sambuc goto err_return;
508d65f6f70SBen Gras }
509d65f6f70SBen Gras KASSERT(lnr == cheb->lnr);
510d65f6f70SBen Gras err = chfs_scan_check_vnode(chmp,
511d65f6f70SBen Gras cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
512*0a6a1f1dSLionel Sambuc if (err)
513*0a6a1f1dSLionel Sambuc goto err_return;
514d65f6f70SBen Gras
515d65f6f70SBen Gras break;
516d65f6f70SBen Gras case CHFS_NODETYPE_DIRENT:
51784d9c625SLionel Sambuc /* directory entry */
51884d9c625SLionel Sambuc /* read up the node */
519d65f6f70SBen Gras len = le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
520d65f6f70SBen Gras
521d65f6f70SBen Gras err = chfs_read_leb(chmp,
522d65f6f70SBen Gras lnr, buf + CHFS_NODE_HDR_SIZE,
523d65f6f70SBen Gras ofs, len, &retlen);
524*0a6a1f1dSLionel Sambuc if (err)
525*0a6a1f1dSLionel Sambuc goto err_return;
526d65f6f70SBen Gras
527d65f6f70SBen Gras if (retlen != len) {
528d65f6f70SBen Gras chfs_err("Error reading dirent node: read: %zu "
529d65f6f70SBen Gras "instead of: %zu\n", len, retlen);
530*0a6a1f1dSLionel Sambuc err = EIO;
531*0a6a1f1dSLionel Sambuc goto err_return;
532d65f6f70SBen Gras }
533d65f6f70SBen Gras
534d65f6f70SBen Gras KASSERT(lnr == cheb->lnr);
535d65f6f70SBen Gras
536d65f6f70SBen Gras err = chfs_scan_check_dirent_node(chmp,
537d65f6f70SBen Gras cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
538*0a6a1f1dSLionel Sambuc if (err)
539*0a6a1f1dSLionel Sambuc goto err_return;
540d65f6f70SBen Gras
541d65f6f70SBen Gras break;
542d65f6f70SBen Gras case CHFS_NODETYPE_DATA:
54384d9c625SLionel Sambuc /* data node */
544d65f6f70SBen Gras len = sizeof(struct chfs_flash_data_node) -
545d65f6f70SBen Gras CHFS_NODE_HDR_SIZE;
546d65f6f70SBen Gras err = chfs_read_leb(chmp,
547d65f6f70SBen Gras lnr, buf + CHFS_NODE_HDR_SIZE,
548d65f6f70SBen Gras ofs, len, &retlen);
549*0a6a1f1dSLionel Sambuc if (err)
550*0a6a1f1dSLionel Sambuc goto err_return;
551d65f6f70SBen Gras
552d65f6f70SBen Gras if (retlen != len) {
553d65f6f70SBen Gras chfs_err("Error reading data node: read: %zu "
554d65f6f70SBen Gras "instead of: %zu\n", len, retlen);
555*0a6a1f1dSLionel Sambuc err = EIO;
556*0a6a1f1dSLionel Sambuc goto err_return;
557d65f6f70SBen Gras }
558d65f6f70SBen Gras KASSERT(lnr == cheb->lnr);
559d65f6f70SBen Gras err = chfs_scan_check_data_node(chmp,
560d65f6f70SBen Gras cheb, buf, ofs - CHFS_NODE_HDR_SIZE);
561d65f6f70SBen Gras if (err)
562*0a6a1f1dSLionel Sambuc goto err_return;
563d65f6f70SBen Gras
564d65f6f70SBen Gras break;
565d65f6f70SBen Gras case CHFS_NODETYPE_PADDING:
56684d9c625SLionel Sambuc /* padding node, set size and update dirty */
567d65f6f70SBen Gras nref = chfs_alloc_node_ref(cheb);
568d65f6f70SBen Gras nref->nref_offset = ofs - CHFS_NODE_HDR_SIZE;
569d65f6f70SBen Gras nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
570d65f6f70SBen Gras CHFS_OBSOLETE_NODE_MASK;
571d65f6f70SBen Gras
572d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb,
573d65f6f70SBen Gras le32toh(nhdr->length));
574d65f6f70SBen Gras if (err)
575*0a6a1f1dSLionel Sambuc goto err_return;
576d65f6f70SBen Gras
577d65f6f70SBen Gras break;
578d65f6f70SBen Gras default:
57984d9c625SLionel Sambuc /* unknown node type, update dirty and skip */
580d65f6f70SBen Gras err = chfs_update_eb_dirty(chmp, cheb,
581d65f6f70SBen Gras le32toh(nhdr->length));
582d65f6f70SBen Gras if (err)
583*0a6a1f1dSLionel Sambuc goto err_return;
584d65f6f70SBen Gras
585d65f6f70SBen Gras break;
586d65f6f70SBen Gras }
587d65f6f70SBen Gras ofs += le32toh(nhdr->length) - CHFS_NODE_HDR_SIZE;
588d65f6f70SBen Gras }
589d65f6f70SBen Gras
590d65f6f70SBen Gras KASSERT(cheb->used_size + cheb->free_size + cheb->dirty_size +
591d65f6f70SBen Gras cheb->unchecked_size + cheb->wasted_size == chmp->chm_ebh->eb_size);
592d65f6f70SBen Gras
593*0a6a1f1dSLionel Sambuc err = chfs_scan_classify_cheb(chmp, cheb);
594*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
595*0a6a1f1dSLionel Sambuc err_return:
596*0a6a1f1dSLionel Sambuc kmem_free(buf, CHFS_MAX_NODE_SIZE);
597*0a6a1f1dSLionel Sambuc return err;
598d65f6f70SBen Gras }
599