From 474b93704f32163af4bfa728fddc570eba357353 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 14 Sep 2016 22:28:49 +0200 Subject: ubifs: Implement O_TMPFILE This patchs adds O_TMPFILE support to UBIFS. A temp file is a reference to an unlinked inode, a user holding the reference can use it. As soon it is being closed all data vanishes. Signed-off-by: Richard Weinberger --- fs/ubifs/dir.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 4b86d3a738e1..8460f481eeb9 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -301,6 +301,76 @@ out_budg: return err; } +static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + struct inode *inode; + struct ubifs_info *c = dir->i_sb->s_fs_info; + struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1}; + struct ubifs_budget_req ino_req = { .dirtied_ino = 1 }; + struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir); + int err, instantiated = 0; + + /* + * Budget request settings: new dirty inode, new direntry, + * budget for dirtied inode will be released via writeback. + */ + + dbg_gen("dent '%pd', mode %#hx in dir ino %lu", + dentry, mode, dir->i_ino); + + err = ubifs_budget_space(c, &req); + if (err) + return err; + + err = ubifs_budget_space(c, &ino_req); + if (err) { + ubifs_release_budget(c, &req); + return err; + } + + inode = ubifs_new_inode(c, dir, mode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out_budg; + } + ui = ubifs_inode(inode); + + err = ubifs_init_security(dir, inode, &dentry->d_name); + if (err) + goto out_inode; + + mutex_lock(&ui->ui_mutex); + insert_inode_hash(inode); + d_tmpfile(dentry, inode); + ubifs_assert(ui->dirty); + instantiated = 1; + mutex_unlock(&ui->ui_mutex); + + mutex_lock(&dir_ui->ui_mutex); + err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0); + if (err) + goto out_cancel; + mutex_unlock(&dir_ui->ui_mutex); + + ubifs_release_budget(c, &req); + + return 0; + +out_cancel: + mutex_unlock(&dir_ui->ui_mutex); +out_inode: + make_bad_inode(inode); + if (!instantiated) + iput(inode); +out_budg: + ubifs_release_budget(c, &req); + if (!instantiated) + ubifs_release_budget(c, &ino_req); + ubifs_err(c, "cannot create temporary file, error %d", err); + return err; +} + /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type @@ -1189,6 +1259,7 @@ const struct inode_operations ubifs_dir_inode_operations = { #ifdef CONFIG_UBIFS_ATIME_SUPPORT .update_time = ubifs_update_time, #endif + .tmpfile = ubifs_tmpfile, }; const struct file_operations ubifs_dir_operations = { -- cgit v1.2.3-59-g8ed1b From 9e0a1fff8db56eaaebb74b4a3ef65f86811c4798 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 14 Sep 2016 22:28:50 +0200 Subject: ubifs: Implement RENAME_WHITEOUT Adds RENAME_WHITEOUT support to UBIFS, we implement it in the same way as ext4 and xfs do. For an overview of other ways to implement it please refere to commit 7dcf5c3e4527 ("xfs: add RENAME_WHITEOUT support"). Signed-off-by: Richard Weinberger --- fs/ubifs/dir.c | 120 ++++++++++++++++++++++++++++++++++++++++++++--------- fs/ubifs/journal.c | 43 +++++++++++++------ fs/ubifs/ubifs.h | 4 +- 3 files changed, 134 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 8460f481eeb9..e0282534f170 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -301,8 +301,8 @@ out_budg: return err; } -static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, - umode_t mode) +static int do_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode, struct inode **whiteout) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; @@ -336,14 +336,27 @@ static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, } ui = ubifs_inode(inode); + if (whiteout) { + init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); + ubifs_assert(inode->i_op == &ubifs_file_inode_operations); + } + err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&ui->ui_mutex); insert_inode_hash(inode); - d_tmpfile(dentry, inode); + + if (whiteout) { + mark_inode_dirty(inode); + drop_nlink(inode); + *whiteout = inode; + } else { + d_tmpfile(dentry, inode); + } ubifs_assert(ui->dirty); + instantiated = 1; mutex_unlock(&ui->ui_mutex); @@ -371,6 +384,12 @@ out_budg: return err; } +static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + return do_tmpfile(dir, dentry, mode, NULL); +} + /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type @@ -997,37 +1016,43 @@ out_budg: } /** - * lock_3_inodes - a wrapper for locking three UBIFS inodes. + * lock_4_inodes - a wrapper for locking three UBIFS inodes. * @inode1: first inode * @inode2: second inode * @inode3: third inode + * @inode4: fouth inode * * This function is used for 'ubifs_rename()' and @inode1 may be the same as - * @inode2 whereas @inode3 may be %NULL. + * @inode2 whereas @inode3 and @inode4 may be %NULL. * * We do not implement any tricks to guarantee strict lock ordering, because * VFS has already done it for us on the @i_mutex. So this is just a simple * wrapper function. */ -static void lock_3_inodes(struct inode *inode1, struct inode *inode2, - struct inode *inode3) +static void lock_4_inodes(struct inode *inode1, struct inode *inode2, + struct inode *inode3, struct inode *inode4) { mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); if (inode2 != inode1) mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); if (inode3) mutex_lock_nested(&ubifs_inode(inode3)->ui_mutex, WB_MUTEX_3); + if (inode4) + mutex_lock_nested(&ubifs_inode(inode4)->ui_mutex, WB_MUTEX_4); } /** - * unlock_3_inodes - a wrapper for unlocking three UBIFS inodes for rename. + * unlock_4_inodes - a wrapper for unlocking three UBIFS inodes for rename. * @inode1: first inode * @inode2: second inode * @inode3: third inode + * @inode4: fouth inode */ -static void unlock_3_inodes(struct inode *inode1, struct inode *inode2, - struct inode *inode3) +static void unlock_4_inodes(struct inode *inode1, struct inode *inode2, + struct inode *inode3, struct inode *inode4) { + if (inode4) + mutex_unlock(&ubifs_inode(inode4)->ui_mutex); if (inode3) mutex_unlock(&ubifs_inode(inode3)->ui_mutex); if (inode1 != inode2) @@ -1036,12 +1061,15 @@ static void unlock_3_inodes(struct inode *inode1, struct inode *inode2, } static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { struct ubifs_info *c = old_dir->i_sb->s_fs_info; struct inode *old_inode = d_inode(old_dentry); struct inode *new_inode = d_inode(new_dentry); + struct inode *whiteout = NULL; struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode); + struct ubifs_inode *whiteout_ui = NULL; int err, release, sync = 0, move = (new_dir != old_dir); int is_dir = S_ISDIR(old_inode->i_mode); int unlink = !!new_inode; @@ -1063,15 +1091,18 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, * separately. */ - dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu", + dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x", old_dentry, old_inode->i_ino, old_dir->i_ino, - new_dentry, new_dir->i_ino); + new_dentry, new_dir->i_ino, flags); + + if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT)) + return -EINVAL; + ubifs_assert(inode_is_locked(old_dir)); ubifs_assert(inode_is_locked(new_dir)); if (unlink) ubifs_assert(inode_is_locked(new_inode)); - if (unlink && is_dir) { err = check_dir_empty(c, new_inode); if (err) @@ -1087,7 +1118,32 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, return err; } - lock_3_inodes(old_dir, new_dir, new_inode); + if (flags & RENAME_WHITEOUT) { + union ubifs_dev_desc *dev = NULL; + + dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); + if (!dev) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + return -ENOMEM; + } + + err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout); + if (err) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + kfree(dev); + return err; + } + + whiteout->i_state |= I_LINKABLE; + whiteout_ui = ubifs_inode(whiteout); + whiteout_ui->data = dev; + whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0)); + ubifs_assert(!whiteout_ui->dirty); + } + + lock_4_inodes(old_dir, new_dir, new_inode, whiteout); /* * Like most other Unix systems, set the @i_ctime for inodes on a @@ -1157,12 +1213,34 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, if (unlink && IS_SYNC(new_inode)) sync = 1; } - err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, + + if (whiteout) { + struct ubifs_budget_req wht_req = { .dirtied_ino = 1, + .dirtied_ino_d = \ + ALIGN(ubifs_inode(whiteout)->data_len, 8) }; + + err = ubifs_budget_space(c, &wht_req); + if (err) { + ubifs_release_budget(c, &req); + ubifs_release_budget(c, &ino_req); + kfree(whiteout_ui->data); + whiteout_ui->data_len = 0; + iput(whiteout); + return err; + } + + inc_nlink(whiteout); + mark_inode_dirty(whiteout); + whiteout->i_state &= ~I_LINKABLE; + iput(whiteout); + } + + err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry, whiteout, sync); if (err) goto out_cancel; - unlock_3_inodes(old_dir, new_dir, new_inode); + unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); ubifs_release_budget(c, &req); mutex_lock(&old_inode_ui->ui_mutex); @@ -1195,7 +1273,11 @@ out_cancel: inc_nlink(old_dir); } } - unlock_3_inodes(old_dir, new_dir, new_inode); + if (whiteout) { + drop_nlink(whiteout); + iput(whiteout); + } + unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); ubifs_release_budget(c, &ino_req); ubifs_release_budget(c, &req); return err; @@ -1249,7 +1331,7 @@ const struct inode_operations ubifs_dir_inode_operations = { .mkdir = ubifs_mkdir, .rmdir = ubifs_rmdir, .mknod = ubifs_mknod, - .rename = ubifs_rename, + .rename2 = ubifs_rename, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = generic_setxattr, diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 0b9da5b6e0f9..6b27ddd2c484 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -917,14 +917,15 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode) * @sync: non-zero if the write-buffer has to be synchronized * * This function implements the re-name operation which may involve writing up - * to 3 inodes and 2 directory entries. It marks the written inodes as clean + * to 4 inodes and 2 directory entries. It marks the written inodes as clean * and returns zero on success. In case of failure, a negative error code is * returned. */ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, const struct dentry *old_dentry, const struct inode *new_dir, - const struct dentry *new_dentry, int sync) + const struct dentry *new_dentry, + const struct inode *whiteout, int sync) { void *p; union ubifs_key key; @@ -980,13 +981,19 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, zero_dent_node_unused(dent); ubifs_prep_grp_node(c, dent, dlen1, 0); - /* Make deletion dent */ dent2 = (void *)dent + aligned_dlen1; dent2->ch.node_type = UBIFS_DENT_NODE; dent_key_init_flash(c, &dent2->key, old_dir->i_ino, &old_dentry->d_name); - dent2->inum = 0; - dent2->type = DT_UNKNOWN; + + if (whiteout) { + dent2->inum = cpu_to_le64(whiteout->i_ino); + dent2->type = get_dent_type(whiteout->i_mode); + } else { + /* Make deletion dent */ + dent2->inum = 0; + dent2->type = DT_UNKNOWN; + } dent2->nlen = cpu_to_le16(old_dentry->d_name.len); memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len); dent2->name[old_dentry->d_name.len] = '\0'; @@ -1035,16 +1042,26 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, if (err) goto out_ro; - err = ubifs_add_dirt(c, lnum, dlen2); - if (err) - goto out_ro; + offs += aligned_dlen1; + if (whiteout) { + dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &old_dentry->d_name); + if (err) + goto out_ro; - dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); - err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); - if (err) - goto out_ro; + ubifs_delete_orphan(c, whiteout->i_ino); + } else { + err = ubifs_add_dirt(c, lnum, dlen2); + if (err) + goto out_ro; + + dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name); + err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name); + if (err) + goto out_ro; + } - offs += aligned_dlen1 + aligned_dlen2; + offs += aligned_dlen2; if (new_inode) { ino_key_init(c, &key, new_inode->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, ilen); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 4617d459022a..ca86c9386674 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -157,6 +157,7 @@ enum { WB_MUTEX_1 = 0, WB_MUTEX_2 = 1, WB_MUTEX_3 = 2, + WB_MUTEX_4 = 3, }; /* @@ -1523,7 +1524,8 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode); int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, const struct dentry *old_dentry, const struct inode *new_dir, - const struct dentry *new_dentry, int sync); + const struct dentry *new_dentry, + const struct inode *whiteout, int sync); int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode, loff_t old_size, loff_t new_size); int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, -- cgit v1.2.3-59-g8ed1b From 9ec64962afb1702f75bdf046fffeecf9c8737518 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 14 Sep 2016 22:28:51 +0200 Subject: ubifs: Implement RENAME_EXCHANGE Adds RENAME_EXCHANGE to UBIFS, the operation itself is completely disjunct from a regular rename() that's why we dispatch very early in ubifs_reaname(). RENAME_EXCHANGE used by the renameat2() system call allows the caller to exchange two paths atomically. Both paths have to exist and have to be on the same filesystem. Signed-off-by: Richard Weinberger --- fs/ubifs/dir.c | 65 +++++++++++++++++++++--- fs/ubifs/journal.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ubifs/ubifs.h | 4 ++ 3 files changed, 204 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index e0282534f170..ccd9128f8faf 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -1095,11 +1095,6 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry, old_inode->i_ino, old_dir->i_ino, new_dentry, new_dir->i_ino, flags); - if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT)) - return -EINVAL; - - ubifs_assert(inode_is_locked(old_dir)); - ubifs_assert(inode_is_locked(new_dir)); if (unlink) ubifs_assert(inode_is_locked(new_inode)); @@ -1283,6 +1278,64 @@ out_cancel: return err; } +static int ubifs_xrename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct ubifs_info *c = old_dir->i_sb->s_fs_info; + struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1, + .dirtied_ino = 2 }; + int sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir); + struct inode *fst_inode = d_inode(old_dentry); + struct inode *snd_inode = d_inode(new_dentry); + struct timespec time; + int err; + + ubifs_assert(fst_inode && snd_inode); + + lock_4_inodes(old_dir, new_dir, NULL, NULL); + + time = ubifs_current_time(old_dir); + fst_inode->i_ctime = time; + snd_inode->i_ctime = time; + old_dir->i_mtime = old_dir->i_ctime = time; + new_dir->i_mtime = new_dir->i_ctime = time; + + if (old_dir != new_dir) { + if (S_ISDIR(fst_inode->i_mode) && !S_ISDIR(snd_inode->i_mode)) { + inc_nlink(new_dir); + drop_nlink(old_dir); + } + else if (!S_ISDIR(fst_inode->i_mode) && S_ISDIR(snd_inode->i_mode)) { + drop_nlink(new_dir); + inc_nlink(old_dir); + } + } + + err = ubifs_jnl_xrename(c, old_dir, old_dentry, new_dir, new_dentry, + sync); + + unlock_4_inodes(old_dir, new_dir, NULL, NULL); + ubifs_release_budget(c, &req); + + return err; +} + +static int ubifs_rename2(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + if (flags & ~(RENAME_NOREPLACE | RENAME_WHITEOUT | RENAME_EXCHANGE)) + return -EINVAL; + + ubifs_assert(inode_is_locked(old_dir)); + ubifs_assert(inode_is_locked(new_dir)); + + if (flags & RENAME_EXCHANGE) + return ubifs_xrename(old_dir, old_dentry, new_dir, new_dentry); + + return ubifs_rename(old_dir, old_dentry, new_dir, new_dentry, flags); +} + int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -1331,7 +1384,7 @@ const struct inode_operations ubifs_dir_inode_operations = { .mkdir = ubifs_mkdir, .rmdir = ubifs_rmdir, .mknod = ubifs_mknod, - .rename2 = ubifs_rename, + .rename2 = ubifs_rename2, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = generic_setxattr, diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 6b27ddd2c484..8e07f132e024 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -907,6 +907,147 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode) return err; } +/** + * ubifs_jnl_xrename - cross rename two directory entries. + * @c: UBIFS file-system description object + * @fst_dir: parent inode of 1st directory entry to exchange + * @fst_dentry: 1st directory entry to exchange + * @snd_dir: parent inode of 2nd directory entry to exchange + * @snd_dentry: 2nd directory entry to exchange + * @sync: non-zero if the write-buffer has to be synchronized + * + * This function implements the cross rename operation which may involve + * writing 2 inodes and 2 directory entries. It marks the written inodes as clean + * and returns zero on success. In case of failure, a negative error code is + * returned. + */ +int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, + const struct dentry *fst_dentry, + const struct inode *snd_dir, + const struct dentry *snd_dentry, int sync) +{ + union ubifs_key key; + struct ubifs_dent_node *dent1, *dent2; + int err, dlen1, dlen2, lnum, offs, len, plen = UBIFS_INO_NODE_SZ; + int aligned_dlen1, aligned_dlen2; + int twoparents = (fst_dir != snd_dir); + const struct inode *fst_inode = d_inode(fst_dentry); + const struct inode *snd_inode = d_inode(snd_dentry); + void *p; + + dbg_jnl("dent '%pd' in dir ino %lu between dent '%pd' in dir ino %lu", + fst_dentry, fst_dir->i_ino, snd_dentry, snd_dir->i_ino); + + ubifs_assert(ubifs_inode(fst_dir)->data_len == 0); + ubifs_assert(ubifs_inode(snd_dir)->data_len == 0); + ubifs_assert(mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex)); + ubifs_assert(mutex_is_locked(&ubifs_inode(snd_dir)->ui_mutex)); + + dlen1 = UBIFS_DENT_NODE_SZ + snd_dentry->d_name.len + 1; + dlen2 = UBIFS_DENT_NODE_SZ + fst_dentry->d_name.len + 1; + aligned_dlen1 = ALIGN(dlen1, 8); + aligned_dlen2 = ALIGN(dlen2, 8); + + len = aligned_dlen1 + aligned_dlen2 + ALIGN(plen, 8); + if (twoparents) + len += plen; + + dent1 = kmalloc(len, GFP_NOFS); + if (!dent1) + return -ENOMEM; + + /* Make reservation before allocating sequence numbers */ + err = make_reservation(c, BASEHD, len); + if (err) + goto out_free; + + /* Make new dent for 1st entry */ + dent1->ch.node_type = UBIFS_DENT_NODE; + dent_key_init_flash(c, &dent1->key, snd_dir->i_ino, &snd_dentry->d_name); + dent1->inum = cpu_to_le64(fst_inode->i_ino); + dent1->type = get_dent_type(fst_inode->i_mode); + dent1->nlen = cpu_to_le16(snd_dentry->d_name.len); + memcpy(dent1->name, snd_dentry->d_name.name, snd_dentry->d_name.len); + dent1->name[snd_dentry->d_name.len] = '\0'; + zero_dent_node_unused(dent1); + ubifs_prep_grp_node(c, dent1, dlen1, 0); + + /* Make new dent for 2nd entry */ + dent2 = (void *)dent1 + aligned_dlen1; + dent2->ch.node_type = UBIFS_DENT_NODE; + dent_key_init_flash(c, &dent2->key, fst_dir->i_ino, &fst_dentry->d_name); + dent2->inum = cpu_to_le64(snd_inode->i_ino); + dent2->type = get_dent_type(snd_inode->i_mode); + dent2->nlen = cpu_to_le16(fst_dentry->d_name.len); + memcpy(dent2->name, fst_dentry->d_name.name, fst_dentry->d_name.len); + dent2->name[fst_dentry->d_name.len] = '\0'; + zero_dent_node_unused(dent2); + ubifs_prep_grp_node(c, dent2, dlen2, 0); + + p = (void *)dent2 + aligned_dlen2; + if (!twoparents) + pack_inode(c, p, fst_dir, 1); + else { + pack_inode(c, p, fst_dir, 0); + p += ALIGN(plen, 8); + pack_inode(c, p, snd_dir, 1); + } + + err = write_head(c, BASEHD, dent1, len, &lnum, &offs, sync); + if (err) + goto out_release; + if (!sync) { + struct ubifs_wbuf *wbuf = &c->jheads[BASEHD].wbuf; + + ubifs_wbuf_add_ino_nolock(wbuf, fst_dir->i_ino); + ubifs_wbuf_add_ino_nolock(wbuf, snd_dir->i_ino); + } + release_head(c, BASEHD); + + dent_key_init(c, &key, snd_dir->i_ino, &snd_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &snd_dentry->d_name); + if (err) + goto out_ro; + + offs += aligned_dlen1; + dent_key_init(c, &key, fst_dir->i_ino, &fst_dentry->d_name); + err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, &fst_dentry->d_name); + if (err) + goto out_ro; + + offs += aligned_dlen2; + + ino_key_init(c, &key, fst_dir->i_ino); + err = ubifs_tnc_add(c, &key, lnum, offs, plen); + if (err) + goto out_ro; + + if (twoparents) { + offs += ALIGN(plen, 8); + ino_key_init(c, &key, snd_dir->i_ino); + err = ubifs_tnc_add(c, &key, lnum, offs, plen); + if (err) + goto out_ro; + } + + finish_reservation(c); + + mark_inode_clean(c, ubifs_inode(fst_dir)); + if (twoparents) + mark_inode_clean(c, ubifs_inode(snd_dir)); + kfree(dent1); + return 0; + +out_release: + release_head(c, BASEHD); +out_ro: + ubifs_ro_mode(c, err); + finish_reservation(c); +out_free: + kfree(dent1); + return err; +} + /** * ubifs_jnl_rename - rename a directory entry. * @c: UBIFS file-system description object diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index ca86c9386674..096035eb29d0 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1521,6 +1521,10 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, const union ubifs_key *key, const void *buf, int len); int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode); int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode); +int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, + const struct dentry *fst_dentry, + const struct inode *snd_dir, + const struct dentry *snd_dentry, int sync); int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, const struct dentry *old_dentry, const struct inode *new_dir, -- cgit v1.2.3-59-g8ed1b From 1e039533886765f50b4e58e7c337c84523ebf7b2 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 14 Sep 2016 22:28:52 +0200 Subject: ubifs: Use move variable in ubifs_rename() ...to make the code more consistent since we use move already in other places. Signed-off-by: Richard Weinberger --- fs/ubifs/journal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 8e07f132e024..91bc76dc559e 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -1100,7 +1100,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, aligned_dlen1 = ALIGN(dlen1, 8); aligned_dlen2 = ALIGN(dlen2, 8); len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8); - if (old_dir != new_dir) + if (move) len += plen; dent = kmalloc(len, GFP_NOFS); if (!dent) @@ -1216,7 +1216,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, if (err) goto out_ro; - if (old_dir != new_dir) { + if (move) { offs += ALIGN(plen, 8); ino_key_init(c, &key, new_dir->i_ino); err = ubifs_tnc_add(c, &key, lnum, offs, plen); -- cgit v1.2.3-59-g8ed1b From 74e9c700bcb7529a0ab3f57d1125f4494ec97b3c Mon Sep 17 00:00:00 2001 From: Pascal Eberhard Date: Wed, 21 Sep 2016 23:57:30 +0200 Subject: ubifs: fix host xattr_len when changing xattr When an extended attribute is changed, xattr_len of host inode is recalculated. ui->data_len is updated before computation and result is wrong. This patch adds a temporary variable to fix computation. To reproduce the issue: ~# > a.txt ~# attr -s an-attr -V a-value a.txt ~# attr -s an-attr -V a-bit-bigger-value a.txt Now host inode xattr_len is wrong. Forcing dbg_check_filesystem() generates the following error: [ 130.620140] UBIFS (ubi0:2): background thread "ubifs_bgt0_2" started, PID 565 [ 131.470790] UBIFS error (ubi0:2 pid 564): check_inodes: inode 646 has xattr size 240, but calculated size is 256 [ 131.481697] UBIFS (ubi0:2): dump of the inode 646 sitting in LEB 29:114688 [ 131.488953] magic 0x6101831 [ 131.492876] crc 0x9fce9091 [ 131.496836] node_type 0 (inode node) [ 131.501193] group_type 1 (in node group) [ 131.505788] sqnum 9278 [ 131.509191] len 160 [ 131.512549] key (646, inode) [ 131.516688] creat_sqnum 9270 [ 131.520133] size 0 [ 131.523264] nlink 1 [ 131.526398] atime 1053025857.0 [ 131.530574] mtime 1053025857.0 [ 131.534714] ctime 1053025906.0 [ 131.538849] uid 0 [ 131.542009] gid 0 [ 131.545140] mode 33188 [ 131.548636] flags 0x1 [ 131.551977] xattr_cnt 1 [ 131.555108] xattr_size 240 [ 131.558420] xattr_names 12 [ 131.561670] compr_type 0x1 [ 131.564983] data len 0 [ 131.568125] UBIFS error (ubi0:2 pid 564): dbg_check_filesystem: file-system check failed with error -22 [ 131.578074] CPU: 0 PID: 564 Comm: mount Not tainted 4.4.12-g3639bea54a #24 [ 131.585352] Hardware name: Generic AM33XX (Flattened Device Tree) [ 131.591918] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 131.600177] [] (show_stack) from [] (dbg_check_filesystem+0x464/0x4d0) [ 131.608934] [] (dbg_check_filesystem) from [] (ubifs_mount+0x14f8/0x2130) [ 131.617991] [] (ubifs_mount) from [] (mount_fs+0x14/0x98) [ 131.625572] [] (mount_fs) from [] (vfs_kern_mount+0x4c/0xd4) [ 131.633435] [] (vfs_kern_mount) from [] (do_mount+0x988/0xb50) [ 131.641471] [] (do_mount) from [] (SyS_mount+0x74/0xa0) [ 131.648837] [] (SyS_mount) from [] (ret_fast_syscall+0x0/0x3c) [ 131.665315] UBIFS (ubi0:2): background thread "ubifs_bgt0_2" stops Signed-off-by: Pascal Eberhard Signed-off-by: Richard Weinberger --- fs/ubifs/xattr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 11a004114eba..6c2f4d41ed73 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -200,6 +200,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, struct ubifs_inode *host_ui = ubifs_inode(host); struct ubifs_inode *ui = ubifs_inode(inode); void *buf = NULL; + int old_size; struct ubifs_budget_req req = { .dirtied_ino = 2, .dirtied_ino_d = ALIGN(size, 8) + ALIGN(host_ui->data_len, 8) }; @@ -217,12 +218,13 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, kfree(ui->data); ui->data = buf; inode->i_size = ui->ui_size = size; + old_size = ui->data_len; ui->data_len = size; mutex_unlock(&ui->ui_mutex); mutex_lock(&host_ui->ui_mutex); host->i_ctime = ubifs_current_time(host); - host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len); + host_ui->xattr_size -= CALC_XATTR_BYTES(old_size); host_ui->xattr_size += CALC_XATTR_BYTES(size); /* @@ -241,7 +243,7 @@ static int change_xattr(struct ubifs_info *c, struct inode *host, out_cancel: host_ui->xattr_size -= CALC_XATTR_BYTES(size); - host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len); + host_ui->xattr_size += CALC_XATTR_BYTES(old_size); mutex_unlock(&host_ui->ui_mutex); make_bad_inode(inode); out_free: -- cgit v1.2.3-59-g8ed1b From ec037dfcc064f5f81982c78e95bab783568ae35f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 1 Oct 2016 22:53:11 +0200 Subject: UBIFS: improve function-level documentation Fix various inconsistencies in the documentation associated with various functions. In the case of fs/ubifs/lprops.c, the second parameter of ubifs_get_lp_stats was renamed from st to lst in commit 84abf972ccff ("UBIFS: add re-mount debugging checks") In the case of fs/ubifs/lpt_commit.c, the excess variables have never existed in the associated functions since the code was introduced into the kernel. The others appear to be straightforward typos. Issues detected using Coccinelle (http://coccinelle.lip6.fr/) Signed-off-by: Julia Lawall Signed-off-by: Richard Weinberger --- fs/ubifs/file.c | 2 +- fs/ubifs/gc.c | 2 +- fs/ubifs/lprops.c | 2 +- fs/ubifs/lpt_commit.c | 4 +--- fs/ubifs/replay.c | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 7bbf420d1289..4ed270beabbc 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1397,7 +1397,7 @@ int ubifs_update_time(struct inode *inode, struct timespec *time, #endif /** - * update_ctime - update mtime and ctime of an inode. + * update_mctime - update mtime and ctime of an inode. * @inode: inode to update * * This function updates mtime and ctime of the inode if it is not equivalent to diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 821b34816976..e845c64b6ce1 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c @@ -113,7 +113,7 @@ static int switch_gc_head(struct ubifs_info *c) * data_nodes_cmp - compare 2 data nodes. * @priv: UBIFS file-system description object * @a: first data node - * @a: second data node + * @b: second data node * * This function compares data nodes @a and @b. Returns %1 if @a has greater * inode or block number, and %-1 otherwise. diff --git a/fs/ubifs/lprops.c b/fs/ubifs/lprops.c index a0011aa3a779..6c3a1abd0e22 100644 --- a/fs/ubifs/lprops.c +++ b/fs/ubifs/lprops.c @@ -636,7 +636,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, /** * ubifs_get_lp_stats - get lprops statistics. * @c: UBIFS file-system description object - * @st: return statistics + * @lst: return statistics */ void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst) { diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index ce89bdc3eb02..235654c2fe89 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -34,7 +34,6 @@ static int dbg_populate_lsave(struct ubifs_info *c); /** * first_dirty_cnode - find first dirty cnode. - * @c: UBIFS file-system description object * @nnode: nnode at which to start * * This function returns the first dirty cnode or %NULL if there is not one. @@ -1623,7 +1622,6 @@ static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum, * dbg_check_ltab_lnum - check the ltab for a LPT LEB number. * @c: the UBIFS file-system description object * @lnum: LEB number where node was written - * @offs: offset where node was written * * This function returns %0 on success and a negative error code on failure. */ @@ -1870,7 +1868,7 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) } /** - * ubifs_dump_lpt_leb - dump an LPT LEB. + * dump_lpt_leb - dump an LPT LEB. * @c: UBIFS file-system description object * @lnum: LEB number to dump * diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 3ca4540130b5..fb0f44cd1e28 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -267,7 +267,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) * replay_entries_cmp - compare 2 replay entries. * @priv: UBIFS file-system description object * @a: first replay entry - * @a: second replay entry + * @b: second replay entry * * This is a comparios function for 'list_sort()' which compares 2 replay * entries @a and @b by comparing their sequence numer. Returns %1 if @a has -- cgit v1.2.3-59-g8ed1b