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