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:
Linus Torvalds 2022-03-31 16:09:41 -07:00
commit a87a08e3bf
13 changed files with 259 additions and 144 deletions

View file

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

View file

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

View file

@ -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;
}

View file

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

View file

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

View file

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

View file

@ -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;
}

View file

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

View file

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

View file

@ -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;
}

View file

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

View file

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

View file

@ -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