diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 80069e34325dda0f7c9ca9ee61b5debef79b8d78..812f969e8390ce3f9ddbc3cdacb12a7832fd10a6 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -104,6 +104,167 @@ int jffs2_link(struct jffs2_inode *old_d_inode, struct jffs2_inode *dir_i, const return ret; } +int jffs2_symlink(struct jffs2_inode *dir_i, struct jffs2_inode **d_inode, const unsigned char *d_name, const char *target) +{ + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; + struct jffs2_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 = strlen((char *)d_name); + 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); + } + + 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, (const unsigned char *)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 = (unsigned char *)malloc(targetlen + 1); + if (!f->target) { + pr_warn("Can't allocate %d bytes of memory\n", targetlen + 1); + mutex_unlock(&f->sem); + jffs2_complete_reservation(c); + ret = -ENOMEM; + goto fail; + } + + ret = LOS_CopyToKernel((char *)f->target, targetlen + 1, target, targetlen + 1); + if (ret != EOK) { + (void)free(f->target); + f->target = NULL; + mutex_unlock(&f->sem); + jffs2_complete_reservation(c); + goto fail; + } + + jffs2_dbg(1, "%s(): symlink's target '%s' cached\n", + __func__, (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_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(Jffs2CurSec()); + 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, (const char *)d_name, namelen)); + + fd = jffs2_write_dirent(c, dir_f, rd, (const unsigned char *)d_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 = 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_inode = inode; + return 0; + + fail: + inode->i_nlink = 0; + jffs2_iput(inode); + return ret; +} + int jffs2_mkdir(struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i) { struct jffs2_inode_info *f, *dir_f; diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index d574ed0e96f2668872845cdc8331901ad547fe52..77681a2e99d9f2ffb709478ad3987c107dd4c1ff 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -68,7 +68,6 @@ struct jffs2_inode { time_t i_atime; time_t i_mtime; time_t i_ctime; - struct Vnode *i_vnode; off_t i_size; struct super_block *i_sb; LOS_DL_LIST i_hashlist; diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 261cf174f9d08e432d9389cb8fb196ddb75a8e13..b45f83bff4c10653fdacf8163c004f3c4558d330 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -150,6 +150,7 @@ struct jffs2_inode *jffs2_lookup(struct jffs2_inode *dir_i, const unsigned char int jffs2_create(struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i); int jffs2_mkdir (struct jffs2_inode *dir_i, const unsigned char *d_name, int mode, struct jffs2_inode **new_i); int jffs2_link (struct jffs2_inode *old_d_inode, struct jffs2_inode *dir_i, const unsigned char *d_name); +int jffs2_symlink(struct jffs2_inode *dir_i, struct jffs2_inode **d_inode, const unsigned char *d_name, const char *target); int jffs2_unlink(struct jffs2_inode *dir_i, struct jffs2_inode *d_inode, const unsigned char *d_name); int jffs2_rmdir (struct jffs2_inode *dir_i, struct jffs2_inode *d_inode, const unsigned char *d_name); int jffs2_rename (struct jffs2_inode *old_dir_i, struct jffs2_inode *d_inode, const unsigned char *old_d_name,