Skip to content

Commit

Permalink
[JFFS2] Track parent inode for directories (for NFS export)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
dwmw2 committed May 1, 2008
1 parent 1b690b4 commit 27c72b0
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 42 deletions.
31 changes: 20 additions & 11 deletions fs/jffs2/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct jffs2_inode_cache *ic)
struct jffs2_inode_cache *ic)
{
struct jffs2_full_dirent *fd;

Expand All @@ -68,11 +68,17 @@ static void jffs2_build_inode_pass1(struct jffs2_sb_info *c,
continue;
}

if (child_ic->nlink++ && fd->type == DT_DIR) {
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
fd->name, fd->ino, ic->ino);
/* TODO: What do we do about it? */
}
if (fd->type == DT_DIR) {
if (child_ic->pino_nlink) {
JFFS2_ERROR("child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n",
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);
/* Can't free scan_dents so far. We might need them in pass 2 */
}
Expand Down Expand Up @@ -125,7 +131,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
dbg_fsbuild("pass 2 starting\n");

for_each_inode(i, c, ic) {
if (ic->nlink)
if (ic->pino_nlink)
continue;

jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
Expand Down Expand Up @@ -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
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) {
dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n",
if (!child_ic->pino_nlink) {
dbg_fsbuild("inode #%u (\"%s\") now has no links; adding to dead_fds list.\n",
fd->ino, fd->name);
fd->next = *dead_fds;
*dead_fds = fd;
} else {
dbg_fsbuild("inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
fd->ino, fd->name, child_ic->nlink);
fd->ino, fd->name, child_ic->pino_nlink);
jffs2_free_full_dirent(fd);
}
}
Expand Down
35 changes: 25 additions & 10 deletions fs/jffs2/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
d_instantiate(dentry, inode);

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;

fail:
Expand All @@ -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,
dentry->d_name.len, dead_f, now);
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)
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
return ret;
Expand Down Expand Up @@ -283,7 +284,7 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de

if (!ret) {
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);
d_instantiate(dentry, old_dentry->d_inode);
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
Expand Down Expand Up @@ -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_fop = &jffs2_dir_operations;
/* Directories get nlink 2 at start */
inode->i_nlink = 2;

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));

Expand Down Expand Up @@ -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)
{
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_full_dirent *fd;
int ret;
uint32_t now = get_seconds();

for (fd = f->dents ; fd; fd = fd->next) {
if (fd->ino)
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);
}
return ret;
}

Expand Down Expand Up @@ -824,7 +836,10 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
inode which didn't exist. */
if (victim_f->inocache) {
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);
}
}
Expand All @@ -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);
mutex_lock(&f->sem);
inc_nlink(old_dentry->d_inode);
if (f->inocache)
f->inocache->nlink++;
if (f->inocache && !S_ISDIR(old_dentry->d_inode->i_mode))
f->inocache->pino_nlink++;
mutex_unlock(&f->sem);

printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
Expand Down
2 changes: 1 addition & 1 deletion fs/jffs2/erase.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
break;
#endif
default:
if (ic->nodes == (void *)ic && ic->nlink == 0)
if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
jffs2_del_ino_cache(c, ic);
}
}
Expand Down
5 changes: 2 additions & 3 deletions fs/jffs2/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_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;

Expand All @@ -286,13 +286,12 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
case S_IFDIR:
{
struct jffs2_full_dirent *fd;
inode->i_nlink = 2; /* parent and '.' */

for (fd=f->dents; fd; fd = fd->next) {
if (fd->type == DT_DIR && fd->ino)
inc_nlink(inode);
}
/* and '..' */
inc_nlink(inode);
/* Root dir gets i_nlink 3 for some reason */
if (inode->i_ino == 1)
inc_nlink(inode);
Expand Down
6 changes: 3 additions & 3 deletions fs/jffs2/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
continue;
}

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

inum = ic->ino;
nlink = ic->nlink;
nlink = ic->pino_nlink;
spin_unlock(&c->inocache_lock);

f = jffs2_gc_fetch_inode(c, inum, !nlink);
Expand Down
5 changes: 4 additions & 1 deletion fs/jffs2/nodelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,10 @@ struct jffs2_inode_cache {
#ifdef CONFIG_JFFS2_FS_XATTR
struct jffs2_xattr_ref *xref;
#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
Expand Down
2 changes: 1 addition & 1 deletion fs/jffs2/nodemgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
break;
#endif
default:
if (ic->nodes == (void *)ic && ic->nlink == 0)
if (ic->nodes == (void *)ic && ic->pino_nlink == 0)
jffs2_del_ino_cache(c, ic);
break;
}
Expand Down
7 changes: 4 additions & 3 deletions fs/jffs2/readinode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,8 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
size_t retlen;
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));

Expand Down Expand Up @@ -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");
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->state = INO_STATE_READING;
jffs2_add_ino_cache(c, f->inocache);
Expand Down Expand Up @@ -1401,7 +1402,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
jffs2_clear_acl(f);
jffs2_xattr_delete_inode(c, f->inocache);
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)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
Expand Down
2 changes: 1 addition & 1 deletion fs/jffs2/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
ic->nodes = (void *)ic;
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink = 1;
ic->pino_nlink = 1;
return ic;
}

Expand Down
2 changes: 1 addition & 1 deletion fs/jffs2/wbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
full_dirent or full_dnode structure to point to the
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)) {
/* Should never happen; it _must_ be present */
JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n",
Expand Down
11 changes: 6 additions & 5 deletions fs/jffs2/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
#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;

Expand All @@ -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));

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->state = INO_STATE_PRESENT;

Expand Down Expand Up @@ -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_free_full_dirent(fd);
}
}

dead_f->inocache->nlink--;
dead_f->inocache->pino_nlink = 0;
} else
dead_f->inocache->pino_nlink--;
/* NB: Caller must set inode nlink if appropriate */
mutex_unlock(&dead_f->sem);
}
Expand Down
4 changes: 2 additions & 2 deletions fs/jffs2/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
struct jffs2_xattr_ref *ref, *_ref;

if (!ic || ic->nlink > 0)
if (!ic || ic->pino_nlink > 0)
return;

down_write(&c->xattr_sem);
Expand Down Expand Up @@ -829,7 +829,7 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
ref->xd and ref->ic are not valid yet. */
xd = jffs2_find_xattr_datum(c, ref->xid);
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",
ref->ino, ref->xid, ref->xseqno);
ref->xseqno |= XREF_DELETE_MARKER;
Expand Down

0 comments on commit 27c72b0

Please sign in to comment.