mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
This pull request contains fixes for JFFS2, UBI and UBIFS
JFFS2: - Fixes for various memory issues UBI: - Fix for a race condition in cdev ioctl handler UBIFS: - Fixes for O_TMPFILE and whiteout handling - Fixes for various memory issues -----BEGIN PGP SIGNATURE----- iQJKBAABCAA0FiEEdgfidid8lnn52cLTZvlZhesYu8EFAmJGFFEWHHJpY2hhcmRA c2lnbWEtc3Rhci5hdAAKCRBm+VmF6xi7wcV9D/9DCaOP5Zso6Vi2QlAM3pi8ScgG 7PXCK/l+iEMqWKv+5l4fOZuGDF9w8mf7MyBnzV6auRKEJqbOW6d3mDs2bPNORuPN LKLSOiumqxwrSqmpEFjS9Q3MsVMOWtx0aN57R3d8oCen44a5gGhYyP8iQSCLc8nQ pJszRpWnBhoWqxnc0BxDHYOYQhcQ/ckWc5mn0cEd0oN1NYmEbrAo0SpwVOjX9r1B TnTwDUzcAVK/M/hEsvHbEOChgibD+cTKfkORz6BkGB/VR5AlduF73UYUt/np4WH4 6ZTe6RXKp6gqad8pJq6T/aev2m0aM5OHxwd6JTC8qK5KSa0iXnoeungsKy0ouhYv 8V4yPRWKP9q7WEPJsiTM9KKQRiljXM+C/GOsFLKmme7zw66uu2qPdc2k99XRJzYj kO42AuXZ0Wx5Xueb+ZNmbPG3brNJ5ku3EXrzy/iAF6zS+uvmv1ZG5ZraLQ8RNqRT f8vO3NbtVnpbB6tvpPHYpjlcXkAGjumOIy9cxpl/vR5D87lRrUVSZiGu3xcW8tYm FOohVnLZFk3vgt9odU+TJ6qBUzWW5LVQx90Rr4o0sR7HDG8pFvX3cl3Z98bqCWGw wHSqy3tmUYOUaiIHvf94qJmjfY9vNlUyrnjZcoK+hFCqJz0IbrXQlrmq7o7OOvyh 2mnOksk0XcFd41gsuw== =ZU5j -----END PGP SIGNATURE----- Merge tag 'for-linus-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs Pull JFFS2, UBI and UBIFS updates from Richard Weinberger: "JFFS2: - Fixes for various memory issues UBI: - Fix for a race condition in cdev ioctl handler UBIFS: - Fixes for O_TMPFILE and whiteout handling - Fixes for various memory issues" * tag 'for-linus-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/ubifs: ubifs: rename_whiteout: correct old_dir size computing jffs2: fix memory leak in jffs2_scan_medium jffs2: fix memory leak in jffs2_do_mount_fs jffs2: fix use-after-free in jffs2_clear_xattr_subsystem fs/jffs2: fix comments mentioning i_mutex ubi: fastmap: Return error code if memory allocation fails in add_aeb() ubifs: Fix to add refcount once page is set private ubifs: Fix read out-of-bounds in ubifs_wbuf_write_nolock() ubifs: setflags: Make dirtied_ino_d 8 bytes aligned ubifs: Rectify space amount budget for mkdir/tmpfile operations ubifs: Fix 'ui->dirty' race between do_tmpfile() and writeback work ubifs: Rename whiteout atomically ubifs: Add missing iput if do_tmpfile() failed in rename whiteout ubifs: Fix wrong number of inodes locked by ui_mutex in ubifs_inode comment ubifs: Fix deadlock in concurrent rename whiteout and inode writeback ubifs: rename_whiteout: Fix double free for whiteout_ui->data ubi: Fix race condition between ctrl_cdev_ioctl and ubi_cdev_ioctl
This commit is contained in:
commit
a87a08e3bf
@ -351,9 +351,6 @@ static ssize_t dev_attribute_show(struct device *dev,
|
||||
* we still can use 'ubi->ubi_num'.
|
||||
*/
|
||||
ubi = container_of(dev, struct ubi_device, dev);
|
||||
ubi = ubi_get_device(ubi->ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
|
||||
if (attr == &dev_eraseblock_size)
|
||||
ret = sprintf(buf, "%d\n", ubi->leb_size);
|
||||
@ -382,7 +379,6 @@ static ssize_t dev_attribute_show(struct device *dev,
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
ubi_put_device(ubi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -979,9 +975,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
||||
goto out_detach;
|
||||
}
|
||||
|
||||
/* Make device "available" before it becomes accessible via sysfs */
|
||||
ubi_devices[ubi_num] = ubi;
|
||||
|
||||
err = uif_init(ubi);
|
||||
if (err)
|
||||
goto out_detach;
|
||||
@ -1026,6 +1019,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
||||
wake_up_process(ubi->bgt_thread);
|
||||
spin_unlock(&ubi->wl_lock);
|
||||
|
||||
ubi_devices[ubi_num] = ubi;
|
||||
ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
|
||||
return ubi_num;
|
||||
|
||||
@ -1034,7 +1028,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num,
|
||||
out_uif:
|
||||
uif_close(ubi);
|
||||
out_detach:
|
||||
ubi_devices[ubi_num] = NULL;
|
||||
ubi_wl_close(ubi);
|
||||
ubi_free_all_volumes(ubi);
|
||||
vfree(ubi->vtbl);
|
||||
|
@ -468,7 +468,9 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
|
||||
if (err == UBI_IO_FF_BITFLIPS)
|
||||
scrub = 1;
|
||||
|
||||
add_aeb(ai, free, pnum, ec, scrub);
|
||||
ret = add_aeb(ai, free, pnum, ec, scrub);
|
||||
if (ret)
|
||||
goto out;
|
||||
continue;
|
||||
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
|
||||
dbg_bld("Found non empty PEB:%i in pool", pnum);
|
||||
@ -638,8 +640,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
||||
if (fm_pos >= fm_size)
|
||||
goto fail_bad;
|
||||
|
||||
add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 0);
|
||||
ret = add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read EC values from used list */
|
||||
@ -649,8 +653,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
||||
if (fm_pos >= fm_size)
|
||||
goto fail_bad;
|
||||
|
||||
add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 0);
|
||||
ret = add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read EC values from scrub list */
|
||||
@ -660,8 +666,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
||||
if (fm_pos >= fm_size)
|
||||
goto fail_bad;
|
||||
|
||||
add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 1);
|
||||
ret = add_aeb(ai, &used, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 1);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* read EC values from erase list */
|
||||
@ -671,8 +679,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
|
||||
if (fm_pos >= fm_size)
|
||||
goto fail_bad;
|
||||
|
||||
add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 1);
|
||||
ret = add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum),
|
||||
be32_to_cpu(fmec->ec), 1);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count);
|
||||
|
@ -56,16 +56,11 @@ static ssize_t vol_attribute_show(struct device *dev,
|
||||
{
|
||||
int ret;
|
||||
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
|
||||
struct ubi_device *ubi;
|
||||
|
||||
ubi = ubi_get_device(vol->ubi->ubi_num);
|
||||
if (!ubi)
|
||||
return -ENODEV;
|
||||
struct ubi_device *ubi = vol->ubi;
|
||||
|
||||
spin_lock(&ubi->volumes_lock);
|
||||
if (!ubi->volumes[vol->vol_id]) {
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
ubi_put_device(ubi);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Take a reference to prevent volume removal */
|
||||
@ -103,7 +98,6 @@ static ssize_t vol_attribute_show(struct device *dev,
|
||||
vol->ref_count -= 1;
|
||||
ubi_assert(vol->ref_count >= 0);
|
||||
spin_unlock(&ubi->volumes_lock);
|
||||
ubi_put_device(ubi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -415,13 +415,15 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
|
||||
jffs2_free_ino_caches(c);
|
||||
jffs2_free_raw_node_refs(c);
|
||||
ret = -EIO;
|
||||
goto out_free;
|
||||
goto out_sum_exit;
|
||||
}
|
||||
|
||||
jffs2_calc_trigger_levels(c);
|
||||
|
||||
return 0;
|
||||
|
||||
out_sum_exit:
|
||||
jffs2_sum_exit(c);
|
||||
out_free:
|
||||
kvfree(c->blocks);
|
||||
|
||||
|
@ -603,8 +603,8 @@ int jffs2_do_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
jffs2_free_ino_caches(c);
|
||||
jffs2_free_raw_node_refs(c);
|
||||
kvfree(c->blocks);
|
||||
out_inohash:
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
out_inohash:
|
||||
kfree(c->inocache_list);
|
||||
out_wbuf:
|
||||
jffs2_flash_cleanup(c);
|
||||
|
@ -18,11 +18,11 @@
|
||||
#include <linux/mutex.h>
|
||||
|
||||
struct jffs2_inode_info {
|
||||
/* We need an internal mutex similar to inode->i_mutex.
|
||||
/* We need an internal mutex similar to inode->i_rwsem.
|
||||
Unfortunately, we can't used the existing one, because
|
||||
either the GC would deadlock, or we'd have to release it
|
||||
before letting GC proceed. Or we'd have to put ugliness
|
||||
into the GC code so it didn't attempt to obtain the i_mutex
|
||||
into the GC code so it didn't attempt to obtain the i_rwsem
|
||||
for the inode(s) which are already locked */
|
||||
struct mutex sem;
|
||||
|
||||
|
@ -136,7 +136,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
if (!s) {
|
||||
JFFS2_WARNING("Can't allocate memory for summary\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
goto out_buf;
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,13 +275,15 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
jffs2_sum_reset_collected(s);
|
||||
kfree(s);
|
||||
out_buf:
|
||||
if (buf_size)
|
||||
kfree(flashbuf);
|
||||
#ifndef __ECOS
|
||||
else
|
||||
mtd_unpoint(c->mtd, 0, c->mtd->size);
|
||||
#endif
|
||||
kfree(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
238
fs/ubifs/dir.c
238
fs/ubifs/dir.c
@ -349,20 +349,97 @@ static int ubifs_create(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, struct inode **whiteout)
|
||||
static struct inode *create_whiteout(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
umode_t mode = S_IFCHR | WHITEOUT_MODE;
|
||||
struct inode *inode;
|
||||
struct ubifs_info *c = dir->i_sb->s_fs_info;
|
||||
struct fscrypt_name nm;
|
||||
|
||||
/*
|
||||
* Create an inode('nlink = 1') for whiteout without updating journal,
|
||||
* let ubifs_jnl_rename() store it on flash to complete rename whiteout
|
||||
* atomically.
|
||||
*/
|
||||
|
||||
dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
|
||||
dentry, mode, dir->i_ino);
|
||||
|
||||
err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
inode = ubifs_new_inode(c, dir, mode);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
|
||||
ubifs_assert(c, inode->i_op == &ubifs_file_inode_operations);
|
||||
|
||||
err = ubifs_init_security(dir, inode, &dentry->d_name);
|
||||
if (err)
|
||||
goto out_inode;
|
||||
|
||||
/* The dir size is updated by do_rename. */
|
||||
insert_inode_hash(inode);
|
||||
|
||||
return inode;
|
||||
|
||||
out_inode:
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
out_free:
|
||||
fscrypt_free_filename(&nm);
|
||||
ubifs_err(c, "cannot create whiteout file, error %d", err);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* lock_2_inodes - a wrapper for locking two UBIFS inodes.
|
||||
* @inode1: first inode
|
||||
* @inode2: second inode
|
||||
*
|
||||
* 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_2_inodes(struct inode *inode1, struct inode *inode2)
|
||||
{
|
||||
mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
|
||||
mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* unlock_2_inodes - a wrapper for unlocking two UBIFS inodes.
|
||||
* @inode1: first inode
|
||||
* @inode2: second inode
|
||||
*/
|
||||
static void unlock_2_inodes(struct inode *inode1, struct inode *inode2)
|
||||
{
|
||||
mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
|
||||
mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
|
||||
}
|
||||
|
||||
static int ubifs_tmpfile(struct user_namespace *mnt_userns, 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 req = { .new_ino = 1, .new_dent = 1,
|
||||
.dirtied_ino = 1};
|
||||
struct ubifs_budget_req ino_req = { .dirtied_ino = 1 };
|
||||
struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir);
|
||||
struct ubifs_inode *ui;
|
||||
int err, instantiated = 0;
|
||||
struct fscrypt_name nm;
|
||||
|
||||
/*
|
||||
* Budget request settings: new dirty inode, new direntry,
|
||||
* budget for dirtied inode will be released via writeback.
|
||||
* Budget request settings: new inode, new direntry, changing the
|
||||
* parent directory inode.
|
||||
* Allocate budget separately for new dirtied inode, the budget will
|
||||
* be released via writeback.
|
||||
*/
|
||||
|
||||
dbg_gen("dent '%pd', mode %#hx in dir ino %lu",
|
||||
@ -392,42 +469,30 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
ui = ubifs_inode(inode);
|
||||
|
||||
if (whiteout) {
|
||||
init_special_inode(inode, inode->i_mode, WHITEOUT_DEV);
|
||||
ubifs_assert(c, 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);
|
||||
|
||||
if (whiteout) {
|
||||
mark_inode_dirty(inode);
|
||||
drop_nlink(inode);
|
||||
*whiteout = inode;
|
||||
} else {
|
||||
d_tmpfile(dentry, inode);
|
||||
}
|
||||
d_tmpfile(dentry, inode);
|
||||
ubifs_assert(c, ui->dirty);
|
||||
|
||||
instantiated = 1;
|
||||
mutex_unlock(&ui->ui_mutex);
|
||||
|
||||
mutex_lock(&dir_ui->ui_mutex);
|
||||
lock_2_inodes(dir, inode);
|
||||
err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
mutex_unlock(&dir_ui->ui_mutex);
|
||||
unlock_2_inodes(dir, inode);
|
||||
|
||||
ubifs_release_budget(c, &req);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel:
|
||||
mutex_unlock(&dir_ui->ui_mutex);
|
||||
unlock_2_inodes(dir, inode);
|
||||
out_inode:
|
||||
make_bad_inode(inode);
|
||||
if (!instantiated)
|
||||
@ -441,12 +506,6 @@ static int do_tmpfile(struct inode *dir, struct dentry *dentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ubifs_tmpfile(struct user_namespace *mnt_userns, 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
|
||||
@ -660,32 +719,6 @@ static int ubifs_dir_release(struct inode *dir, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lock_2_inodes - a wrapper for locking two UBIFS inodes.
|
||||
* @inode1: first inode
|
||||
* @inode2: second inode
|
||||
*
|
||||
* 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_2_inodes(struct inode *inode1, struct inode *inode2)
|
||||
{
|
||||
mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1);
|
||||
mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2);
|
||||
}
|
||||
|
||||
/**
|
||||
* unlock_2_inodes - a wrapper for unlocking two UBIFS inodes.
|
||||
* @inode1: first inode
|
||||
* @inode2: second inode
|
||||
*/
|
||||
static void unlock_2_inodes(struct inode *inode1, struct inode *inode2)
|
||||
{
|
||||
mutex_unlock(&ubifs_inode(inode2)->ui_mutex);
|
||||
mutex_unlock(&ubifs_inode(inode1)->ui_mutex);
|
||||
}
|
||||
|
||||
static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
@ -949,7 +982,8 @@ static int ubifs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
|
||||
struct ubifs_inode *dir_ui = ubifs_inode(dir);
|
||||
struct ubifs_info *c = dir->i_sb->s_fs_info;
|
||||
int err, sz_change;
|
||||
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 };
|
||||
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
|
||||
.dirtied_ino = 1};
|
||||
struct fscrypt_name nm;
|
||||
|
||||
/*
|
||||
@ -1264,17 +1298,19 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
.dirtied_ino = 3 };
|
||||
struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
|
||||
.dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
|
||||
struct ubifs_budget_req wht_req;
|
||||
struct timespec64 time;
|
||||
unsigned int saved_nlink;
|
||||
struct fscrypt_name old_nm, new_nm;
|
||||
|
||||
/*
|
||||
* Budget request settings: deletion direntry, new direntry, removing
|
||||
* the old inode, and changing old and new parent directory inodes.
|
||||
* Budget request settings:
|
||||
* req: deletion direntry, new direntry, removing the old inode,
|
||||
* and changing old and new parent directory inodes.
|
||||
*
|
||||
* However, this operation also marks the target inode as dirty and
|
||||
* does not write it, so we allocate budget for the target inode
|
||||
* separately.
|
||||
* wht_req: new whiteout inode for RENAME_WHITEOUT.
|
||||
*
|
||||
* ino_req: marks the target inode as dirty and does not write it.
|
||||
*/
|
||||
|
||||
dbg_gen("dent '%pd' ino %lu in dir ino %lu to dent '%pd' in dir ino %lu flags 0x%x",
|
||||
@ -1331,20 +1367,44 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout);
|
||||
if (err) {
|
||||
/*
|
||||
* The whiteout inode without dentry is pinned in memory,
|
||||
* umount won't happen during rename process because we
|
||||
* got parent dentry.
|
||||
*/
|
||||
whiteout = create_whiteout(old_dir, old_dentry);
|
||||
if (IS_ERR(whiteout)) {
|
||||
err = PTR_ERR(whiteout);
|
||||
kfree(dev);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
spin_lock(&whiteout->i_lock);
|
||||
whiteout->i_state |= I_LINKABLE;
|
||||
spin_unlock(&whiteout->i_lock);
|
||||
|
||||
whiteout_ui = ubifs_inode(whiteout);
|
||||
whiteout_ui->data = dev;
|
||||
whiteout_ui->data_len = ubifs_encode_dev(dev, MKDEV(0, 0));
|
||||
ubifs_assert(c, !whiteout_ui->dirty);
|
||||
|
||||
memset(&wht_req, 0, sizeof(struct ubifs_budget_req));
|
||||
wht_req.new_ino = 1;
|
||||
wht_req.new_ino_d = ALIGN(whiteout_ui->data_len, 8);
|
||||
/*
|
||||
* To avoid deadlock between space budget (holds ui_mutex and
|
||||
* waits wb work) and writeback work(waits ui_mutex), do space
|
||||
* budget before ubifs inodes locked.
|
||||
*/
|
||||
err = ubifs_budget_space(c, &wht_req);
|
||||
if (err) {
|
||||
/*
|
||||
* Whiteout inode can not be written on flash by
|
||||
* ubifs_jnl_write_inode(), because it's neither
|
||||
* dirty nor zero-nlink.
|
||||
*/
|
||||
iput(whiteout);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
/* Add the old_dentry size to the old_dir size. */
|
||||
old_sz -= CALC_DENT_SIZE(fname_len(&old_nm));
|
||||
}
|
||||
|
||||
lock_4_inodes(old_dir, new_dir, new_inode, whiteout);
|
||||
@ -1416,29 +1476,11 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
sync = IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir);
|
||||
if (unlink && IS_SYNC(new_inode))
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
kfree(whiteout_ui->data);
|
||||
whiteout_ui->data_len = 0;
|
||||
iput(whiteout);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
inc_nlink(whiteout);
|
||||
mark_inode_dirty(whiteout);
|
||||
|
||||
spin_lock(&whiteout->i_lock);
|
||||
whiteout->i_state &= ~I_LINKABLE;
|
||||
spin_unlock(&whiteout->i_lock);
|
||||
|
||||
iput(whiteout);
|
||||
/*
|
||||
* S_SYNC flag of whiteout inherits from the old_dir, and we
|
||||
* have already checked the old dir inode. So there is no need
|
||||
* to check whiteout.
|
||||
*/
|
||||
}
|
||||
|
||||
err = ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir,
|
||||
@ -1449,6 +1491,11 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
|
||||
ubifs_release_budget(c, &req);
|
||||
|
||||
if (whiteout) {
|
||||
ubifs_release_budget(c, &wht_req);
|
||||
iput(whiteout);
|
||||
}
|
||||
|
||||
mutex_lock(&old_inode_ui->ui_mutex);
|
||||
release = old_inode_ui->dirty;
|
||||
mark_inode_dirty_sync(old_inode);
|
||||
@ -1457,11 +1504,16 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
if (release)
|
||||
ubifs_release_budget(c, &ino_req);
|
||||
if (IS_SYNC(old_inode))
|
||||
err = old_inode->i_sb->s_op->write_inode(old_inode, NULL);
|
||||
/*
|
||||
* Rename finished here. Although old inode cannot be updated
|
||||
* on flash, old ctime is not a big problem, don't return err
|
||||
* code to userspace.
|
||||
*/
|
||||
old_inode->i_sb->s_op->write_inode(old_inode, NULL);
|
||||
|
||||
fscrypt_free_filename(&old_nm);
|
||||
fscrypt_free_filename(&new_nm);
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
out_cancel:
|
||||
if (unlink) {
|
||||
@ -1482,11 +1534,11 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
inc_nlink(old_dir);
|
||||
}
|
||||
}
|
||||
unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
|
||||
if (whiteout) {
|
||||
drop_nlink(whiteout);
|
||||
ubifs_release_budget(c, &wht_req);
|
||||
iput(whiteout);
|
||||
}
|
||||
unlock_4_inodes(old_dir, new_dir, new_inode, whiteout);
|
||||
out_release:
|
||||
ubifs_release_budget(c, &ino_req);
|
||||
ubifs_release_budget(c, &req);
|
||||
|
@ -570,7 +570,7 @@ static int ubifs_write_end(struct file *file, struct address_space *mapping,
|
||||
}
|
||||
|
||||
if (!PagePrivate(page)) {
|
||||
SetPagePrivate(page);
|
||||
attach_page_private(page, (void *)1);
|
||||
atomic_long_inc(&c->dirty_pg_cnt);
|
||||
__set_page_dirty_nobuffers(page);
|
||||
}
|
||||
@ -947,7 +947,7 @@ static int do_writepage(struct page *page, int len)
|
||||
release_existing_page_budget(c);
|
||||
|
||||
atomic_long_dec(&c->dirty_pg_cnt);
|
||||
ClearPagePrivate(page);
|
||||
detach_page_private(page);
|
||||
ClearPageChecked(page);
|
||||
|
||||
kunmap(page);
|
||||
@ -1304,7 +1304,7 @@ static void ubifs_invalidate_folio(struct folio *folio, size_t offset,
|
||||
release_existing_page_budget(c);
|
||||
|
||||
atomic_long_dec(&c->dirty_pg_cnt);
|
||||
folio_clear_private(folio);
|
||||
folio_detach_private(folio);
|
||||
folio_clear_checked(folio);
|
||||
}
|
||||
|
||||
@ -1471,8 +1471,8 @@ static int ubifs_migrate_page(struct address_space *mapping,
|
||||
return rc;
|
||||
|
||||
if (PagePrivate(page)) {
|
||||
ClearPagePrivate(page);
|
||||
SetPagePrivate(newpage);
|
||||
detach_page_private(page);
|
||||
attach_page_private(newpage, (void *)1);
|
||||
}
|
||||
|
||||
if (mode != MIGRATE_SYNC_NO_COPY)
|
||||
@ -1496,7 +1496,7 @@ static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags)
|
||||
return 0;
|
||||
ubifs_assert(c, PagePrivate(page));
|
||||
ubifs_assert(c, 0);
|
||||
ClearPagePrivate(page);
|
||||
detach_page_private(page);
|
||||
ClearPageChecked(page);
|
||||
return 1;
|
||||
}
|
||||
@ -1567,7 +1567,7 @@ static vm_fault_t ubifs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
else {
|
||||
if (!PageChecked(page))
|
||||
ubifs_convert_page_budget(c);
|
||||
SetPagePrivate(page);
|
||||
attach_page_private(page, (void *)1);
|
||||
atomic_long_inc(&c->dirty_pg_cnt);
|
||||
__set_page_dirty_nobuffers(page);
|
||||
}
|
||||
|
@ -854,16 +854,42 @@ int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len)
|
||||
*/
|
||||
n = aligned_len >> c->max_write_shift;
|
||||
if (n) {
|
||||
n <<= c->max_write_shift;
|
||||
int m = n - 1;
|
||||
|
||||
dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum,
|
||||
wbuf->offs);
|
||||
err = ubifs_leb_write(c, wbuf->lnum, buf + written,
|
||||
wbuf->offs, n);
|
||||
|
||||
if (m) {
|
||||
/* '(n-1)<<c->max_write_shift < len' is always true. */
|
||||
m <<= c->max_write_shift;
|
||||
err = ubifs_leb_write(c, wbuf->lnum, buf + written,
|
||||
wbuf->offs, m);
|
||||
if (err)
|
||||
goto out;
|
||||
wbuf->offs += m;
|
||||
aligned_len -= m;
|
||||
len -= m;
|
||||
written += m;
|
||||
}
|
||||
|
||||
/*
|
||||
* The non-written len of buf may be less than 'n' because
|
||||
* parameter 'len' is not 8 bytes aligned, so here we read
|
||||
* min(len, n) bytes from buf.
|
||||
*/
|
||||
n = 1 << c->max_write_shift;
|
||||
memcpy(wbuf->buf, buf + written, min(len, n));
|
||||
if (n > len) {
|
||||
ubifs_assert(c, n - len < 8);
|
||||
ubifs_pad(c, wbuf->buf + len, n - len);
|
||||
}
|
||||
|
||||
err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, n);
|
||||
if (err)
|
||||
goto out;
|
||||
wbuf->offs += n;
|
||||
aligned_len -= n;
|
||||
len -= n;
|
||||
len -= min(len, n);
|
||||
written += n;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ static int setflags(struct inode *inode, int flags)
|
||||
struct ubifs_inode *ui = ubifs_inode(inode);
|
||||
struct ubifs_info *c = inode->i_sb->s_fs_info;
|
||||
struct ubifs_budget_req req = { .dirtied_ino = 1,
|
||||
.dirtied_ino_d = ui->data_len };
|
||||
.dirtied_ino_d = ALIGN(ui->data_len, 8) };
|
||||
|
||||
err = ubifs_budget_space(c, &req);
|
||||
if (err)
|
||||
|
@ -1207,9 +1207,9 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir,
|
||||
* @sync: non-zero if the write-buffer has to be synchronized
|
||||
*
|
||||
* This function implements the re-name operation which may involve writing up
|
||||
* 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.
|
||||
* to 4 inodes(new inode, whiteout inode, old and new parent directory 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 inode *old_inode,
|
||||
@ -1222,14 +1222,15 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
void *p;
|
||||
union ubifs_key key;
|
||||
struct ubifs_dent_node *dent, *dent2;
|
||||
int err, dlen1, dlen2, ilen, lnum, offs, len, orphan_added = 0;
|
||||
int err, dlen1, dlen2, ilen, wlen, lnum, offs, len, orphan_added = 0;
|
||||
int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ;
|
||||
int last_reference = !!(new_inode && new_inode->i_nlink == 0);
|
||||
int move = (old_dir != new_dir);
|
||||
struct ubifs_inode *new_ui;
|
||||
struct ubifs_inode *new_ui, *whiteout_ui;
|
||||
u8 hash_old_dir[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_new_dir[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_new_inode[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_whiteout_inode[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dent1[UBIFS_HASH_ARR_SZ];
|
||||
u8 hash_dent2[UBIFS_HASH_ARR_SZ];
|
||||
|
||||
@ -1249,9 +1250,20 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
} else
|
||||
ilen = 0;
|
||||
|
||||
if (whiteout) {
|
||||
whiteout_ui = ubifs_inode(whiteout);
|
||||
ubifs_assert(c, mutex_is_locked(&whiteout_ui->ui_mutex));
|
||||
ubifs_assert(c, whiteout->i_nlink == 1);
|
||||
ubifs_assert(c, !whiteout_ui->dirty);
|
||||
wlen = UBIFS_INO_NODE_SZ;
|
||||
wlen += whiteout_ui->data_len;
|
||||
} else
|
||||
wlen = 0;
|
||||
|
||||
aligned_dlen1 = ALIGN(dlen1, 8);
|
||||
aligned_dlen2 = ALIGN(dlen2, 8);
|
||||
len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8);
|
||||
len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) +
|
||||
ALIGN(wlen, 8) + ALIGN(plen, 8);
|
||||
if (move)
|
||||
len += plen;
|
||||
|
||||
@ -1313,6 +1325,15 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
p += ALIGN(ilen, 8);
|
||||
}
|
||||
|
||||
if (whiteout) {
|
||||
pack_inode(c, p, whiteout, 0);
|
||||
err = ubifs_node_calc_hash(c, p, hash_whiteout_inode);
|
||||
if (err)
|
||||
goto out_release;
|
||||
|
||||
p += ALIGN(wlen, 8);
|
||||
}
|
||||
|
||||
if (!move) {
|
||||
pack_inode(c, p, old_dir, 1);
|
||||
err = ubifs_node_calc_hash(c, p, hash_old_dir);
|
||||
@ -1352,6 +1373,9 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
if (new_inode)
|
||||
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
|
||||
new_inode->i_ino);
|
||||
if (whiteout)
|
||||
ubifs_wbuf_add_ino_nolock(&c->jheads[BASEHD].wbuf,
|
||||
whiteout->i_ino);
|
||||
}
|
||||
release_head(c, BASEHD);
|
||||
|
||||
@ -1368,8 +1392,6 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen2, hash_dent2, old_nm);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
|
||||
ubifs_delete_orphan(c, whiteout->i_ino);
|
||||
} else {
|
||||
err = ubifs_add_dirt(c, lnum, dlen2);
|
||||
if (err)
|
||||
@ -1390,6 +1412,15 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
offs += ALIGN(ilen, 8);
|
||||
}
|
||||
|
||||
if (whiteout) {
|
||||
ino_key_init(c, &key, whiteout->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, wlen,
|
||||
hash_whiteout_inode);
|
||||
if (err)
|
||||
goto out_ro;
|
||||
offs += ALIGN(wlen, 8);
|
||||
}
|
||||
|
||||
ino_key_init(c, &key, old_dir->i_ino);
|
||||
err = ubifs_tnc_add(c, &key, lnum, offs, plen, hash_old_dir);
|
||||
if (err)
|
||||
@ -1410,6 +1441,11 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
|
||||
new_ui->synced_i_size = new_ui->ui_size;
|
||||
spin_unlock(&new_ui->ui_lock);
|
||||
}
|
||||
/*
|
||||
* No need to mark whiteout inode clean.
|
||||
* Whiteout doesn't have non-zero size, no need to update
|
||||
* synced_i_size for whiteout_ui.
|
||||
*/
|
||||
mark_inode_clean(c, ubifs_inode(old_dir));
|
||||
if (move)
|
||||
mark_inode_clean(c, ubifs_inode(new_dir));
|
||||
|
@ -381,7 +381,7 @@ struct ubifs_gced_idx_leb {
|
||||
* @ui_mutex exists for two main reasons. At first it prevents inodes from
|
||||
* being written back while UBIFS changing them, being in the middle of an VFS
|
||||
* operation. This way UBIFS makes sure the inode fields are consistent. For
|
||||
* example, in 'ubifs_rename()' we change 3 inodes simultaneously, and
|
||||
* example, in 'ubifs_rename()' we change 4 inodes simultaneously, and
|
||||
* write-back must not write any of them before we have finished.
|
||||
*
|
||||
* The second reason is budgeting - UBIFS has to budget all operations. If an
|
||||
|
Loading…
Reference in New Issue
Block a user