/* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go - Scan directory tree from top down, setting nlink in inocaches - Scan inocaches for inodes with nlink==0 */ static int jffs2_build_filesystem(struct jffs2_sb_info *c) { int ret; int i; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; dbg_fsbuild("build FS data structures\n"); /* First, scan the medium and build all the inode caches with lists of physical nodes */ c->flags |= JFFS2_SB_FLAG_SCANNING; ret = jffs2_scan_medium(c); c->flags &= ~JFFS2_SB_FLAG_SCANNING; if (ret) goto exit; dbg_fsbuild("scanned flash completely\n"); jffs2_dbg_dump_block_lists_nolock(c); if (c->flags & (1 << 7)) { printk("%s(): unlocking the mtd device... ", __func__); if (c->mtd->unlock) c->mtd->unlock(c->mtd, 0, c->mtd->size); printk("done.\n"); printk("%s(): erasing all blocks after the end marker... ", __func__); jffs2_erase_pending_blocks(c, -1); printk("done.\n"); } dbg_fsbuild("pass 1 starting\n"); c->flags |= JFFS2_SB_FLAG_BUILDING; /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { if (ic->scan_dents) { jffs2_build_inode_pass1(c, ic); cond_resched(); } } dbg_fsbuild("pass 1 complete\n"); /* Next, scan for inodes with nlink == 0 and remove them. If they were directories, then decrement the nlink of their children too, and repeat the scan. As that's going to be a fairly uncommon occurrence, it's not so evil to do it this way. Recursion bad. */ dbg_fsbuild("pass 2 starting\n"); for_each_inode(i, c, ic) { if (ic->pino_nlink) continue; jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); cond_resched(); } dbg_fsbuild("pass 2a starting\n"); while (dead_fds) { fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); if (ic) jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); jffs2_free_full_dirent(fd); } dbg_fsbuild("pass 2a complete\n"); dbg_fsbuild("freeing temporary data structures\n"); /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } ic->scan_dents = NULL; cond_resched(); } jffs2_build_xattr_subsystem(c); c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); ret = 0; exit: if (ret) { for_each_inode(i, c, ic) { while(ic->scan_dents) { fd = ic->scan_dents; ic->scan_dents = fd->next; jffs2_free_full_dirent(fd); } } jffs2_clear_xattr_subsystem(c); } return ret; }
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t checkedlen; uint32_t crc; int err; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); /* We don't get here unless the node is still valid, so we don't have to mask in the ACCURATE bit any more. */ crc = crc32(0, rd, sizeof(*rd)-8); if (crc != je32_to_cpu(rd->node_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) return err; return 0; } pseudo_random += je32_to_cpu(rd->version); /* Should never happen. Did. (OLPC trac #4184)*/ checkedlen = strnlen(rd->name, rd->nsize); if (checkedlen < rd->nsize) { printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n", ofs, checkedlen); } fd = jffs2_alloc_full_dirent(checkedlen+1); if (!fd) { return -ENOMEM; } memcpy(&fd->name, rd->name, checkedlen); fd->name[checkedlen] = 0; crc = crc32(0, fd->name, rd->nsize); if (crc != je32_to_cpu(rd->name_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->name_crc), crc); D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) return err; return 0; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); return -ENOMEM; } fd->raw = jffs2_link_node_ref(c, jeb, ofs | dirent_node_state(rd), PAD(je32_to_cpu(rd->totlen)), ic); fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, checkedlen); fd->type = rd->type; jffs2_add_fd_to_list(c, fd, &ic->scan_dents); if (jffs2_sum_active()) { jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset); } return 0; }
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t crc; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); /* We don't get here unless the node is still valid, so we don't have to mask in the ACCURATE bit any more. */ crc = crc32(0, rd, sizeof(*rd)-8); if (crc != je32_to_cpu(rd->node_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } pseudo_random += je32_to_cpu(rd->version); fd = jffs2_alloc_full_dirent(rd->nsize+1); if (!fd) { return -ENOMEM; } memcpy(&fd->name, rd->name, rd->nsize); fd->name[rd->nsize] = 0; crc = crc32(0, fd->name, rd->nsize); if (crc != je32_to_cpu(rd->name_crc)) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->name_crc), crc); D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); return 0; } raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); return -ENOMEM; } raw->totlen = PAD(je32_to_cpu(rd->totlen)); raw->flash_offset = ofs | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, rd->nsize); fd->type = rd->type; USED_SPACE(PAD(je32_to_cpu(rd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); return 0; }
static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; dbg_fsbuild("removing ino #%u with nlink == zero.\n", ic->ino); raw = ic->nodes; while (raw != (void *)ic) { struct jffs2_raw_node_ref *next = raw->next_in_ino; dbg_fsbuild("obsoleting node at 0x%08x\n", ref_offset(raw)); jffs2_mark_node_obsolete(c, raw); raw = next; } if (ic->scan_dents) { int whinged = 0; dbg_fsbuild("inode #%u was a directory which may have children...\n", ic->ino); while(ic->scan_dents) { struct jffs2_inode_cache *child_ic; fd = ic->scan_dents; ic->scan_dents = fd->next; if (!fd->ino) { /* It's a deletion dirent. Ignore it */ dbg_fsbuild("child \"%s\" is a deletion dirent, skipping...\n", fd->name); jffs2_free_full_dirent(fd); continue; } if (!whinged) whinged = 1; dbg_fsbuild("removing child \"%s\", ino #%u\n", fd->name, fd->ino); child_ic = jffs2_get_ino_cache(c, fd->ino); if (!child_ic) { dbg_fsbuild("cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); jffs2_free_full_dirent(fd); continue; } /* Reduce nlink of the child. If it's now zero, stick it on the dead_fds list to be cleaned up later. Else just free the fd */ if (fd->type == DT_DIR) child_ic->pino_nlink = 0; else child_ic->pino_nlink--; if (!child_ic->pino_nlink) { dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n", fd->ino, fd->name); fd->next = *dead_fds; *dead_fds = fd; } else { dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", fd->ino, fd->name, child_ic->pino_nlink); jffs2_free_full_dirent(fd); } } } /* We don't delete the inocache from the hash list and free it yet. The erase code will do that, when all the nodes are completely gone. */ }
/* * Helper function for jffs2_get_inode_nodes(). * It is called every time an directory entry node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_dirent *rd, size_t read, struct jffs2_full_dirent **fdp, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_full_dirent *fd; uint32_t crc; /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); crc = crc32(0, rd, sizeof(*rd) - 8); if (unlikely(crc != je32_to_cpu(rd->node_crc))) { JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n", ref_offset(ref), je32_to_cpu(rd->node_crc), crc); return 1; } /* If we've never checked the CRCs on this node, check them now */ if (ref_flags(ref) == REF_UNCHECKED) { struct jffs2_eraseblock *jeb; int len; /* Sanity check */ if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); return 1; } jeb = &c->blocks[ref->flash_offset / c->sector_size]; len = ref_totlen(c, jeb, ref); spin_lock(&c->erase_completion_lock); jeb->used_size += len; jeb->unchecked_size -= len; c->used_size += len; c->unchecked_size -= len; ref->flash_offset = ref_offset(ref) | REF_PRISTINE; spin_unlock(&c->erase_completion_lock); } fd = jffs2_alloc_full_dirent(rd->nsize + 1); if (unlikely(!fd)) return -ENOMEM; fd->raw = ref; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->type = rd->type; /* Pick out the mctime of the latest dirent */ if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { *mctime_ver = fd->version; *latest_mctime = je32_to_cpu(rd->mctime); } /* * Copy as much of the name as possible from the raw * dirent we've already read from the flash. */ if (read > sizeof(*rd)) memcpy(&fd->name[0], &rd->name[0], min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); /* Do we need to copy any more of the name directly from the flash? */ if (rd->nsize + sizeof(*rd) > read) { /* FIXME: point() */ int err; int already = read - sizeof(*rd); err = jffs2_flash_read(c, (ref_offset(ref)) + read, rd->nsize - already, &read, &fd->name[already]); if (unlikely(read != rd->nsize - already) && likely(!err)) return -EIO; if (unlikely(err)) { JFFS2_ERROR("read remainder of name: error %d\n", err); jffs2_free_full_dirent(fd); return -EIO; } } fd->nhash = full_name_hash(fd->name, rd->nsize); fd->next = NULL; fd->name[rd->nsize] = '\0'; /* * Wheee. We now have a complete jffs2_full_dirent structure, with * the name in it and everything. Link it into the list */ jffs2_add_fd_to_list(c, fd, fdp); return 0; }
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs) { struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; struct jffs2_raw_dirent rd; __u16 oldnodetype; int ret; __u32 crc; ssize_t retlen; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs)); ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd); if (ret) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret); return ret; } if (retlen != sizeof(rd)) { printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", retlen, *ofs, sizeof(rd)); return -EIO; } /* We sort of assume that the node was accurate when it was first written to the medium :) */ oldnodetype = rd.nodetype; rd.nodetype |= JFFS2_NODE_ACCURATE; crc = crc32(0, &rd, sizeof(rd)-8); rd.nodetype = oldnodetype; if (crc != rd.node_crc) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", *ofs, rd.node_crc, crc); /* FIXME: Why do we believe totlen? */ DIRTY_SPACE(4); *ofs += 4; return 0; } pseudo_random += rd.version; fd = jffs2_alloc_full_dirent(rd.nsize+1); if (!fd) { return -ENOMEM; } ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]); if (ret) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs + sizeof(rd), ret); return ret; } if (retlen != rd.nsize) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", retlen, *ofs + sizeof(rd), rd.nsize); return -EIO; } crc = crc32(0, fd->name, rd.nsize); if (crc != rd.name_crc) { printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", *ofs, rd.name_crc, crc); fd->name[rd.nsize]=0; D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino)); jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ DIRTY_SPACE(PAD(rd.totlen)); *ofs += PAD(rd.totlen); return 0; } raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, rd.pino); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); return -ENOMEM; } raw->totlen = PAD(rd.totlen); raw->flash_offset = *ofs; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; if (rd.nodetype & JFFS2_NODE_ACCURATE) { fd->raw = raw; fd->next = NULL; fd->version = rd.version; fd->ino = rd.ino; fd->name[rd.nsize]=0; fd->nhash = full_name_hash(fd->name, rd.nsize); fd->type = rd.type; USED_SPACE(PAD(rd.totlen)); jffs2_add_fd_to_list(c, fd, &ic->scan->dents); } else { raw->flash_offset |= 1; jffs2_free_full_dirent(fd); DIRTY_SPACE(PAD(rd.totlen)); } *ofs += PAD(rd.totlen); return 0; }
static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename) { struct jffs2_inode_info *dir_f, *f; struct jffs2_sb_info *c; struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; __u32 alloclen, phys_ofs; int ret; c = JFFS2_SB_INFO(dir_i->i_sb); rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION); if (ret) { jffs2_free_raw_dirent(rd); return ret; } dir_f = JFFS2_INODE_INFO(dir_i); down(&dir_f->sem); /* Build a deletion node */ rd->magic = JFFS2_MAGIC_BITMASK; rd->nodetype = JFFS2_NODETYPE_DIRENT; rd->totlen = sizeof(*rd) + dentry->d_name.len; rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); rd->pino = dir_i->i_ino; rd->version = ++dir_f->highest_version; rd->ino = 0; rd->mctime = CURRENT_TIME; rd->nsize = dentry->d_name.len; rd->type = DT_UNKNOWN; rd->node_crc = crc32(0, rd, sizeof(*rd)-8); rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len); fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL); jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { up(&dir_f->sem); return PTR_ERR(fd); } /* File it. This will mark the old one obsolete. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); if (!rename) { f = JFFS2_INODE_INFO(dentry->d_inode); down(&f->sem); while (f->dents) { /* There can be only deleted ones */ fd = f->dents; f->dents = fd->next; if (fd->ino) { printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", f->inocache->ino, fd->name, fd->ino); } else { D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino)); } jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); } /* Don't oops on unlinking a bad inode */ if (f->inocache) f->inocache->nlink--; dentry->d_inode->i_nlink--; up(&f->sem); } return 0; }
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t checkedlen; uint32_t crc; int err; jffs2_dbg(1, "%s(): Node at 0x%08x\n", __func__, ofs); crc = crc32(0, rd, sizeof(*rd)-8); if (crc != je32_to_cpu(rd->node_crc)) { pr_notice("%s(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", __func__, ofs, je32_to_cpu(rd->node_crc), crc); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) return err; return 0; } pseudo_random += je32_to_cpu(rd->version); checkedlen = strnlen(rd->name, rd->nsize); if (checkedlen < rd->nsize) { pr_err("Dirent at %08x has zeroes in name. Truncating to %d chars\n", ofs, checkedlen); } fd = jffs2_alloc_full_dirent(checkedlen+1); if (!fd) { return -ENOMEM; } memcpy(&fd->name, rd->name, checkedlen); fd->name[checkedlen] = 0; crc = crc32(0, fd->name, rd->nsize); if (crc != je32_to_cpu(rd->name_crc)) { pr_notice("%s(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", __func__, ofs, je32_to_cpu(rd->name_crc), crc); jffs2_dbg(1, "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)); jffs2_free_full_dirent(fd); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) return err; return 0; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); return -ENOMEM; } fd->raw = jffs2_link_node_ref(c, jeb, ofs | dirent_node_state(rd), PAD(je32_to_cpu(rd->totlen)), ic); fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, checkedlen); fd->type = rd->type; jffs2_add_fd_to_list(c, fd, &ic->scan_dents); if (jffs2_sum_active()) { jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset); } return 0; }
static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t *pseudo_random) { struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; void *sp; int i, ino; sp = summary->sum; for (i=0; i<je32_to_cpu(summary->sum_num); i++) { dbg_summary("processing summary index %d\n", i); switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *spi; spi = sp; ino = je32_to_cpu(spi->inode); dbg_summary("Inode at 0x%08x\n", jeb->offset + je32_to_cpu(spi->offset)); raw = jffs2_alloc_raw_node_ref(); if (!raw) { JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { JFFS2_NOTICE("scan_make_ino_cache failed\n"); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED; raw->__totlen = PAD(je32_to_cpu(spi->totlen)); raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; *pseudo_random += je32_to_cpu(spi->version); UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen))); sp += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *spd; spd = sp; dbg_summary("Dirent at 0x%08x\n", jeb->offset + je32_to_cpu(spd->offset)); fd = jffs2_alloc_full_dirent(spd->nsize+1); if (!fd) { kfree(summary); return -ENOMEM; } memcpy(&fd->name, spd->name, spd->nsize); fd->name[spd->nsize] = 0; raw = jffs2_alloc_raw_node_ref(); if (!raw) { jffs2_free_full_dirent(fd); JFFS2_NOTICE("allocation of node reference failed\n"); kfree(summary); return -ENOMEM; } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); if (!ic) { jffs2_free_full_dirent(fd); jffs2_free_raw_node_ref(raw); kfree(summary); return -ENOMEM; } raw->__totlen = PAD(je32_to_cpu(spd->totlen)); raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE; raw->next_phys = NULL; raw->next_in_ino = ic->nodes; ic->nodes = raw; if (!jeb->first_node) jeb->first_node = raw; if (jeb->last_node) jeb->last_node->next_phys = raw; jeb->last_node = raw; fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(spd->version); fd->ino = je32_to_cpu(spd->ino); fd->nhash = full_name_hash(fd->name, spd->nsize); fd->type = spd->type; USED_SPACE(PAD(je32_to_cpu(spd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); *pseudo_random += je32_to_cpu(spd->version); sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); break; } default : { JFFS2_WARNING("Unsupported node type found in summary! Exiting..."); kfree(summary); return -EIO; } } } kfree(summary); return 0; }
static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t *pseudo_random) { struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; void *sp; int i, ino; int err; sp = summary->sum; for (i=0; i<je32_to_cpu(summary->sum_num); i++) { dbg_summary("processing summary index %d\n", i); cond_resched(); /* Make sure there's a spare ref for dirty space */ err = jffs2_prealloc_raw_node_refs(c, jeb, 2); if (err) return err; switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *spi; spi = sp; ino = je32_to_cpu(spi->inode); dbg_summary("Inode at 0x%08x-0x%08x\n", jeb->offset + je32_to_cpu(spi->offset), jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen)); ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { JFFS2_NOTICE("scan_make_ino_cache failed\n"); return -ENOMEM; } sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED, PAD(je32_to_cpu(spi->totlen)), ic); *pseudo_random += je32_to_cpu(spi->version); sp += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *spd; int checkedlen; spd = sp; dbg_summary("Dirent at 0x%08x-0x%08x\n", jeb->offset + je32_to_cpu(spd->offset), jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen)); /* This should never happen, but https://dev.laptop.org/ticket/4184 */ checkedlen = strnlen(spd->name, spd->nsize); if (!checkedlen) { printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n", jeb->offset + je32_to_cpu(spd->offset)); return -EIO; } if (checkedlen < spd->nsize) { printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n", jeb->offset + je32_to_cpu(spd->offset), checkedlen); } fd = jffs2_alloc_full_dirent(checkedlen+1); if (!fd) return -ENOMEM; memcpy(&fd->name, spd->name, checkedlen); fd->name[checkedlen] = 0; ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); if (!ic) { jffs2_free_full_dirent(fd); return -ENOMEM; } fd->raw = sum_link_node_ref(c, jeb, je32_to_cpu(spd->offset) | REF_UNCHECKED, PAD(je32_to_cpu(spd->totlen)), ic); fd->next = NULL; fd->version = je32_to_cpu(spd->version); fd->ino = je32_to_cpu(spd->ino); fd->nhash = full_name_hash(fd->name, checkedlen); fd->type = spd->type; jffs2_add_fd_to_list(c, fd, &ic->scan_dents); *pseudo_random += je32_to_cpu(spd->version); sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); break; } #ifdef CONFIG_JFFS2_FS_XATTR case JFFS2_NODETYPE_XATTR: { struct jffs2_xattr_datum *xd; struct jffs2_sum_xattr_flash *spx; spx = (struct jffs2_sum_xattr_flash *)sp; dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n", jeb->offset + je32_to_cpu(spx->offset), jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen), je32_to_cpu(spx->xid), je32_to_cpu(spx->version)); xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid), je32_to_cpu(spx->version)); if (IS_ERR(xd)) return PTR_ERR(xd); if (xd->version > je32_to_cpu(spx->version)) { /* node is not the newest one */ struct jffs2_raw_node_ref *raw = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, PAD(je32_to_cpu(spx->totlen)), NULL); raw->next_in_ino = xd->node->next_in_ino; xd->node->next_in_ino = raw; } else { xd->version = je32_to_cpu(spx->version); sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, PAD(je32_to_cpu(spx->totlen)), (void *)xd); } *pseudo_random += je32_to_cpu(spx->xid); sp += JFFS2_SUMMARY_XATTR_SIZE; break; } case JFFS2_NODETYPE_XREF: { struct jffs2_xattr_ref *ref; struct jffs2_sum_xref_flash *spr; spr = (struct jffs2_sum_xref_flash *)sp; dbg_summary("xref at %#08x-%#08x\n", jeb->offset + je32_to_cpu(spr->offset), jeb->offset + je32_to_cpu(spr->offset) + (uint32_t)PAD(sizeof(struct jffs2_raw_xref))); ref = jffs2_alloc_xattr_ref(); if (!ref) { JFFS2_NOTICE("allocation of xattr_datum failed\n"); return -ENOMEM; } ref->next = c->xref_temp; c->xref_temp = ref; sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED, PAD(sizeof(struct jffs2_raw_xref)), (void *)ref); *pseudo_random += ref->node->flash_offset; sp += JFFS2_SUMMARY_XREF_SIZE; break; } #endif default : { uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype); JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype); if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT) return -EIO; /* For compatible node types, just fall back to the full scan */ c->wasted_size -= jeb->wasted_size; c->free_size += c->sector_size - jeb->free_size; c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; jeb->wasted_size = jeb->used_size = jeb->dirty_size = 0; jeb->free_size = c->sector_size; jffs2_free_jeb_node_refs(c, jeb); return -ENOTRECOVERABLE; } } } return 0; }
/* * Helper function for jffs2_get_inode_nodes(). * It is called every time an directory entry node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_raw_dirent *rd, uint32_t read, struct jffs2_full_dirent **fdp, uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_full_dirent *fd; /* The direntry nodes are checked during the flash scanning */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); /* Sanity check */ if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); return 1; } fd = jffs2_alloc_full_dirent(rd->nsize + 1); if (unlikely(!fd)) return -ENOMEM; fd->raw = ref; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->type = rd->type; /* Pick out the mctime of the latest dirent */ if(fd->version > *mctime_ver && je32_to_cpu(rd->mctime)) { *mctime_ver = fd->version; *latest_mctime = je32_to_cpu(rd->mctime); } /* * Copy as much of the name as possible from the raw * dirent we've already read from the flash. */ if (read > sizeof(*rd)) memcpy(&fd->name[0], &rd->name[0], min_t(uint32_t, rd->nsize, (read - sizeof(*rd)) )); /* Do we need to copy any more of the name directly from the flash? */ if (rd->nsize + sizeof(*rd) > read) { /* FIXME: point() */ int err; int already = read - sizeof(*rd); err = jffs2_flash_read(c, (ref_offset(ref)) + read, rd->nsize - already, &read, &fd->name[already]); if (unlikely(read != rd->nsize - already) && likely(!err)) return -EIO; if (unlikely(err)) { JFFS2_ERROR("read remainder of name: error %d\n", err); jffs2_free_full_dirent(fd); return -EIO; } } fd->nhash = full_name_hash(fd->name, rd->nsize); fd->next = NULL; fd->name[rd->nsize] = '\0'; /* * Wheee. We now have a complete jffs2_full_dirent structure, with * the name in it and everything. Link it into the list */ jffs2_add_fd_to_list(c, fd, fdp); return 0; }