[JFFS2] Track parent inode for directories (for NFS export)

To support NFS export, we need to know the parent inode of directories.
Rather than growing the jffs2_inode_cache structure, share space with
the nlink field -- which was always set to 1 for directories anyway.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
David Woodhouse 2008-05-01 18:47:17 +01:00
parent 1b690b4878
commit 27c72b040c
12 changed files with 70 additions and 42 deletions

View file

@ -46,7 +46,7 @@ next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
static void jffs2_build_inode_pass1(struct jffs2_sb_info *c, static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
struct jffs2_inode_cache *ic) struct jffs2_inode_cache *ic)
{ {
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
@ -68,11 +68,17 @@ static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
continue; continue;
} }
if (child_ic->nlink++ && fd->type == DT_DIR) { if (fd->type == DT_DIR) {
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", if (child_ic->pino_nlink) {
fd->name, fd->ino, ic->ino); JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
/* TODO: What do we do about it? */ fd->name, fd->ino, ic->ino);
} /* TODO: What do we do about it? */
} else {
child_ic->pino_nlink = ic->ino;
}
} else
child_ic->pino_nlink++;
dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino); dbg_fsbuild("increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino);
/* Can't free scan_dents so far. We might need them in pass 2 */ /* Can't free scan_dents so far. We might need them in pass 2 */
} }
@ -125,7 +131,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
dbg_fsbuild("pass 2 starting\n"); dbg_fsbuild("pass 2 starting\n");
for_each_inode(i, c, ic) { for_each_inode(i, c, ic) {
if (ic->nlink) if (ic->pino_nlink)
continue; continue;
jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
@ -232,16 +238,19 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c,
/* Reduce nlink of the child. If it's now zero, stick it on the /* Reduce nlink of the child. If it's now zero, stick it on the
dead_fds list to be cleaned up later. Else just free the fd */ dead_fds list to be cleaned up later. Else just free the fd */
child_ic->nlink--; if (fd->type == DT_DIR)
child_ic->pino_nlink = 0;
else
child_ic->pino_nlink--;
if (!child_ic->nlink) { if (!child_ic->pino_nlink) {
dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n", dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n",
fd->ino, fd->name); fd->ino, fd->name);
fd->next = *dead_fds; fd->next = *dead_fds;
*dead_fds = fd; *dead_fds = fd;
} else { } else {
dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
fd->ino, fd->name, child_ic->nlink); fd->ino, fd->name, child_ic->pino_nlink);
jffs2_free_full_dirent(fd); jffs2_free_full_dirent(fd);
} }
} }

View file

@ -226,7 +226,8 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); inode->i_ino, inode->i_mode, inode->i_nlink,
f->inocache->pino_nlink, inode->i_mapping->nrpages));
return 0; return 0;
fail: fail:
@ -250,7 +251,7 @@ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
dentry->d_name.len, dead_f, now); dentry->d_name.len, dead_f, now);
if (dead_f->inocache) if (dead_f->inocache)
dentry->d_inode->i_nlink = dead_f->inocache->nlink; dentry->d_inode->i_nlink = dead_f->inocache->pino_nlink;
if (!ret) if (!ret)
dir_i->i_mtime = dir_i->i_ctime = ITIME(now); dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
return ret; return ret;
@ -283,7 +284,7 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de
if (!ret) { if (!ret) {
mutex_lock(&f->sem); mutex_lock(&f->sem);
old_dentry->d_inode->i_nlink = ++f->inocache->nlink; old_dentry->d_inode->i_nlink = ++f->inocache->pino_nlink;
mutex_unlock(&f->sem); mutex_unlock(&f->sem);
d_instantiate(dentry, old_dentry->d_inode); d_instantiate(dentry, old_dentry->d_inode);
dir_i->i_mtime = dir_i->i_ctime = ITIME(now); dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
@ -500,11 +501,14 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
inode->i_op = &jffs2_dir_inode_operations; inode->i_op = &jffs2_dir_inode_operations;
inode->i_fop = &jffs2_dir_operations; inode->i_fop = &jffs2_dir_operations;
/* Directories get nlink 2 at start */
inode->i_nlink = 2;
f = JFFS2_INODE_INFO(inode); 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->data_crc = cpu_to_je32(0);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
@ -601,17 +605,25 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
{ {
struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
int ret; int ret;
uint32_t now = get_seconds();
for (fd = f->dents ; fd; fd = fd->next) { for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino) if (fd->ino)
return -ENOTEMPTY; return -ENOTEMPTY;
} }
ret = jffs2_unlink(dir_i, dentry);
if (!ret) ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
dentry->d_name.len, f, now);
if (!ret) {
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
clear_nlink(dentry->d_inode);
drop_nlink(dir_i); drop_nlink(dir_i);
}
return ret; return ret;
} }
@ -824,7 +836,10 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
inode which didn't exist. */ inode which didn't exist. */
if (victim_f->inocache) { if (victim_f->inocache) {
mutex_lock(&victim_f->sem); mutex_lock(&victim_f->sem);
victim_f->inocache->nlink--; if (S_ISDIR(new_dentry->d_inode->i_mode))
victim_f->inocache->pino_nlink = 0;
else
victim_f->inocache->pino_nlink--;
mutex_unlock(&victim_f->sem); mutex_unlock(&victim_f->sem);
} }
} }
@ -845,8 +860,8 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
mutex_lock(&f->sem); mutex_lock(&f->sem);
inc_nlink(old_dentry->d_inode); inc_nlink(old_dentry->d_inode);
if (f->inocache) if (f->inocache && !S_ISDIR(old_dentry->d_inode->i_mode))
f->inocache->nlink++; f->inocache->pino_nlink++;
mutex_unlock(&f->sem); mutex_unlock(&f->sem);
printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);

View file

@ -294,7 +294,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
break; break;
#endif #endif
default: default:
if (ic->nodes == (void *)ic && ic->nlink == 0) if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
jffs2_del_ino_cache(c, ic); jffs2_del_ino_cache(c, ic);
} }
} }

View file

@ -273,7 +273,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
inode->i_nlink = f->inocache->nlink; inode->i_nlink = f->inocache->pino_nlink;
inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_blocks = (inode->i_size + 511) >> 9;
@ -286,13 +286,12 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
case S_IFDIR: case S_IFDIR:
{ {
struct jffs2_full_dirent *fd; struct jffs2_full_dirent *fd;
inode->i_nlink = 2; /* parent and '.' */
for (fd=f->dents; fd; fd = fd->next) { for (fd=f->dents; fd; fd = fd->next) {
if (fd->type == DT_DIR && fd->ino) if (fd->type == DT_DIR && fd->ino)
inc_nlink(inode); inc_nlink(inode);
} }
/* and '..' */
inc_nlink(inode);
/* Root dir gets i_nlink 3 for some reason */ /* Root dir gets i_nlink 3 for some reason */
if (inode->i_ino == 1) if (inode->i_ino == 1)
inc_nlink(inode); inc_nlink(inode);

View file

@ -161,8 +161,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
continue; continue;
} }
if (!ic->nlink) { if (!ic->pino_nlink) {
D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink/pino zero\n",
ic->ino)); ic->ino));
spin_unlock(&c->inocache_lock); spin_unlock(&c->inocache_lock);
jffs2_xattr_delete_inode(c, ic); jffs2_xattr_delete_inode(c, ic);
@ -398,7 +398,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
it's vaguely possible. */ it's vaguely possible. */
inum = ic->ino; inum = ic->ino;
nlink = ic->nlink; nlink = ic->pino_nlink;
spin_unlock(&c->inocache_lock); spin_unlock(&c->inocache_lock);
f = jffs2_gc_fetch_inode(c, inum, !nlink); f = jffs2_gc_fetch_inode(c, inum, !nlink);

View file

@ -177,7 +177,10 @@ struct jffs2_inode_cache {
#ifdef CONFIG_JFFS2_FS_XATTR #ifdef CONFIG_JFFS2_FS_XATTR
struct jffs2_xattr_ref *xref; struct jffs2_xattr_ref *xref;
#endif #endif
int nlink; uint32_t pino_nlink; /* Directories store parent inode
here; other inodes store nlink.
Zero always means that it's
completely unlinked. */
}; };
/* Inode states for 'state' above. We need the 'GC' state to prevent /* Inode states for 'state' above. We need the 'GC' state to prevent

View file

@ -709,7 +709,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
break; break;
#endif #endif
default: default:
if (ic->nodes == (void *)ic && ic->nlink == 0) if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
jffs2_del_ino_cache(c, ic); jffs2_del_ino_cache(c, ic);
break; break;
} }

View file

@ -1123,7 +1123,8 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
size_t retlen; size_t retlen;
int ret; int ret;
dbg_readinode("ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink); dbg_readinode("ino #%u pino/nlink is %d\n", f->inocache->ino,
f->inocache->pino_nlink);
memset(&rii, 0, sizeof(rii)); memset(&rii, 0, sizeof(rii));
@ -1358,7 +1359,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
} }
dbg_readinode("creating inocache for root inode\n"); dbg_readinode("creating inocache for root inode\n");
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
f->inocache->ino = f->inocache->nlink = 1; f->inocache->ino = f->inocache->pino_nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_READING; f->inocache->state = INO_STATE_READING;
jffs2_add_ino_cache(c, f->inocache); jffs2_add_ino_cache(c, f->inocache);
@ -1401,7 +1402,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
jffs2_clear_acl(f); jffs2_clear_acl(f);
jffs2_xattr_delete_inode(c, f->inocache); jffs2_xattr_delete_inode(c, f->inocache);
mutex_lock(&f->sem); mutex_lock(&f->sem);
deleted = f->inocache && !f->inocache->nlink; deleted = f->inocache && !f->inocache->pino_nlink;
if (f->inocache && f->inocache->state != INO_STATE_CHECKING) if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING); jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);

View file

@ -940,7 +940,7 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
ic->nodes = (void *)ic; ic->nodes = (void *)ic;
jffs2_add_ino_cache(c, ic); jffs2_add_ino_cache(c, ic);
if (ino == 1) if (ino == 1)
ic->nlink = 1; ic->pino_nlink = 1;
return ic; return ic;
} }

View file

@ -494,7 +494,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
/* If it's an in-core inode, then we have to adjust any /* If it's an in-core inode, then we have to adjust any
full_dirent or full_dnode structure to point to the full_dirent or full_dnode structure to point to the
new version instead of the old */ new version instead of the old */
f = jffs2_gc_fetch_inode(c, ic->ino, !ic->nlink); f = jffs2_gc_fetch_inode(c, ic->ino, !ic->pino_nlink);
if (IS_ERR(f)) { if (IS_ERR(f)) {
/* Should never happen; it _must_ be present */ /* Should never happen; it _must_ be present */
JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n", JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",

View file

@ -19,7 +19,8 @@
#include "compr.h" #include "compr.h"
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) 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; struct jffs2_inode_cache *ic;
@ -31,7 +32,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
memset(ic, 0, sizeof(*ic)); memset(ic, 0, sizeof(*ic));
f->inocache = ic; f->inocache = ic;
f->inocache->nlink = 1; f->inocache->pino_nlink = 1; /* Will be overwritten shortly for directories */
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_PRESENT; f->inocache->state = INO_STATE_PRESENT;
@ -635,9 +636,9 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
jffs2_mark_node_obsolete(c, fd->raw); jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd); jffs2_free_full_dirent(fd);
} }
} dead_f->inocache->pino_nlink = 0;
} else
dead_f->inocache->nlink--; dead_f->inocache->pino_nlink--;
/* NB: Caller must set inode nlink if appropriate */ /* NB: Caller must set inode nlink if appropriate */
mutex_unlock(&dead_f->sem); mutex_unlock(&dead_f->sem);
} }

View file

@ -592,7 +592,7 @@ void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache
When an inode with XATTR is removed, those XATTRs must be removed. */ When an inode with XATTR is removed, those XATTRs must be removed. */
struct jffs2_xattr_ref *ref, *_ref; struct jffs2_xattr_ref *ref, *_ref;
if (!ic || ic->nlink > 0) if (!ic || ic->pino_nlink > 0)
return; return;
down_write(&c->xattr_sem); down_write(&c->xattr_sem);
@ -829,7 +829,7 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
ref->xd and ref->ic are not valid yet. */ ref->xd and ref->ic are not valid yet. */
xd = jffs2_find_xattr_datum(c, ref->xid); xd = jffs2_find_xattr_datum(c, ref->xid);
ic = jffs2_get_ino_cache(c, ref->ino); ic = jffs2_get_ino_cache(c, ref->ino);
if (!xd || !ic || !ic->nlink) { if (!xd || !ic || !ic->pino_nlink) {
dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n", dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n",
ref->ino, ref->xid, ref->xseqno); ref->ino, ref->xid, ref->xseqno);
ref->xseqno |= XREF_DELETE_MARKER; ref->xseqno |= XREF_DELETE_MARKER;