int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) { struct jffs2_inode_cache *ic; ic = jffs2_alloc_inode_cache(); if (!ic) { return -ENOMEM; } memset(ic, 0, sizeof(*ic)); f->inocache = ic; f->inocache->nlink = 1; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_PRESENT; jffs2_add_ino_cache(c, f->inocache); D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ri->ino = cpu_to_je32(f->inocache->ino); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->mode = cpu_to_jemode(mode); f->highest_version = 1; ri->version = cpu_to_je32(f->highest_version); return 0; }
static void write_dirent(struct filesystem_entry *e) { char *name = e->name; struct jffs2_raw_dirent rd; struct stat *statbuf = &(e->sb); static uint32_t version = 0; memset(&rd, 0, sizeof(rd)); rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd.totlen = cpu_to_je32(sizeof(rd) + strlen(name)); rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node) - 4)); rd.pino = cpu_to_je32((e->parent) ? e->parent->sb.st_ino : 1); rd.version = cpu_to_je32(version++); rd.ino = cpu_to_je32(statbuf->st_ino); rd.mctime = cpu_to_je32(statbuf->st_mtime); rd.nsize = strlen(name); rd.type = IFTODT(statbuf->st_mode); //rd.unused[0] = 0; //rd.unused[1] = 0; rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd) - 8)); rd.name_crc = cpu_to_je32(crc32(0, name, strlen(name))); pad_block_if_less_than(sizeof(rd) + rd.nsize); full_write(out_fd, &rd, sizeof(rd)); full_write(out_fd, name, rd.nsize); padword(); }
//int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) int jffs2_commit_write (struct inode *d_inode, struct page *pg, unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = d_inode; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end)); ri = jffs2_alloc_raw_inode(); if (!ri) { D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(inode->i_uid); ri->gid = cpu_to_je16(inode->i_gid); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime = ri->mtime = cpu_to_je32(cyg_timestamp()); ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start, (pg->index << PAGE_CACHE_SHIFT) + start, end - start, &writtenlen); if (ret) { /* There was an error writing. */ SetPageError(pg); } if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; inode->i_ctime = inode->i_mtime = je32_to_cpu(ri->ctime); } } jffs2_free_raw_inode(ri); if (start+writtenlen < end) { /* generic_file_write has written more to the page cache than we've actually written to the medium. Mark the page !Uptodate so that it gets reread */ D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n")); SetPageError(pg); ClearPageUptodate(pg); } D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret)); return writtenlen?writtenlen:ret; }
void setup_cleanmarker() { cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(cleanmarker_size); cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); }
static void create_target_filesystem(struct filesystem_entry *root) { cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); cleanmarker.totlen = cpu_to_je32(cleanmarker_size); cleanmarker.hdr_crc = cpu_to_je32(crc32(0, &cleanmarker, sizeof(struct jffs2_unknown_node)-4)); if (ino == 0) ino = 1; root->sb.st_ino = 1; recursive_populate_directory(root); if (pad_fs_size == -1) { padblock(); } else { if (pad_fs_size && add_cleanmarkers){ padblock(); while (out_ofs < pad_fs_size) { full_write(out_fd, &cleanmarker, sizeof(cleanmarker)); pad(cleanmarker_size - sizeof(cleanmarker)); padblock(); } } else { while (out_ofs < pad_fs_size) { full_write(out_fd, ffbuf, min(sizeof(ffbuf), pad_fs_size - out_ofs)); } } } }
// ------------------------------------------------------------------------- // jffs2_fo_write() // Write data to file. static int jffs2_extend_file (struct _inode *inode, struct jffs2_raw_inode *ri, unsigned long offset) { struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_full_dnode *fn; uint32_t alloc_len; int ret = 0; /* Make new hole frag from old EOF to new page */ D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", (unsigned int)inode->i_size, offset)); ret = jffs2_reserve_space(c, sizeof(*ri), &alloc_len, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) return ret; mutex_lock(&f->sem); ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri)); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->version = cpu_to_je32(++f->highest_version); ri->isize = cpu_to_je32(max((uint32_t)inode->i_size, offset)); ri->offset = cpu_to_je32(inode->i_size); ri->dsize = cpu_to_je32(offset - inode->i_size); ri->csize = cpu_to_je32(0); ri->compr = JFFS2_COMPR_ZERO; ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(0); fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL); jffs2_complete_reservation(c); if (IS_ERR(fn)) { ret = PTR_ERR(fn); mutex_unlock(&f->sem); return ret; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); mutex_unlock(&f->sem); return ret; } inode->i_size = offset; mutex_unlock(&f->sem); return 0; }
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) { struct inode *inode; struct super_block *sb = dir_i->i_sb; struct jffs2_sb_info *c; struct jffs2_inode_info *f; int ret; D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); c = JFFS2_SB_INFO(sb); inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); f = JFFS2_INODE_INFO(inode); jffs2_init_inode_info(f); down(&f->sem); memset(ri, 0, sizeof(*ri)); /* Set OS-specific defaults for new inodes */ ri->uid = cpu_to_je16(current->fsuid); if (dir_i->i_mode & S_ISGID) { ri->gid = cpu_to_je16(dir_i->i_gid); if (S_ISDIR(mode)) mode |= S_ISGID; } else { ri->gid = cpu_to_je16(current->fsgid); } ri->mode = cpu_to_jemode(mode); ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { make_bad_inode(inode); iput(inode); return ERR_PTR(ret); } inode->i_nlink = 1; inode->i_ino = je32_to_cpu(ri->ino); inode->i_mode = jemode_to_cpu(ri->mode); inode->i_gid = je16_to_cpu(ri->gid); inode->i_uid = je16_to_cpu(ri->uid); inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); inode->i_blksize = PAGE_SIZE; inode->i_blocks = 0; inode->i_size = 0; insert_inode_hash(inode); return inode; }
static int jffs2_write_end(struct file *filp, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *pg, void *fsdata) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned start = pos & (PAGE_CACHE_SIZE - 1); unsigned end = start + copied; unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; jffs2_dbg(1, "%s(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags); /* We need to avoid deadlock with page_cache_read() in jffs2_garbage_collect_pass(). So the page must be up to date to prevent page_cache_read() from trying to re-lock it. */ BUG_ON(!PageUptodate(pg)); if (end == PAGE_CACHE_SIZE) { /* When writing out the end of a page, write out the _whole_ page. This helps to reduce the number of nodes in files which have many short writes, like syslog files. */ aligned_start = 0; } ri = jffs2_alloc_raw_inode(); if (!ri) { jffs2_dbg(1, "%s(): Allocation of raw inode failed\n", __func__); unlock_page(pg); page_cache_release(pg); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(i_uid_read(inode)); ri->gid = cpu_to_je16(i_gid_read(inode)); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime =
static void formalize_posix_acl(void *xvalue, int *value_len) { struct posix_acl_xattr_header *pacl_header; struct posix_acl_xattr_entry *pent, *plim; struct jffs2_acl_header *jacl_header; struct jffs2_acl_entry *jent; struct jffs2_acl_entry_short *jent_s; char buffer[XATTR_BUFFER_SIZE]; int offset = 0; pacl_header = xvalue;; pent = pacl_header->a_entries; plim = xvalue + *value_len; jacl_header = (struct jffs2_acl_header *)buffer; offset += sizeof(struct jffs2_acl_header); jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); while (pent < plim) { switch(le16_to_cpu(pent->e_tag)) { case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: jent_s = (struct jffs2_acl_entry_short *)(buffer + offset); offset += sizeof(struct jffs2_acl_entry_short); jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); break; case ACL_USER: case ACL_GROUP: jent = (struct jffs2_acl_entry *)(buffer + offset); offset += sizeof(struct jffs2_acl_entry); jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag)); jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm)); jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id)); break; default: printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag)); exit(1); } pent++; } if (offset > *value_len) { printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n", offset, *value_len); exit(1); } memcpy(xvalue, buffer, offset); *value_len = offset; }
/* * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * * Returns: 0 on success; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un) { /* We don't mark unknown nodes as REF_UNCHECKED */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); /* EEP */ BUG(); break; case JFFS2_FEATURE_ROCOMPAT: JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); return 1; } return 0; }
static void write_symlink(struct filesystem_entry *e) { int len; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); statbuf->st_ino = ++ino; mkfs_debug_msg("writing symlink '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) statbuf->st_ino, (unsigned long) e->parent->sb.st_ino); write_dirent(e); len = strlen(e->link); if (len > JFFS2_MAX_SYMLINK_LEN) { error_msg("symlink too large. Truncated to %d chars.", JFFS2_MAX_SYMLINK_LEN); len = JFFS2_MAX_SYMLINK_LEN; } memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + len); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(statbuf->st_ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(len); ri.dsize = cpu_to_je32(len); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(crc32(0, e->link, len)); pad_block_if_less_than(sizeof(ri) + len); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, e->link, len); padword(); }
static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size) { struct jffs2_acl_header *header; struct jffs2_acl_entry *entry; void *e; size_t i; *size = jffs2_acl_size(acl->a_count); header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL); if (!header) return ERR_PTR(-ENOMEM); header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); e = header + 1; for (i=0; i < acl->a_count; i++) { const struct posix_acl_entry *acl_e = &acl->a_entries[i]; entry = e; entry->e_tag = cpu_to_je16(acl_e->e_tag); entry->e_perm = cpu_to_je16(acl_e->e_perm); switch(acl_e->e_tag) { case ACL_USER: entry->e_id = cpu_to_je32( from_kuid(&init_user_ns, acl_e->e_uid)); e += sizeof(struct jffs2_acl_entry); break; case ACL_GROUP: entry->e_id = cpu_to_je32( from_kgid(&init_user_ns, acl_e->e_gid)); e += sizeof(struct jffs2_acl_entry); break; case ACL_USER_OBJ: case ACL_GROUP_OBJ: case ACL_MASK: case ACL_OTHER: e += sizeof(struct jffs2_acl_entry_short); break; default: goto fail; } } return header; fail: kfree(header); return ERR_PTR(-EINVAL); }
static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) { xattr_entry_t *xe; struct jffs2_raw_xattr rx; int name_len; /* create xattr entry */ name_len = strlen(xname); xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len); xe->next = NULL; xe->xid = ++highest_xid; xe->xprefix = xprefix; xe->xname = ((char *)xe) + sizeof(xattr_entry_t); xe->xvalue = xe->xname + name_len + 1; xe->name_len = name_len; xe->value_len = value_len; strcpy(xe->xname, xname); memcpy(xe->xvalue, xvalue, value_len); /* write xattr node */ memset(&rx, 0, sizeof(rx)); rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len)); rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); rx.xid = cpu_to_je32(xe->xid); rx.version = cpu_to_je32(1); /* initial version */ rx.xprefix = xprefix; rx.name_len = xe->name_len; rx.value_len = cpu_to_je16(xe->value_len); rx.data_crc = cpu_to_je32(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len)); rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 4)); pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len); full_write(out_fd, &rx, sizeof(rx)); full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len); padword(); return xe; }
static void write_special_file(struct filesystem_entry *e) { jint16_t kdev; struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); statbuf->st_ino = ++ino; write_dirent(e); kdev = cpu_to_je16((major(statbuf->st_rdev) << 8) + minor(statbuf->st_rdev)); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri) + sizeof(kdev)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(statbuf->st_ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(statbuf->st_size); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(sizeof(kdev)); ri.dsize = cpu_to_je32(sizeof(kdev)); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(crc32(0, &kdev, sizeof(kdev))); pad_block_if_less_than(sizeof(ri) + sizeof(kdev)); full_write(out_fd, &ri, sizeof(ri)); full_write(out_fd, &kdev, sizeof(kdev)); padword(); }
static void write_pipe(struct filesystem_entry *e) { struct stat *statbuf; struct jffs2_raw_inode ri; statbuf = &(e->sb); statbuf->st_ino = ++ino; if (S_ISDIR(statbuf->st_mode)) { mkfs_debug_msg("writing dir '%s' ino=%lu parent_ino=%lu", e->name, (unsigned long) statbuf->st_ino, (unsigned long) (e->parent) ? e->parent->sb. st_ino : 1); } write_dirent(e); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4)); ri.ino = cpu_to_je32(statbuf->st_ino); ri.mode = cpu_to_jemode(statbuf->st_mode); ri.uid = cpu_to_je16(statbuf->st_uid); ri.gid = cpu_to_je16(statbuf->st_gid); ri.atime = cpu_to_je32(statbuf->st_atime); ri.ctime = cpu_to_je32(statbuf->st_ctime); ri.mtime = cpu_to_je32(statbuf->st_mtime); ri.isize = cpu_to_je32(0); ri.version = cpu_to_je32(1); ri.csize = cpu_to_je32(0); ri.dsize = cpu_to_je32(0); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri) - 8)); ri.data_crc = cpu_to_je32(0); pad_block_if_less_than(sizeof(ri)); full_write(out_fd, &ri, sizeof(ri)); padword(); }
/* * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * * Returns: 0 on succes; * 1 if the node should be marked obsolete; * negative error code on failure. */ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un, uint32_t read) { /* We don't mark unknown nodes as REF_UNCHECKED */ BUG_ON(ref_flags(ref) == REF_UNCHECKED); un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { /* Hmmm. This should have been caught at scan time. */ JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); jffs2_dbg_dump_node(c, ref_offset(ref)); return 1; } else { switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); /* EEP */ BUG(); break; case JFFS2_FEATURE_ROCOMPAT: JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); return 1; } } return 0; }
/* * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * * Returns: 0 on success; * negative error code on failure. */ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref, struct jffs2_unknown_node *un) { /* We don't mark unknown nodes as REF_UNCHECKED */ if (ref_flags(ref) == REF_UNCHECKED) { JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n", ref_offset(ref)); JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n", je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); jffs2_mark_node_obsolete(c, ref); return 0; } un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_INCOMPAT: JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); /* EEP */ BUG(); break; case JFFS2_FEATURE_ROCOMPAT: JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); break; case JFFS2_FEATURE_RWCOMPAT_DELETE: JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", je16_to_cpu(un->nodetype), ref_offset(ref)); jffs2_mark_node_obsolete(c, ref); return 0; } return 0; }
int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) { struct jffs2_full_dnode *old_metadata, *new_metadata; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; union jffs2_device_node dev; unsigned char *mdata = NULL; int mdatalen = 0; unsigned int ivalid; uint32_t alloclen; int ret; int alloc_type = ALLOC_NORMAL; jffs2_dbg(1, "%s(): ino #%lu\n", __func__, inode->i_ino); /* Special cases - we don't want more than one data node for these types on the medium at any time. So setattr must read the original data associated with the node (i.e. the device numbers or the target name) and write it out again with the appropriate data attached */ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ mdatalen = jffs2_encode_dev(&dev, inode->i_rdev); mdata = (char *)&dev; jffs2_dbg(1, "%s(): Writing %d bytes of kdev_t\n", __func__, mdatalen); } else if (S_ISLNK(inode->i_mode)) { mutex_lock(&f->sem); mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) { mutex_unlock(&f->sem); return -ENOMEM; } ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { mutex_unlock(&f->sem); kfree(mdata); return ret; } mutex_unlock(&f->sem); jffs2_dbg(1, "%s(): Writing %d bytes of symlink target\n", __func__, mdatalen); } ri = jffs2_alloc_raw_inode(); if (!ri) { if (S_ISLNK(inode->i_mode)) kfree(mdata); return -ENOMEM; } ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); if (S_ISLNK(inode->i_mode)) kfree(mdata); return ret; } mutex_lock(&f->sem); ivalid = iattr->ia_valid; ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->ino = cpu_to_je32(inode->i_ino); ri->version = cpu_to_je32(++f->highest_version); ri->uid = cpu_to_je16((ivalid & ATTR_UID)? from_kuid(&init_user_ns, iattr->ia_uid):i_uid_read(inode)); ri->gid = cpu_to_je16((ivalid & ATTR_GID)? from_kgid(&init_user_ns, iattr->ia_gid):i_gid_read(inode)); if (ivalid & ATTR_MODE) ri->mode = cpu_to_jemode(iattr->ia_mode); else ri->mode = cpu_to_jemode(inode->i_mode); ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); ri->offset = cpu_to_je32(0); ri->csize = ri->dsize = cpu_to_je32(mdatalen); ri->compr = JFFS2_COMPR_NONE; if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { /* It's an extension. Make it a hole node */ ri->compr = JFFS2_COMPR_ZERO; ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ri->offset = cpu_to_je32(inode->i_size); } else if (ivalid & ATTR_SIZE && !iattr->ia_size) { /* For truncate-to-zero, treat it as deletion because it'll always be obsoleting all previous nodes */ alloc_type = ALLOC_DELETION; } ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); if (mdatalen) ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); else ri->data_crc = cpu_to_je32(0); new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, alloc_type); if (S_ISLNK(inode->i_mode)) kfree(mdata); if (IS_ERR(new_metadata)) { jffs2_complete_reservation(c); jffs2_free_raw_inode(ri); mutex_unlock(&f->sem); return PTR_ERR(new_metadata); } /* It worked. Update the inode */ inode->i_atime = ITIME(je32_to_cpu(ri->atime)); inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); inode->i_mode = jemode_to_cpu(ri->mode); i_uid_write(inode, je16_to_cpu(ri->uid)); i_gid_write(inode, je16_to_cpu(ri->gid)); old_metadata = f->metadata; if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) jffs2_truncate_fragtree (c, &f->fragtree, iattr->ia_size); if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { jffs2_add_full_dnode_to_inode(c, f, new_metadata); inode->i_size = iattr->ia_size; inode->i_blocks = (inode->i_size + 511) >> 9; f->metadata = NULL; } else {
int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); if (!start && end == PAGE_CACHE_SIZE) { /* We need to avoid deadlock with page_cache_read() in jffs2_garbage_collect_pass(). So we have to mark the page up to date, to prevent page_cache_read() from trying to re-lock it. */ SetPageUptodate(pg); } ri = jffs2_alloc_raw_inode(); if (!ri) { D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(inode->i_uid); ri->gid = cpu_to_je16(inode->i_gid); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); /* In 2.4, it was already kmapped by generic_file_write(). Doesn't hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, (pg->index << PAGE_CACHE_SHIFT) + aligned_start, end - aligned_start, &writtenlen); kunmap(pg); if (ret) { /* There was an error writing. */ SetPageError(pg); } /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ if (writtenlen < (start&3)) writtenlen = 0; else writtenlen -= (start&3); if (writtenlen) { if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); } }
int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end) { struct inode *inode = pg->mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; int ret = 0; D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); if (pageofs > inode->i_size) { /* Make new hole frag from old EOF to new page */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode ri; struct jffs2_full_dnode *fn; uint32_t phys_ofs, alloc_len; D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", (unsigned int)inode->i_size, pageofs)); ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL); if (ret) return ret; down(&f->sem); memset(&ri, 0, sizeof(ri)); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ri.totlen = cpu_to_je32(sizeof(ri)); ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); ri.mode = cpu_to_jemode(inode->i_mode); ri.uid = cpu_to_je16(inode->i_uid); ri.gid = cpu_to_je16(inode->i_gid); ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); ri.offset = cpu_to_je32(inode->i_size); ri.dsize = cpu_to_je32(pageofs - inode->i_size); ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(0); fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); if (IS_ERR(fn)) { ret = PTR_ERR(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } ret = jffs2_add_full_dnode_to_inode(c, f, fn); if (f->metadata) { jffs2_mark_node_obsolete(c, f->metadata->raw); jffs2_free_full_dnode(f->metadata); f->metadata = NULL; } if (ret) { D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); jffs2_mark_node_obsolete(c, fn->raw); jffs2_free_full_dnode(fn); jffs2_complete_reservation(c); up(&f->sem); return ret; } jffs2_complete_reservation(c); inode->i_size = pageofs; up(&f->sem); } /* Read in the page if it wasn't already present, unless it's a whole page */ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { down(&f->sem); ret = jffs2_do_readpage_nolock(inode, pg); up(&f->sem); } D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags)); return ret; }
/* * Dump image contents */ void do_dumpcontent (void) { char *p = data, *p_free_begin; union jffs2_node_union *node; int empty = 0, dirty = 0; char name[256]; uint32_t crc; uint16_t type; int bitchbitmask = 0; int obsolete; p_free_begin = NULL; while ( p < (data + imglen)) { node = (union jffs2_node_union*) p; /* Skip empty space */ if (!p_free_begin) p_free_begin = p; if (je16_to_cpu (node->u.magic) == 0xFFFF && je16_to_cpu (node->u.nodetype) == 0xFFFF) { p += 4; empty += 4; continue; } if (p != p_free_begin) printf("Empty space found from 0x%08x to 0x%08x\n", p_free_begin-data, p-data); p_free_begin = NULL; if (je16_to_cpu (node->u.magic) != JFFS2_MAGIC_BITMASK) { if (!bitchbitmask++) printf ("Wrong bitmask at 0x%08x, 0x%04x\n", p - data, je16_to_cpu (node->u.magic)); p += 4; dirty += 4; continue; } bitchbitmask = 0; type = je16_to_cpu(node->u.nodetype); if ((type & JFFS2_NODE_ACCURATE) != JFFS2_NODE_ACCURATE) { obsolete = 1; type |= JFFS2_NODE_ACCURATE; } else obsolete = 0; /* Set accurate for CRC check */ node->u.nodetype = cpu_to_je16(type); crc = crc32 (0, node, sizeof (struct jffs2_unknown_node) - 4); if (crc != je32_to_cpu (node->u.hdr_crc)) { printf ("Wrong hdr_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->u.hdr_crc), crc); p += 4; dirty += 4; continue; } switch(je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_INODE: printf ("%8s Inode node at 0x%08x, totlen 0x%08x, #ino %5d, version %5d, isize %8d, csize %8d, dsize %8d, offset %8d\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->i.totlen), je32_to_cpu (node->i.ino), je32_to_cpu ( node->i.version), je32_to_cpu (node->i.isize), je32_to_cpu (node->i.csize), je32_to_cpu (node->i.dsize), je32_to_cpu (node->i.offset)); crc = crc32 (0, node, sizeof (struct jffs2_raw_inode) - 8); if (crc != je32_to_cpu (node->i.node_crc)) { printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.node_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); dirty += PAD(je32_to_cpu (node->i.totlen));; continue; } crc = crc32(0, p + sizeof (struct jffs2_raw_inode), je32_to_cpu(node->i.csize)); if (crc != je32_to_cpu(node->i.data_crc)) { printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->i.data_crc), crc); p += PAD(je32_to_cpu (node->i.totlen)); dirty += PAD(je32_to_cpu (node->i.totlen));; continue; } p += PAD(je32_to_cpu (node->i.totlen)); break; case JFFS2_NODETYPE_DIRENT: memcpy (name, node->d.name, node->d.nsize); name [node->d.nsize] = 0x0; printf ("%8s Dirent node at 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->d.totlen), je32_to_cpu (node->d.pino), je32_to_cpu ( node->d.version), je32_to_cpu (node->d.ino), node->d.nsize, name); crc = crc32 (0, node, sizeof (struct jffs2_raw_dirent) - 8); if (crc != je32_to_cpu (node->d.node_crc)) { printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.node_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); dirty += PAD(je32_to_cpu (node->d.totlen));; continue; } crc = crc32(0, p + sizeof (struct jffs2_raw_dirent), node->d.nsize); if (crc != je32_to_cpu(node->d.name_crc)) { printf ("Wrong name_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->d.name_crc), crc); p += PAD(je32_to_cpu (node->d.totlen)); dirty += PAD(je32_to_cpu (node->d.totlen));; continue; } p += PAD(je32_to_cpu (node->d.totlen)); break; case JFFS2_NODETYPE_SUMMARY: { int i; struct jffs2_sum_marker * sm; printf("%8s Inode Sum node at 0x%08x, totlen 0x%08x, sum_num %5d, cleanmarker size %5d\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->s.totlen), je32_to_cpu (node->s.sum_num), je32_to_cpu (node->s.cln_mkr)); crc = crc32 (0, node, sizeof (struct jffs2_raw_summary) - 8); if (crc != je32_to_cpu (node->s.node_crc)) { printf ("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.node_crc), crc); p += PAD(je32_to_cpu (node->s.totlen)); dirty += PAD(je32_to_cpu (node->s.totlen));; continue; } crc = crc32(0, p + sizeof (struct jffs2_raw_summary), je32_to_cpu (node->s.totlen) - sizeof(struct jffs2_raw_summary)); if (crc != je32_to_cpu(node->s.sum_crc)) { printf ("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n", p - data, je32_to_cpu (node->s.sum_crc), crc); p += PAD(je32_to_cpu (node->s.totlen)); dirty += PAD(je32_to_cpu (node->s.totlen));; continue; } if (verbose) { void *sp; sp = (p + sizeof(struct jffs2_raw_summary)); for(i=0; i<je32_to_cpu(node->s.sum_num); i++) { switch(je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE : { struct jffs2_sum_inode_flash *spi; spi = sp; printf ("%14s #ino %5d, version %5d, offset 0x%08x, totlen 0x%08x\n", "", je32_to_cpu (spi->inode), je32_to_cpu (spi->version), je32_to_cpu (spi->offset), je32_to_cpu (spi->totlen)); sp += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT : { char name[255]; struct jffs2_sum_dirent_flash *spd; spd = sp; memcpy(name,spd->name,spd->nsize); name [spd->nsize] = 0x0; printf ("%14s dirent offset 0x%08x, totlen 0x%08x, #pino %5d, version %5d, #ino %8d, nsize %8d, name %s \n", "", je32_to_cpu (spd->offset), je32_to_cpu (spd->totlen), je32_to_cpu (spd->pino), je32_to_cpu (spd->version), je32_to_cpu (spd->ino), spd->nsize, name); sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize); break; } default : printf("Unknown summary node!\n"); break; } } sm = (struct jffs2_sum_marker *) ((char *)p + je32_to_cpu(node->s.totlen) - sizeof(struct jffs2_sum_marker)); printf("%14s Sum Node Offset 0x%08x, Magic 0x%08x, Padded size 0x%08x\n", "", je32_to_cpu(sm->offset), je32_to_cpu(sm->magic), je32_to_cpu(node->s.padded)); } p += PAD(je32_to_cpu (node->s.totlen)); break; } case JFFS2_NODETYPE_CLEANMARKER: if (verbose) { printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case JFFS2_NODETYPE_PADDING: if (verbose) { printf ("%8s Padding node at 0x%08x, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); break; case 0xffff: p += 4; empty += 4; break; default: if (verbose) { printf ("%8s Unknown node at 0x%08x, totlen 0x%08x\n", obsolete ? "Obsolete" : "", p - data, je32_to_cpu (node->u.totlen)); } p += PAD(je32_to_cpu (node->u.totlen)); dirty += PAD(je32_to_cpu (node->u.totlen)); } } if (verbose) printf ("Empty space: %d, dirty space: %d\n", empty, dirty); }
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; union jffs2_device_node dev; int devlen = 0; uint32_t alloclen; int ret; if (!new_valid_dev(rdev)) return -EINVAL; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); if (S_ISBLK(mode) || S_ISCHR(mode)) devlen = jffs2_encode_dev(&dev, rdev); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_file_inode_operations; init_special_inode(inode, inode->i_mode, rdev); f = JFFS2_INODE_INFO(inode); ri->dsize = ri->csize = cpu_to_je32(devlen); ri->totlen = cpu_to_je32(sizeof(*ri) + devlen); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = PTR_ERR(fn); goto fail; } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; ret = jffs2_init_acl_post(inode); if (ret) goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); ret = -ENOMEM; goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; /* XXX: This is ugly. */ rd->type = (mode & S_IFMT) >> 12; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); ret = PTR_ERR(fd); goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); unlock_new_inode(inode); return 0; fail: iget_failed(inode); return ret; }
static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; int ret; mode |= S_IFDIR; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; f = JFFS2_INODE_INFO(inode); /* Directories get nlink 2 at start */ inode->i_nlink = 2; /* but ic->pino_nlink is the parent ino# */ f->inocache->pino_nlink = dir_i->i_ino; ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = PTR_ERR(fn); goto fail; } /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; ret = jffs2_init_acl_post(inode); if (ret) goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); ret = -ENOMEM; goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_DIR; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); ret = PTR_ERR(fd); goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); inc_nlink(dir_i); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); unlock_new_inode(inode); return 0; fail: iget_failed(inode); return ret; }
static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char *target) { struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; struct jffs2_raw_inode *ri; struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target if it grows much more than this */ if (targetlen > 254) return -ENAMETOOLONG; ri = jffs2_alloc_raw_inode(); if (!ri) return -ENOMEM; c = JFFS2_SB_INFO(dir_i->i_sb); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); return ret; } inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); jffs2_complete_reservation(c); return PTR_ERR(inode); } inode->i_op = &jffs2_symlink_inode_operations; f = JFFS2_INODE_INFO(inode); inode->i_size = targetlen; ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ri->compr = JFFS2_COMPR_NONE; ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); fn = jffs2_write_dnode(c, f, ri, target, targetlen, ALLOC_NORMAL); jffs2_free_raw_inode(ri); if (IS_ERR(fn)) { /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = PTR_ERR(fn); goto fail; } /* We use f->target field to store the target path. */ f->target = kmemdup(target, targetlen + 1, GFP_KERNEL); if (!f->target) { ; mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = -ENOMEM; goto fail; } D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target)); /* No data here. Only a metadata node, which will be obsoleted by the first data write */ f->metadata = fn; mutex_unlock(&f->sem); jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i, &dentry->d_name); if (ret) goto fail; ret = jffs2_init_acl_post(inode); if (ret) goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); ret = -ENOMEM; goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); mutex_lock(&dir_f->sem); rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); rd->pino = cpu_to_je32(dir_i->i_ino); rd->version = cpu_to_je32(++dir_f->highest_version); rd->ino = cpu_to_je32(inode->i_ino); rd->mctime = cpu_to_je32(get_seconds()); rd->nsize = namelen; rd->type = DT_LNK; rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); ret = PTR_ERR(fd); goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ jffs2_add_fd_to_list(c, fd, &dir_f->dents); mutex_unlock(&dir_f->sem); jffs2_complete_reservation(c); d_instantiate(dentry, inode); unlock_new_inode(inode); return 0; fail: iget_failed(inode); return ret; }
static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t infosize, uint32_t datasize, int padsize) { struct jffs2_raw_summary isum; union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; uint32_t sum_ofs; void *wpage; int ret; size_t retlen; if (padsize + datasize > MAX_SUMMARY_SIZE) { /* It won't fit in the buffer. Abort summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Summary too big (%d data, %d pad) in eraseblock at %08x\n", datasize, padsize, jeb->offset); /* Non-fatal */ return 0; } /* Is there enough space for summary? */ if (padsize < 0) { /* don't try to write out summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize); /* Non-fatal */ return 0; } memset(c->summary->sum_buf, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(c->summary->sum_padded); isum.cln_mkr = cpu_to_je32(c->cleanmarker_size); isum.sum_num = cpu_to_je32(c->summary->sum_num); wpage = c->summary->sum_buf; while (c->summary->sum_num) { temp = c->summary->sum_list_head; switch (je16_to_cpu(temp->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = temp->i.nodetype; sino_ptr->inode = temp->i.inode; sino_ptr->version = temp->i.version; sino_ptr->offset = temp->i.offset; sino_ptr->totlen = temp->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = temp->d.nodetype; sdrnt_ptr->totlen = temp->d.totlen; sdrnt_ptr->offset = temp->d.offset; sdrnt_ptr->pino = temp->d.pino; sdrnt_ptr->version = temp->d.version; sdrnt_ptr->ino = temp->d.ino; sdrnt_ptr->nsize = temp->d.nsize; sdrnt_ptr->type = temp->d.type; memcpy(sdrnt_ptr->name, temp->d.name, temp->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize); break; } #ifdef CONFIG_JFFS2_FS_XATTR case JFFS2_NODETYPE_XATTR: { struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; temp = c->summary->sum_list_head; sxattr_ptr->nodetype = temp->x.nodetype; sxattr_ptr->xid = temp->x.xid; sxattr_ptr->version = temp->x.version; sxattr_ptr->offset = temp->x.offset; sxattr_ptr->totlen = temp->x.totlen; wpage += JFFS2_SUMMARY_XATTR_SIZE; break; } case JFFS2_NODETYPE_XREF: { struct jffs2_sum_xref_flash *sxref_ptr = wpage; temp = c->summary->sum_list_head; sxref_ptr->nodetype = temp->r.nodetype; sxref_ptr->offset = temp->r.offset; wpage += JFFS2_SUMMARY_XREF_SIZE; break; } #endif default : { if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_RWCOMPAT_COPY) { dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n", je16_to_cpu(temp->u.nodetype)); jffs2_sum_disable_collecting(c->summary); } else { BUG(); /* unknown node in summary information */ } } } c->summary->sum_list_head = temp->u.next; kfree(temp); c->summary->sum_num--; } jffs2_sum_reset_collected(c->summary); wpage += padsize; sm = wpage; sm->offset = cpu_to_je32(c->sector_size - jeb->free_size); sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC); isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize)); isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); vecs[0].iov_base = &isum; vecs[0].iov_len = sizeof(isum); vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; sum_ofs = jeb->offset + c->sector_size - jeb->free_size; dbg_summary("writing out data to flash to pos : 0x%08x\n", sum_ofs); ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0); if (ret || (retlen != infosize)) { JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n", infosize, sum_ofs, ret, retlen); if (retlen) { /* Waste remaining space */ spin_lock(&c->erase_completion_lock); jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL); spin_unlock(&c->erase_completion_lock); } c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; return 0; } spin_lock(&c->erase_completion_lock); jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL); spin_unlock(&c->erase_completion_lock); return 0; }
/* Process the summary node - called from jffs2_scan_eraseblock() */ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t sumsize, uint32_t *pseudo_random) { struct jffs2_unknown_node crcnode; int ret, ofs; uint32_t crc; ofs = c->sector_size - sumsize; dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n", jeb->offset, jeb->offset + ofs, sumsize); /* OK, now check for node validity and CRC */ crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); crcnode.totlen = summary->totlen; crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (je32_to_cpu(summary->hdr_crc) != crc) { dbg_summary("Summary node header is corrupt (bad CRC or " "no summary at all)\n"); goto crc_err; } if (je32_to_cpu(summary->totlen) != sumsize) { dbg_summary("Summary node is corrupt (wrong erasesize?)\n"); goto crc_err; } crc = crc32(0, summary, sizeof(struct jffs2_raw_summary)-8); if (je32_to_cpu(summary->node_crc) != crc) { dbg_summary("Summary node is corrupt (bad CRC)\n"); goto crc_err; } crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_raw_summary)); if (je32_to_cpu(summary->sum_crc) != crc) { dbg_summary("Summary node data is corrupt (bad CRC)\n"); goto crc_err; } if ( je32_to_cpu(summary->cln_mkr) ) { dbg_summary("Summary : CLEANMARKER node \n"); ret = jffs2_prealloc_raw_node_refs(c, jeb, 1); if (ret) return ret; if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) { dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n", je32_to_cpu(summary->cln_mkr), c->cleanmarker_size); if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr))))) return ret; } else if (jeb->first_node) { dbg_summary("CLEANMARKER node not first node in block " "(0x%08x)\n", jeb->offset); if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr))))) return ret; } else { jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, je32_to_cpu(summary->cln_mkr), NULL); } } ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random); /* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full scan of this eraseblock. So return zero */ if (ret == -ENOTRECOVERABLE) return 0; if (ret) return ret; /* real error */ /* for PARANOIA_CHECK */ ret = jffs2_prealloc_raw_node_refs(c, jeb, 2); if (ret) return ret; sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL); if (unlikely(jeb->free_size)) { JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n", jeb->free_size, jeb->offset); jeb->wasted_size += jeb->free_size; c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->free_size = 0; } return jffs2_scan_classify_jeb(c, jeb); crc_err: JFFS2_WARNING("Summary node crc error, skipping summary information.\n"); return 0; }
static int jffs2_write_end(struct file *filp, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *pg, void *fsdata) { /* Actually commit the write from the page cache page we're looking at. * For now, we write the full page out each time. It sucks, but it's simple */ struct inode *inode = mapping->host; struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; unsigned start = pos & (PAGE_CACHE_SIZE - 1); unsigned end = start + copied; unsigned aligned_start = start & ~3; int ret = 0; uint32_t writtenlen = 0; jffs2_dbg(1, "%s(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", __func__, inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags); /* We need to avoid deadlock with page_cache_read() in jffs2_garbage_collect_pass(). So the page must be up to date to prevent page_cache_read() from trying to re-lock it. */ BUG_ON(!PageUptodate(pg)); if (end == PAGE_CACHE_SIZE) { /* When writing out the end of a page, write out the _whole_ page. This helps to reduce the number of nodes in files which have many short writes, like syslog files. */ aligned_start = 0; } ri = jffs2_alloc_raw_inode(); if (!ri) { jffs2_dbg(1, "%s(): Allocation of raw inode failed\n", __func__); unlock_page(pg); page_cache_release(pg); return -ENOMEM; } /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ri->ino = cpu_to_je32(inode->i_ino); ri->mode = cpu_to_jemode(inode->i_mode); ri->uid = cpu_to_je16(inode->i_uid); ri->gid = cpu_to_je16(inode->i_gid); ri->isize = cpu_to_je32((uint32_t)inode->i_size); ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); /* In 2.4, it was already kmapped by generic_file_write(). Doesn't hurt to do it again. The alternative is ifdefs, which are ugly. */ kmap(pg); ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start, (pg->index << PAGE_CACHE_SHIFT) + aligned_start, end - aligned_start, &writtenlen); kunmap(pg); if (ret) { /* There was an error writing. */ SetPageError(pg); } /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ writtenlen -= min(writtenlen, (start - aligned_start)); if (writtenlen) { if (inode->i_size < pos + writtenlen) { inode->i_size = pos + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); } }
static ssize_t rtems_jffs2_file_write(rtems_libio_t *iop, const void *buf, size_t len) { struct _inode *inode = rtems_jffs2_get_inode_by_iop(iop); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode ri; uint32_t writtenlen; off_t pos; int eno = 0; memset(&ri, 0, sizeof(ri)); ri.ino = cpu_to_je32(f->inocache->ino); ri.mode = cpu_to_jemode(inode->i_mode); ri.uid = cpu_to_je16(inode->i_uid); ri.gid = cpu_to_je16(inode->i_gid); ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); rtems_jffs2_do_lock(inode->i_sb); if ((iop->flags & LIBIO_FLAGS_APPEND) == 0) { pos = iop->offset; } else { pos = inode->i_size; } if (pos > inode->i_size) { ri.version = cpu_to_je32(++f->highest_version); eno = -jffs2_extend_file(inode, &ri, pos); } if (eno == 0) { ri.isize = cpu_to_je32(inode->i_size); eno = -jffs2_write_inode_range(c, f, &ri, (void *) buf, pos, len, &writtenlen); } if (eno == 0) { pos += writtenlen; inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime); if (pos > inode->i_size) { inode->i_size = pos; } iop->offset = pos; if (writtenlen != len) { eno = ENOSPC; } } rtems_jffs2_do_unlock(inode->i_sb); if (eno == 0) { return writtenlen; } else { errno = eno; return -1; } }
static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *latest_node) { struct jffs2_tmp_dnode_info *tn; struct rb_root tn_list; struct rb_node *rb, *repl_rb; struct jffs2_full_dirent *fd_list; struct jffs2_full_dnode *fn, *first_fn = NULL; uint32_t crc; uint32_t latest_mctime, mctime_ver; size_t retlen; int ret; dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); /* Grab all nodes relevant to this ino */ ret = jffs2_get_inode_nodes(c, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return ret; } f->dents = fd_list; rb = rb_first(&tn_list); while (rb) { cond_resched(); tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb); fn = tn->fn; ret = 1; dbg_readinode("consider node ver %u, phys offset " "%#08x(%d), range %u-%u.\n", tn->version, ref_offset(fn->raw), ref_flags(fn->raw), fn->ofs, fn->ofs + fn->size); if (fn->size) { ret = jffs2_add_older_frag_to_fragtree(c, f, tn); /* TODO: the error code isn't checked, check it */ jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(ret < 0); if (!first_fn && ret == 0) first_fn = fn; } else if (!first_fn) { first_fn = fn; f->metadata = fn; ret = 0; /* Prevent freeing the metadata update node */ } else jffs2_mark_node_obsolete(c, fn->raw); BUG_ON(rb->rb_left); if (rb->rb_parent && rb->rb_parent->rb_left == rb) { /* We were then left-hand child of our parent. We need * to move our own right-hand child into our place. */ repl_rb = rb->rb_right; if (repl_rb) repl_rb->rb_parent = rb->rb_parent; } else repl_rb = NULL; rb = rb_next(rb); /* Remove the spent tn from the tree; don't bother rebalancing * but put our right-hand child in our own place. */ if (tn->rb.rb_parent) { if (tn->rb.rb_parent->rb_left == &tn->rb) tn->rb.rb_parent->rb_left = repl_rb; else if (tn->rb.rb_parent->rb_right == &tn->rb) tn->rb.rb_parent->rb_right = repl_rb; else BUG(); } else if (tn->rb.rb_right) tn->rb.rb_right->rb_parent = NULL; jffs2_free_tmp_dnode_info(tn); if (ret) { dbg_readinode("delete dnode %u-%u.\n", fn->ofs, fn->ofs + fn->size); jffs2_free_full_dnode(fn); } } jffs2_dbg_fragtree_paranoia_check_nolock(f); BUG_ON(first_fn && ref_obsolete(first_fn->raw)); fn = first_fn; if (unlikely(!first_fn)) { /* No data nodes for this inode. */ if (f->inocache->ino != 1) { JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino); if (!fd_list) { if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); return -EIO; } JFFS2_NOTICE("but it has children so we fake some modes for it\n"); } latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); latest_node->version = cpu_to_je32(0); latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); latest_node->isize = cpu_to_je32(0); latest_node->gid = cpu_to_je16(0); latest_node->uid = cpu_to_je16(0); if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); if (ret || retlen != sizeof(*latest_node)) { JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n", ret, retlen, sizeof(*latest_node)); /* FIXME: If this fails, there seems to be a memory leak. Find it. */ up(&f->sem); jffs2_do_clear_inode(c, f); return ret?ret:-EIO; } crc = crc32(0, latest_node, sizeof(*latest_node)-8); if (crc != je32_to_cpu(latest_node->node_crc)) { JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { case S_IFDIR: if (mctime_ver > je32_to_cpu(latest_node->version)) { /* The times in the latest_node are actually older than mctime in the latest dirent. Cheat. */ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); } break; case S_IFREG: /* If it was a regular file, truncate it to the latest node's isize */ jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize)); break; case S_IFLNK: /* Hack to work around broken isize in old symlink code. Remove this when dwmw2 comes to his senses and stops symlinks from being an entirely gratuitous special case. */ if (!je32_to_cpu(latest_node->isize)) latest_node->isize = latest_node->dsize; if (f->inocache->state != INO_STATE_CHECKING) { /* Symlink's inode data is the target path. Read it and * keep in RAM to facilitate quick follow symlink * operation. */ f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); up(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node), je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); if (ret || retlen != je32_to_cpu(latest_node->csize)) { if (retlen != je32_to_cpu(latest_node->csize)) ret = -EIO; kfree(f->target); f->target = NULL; up(&f->sem); jffs2_do_clear_inode(c, f); return -ret; } f->target[je32_to_cpu(latest_node->csize)] = '\0'; dbg_readinode("symlink's target '%s' cached\n", f->target); } /* fall through... */ case S_IFBLK: case S_IFCHR: /* Certain inode types should have only one data node, and it's kept as the metadata node */ if (f->metadata) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } if (!frag_first(&f->fragtree)) { JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* ASSERT: f->fraglist != NULL */ if (frag_next(frag_first(&f->fragtree))) { JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n", f->inocache->ino, jemode_to_cpu(latest_node->mode)); /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ up(&f->sem); jffs2_do_clear_inode(c, f); return -EIO; } /* OK. We're happy */ f->metadata = frag_first(&f->fragtree)->node; jffs2_free_node_frag(frag_first(&f->fragtree)); f->fragtree = RB_ROOT; break; } if (f->inocache->state == INO_STATE_READING) jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; }
/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into the flash, XIP-style */ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; uint32_t ofs, prevofs, max_ofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; int noise = 0; #ifdef CONFIG_JFFS2_FS_WRITEBUFFER int cleanmarkerfound = 0; #endif ofs = jeb->offset; prevofs = jeb->offset - 1; D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (c->mtd->type == MTD_NANDFLASH) { int ret; if (c->mtd->block_isbad(c->mtd, jeb->offset)) return BLK_STATE_BADBLOCK; if (jffs2_cleanmarker_oob(c)) { ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n", ret)); /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; default: return ret; } } } #endif if (jffs2_sum_active()) { struct jffs2_sum_marker *sm; void *sumptr = NULL; uint32_t sumlen; if (!buf_size) { /* XIP case. Just look, point at the summary if it's there */ sm = (void *)buf + c->sector_size - sizeof(*sm); if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { sumptr = buf + je32_to_cpu(sm->offset); sumlen = c->sector_size - je32_to_cpu(sm->offset); } } else { /* If NAND flash, read a whole page of it. Else just the end */ if (c->wbuf_pagesize) buf_len = c->wbuf_pagesize; else buf_len = sizeof(*sm); /* Read as much as we want into the _end_ of the preallocated buffer */ err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, jeb->offset + c->sector_size - buf_len, buf_len); if (err) return err; sm = (void *)buf + buf_size - sizeof(*sm); if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { sumlen = c->sector_size - je32_to_cpu(sm->offset); sumptr = buf + buf_size - sumlen; /* Now, make sure the summary itself is available */ if (sumlen > buf_size) { /* Need to kmalloc for this. */ sumptr = kmalloc(sumlen, GFP_KERNEL); if (!sumptr) return -ENOMEM; memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len); } if (buf_len < sumlen) { /* Need to read more so that the entire summary node is present */ err = jffs2_fill_scan_buf(c, sumptr, jeb->offset + c->sector_size - sumlen, sumlen - buf_len); if (err) return err; } } } if (sumptr) { err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random); if (buf_size && sumlen > buf_size) kfree(sumptr); /* If it returns with a real error, bail. If it returns positive, that's a block classification (i.e. BLK_STATE_xxx) so return that too. If it returns zero, fall through to full scan. */ if (err) return err; } } buf_ofs = jeb->offset; if (!buf_size) { /* This is the XIP case -- we're reading _directly_ from the flash chip */ buf_len = c->sector_size; } else { buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); if (err) return err; } /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ofs = 0; max_ofs = EMPTY_SCAN_SIZE(c->sector_size); /* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */ while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ofs += 4; if (ofs == max_ofs) { #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { /* scan oob, take care of cleanmarker */ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); switch (ret) { case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; case 1: return BLK_STATE_ALLDIRTY; default: return ret; } } #endif D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); if (c->cleanmarker_size == 0) return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */ else return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ } if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs)); if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1))) return err; if ((err = jffs2_scan_dirty_space(c, jeb, ofs))) return err; } /* Now ofs is a complete physical flash offset as it always was... */ ofs += jeb->offset; noise = 10; dbg_summary("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset); scan_more: while(ofs < jeb->offset + c->sector_size) { jffs2_dbg_acct_paranoia_check_nolock(c, jeb); /* Make sure there are node refs available for use */ err = jffs2_prealloc_raw_node_refs(c, jeb, 2); if (err) return err; cond_resched(); if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = PAD(ofs); continue; } if (ofs == prevofs) { printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } prevofs = ofs; if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node))); if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs))) return err; break; } if (buf_ofs + buf_len < ofs + sizeof(*node)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_unknown_node), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; } node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { uint32_t inbuf_ofs; uint32_t empty_start, scan_end; empty_start = ofs; ofs += 4; scan_end = min_t(uint32_t, EMPTY_SCAN_SIZE(c->sector_size)/8, buf_len); D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); more_empty: inbuf_ofs = ofs - buf_ofs; while (inbuf_ofs < scan_end) { if (unlikely(*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)) { printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start))) return err; goto scan_more; } inbuf_ofs+=4; ofs += 4; } /* Ran off end. */ D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs)); /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) { D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } if (!buf_size && (scan_end != buf_len)) {/* XIP/point case */ scan_end = buf_len; goto more_empty; } /* See how much more there is to read in this eraseblock... */ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); if (!buf_len) { /* No more to read. Break out of main loop without marking this range of empty space as dirty (because it's not) */ D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n", empty_start)); break; } /* point never reaches here */ scan_end = buf_len; D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; goto more_empty; } if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { /* OK. We're out of possibilities. Whinge and move on */ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, je16_to_cpu(node->magic)); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } /* We seem to have a node of sorts. Check the CRC */ crcnode.magic = node->magic; crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); crcnode.totlen = node->totlen; hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); if (hdr_crc != je32_to_cpu(node->hdr_crc)) { noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", ofs, je16_to_cpu(node->magic), je16_to_cpu(node->nodetype), je32_to_cpu(node->totlen), je32_to_cpu(node->hdr_crc), hdr_crc); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } if (ofs + je32_to_cpu(node->totlen) > jeb->offset + c->sector_size) { /* Eep. Node goes over the end of the erase block. */ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, je32_to_cpu(node->totlen)); printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); if ((err = jffs2_scan_dirty_space(c, jeb, 4))) return err; ofs += 4; continue; } if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { /* Wheee. This is an obsoleted node */ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); continue; } switch(je16_to_cpu(node->nodetype)) { case JFFS2_NODETYPE_INODE: if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", sizeof(struct jffs2_raw_inode), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_DIRENT: if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", je32_to_cpu(node->totlen), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; #ifdef CONFIG_JFFS2_FS_XATTR case JFFS2_NODETYPE_XATTR: if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)" " left to end of buf. Reading 0x%x at 0x%08x\n", je32_to_cpu(node->totlen), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_NODETYPE_XREF: if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)" " left to end of buf. Reading 0x%x at 0x%08x\n", je32_to_cpu(node->totlen), buf_len, ofs)); err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); if (err) return err; buf_ofs = ofs; node = (void *)buf; } err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s); if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; #endif /* CONFIG_JFFS2_FS_XATTR */ case JFFS2_NODETYPE_CLEANMARKER: D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node))))) return err; ofs += PAD(sizeof(struct jffs2_unknown_node)); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node))))) return err; ofs += PAD(sizeof(struct jffs2_unknown_node)); } else { jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL); ofs += PAD(c->cleanmarker_size); } break; case JFFS2_NODETYPE_PADDING: if (jffs2_sum_active()) jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen)); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; default: switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_INCOMPAT: printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); return -EINVAL; case JFFS2_FEATURE_RWCOMPAT_DELETE: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; case JFFS2_FEATURE_RWCOMPAT_COPY: { D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL); /* We can't summarise nodes we don't grok */ jffs2_sum_disable_collecting(s); ofs += PAD(je32_to_cpu(node->totlen)); break; } } } } if (jffs2_sum_active()) { if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) { dbg_summary("There is not enough space for " "summary information, disabling for this jeb!\n"); jffs2_sum_disable_collecting(s); } } D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n", jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size)); /* mark_node_obsolete can add to wasted !! */ if (jeb->wasted_size) { jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } return jffs2_scan_classify_jeb(c, jeb); }