diff options
| author | Dave Jones <davej@suse.de> | 2002-02-26 00:57:44 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@penguin.transmeta.com> | 2002-02-26 00:57:44 -0800 |
| commit | 34a7eea964236ed2e780f6376b79d7791e6b0ec0 (patch) | |
| tree | 511acff77ea1e242c59b73b9f2d5712bac66972f | |
| parent | efc9d68fbff82e5aede0767345166a7ab9033080 (diff) | |
[PATCH] updates.
Forward ports from 2.4, Various janitor bits, and some fixes by me to
make the thing work again in 2.5. I munged the MTDRAM driver to work
also (seperate patch to follow), and it seems to work.
David Woodhouse gave this the once over, and approved the changes.
Complete changelog below:
o Don't create two slabcaches with the same name.
o Don't corrupt eraseblock lists on mount
o Don't mark nodes obsolete during mount
o __attribute__((packed)) on the node definitions.
o Fix up() without down() in jffs2_readdir().
o Fix duplicate version number usage - s/highest_version++/++highest_version/
o Fix (i.e. implement) mtime/ctime on directories.
maybe too busy with the bk stuff
o Don't allow hardlinks of directories.
o s/(mode&S_IFMT)==S_IFLNK/S_ISLNK(mode)/ et al to keep Al happy.
o Fix for garbage-collection of holes, where we used to write nodes out
with csize/dsize swapped. Workarounds for existing such brokenness.
o Improve wear levelling by rotating node lists on mount, to avoid starting
at one end of the flash every time.
o Remember to get internal inode-semaphore on symlink operations.
| -rw-r--r-- | fs/jffs/intrep.c | 2 | ||||
| -rw-r--r-- | fs/jffs2/dir.c | 75 | ||||
| -rw-r--r-- | fs/jffs2/erase.c | 12 | ||||
| -rw-r--r-- | fs/jffs2/file.c | 13 | ||||
| -rw-r--r-- | fs/jffs2/gc.c | 13 | ||||
| -rw-r--r-- | fs/jffs2/malloc.c | 2 | ||||
| -rw-r--r-- | fs/jffs2/nodelist.c | 40 | ||||
| -rw-r--r-- | fs/jffs2/nodelist.h | 5 | ||||
| -rw-r--r-- | fs/jffs2/readinode.c | 34 | ||||
| -rw-r--r-- | fs/jffs2/scan.c | 62 | ||||
| -rw-r--r-- | fs/jffs2/super.c | 9 | ||||
| -rw-r--r-- | fs/jffs2/symlink.c | 11 | ||||
| -rw-r--r-- | include/linux/jffs2.h | 8 | ||||
| -rw-r--r-- | include/linux/jffs2_fs_sb.h | 3 |
14 files changed, 203 insertions, 86 deletions
diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index e1a0d5d9b833..b0df6fbaca37 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c @@ -2643,7 +2643,7 @@ jffs_print_tree(struct jffs_file *first_file, int indent) void jffs_print_memory_allocation_statistics(void) { - static long printout = 0; + static long printout; printk("________ Memory printout #%ld ________\n", ++printout); printk("no_jffs_file = %ld\n", no_jffs_file); printk("no_jffs_node = %ld\n", no_jffs_node); diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index daadcf077ade..c505416ae609 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: dir.c,v 1.42 2001/05/24 22:24:39 dwmw2 Exp $ + * $Id: dir.c,v 1.45.2.5 2002/02/23 14:31:09 dwmw2 Exp $ * */ @@ -39,6 +39,7 @@ #include <linux/slab.h> #include <linux/fs.h> #include <linux/crc32.h> +#include <linux/mtd/compatmac.h> /* For completion */ #include <linux/jffs2.h> #include <linux/jffs2_fs_i.h> #include <linux/jffs2_fs_sb.h> @@ -181,11 +182,11 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir) } D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type)); if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0) - goto out; + break; offset++; } - out: up(&f->sem); + out: filp->f_pos = offset; return 0; } @@ -305,7 +306,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode) 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->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -316,17 +317,21 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode) fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); - + if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = 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); @@ -373,7 +378,7 @@ static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int renam 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->version = ++dir_f->highest_version; rd->ino = 0; rd->mctime = CURRENT_TIME; rd->nsize = dentry->d_name.len; @@ -464,7 +469,7 @@ static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct 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->version = ++dir_f->highest_version; rd->ino = old_dentry->d_inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = dentry->d_name.len; @@ -503,6 +508,9 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de { int ret; + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + lock_kernel(); ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); if (!ret) { @@ -567,7 +575,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char f = JFFS2_INODE_INFO(inode); - ri->dsize = ri->csize = strlen(target); + inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target); ri->totlen = sizeof(*ri) + ri->dsize; ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); @@ -628,7 +636,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char 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->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -639,16 +647,19 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = rd->mctime; + + jffs2_free_raw_dirent(rd); /* Link the fd into the inode's list, obsoleting an old one if necessary. */ @@ -768,7 +779,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) 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->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -779,17 +790,21 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = 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); @@ -841,8 +856,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in c = JFFS2_SB_INFO(dir_i->i_sb); - if ((mode & S_IFMT) == S_IFBLK || - (mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(mode) || S_ISCHR(mode)) { dev = (MAJOR(rdev) << 8) | MINOR(rdev); devlen = sizeof(dev); } @@ -933,7 +947,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in 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->version = ++dir_f->highest_version; rd->ino = inode->i_ino; rd->mctime = CURRENT_TIME; rd->nsize = namelen; @@ -947,17 +961,21 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); jffs2_complete_reservation(c); - jffs2_free_raw_dirent(rd); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally as if it were the final unlink() */ + jffs2_free_raw_dirent(rd); up(&dir_f->sem); jffs2_clear_inode(inode); unlock_kernel(); return PTR_ERR(fd); } + dir_i->i_mtime = dir_i->i_ctime = 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); @@ -965,6 +983,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, in unlock_kernel(); d_instantiate(dentry, inode); + return 0; } @@ -989,20 +1008,16 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, ret = jffs2_do_unlink(old_dir_i, old_dentry, 1); if (ret) { - /* Try to delete the _new_ link to return a clean failure */ - int ret2 = jffs2_do_unlink(new_dir_i, new_dentry, 1); - if (ret2) { - struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); - down(&f->sem); - old_dentry->d_inode->i_nlink = f->inocache->nlink++; - up(&f->sem); + /* Oh shit. We really ought to make a single node which can do both atomically */ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); + down(&f->sem); + old_dentry->d_inode->i_nlink = f->inocache->nlink++; + up(&f->sem); - printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (old err %d, new err %d). You now have a hard link\n", ret, ret2); - /* Might as well let the VFS know */ - d_instantiate(new_dentry, old_dentry->d_inode); - atomic_inc(&old_dentry->d_inode->i_count); - } - + printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); + /* Might as well let the VFS know */ + d_instantiate(new_dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); } unlock_kernel(); return ret; diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 2dc9b5f38294..be16d7cc263d 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: erase.c,v 1.23 2001/09/19 21:51:11 dwmw2 Exp $ + * $Id: erase.c,v 1.24 2001/12/06 16:38:38 dwmw2 Exp $ * */ #include <linux/kernel.h> @@ -214,8 +214,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, D2({ int i=0; - struct jffs2_raw_node_ref *this; - printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); + struct jffs2_raw_node_ref *this; + printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG); this = ic->nodes; @@ -262,7 +262,11 @@ void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) { - static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)}; + static struct jffs2_unknown_node marker = { + magic: JFFS2_MAGIC_BITMASK, + nodetype: JFFS2_NODETYPE_CLEANMARKER, + totlen: sizeof(struct jffs2_unknown_node) + }; struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *marker_ref; unsigned char *ebuf; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index f0446acb9194..190a428acff8 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: file.c,v 1.58 2001/09/20 15:28:31 dwmw2 Exp $ + * $Id: file.c,v 1.58.2.1 2002/02/23 14:25:36 dwmw2 Exp $ * */ @@ -102,15 +102,14 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) 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 ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = (major(dentry->d_inode->i_rdev) << 8) | minor(dentry->d_inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); - } else if ((inode->i_mode & S_IFMT) == S_IFLNK) { + } else if (S_ISLNK(inode->i_mode)) { mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); if (!mdata) @@ -125,7 +124,7 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) ri = jffs2_alloc_raw_inode(); if (!ri) { - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return -ENOMEM; } @@ -133,7 +132,7 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); if (ret) { jffs2_free_raw_inode(ri); - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return ret; } @@ -177,7 +176,7 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) ri->data_crc = 0; new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL); - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); jffs2_complete_reservation(c); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index fc4ab75ea921..f61cbf699414 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: gc.c,v 1.52 2001/09/19 21:53:47 dwmw2 Exp $ + * $Id: gc.c,v 1.52.2.2 2002/02/23 14:25:36 dwmw2 Exp $ * */ @@ -266,15 +266,14 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ __u32 alloclen, phys_ofs; int ret; - if ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ dev = (major(inode->i_rdev) << 8) | minor(inode->i_rdev); mdata = (char *)&dev; mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); - } else if ((inode->i_mode & S_IFMT) == S_IFLNK) { + } else if (S_ISLNK(inode->i_mode)) { mdatalen = fn->size; mdata = kmalloc(fn->size, GFP_KERNEL); if (!mdata) { @@ -331,7 +330,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ jffs2_free_full_dnode(fn); f->metadata = new_fn; out: - if ((inode->i_mode & S_IFMT) == S_IFLNK) + if (S_ISLNK(inode->i_mode)) kfree(mdata); return ret; } @@ -466,8 +465,8 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras ri.ino = inode->i_ino; ri.version = ++f->highest_version; ri.offset = start; - ri.csize = end - start; - ri.dsize = 0; + ri.dsize = end - start; + ri.csize = 0; ri.compr = JFFS2_COMPR_ZERO; } ri.mode = inode->i_mode; diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 18874665d265..b43333100b19 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -139,7 +139,7 @@ int __init jffs2_create_slab_caches(void) if (!jffs2_inode_cachep) goto err; - inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); + inode_cache_slab = kmem_cache_create("jffs2_inode", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); if (inode_cache_slab) return 0; diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 27e90b8b366e..285971b48542 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -1,7 +1,7 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: nodelist.c,v 1.30 2001/11/14 10:35:21 dwmw2 Exp $ + * $Id: nodelist.c,v 1.30.2.3 2002/02/23 14:04:44 dwmw2 Exp $ * */ @@ -94,7 +94,8 @@ void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnod int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - __u32 *highest_version) + __u32 *highest_version, __u32 *latest_mctime, + __u32 *mctime_ver) { struct jffs2_raw_node_ref *ref = f->inocache->nodes; struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; @@ -104,6 +105,8 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode size_t retlen; int err; + *mctime_ver = 0; + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); if (!f->inocache->nodes) { printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino); @@ -115,7 +118,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3)); continue; } - err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), sizeof(node), &retlen, (void *)&node); + err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node); if (err) { printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3); goto free_out; @@ -123,7 +126,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* Check we've managed to read at least the common node header */ - if (retlen < sizeof(node.u)) { + if (retlen < min(ref->totlen, sizeof(node.u))) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; @@ -153,15 +156,22 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode fd->version = node.d.version; fd->ino = node.d.ino; fd->type = node.d.type; - /* memcpy as much of the name as possible from the raw - dirent we've already read from the flash - */ + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; + *latest_mctime = node.d.mctime; + } + + /* memcpy as much of the name as possible from the raw + dirent we've already read from the flash + */ if (retlen > sizeof(struct jffs2_raw_dirent)) memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); - /* Do we need to copy any more of the name directly - from the flash? - */ + /* Do we need to copy any more of the name directly + from the flash? + */ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { int already = retlen - sizeof(struct jffs2_raw_dirent); @@ -217,7 +227,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode } tn->version = node.i.version; tn->fn->ofs = node.i.offset; - tn->fn->size = node.i.dsize; + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ + if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize) + tn->fn->size = node.i.csize; + else // normal case... + tn->fn->size = node.i.dsize; tn->fn->raw = ref; D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize)); jffs2_add_tn_to_list(tn, &ret_tn); @@ -242,6 +257,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode } *tnp = ret_tn; *fdp = ret_fd; + return 0; free_out: diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 850817ef84b0..bec082a02fc8 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: nodelist.h,v 1.46 2001/09/18 23:43:05 dwmw2 Exp $ + * $Id: nodelist.h,v 1.46.2.1 2002/02/23 14:04:44 dwmw2 Exp $ * */ @@ -248,7 +248,8 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, - __u32 *highest_version); + __u32 *highest_version, __u32 *latest_mctime, + __u32 *mctime_ver); struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino); void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 1824be09fcd9..d18ee615bca2 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: readinode.c,v 1.56 2001/07/26 20:32:39 dwmw2 Exp $ + * $Id: readinode.c,v 1.58.2.2 2002/02/23 14:25:37 dwmw2 Exp $ * */ @@ -173,12 +173,12 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_ jffs2_free_node_frag(newfrag); return -ENOMEM; } - printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); + D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); if (this->node) printk("phys 0x%08x\n", this->node->raw->flash_offset &~3); else printk("hole\n"); - + ) newfrag2->ofs = fn->ofs + fn->size; newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; newfrag2->next = this->next; @@ -251,6 +251,7 @@ void jffs2_read_inode (struct inode *inode) struct jffs2_full_dnode *fn = NULL; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; + __u32 latest_mctime, mctime_ver; int ret; ssize_t retlen; @@ -292,7 +293,7 @@ void jffs2_read_inode (struct inode *inode) inode->i_nlink = f->inocache->nlink; /* Grab all nodes relevant to this ino */ - ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version); + ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); if (ret) { printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret); @@ -302,7 +303,7 @@ void jffs2_read_inode (struct inode *inode) f->dents = fd_list; while (tn_list) { - static __u32 mdata_ver = 0; + static __u32 mdata_ver; tn = tn_list; @@ -339,6 +340,7 @@ void jffs2_read_inode (struct inode *inode) printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n"); } inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; + latest_node.version = 0; inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_nlink = f->inocache->nlink; inode->i_size = 0; @@ -366,7 +368,7 @@ void jffs2_read_inode (struct inode *inode) inode->i_uid = latest_node.uid; inode->i_gid = latest_node.gid; inode->i_size = latest_node.isize; - if ((inode->i_mode & S_IFMT) == S_IFREG) + if (S_ISREG(inode->i_mode)) jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize); inode->i_atime = latest_node.atime; inode->i_mtime = latest_node.mtime; @@ -376,9 +378,8 @@ void jffs2_read_inode (struct inode *inode) /* OK, now the special cases. Certain inode types should have only one data node, and it's kept as the metadata node */ - if ((inode->i_mode & S_IFMT) == S_IFBLK || - (inode->i_mode & S_IFMT) == S_IFCHR || - (inode->i_mode & S_IFMT) == S_IFLNK) { + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) || + S_ISLNK(inode->i_mode)) { if (f->metadata) { printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode); jffs2_clear_inode(inode); @@ -393,7 +394,8 @@ void jffs2_read_inode (struct inode *inode) } /* ASSERT: f->fraglist != NULL */ if (f->fraglist->next) { - printk(KERN_WARNING "Argh. Special inode #%lu had more than one node\n", inode->i_ino); + printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode); + /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ jffs2_clear_inode(inode); make_bad_inode(inode); return; @@ -412,9 +414,21 @@ void jffs2_read_inode (struct inode *inode) case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; + /* 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 (!inode->i_size) + inode->i_size = latest_node.dsize; break; case S_IFDIR: + if (mctime_ver > latest_node.version) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ + inode->i_mtime = inode->i_ctime = inode->i_atime = + latest_mctime; + } inode->i_op = &jffs2_dir_inode_operations; inode->i_fop = &jffs2_dir_operations; break; diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index d1b870bd59df..f97cc6078040 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: scan.c,v 1.51 2001/09/19 00:06:35 dwmw2 Exp $ + * $Id: scan.c,v 1.51.2.2 2002/02/23 13:34:31 dwmw2 Exp $ * */ #include <linux/kernel.h> @@ -62,6 +62,9 @@ } \ } while(0) +static uint32_t pseudo_random; +static void jffs2_rotate_lists(struct jffs2_sb_info *c); + static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. @@ -142,6 +145,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) c->nr_erasing_blocks++; } } + /* Rotate the lists by some number to ensure wear levelling */ + jffs2_rotate_lists(c); + if (c->nr_erasing_blocks) { if (!c->used_size && empty_blocks != c->nr_blocks) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); @@ -444,6 +450,12 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc *ofs += 4; return 0; } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ + if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) { + ri.dsize = ri.csize; + ri.csize = 0; + } if (ri.csize) { /* Check data CRC too */ @@ -474,7 +486,7 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc *ofs, ri.data_crc, crc); DIRTY_SPACE(PAD(ri.totlen)); *ofs += PAD(ri.totlen); - return -0; + return 0; } } @@ -518,6 +530,8 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", ri.ino, ri.version, ri.offset, ri.offset+ri.dsize)); + pseudo_random += ri.version; + for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) { if ((*tn_list)->version < ri.version) continue; @@ -613,6 +627,8 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo return 0; } + pseudo_random += rd.version; + fd = jffs2_alloc_full_dirent(rd.nsize+1); if (!fd) { return -ENOMEM; @@ -686,3 +702,45 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo *ofs += PAD(rd.totlen); return 0; } + +static int count_list(struct list_head *l) +{ + uint32_t count = 0; + struct list_head *tmp; + + list_for_each(tmp, l) { + count++; + } + return count; +} + +/* Note: This breaks if list_empty(head). I don't care. You + might, if you copy this code and use it elsewhere :) */ +static void rotate_list(struct list_head *head, uint32_t count) +{ + struct list_head *n = head->next; + + list_del(head); + while(count--) + n = n->next; + list_add(head, n); +} + +static void jffs2_rotate_lists(struct jffs2_sb_info *c) +{ + uint32_t x; + + x = count_list(&c->clean_list); + if (x) + rotate_list((&c->clean_list), pseudo_random % x); + + x = count_list(&c->dirty_list); + if (x) + rotate_list((&c->dirty_list), pseudo_random % x); + + if (c->nr_erasing_blocks) + rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks); + + if (c->nr_free_blocks) /* Not that it should ever be zero */ + rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks); +} diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 05f1185aa809..6c491da8d374 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: super.c,v 1.48 2001/10/02 09:16:23 dwmw2 Exp $ + * $Id: super.c,v 1.48.2.1 2002/02/23 14:13:34 dwmw2 Exp $ * */ @@ -214,7 +214,7 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); if (!c->mtd) { - D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev))); + D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); return -EINVAL; } c->sector_size = c->mtd->erasesize; @@ -249,10 +249,15 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) INIT_LIST_HEAD(&c->bad_used_list); c->highest_ino = 1; + c->flags |= JFFS2_SB_FLAG_MOUNTING; + if (jffs2_build_filesystem(c)) { D1(printk(KERN_DEBUG "build_fs failed\n")); goto out_nodes; } + + c->flags &= ~JFFS2_SB_FLAG_MOUNTING; + sb->s_op = &jffs2_super_operations; D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n")); diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index c6746ed7c425..e1f7dec49f61 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -1,7 +1,7 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001 Red Hat, Inc. + * Copyright (C) 2001, 2002 Red Hat, Inc. * * Created by David Woodhouse <dwmw2@cambridge.redhat.com> * @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: symlink.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $ + * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $ * */ @@ -58,16 +58,21 @@ static char *jffs2_getlink(struct dentry *dentry) char *buf; int ret; + down(&f->sem); if (!f->metadata) { + up(&f->sem); printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino); return ERR_PTR(-EINVAL); } buf = kmalloc(f->metadata->size+1, GFP_USER); - if (!buf) + if (!buf) { + up(&f->sem); return ERR_PTR(-ENOMEM); + } buf[f->metadata->size]=0; ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size); + up(&f->sem); if (ret) { kfree(buf); return ERR_PTR(ret); diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 37d3d931e416..9c67732fb12f 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -31,7 +31,7 @@ * provisions above, a recipient may use your version of this file * under either the RHEPL or the GPL. * - * $Id: jffs2.h,v 1.18 2001/03/25 22:36:12 dwmw2 Exp $ + * $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $ * */ @@ -103,7 +103,7 @@ struct jffs2_unknown_node __u16 nodetype; __u32 totlen; /* So we can skip over nodes we don't grok */ __u32 hdr_crc; -}; +} __attribute__((packed)); struct jffs2_raw_dirent { @@ -121,7 +121,7 @@ struct jffs2_raw_dirent __u32 node_crc; __u32 name_crc; __u8 name[0]; -}; +} __attribute__((packed)); /* The JFFS2 raw inode structure: Used for storage on physical media. */ /* The uid, gid, atime, mtime and ctime members could be longer, but @@ -153,7 +153,7 @@ struct jffs2_raw_inode __u32 data_crc; /* CRC for the (compressed) data. */ __u32 node_crc; /* CRC for the raw inode (excluding data) */ // __u8 data[dsize]; -}; +} __attribute__((packed)); union jffs2_node_union { struct jffs2_raw_inode i; diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index 2d9ad4e54f5f..626c8c57fb72 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.16 2001/09/18 20:15:18 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -12,6 +12,7 @@ #define INOCACHE_HASHSIZE 1 #define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_MOUNTING 2 /* A struct for the overall file system control. Pointers to jffs2_sb_info structs are named `c' in the source code. |
