index 07119c4..662bc73 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -48,7 +48,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; @@ -70,11 +70,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->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 = ic->ino; + + } else + child_ic->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 */ } @@ -234,7 +240,10 @@ 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 = 0; + else + child_ic->nlink--; if (!child_ic->nlink) { dbg_fsbuild("inode #%u (\"%s\") has now got zero nlink, adding to dead_fds list.\n", index 9fa2e27..b7d3e62 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -604,17 +604,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; } @@ -827,7 +835,10 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, inode which didn't exist. */ if (victim_f->inocache) { down(&victim_f->sem); - victim_f->inocache->nlink--; + if (S_ISDIR(new_dentry->d_inode->i_mode)) + victim_f->inocache->pino = 0; + else + victim_f->inocache->nlink--; up(&victim_f->sem); } } @@ -848,7 +859,7 @@ 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); down(&f->sem); inc_nlink(old_dentry->d_inode); - if (f->inocache) + if (f->inocache && !S_ISDIR(old_dentry->d_inode->i_mode)) f->inocache->nlink++; up(&f->sem); index abb90c0..18205b0 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -274,13 +274,12 @@ void jffs2_read_inode (struct inode *inode) 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); index 4178b4b..44a16a0 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -165,7 +165,10 @@ struct jffs2_inode_cache { #ifdef CONFIG_JFFS2_FS_XATTR struct jffs2_xattr_ref *xref; #endif - int nlink; + union { + uint32_t pino; /* For directories */ + uint32_t nlink; /* For everything else */ + }; }; /* Inode states for 'state' above. We need the 'GC' state to prevent index cc7e8e7..bb8e19d 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -66,6 +66,36 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) return 0; } +static struct dentry *jffs2_get_parent(struct dentry *child) +{ + struct jffs2_inode_info *f; + struct dentry *parent; + struct inode *inode; + uint32_t pino; + + if (!S_ISDIR(child->d_inode->i_mode)) + return ERR_PTR(-EACCES); + + f = JFFS2_INODE_INFO(child->d_inode); + + pino = f->inocache->pino; + + inode = iget(child->d_inode->i_sb, pino); + if (!inode) + return ERR_PTR(-EACCES); + + parent = d_alloc_anon(inode); + if (!parent) { + iput(inode); + parent = ERR_PTR(-ENOMEM); + } + return parent; +} + +static struct export_operations jffs2_export_ops = { + .get_parent = jffs2_get_parent, +}; + static const struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, @@ -151,6 +181,7 @@ static int jffs2_get_sb_mtd(struct file_system_type *fs_type, spin_lock_init(&c->inocache_lock); sb->s_op = &jffs2_super_operations; + sb->s_export_op = &jffs2_export_ops; sb->s_flags = flags | MS_NOATIME; sb->s_xattr = jffs2_xattr_handlers; #ifdef CONFIG_JFFS2_FS_POSIX_ACL @@ -322,6 +353,7 @@ static struct file_system_type jffs2_fs_type = { .name = "jffs2", .get_sb = jffs2_get_sb, .kill_sb = jffs2_kill_sb, + .fs_flags = FS_REQUIRES_DEV, }; static int __init init_jffs2_fs(void) index 6717679..822c99a 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -601,9 +601,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 = 0; + } else + dead_f->inocache->nlink--; /* NB: Caller must set inode nlink if appropriate */ up(&dead_f->sem); }