Merge branch 'mm-nonmm-stable' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

This commit is contained in:
Stephen Rothwell 2022-06-28 16:54:02 +10:00
commit 8f850ad6cf
33 changed files with 569 additions and 235 deletions

View File

@ -21092,6 +21092,7 @@ M: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
S: Maintained
F: Documentation/filesystems/vfat.rst
F: fs/fat/
F: tools/testing/selftests/filesystems/fat/
VFIO DRIVER
M: Alex Williamson <alex.williamson@redhat.com>

View File

@ -33,24 +33,24 @@ extern void ia64_xchg_called_with_bad_pointer(void);
\
switch (size) { \
case 1: \
__xchg_result = ia64_xchg1((__u8 *)ptr, x); \
__xchg_result = ia64_xchg1((__u8 __force *)ptr, x); \
break; \
\
case 2: \
__xchg_result = ia64_xchg2((__u16 *)ptr, x); \
__xchg_result = ia64_xchg2((__u16 __force *)ptr, x); \
break; \
\
case 4: \
__xchg_result = ia64_xchg4((__u32 *)ptr, x); \
__xchg_result = ia64_xchg4((__u32 __force *)ptr, x); \
break; \
\
case 8: \
__xchg_result = ia64_xchg8((__u64 *)ptr, x); \
__xchg_result = ia64_xchg8((__u64 __force *)ptr, x); \
break; \
default: \
ia64_xchg_called_with_bad_pointer(); \
} \
__xchg_result; \
(__typeof__ (*(ptr)) __force) __xchg_result; \
})
#ifndef __KERNEL__
@ -76,42 +76,42 @@ extern long ia64_cmpxchg_called_with_bad_pointer(void);
\
switch (size) { \
case 1: \
_o_ = (__u8) (long) (old); \
_o_ = (__u8) (long __force) (old); \
break; \
case 2: \
_o_ = (__u16) (long) (old); \
_o_ = (__u16) (long __force) (old); \
break; \
case 4: \
_o_ = (__u32) (long) (old); \
_o_ = (__u32) (long __force) (old); \
break; \
case 8: \
_o_ = (__u64) (long) (old); \
_o_ = (__u64) (long __force) (old); \
break; \
default: \
break; \
} \
switch (size) { \
case 1: \
_r_ = ia64_cmpxchg1_##sem((__u8 *) ptr, new, _o_); \
_r_ = ia64_cmpxchg1_##sem((__u8 __force *) ptr, new, _o_); \
break; \
\
case 2: \
_r_ = ia64_cmpxchg2_##sem((__u16 *) ptr, new, _o_); \
_r_ = ia64_cmpxchg2_##sem((__u16 __force *) ptr, new, _o_); \
break; \
\
case 4: \
_r_ = ia64_cmpxchg4_##sem((__u32 *) ptr, new, _o_); \
_r_ = ia64_cmpxchg4_##sem((__u32 __force *) ptr, new, _o_); \
break; \
\
case 8: \
_r_ = ia64_cmpxchg8_##sem((__u64 *) ptr, new, _o_); \
_r_ = ia64_cmpxchg8_##sem((__u64 __force *) ptr, new, _o_); \
break; \
\
default: \
_r_ = ia64_cmpxchg_called_with_bad_pointer(); \
break; \
} \
(__typeof__(old)) _r_; \
(__typeof__(old) __force) _r_; \
})
#define cmpxchg_acq(ptr, o, n) \

View File

@ -889,22 +889,57 @@ static int vfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
return err;
}
static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
static int vfat_get_dotdot_de(struct inode *inode, struct buffer_head **bh,
struct msdos_dir_entry **de)
{
if (S_ISDIR(inode->i_mode)) {
if (fat_get_dotdot_entry(inode, bh, de))
return -EIO;
}
return 0;
}
static int vfat_sync_ipos(struct inode *dir, struct inode *inode)
{
if (IS_DIRSYNC(dir))
return fat_sync_inode(inode);
mark_inode_dirty(inode);
return 0;
}
static int vfat_update_dotdot_de(struct inode *dir, struct inode *inode,
struct buffer_head *dotdot_bh,
struct msdos_dir_entry *dotdot_de)
{
fat_set_start(dotdot_de, MSDOS_I(dir)->i_logstart);
mark_buffer_dirty_inode(dotdot_bh, inode);
if (IS_DIRSYNC(dir))
return sync_dirty_buffer(dotdot_bh);
return 0;
}
static void vfat_update_dir_metadata(struct inode *dir, struct timespec64 *ts)
{
inode_inc_iversion(dir);
fat_truncate_time(dir, ts, S_CTIME | S_MTIME);
if (IS_DIRSYNC(dir))
(void)fat_sync_inode(dir);
else
mark_inode_dirty(dir);
}
static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
struct buffer_head *dotdot_bh;
struct msdos_dir_entry *dotdot_de;
struct msdos_dir_entry *dotdot_de = NULL;
struct inode *old_inode, *new_inode;
struct fat_slot_info old_sinfo, sinfo;
struct timespec64 ts;
loff_t new_i_pos;
int err, is_dir, update_dotdot, corrupt = 0;
int err, is_dir, corrupt = 0;
struct super_block *sb = old_dir->i_sb;
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
old_inode = d_inode(old_dentry);
new_inode = d_inode(new_dentry);
@ -913,15 +948,13 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
if (err)
goto out;
is_dir = S_ISDIR(old_inode->i_mode);
update_dotdot = (is_dir && old_dir != new_dir);
if (update_dotdot) {
if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
err = -EIO;
if (old_dir != new_dir) {
err = vfat_get_dotdot_de(old_inode, &dotdot_bh, &dotdot_de);
if (err)
goto out;
}
}
is_dir = S_ISDIR(old_inode->i_mode);
ts = current_time(old_dir);
if (new_inode) {
if (is_dir) {
@ -942,21 +975,15 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
fat_detach(old_inode);
fat_attach(old_inode, new_i_pos);
if (IS_DIRSYNC(new_dir)) {
err = fat_sync_inode(old_inode);
if (err)
goto error_inode;
} else
mark_inode_dirty(old_inode);
err = vfat_sync_ipos(new_dir, old_inode);
if (err)
goto error_inode;
if (update_dotdot) {
fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart);
mark_buffer_dirty_inode(dotdot_bh, old_inode);
if (IS_DIRSYNC(new_dir)) {
err = sync_dirty_buffer(dotdot_bh);
if (err)
goto error_dotdot;
}
if (dotdot_de) {
err = vfat_update_dotdot_de(new_dir, old_inode, dotdot_bh,
dotdot_de);
if (err)
goto error_dotdot;
drop_nlink(old_dir);
if (!new_inode)
inc_nlink(new_dir);
@ -966,12 +993,7 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
old_sinfo.bh = NULL;
if (err)
goto error_dotdot;
inode_inc_iversion(old_dir);
fat_truncate_time(old_dir, &ts, S_CTIME|S_MTIME);
if (IS_DIRSYNC(old_dir))
(void)fat_sync_inode(old_dir);
else
mark_inode_dirty(old_dir);
vfat_update_dir_metadata(old_dir, &ts);
if (new_inode) {
drop_nlink(new_inode);
@ -991,10 +1013,9 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
/* data cluster is shared, serious corruption */
corrupt = 1;
if (update_dotdot) {
fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart);
mark_buffer_dirty_inode(dotdot_bh, old_inode);
corrupt |= sync_dirty_buffer(dotdot_bh);
if (dotdot_de) {
corrupt |= vfat_update_dotdot_de(old_dir, old_inode, dotdot_bh,
dotdot_de);
}
error_inode:
fat_detach(old_inode);
@ -1021,13 +1042,145 @@ static int vfat_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
goto out;
}
static void vfat_exchange_ipos(struct inode *old_inode, struct inode *new_inode,
loff_t old_i_pos, loff_t new_i_pos)
{
fat_detach(old_inode);
fat_detach(new_inode);
fat_attach(old_inode, new_i_pos);
fat_attach(new_inode, old_i_pos);
}
static void vfat_move_nlink(struct inode *src, struct inode *dst)
{
drop_nlink(src);
inc_nlink(dst);
}
static int vfat_rename_exchange(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
struct buffer_head *old_dotdot_bh = NULL, *new_dotdot_bh = NULL;
struct msdos_dir_entry *old_dotdot_de = NULL, *new_dotdot_de = NULL;
struct inode *old_inode, *new_inode;
struct timespec64 ts = current_time(old_dir);
loff_t old_i_pos, new_i_pos;
int err, corrupt = 0;
struct super_block *sb = old_dir->i_sb;
old_inode = d_inode(old_dentry);
new_inode = d_inode(new_dentry);
/* Acquire super block lock for the operation to be atomic */
mutex_lock(&MSDOS_SB(sb)->s_lock);
/* if directories are not the same, get ".." info to update */
if (old_dir != new_dir) {
err = vfat_get_dotdot_de(old_inode, &old_dotdot_bh,
&old_dotdot_de);
if (err)
goto out;
err = vfat_get_dotdot_de(new_inode, &new_dotdot_bh,
&new_dotdot_de);
if (err)
goto out;
}
old_i_pos = MSDOS_I(old_inode)->i_pos;
new_i_pos = MSDOS_I(new_inode)->i_pos;
vfat_exchange_ipos(old_inode, new_inode, old_i_pos, new_i_pos);
err = vfat_sync_ipos(old_dir, new_inode);
if (err)
goto error_exchange;
err = vfat_sync_ipos(new_dir, old_inode);
if (err)
goto error_exchange;
/* update ".." directory entry info */
if (old_dotdot_de) {
err = vfat_update_dotdot_de(new_dir, old_inode, old_dotdot_bh,
old_dotdot_de);
if (err)
goto error_old_dotdot;
}
if (new_dotdot_de) {
err = vfat_update_dotdot_de(old_dir, new_inode, new_dotdot_bh,
new_dotdot_de);
if (err)
goto error_new_dotdot;
}
/* if cross directory and only one is a directory, adjust nlink */
if (!old_dotdot_de != !new_dotdot_de) {
if (old_dotdot_de)
vfat_move_nlink(old_dir, new_dir);
else
vfat_move_nlink(new_dir, old_dir);
}
vfat_update_dir_metadata(old_dir, &ts);
/* if directories are not the same, update new_dir as well */
if (old_dir != new_dir)
vfat_update_dir_metadata(new_dir, &ts);
out:
brelse(old_dotdot_bh);
brelse(new_dotdot_bh);
mutex_unlock(&MSDOS_SB(sb)->s_lock);
return err;
error_new_dotdot:
if (new_dotdot_de) {
corrupt |= vfat_update_dotdot_de(new_dir, new_inode,
new_dotdot_bh, new_dotdot_de);
}
error_old_dotdot:
if (old_dotdot_de) {
corrupt |= vfat_update_dotdot_de(old_dir, old_inode,
old_dotdot_bh, old_dotdot_de);
}
error_exchange:
vfat_exchange_ipos(old_inode, new_inode, new_i_pos, old_i_pos);
corrupt |= vfat_sync_ipos(new_dir, new_inode);
corrupt |= vfat_sync_ipos(old_dir, old_inode);
if (corrupt < 0) {
fat_fs_error(new_dir->i_sb,
"%s: Filesystem corrupted (i_pos %lld, %lld)",
__func__, old_i_pos, new_i_pos);
}
goto out;
}
static int vfat_rename2(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
{
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
return -EINVAL;
if (flags & RENAME_EXCHANGE) {
return vfat_rename_exchange(old_dir, old_dentry,
new_dir, new_dentry);
}
/* VFS already handled RENAME_NOREPLACE, handle it as a normal rename */
return vfat_rename(old_dir, old_dentry, new_dir, new_dentry);
}
static const struct inode_operations vfat_dir_inode_operations = {
.create = vfat_create,
.lookup = vfat_lookup,
.unlink = vfat_unlink,
.mkdir = vfat_mkdir,
.rmdir = vfat_rmdir,
.rename = vfat_rename,
.rename = vfat_rename2,
.setattr = fat_setattr,
.getattr = fat_getattr,
.update_time = fat_update_time,

View File

@ -29,15 +29,15 @@
* change between calls to kernel_read_file().
*
* Returns number of bytes read (no single read will be bigger
* than INT_MAX), or negative on error.
* than SSIZE_MAX), or negative on error.
*
*/
int kernel_read_file(struct file *file, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
ssize_t kernel_read_file(struct file *file, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
loff_t i_size, pos;
size_t copied;
ssize_t copied;
void *allocated = NULL;
bool whole_file;
int ret;
@ -58,7 +58,7 @@ int kernel_read_file(struct file *file, loff_t offset, void **buf,
goto out;
}
/* The file is too big for sane activities. */
if (i_size > INT_MAX) {
if (i_size > SSIZE_MAX) {
ret = -EFBIG;
goto out;
}
@ -124,12 +124,12 @@ int kernel_read_file(struct file *file, loff_t offset, void **buf,
}
EXPORT_SYMBOL_GPL(kernel_read_file);
int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
ssize_t kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
struct file *file;
int ret;
ssize_t ret;
if (!path || !*path)
return -EINVAL;
@ -144,14 +144,14 @@ int kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
}
EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
int kernel_read_file_from_path_initns(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id)
ssize_t kernel_read_file_from_path_initns(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id)
{
struct file *file;
struct path root;
int ret;
ssize_t ret;
if (!path || !*path)
return -EINVAL;
@ -171,12 +171,12 @@ int kernel_read_file_from_path_initns(const char *path, loff_t offset,
}
EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
ssize_t kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
size_t buf_size, size_t *file_size,
enum kernel_read_file_id id)
{
struct fd f = fdget(fd);
int ret = -EBADF;
ssize_t ret = -EBADF;
if (!f.file || !(f.file->f_mode & FMODE_READ))
goto out;

View File

@ -296,17 +296,25 @@ static void dlmfs_evict_inode(struct inode *inode)
{
int status;
struct dlmfs_inode_private *ip;
struct user_lock_res *lockres;
int teardown;
clear_inode(inode);
mlog(0, "inode %lu\n", inode->i_ino);
ip = DLMFS_I(inode);
lockres = &ip->ip_lockres;
if (S_ISREG(inode->i_mode)) {
status = user_dlm_destroy_lock(&ip->ip_lockres);
if (status < 0)
mlog_errno(status);
spin_lock(&lockres->l_lock);
teardown = !!(lockres->l_flags & USER_LOCK_IN_TEARDOWN);
spin_unlock(&lockres->l_lock);
if (!teardown) {
status = user_dlm_destroy_lock(lockres);
if (status < 0)
mlog_errno(status);
}
iput(ip->ip_parent);
goto clear_fields;
}

View File

@ -34,12 +34,15 @@ static int copy_bio_to_actor(struct bio *bio,
struct squashfs_page_actor *actor,
int offset, int req_length)
{
void *actor_addr = squashfs_first_page(actor);
void *actor_addr;
struct bvec_iter_all iter_all = {};
struct bio_vec *bvec = bvec_init_iter_all(&iter_all);
int copied_bytes = 0;
int actor_offset = 0;
squashfs_actor_nobuff(actor);
actor_addr = squashfs_first_page(actor);
if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all)))
return 0;
@ -49,8 +52,9 @@ static int copy_bio_to_actor(struct bio *bio,
bytes_to_copy = min_t(int, bytes_to_copy,
req_length - copied_bytes);
memcpy(actor_addr + actor_offset, bvec_virt(bvec) + offset,
bytes_to_copy);
if (!IS_ERR(actor_addr))
memcpy(actor_addr + actor_offset, bvec_virt(bvec) +
offset, bytes_to_copy);
actor_offset += bytes_to_copy;
copied_bytes += bytes_to_copy;

View File

@ -20,6 +20,7 @@ struct squashfs_decompressor {
struct bio *, int, int, struct squashfs_page_actor *);
int id;
char *name;
int alloc_buffer;
int supported;
};

View File

@ -18,9 +18,6 @@
#include "squashfs.h"
#include "page_actor.h"
static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
int pages, struct page **page, int bytes);
/* Read separately compressed datablock directly into page cache */
int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
int expected)
@ -33,7 +30,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
int start_index = target_page->index & ~mask;
int end_index = start_index | mask;
int i, n, pages, missing_pages, bytes, res = -ENOMEM;
int i, n, pages, bytes, res = -ENOMEM;
struct page **page;
struct squashfs_page_actor *actor;
void *pageaddr;
@ -47,50 +44,38 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
if (page == NULL)
return res;
/*
* Create a "page actor" which will kmap and kunmap the
* page cache pages appropriately within the decompressor
*/
actor = squashfs_page_actor_init_special(page, pages, 0);
if (actor == NULL)
goto out;
/* Try to grab all the pages covered by the Squashfs block */
for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
for (i = 0, n = start_index; n <= end_index; n++) {
page[i] = (n == target_page->index) ? target_page :
grab_cache_page_nowait(target_page->mapping, n);
if (page[i] == NULL) {
missing_pages++;
if (page[i] == NULL)
continue;
}
if (PageUptodate(page[i])) {
unlock_page(page[i]);
put_page(page[i]);
page[i] = NULL;
missing_pages++;
continue;
}
i++;
}
if (missing_pages) {
/*
* Couldn't get one or more pages, this page has either
* been VM reclaimed, but others are still in the page cache
* and uptodate, or we're racing with another thread in
* squashfs_readpage also trying to grab them. Fall back to
* using an intermediate buffer.
*/
res = squashfs_read_cache(target_page, block, bsize, pages,
page, expected);
if (res < 0)
goto mark_errored;
pages = i;
/*
* Create a "page actor" which will kmap and kunmap the
* page cache pages appropriately within the decompressor
*/
actor = squashfs_page_actor_init_special(msblk, page, pages, expected);
if (actor == NULL)
goto out;
}
/* Decompress directly into the page cache buffers */
res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
kfree(actor);
if (res < 0)
goto mark_errored;
@ -99,12 +84,12 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
goto mark_errored;
}
/* Last page may have trailing bytes not filled */
/* Last page (if present) may have trailing bytes not filled */
bytes = res % PAGE_SIZE;
if (bytes) {
pageaddr = kmap_atomic(page[pages - 1]);
if (page[pages - 1]->index == end_index && bytes) {
pageaddr = kmap_local_page(page[pages - 1]);
memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
kunmap_atomic(pageaddr);
kunmap_local(pageaddr);
}
/* Mark pages as uptodate, unlock and release */
@ -116,7 +101,6 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
put_page(page[i]);
}
kfree(actor);
kfree(page);
return 0;
@ -135,40 +119,6 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
}
out:
kfree(actor);
kfree(page);
return res;
}
static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
int pages, struct page **page, int bytes)
{
struct inode *i = target_page->mapping->host;
struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
block, bsize);
int res = buffer->error, n, offset = 0;
if (res) {
ERROR("Unable to read page, block %llx, size %x\n", block,
bsize);
goto out;
}
for (n = 0; n < pages && bytes > 0; n++,
bytes -= PAGE_SIZE, offset += PAGE_SIZE) {
int avail = min_t(int, bytes, PAGE_SIZE);
if (page[n] == NULL)
continue;
squashfs_fill_page(page[n], buffer, offset, avail);
unlock_page(page[n]);
if (page[n] != target_page)
put_page(page[n]);
}
out:
squashfs_cache_put(buffer);
return res;
}

View File

@ -119,10 +119,12 @@ static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm,
buff = stream->output;
while (data) {
if (bytes <= PAGE_SIZE) {
memcpy(data, buff, bytes);
if (!IS_ERR(data))
memcpy(data, buff, bytes);
break;
}
memcpy(data, buff, PAGE_SIZE);
if (!IS_ERR(data))
memcpy(data, buff, PAGE_SIZE);
buff += PAGE_SIZE;
bytes -= PAGE_SIZE;
data = squashfs_next_page(output);
@ -139,5 +141,6 @@ const struct squashfs_decompressor squashfs_lz4_comp_ops = {
.decompress = lz4_uncompress,
.id = LZ4_COMPRESSION,
.name = "lz4",
.alloc_buffer = 0,
.supported = 1
};

View File

@ -93,10 +93,12 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
buff = stream->output;
while (data) {
if (bytes <= PAGE_SIZE) {
memcpy(data, buff, bytes);
if (!IS_ERR(data))
memcpy(data, buff, bytes);
break;
} else {
memcpy(data, buff, PAGE_SIZE);
if (!IS_ERR(data))
memcpy(data, buff, PAGE_SIZE);
buff += PAGE_SIZE;
bytes -= PAGE_SIZE;
data = squashfs_next_page(output);
@ -116,5 +118,6 @@ const struct squashfs_decompressor squashfs_lzo_comp_ops = {
.decompress = lzo_uncompress,
.id = LZO_COMPRESSION,
.name = "lzo",
.alloc_buffer = 0,
.supported = 1
};

View File

@ -7,6 +7,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include "squashfs_fs_sb.h"
#include "decompressor.h"
#include "page_actor.h"
/*
@ -57,29 +59,62 @@ struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
}
/* Implementation of page_actor for decompressing directly into page cache. */
static void *handle_next_page(struct squashfs_page_actor *actor)
{
int max_pages = (actor->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (actor->returned_pages == max_pages)
return NULL;
if ((actor->next_page == actor->pages) ||
(actor->next_index != actor->page[actor->next_page]->index)) {
if (actor->alloc_buffer) {
void *tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (tmp_buffer) {
actor->tmp_buffer = tmp_buffer;
actor->next_index++;
actor->returned_pages++;
return tmp_buffer;
}
}
actor->next_index++;
actor->returned_pages++;
return ERR_PTR(-ENOMEM);
}
actor->next_index++;
actor->returned_pages++;
return actor->pageaddr = kmap_local_page(actor->page[actor->next_page++]);
}
static void *direct_first_page(struct squashfs_page_actor *actor)
{
actor->next_page = 1;
return actor->pageaddr = kmap_atomic(actor->page[0]);
return handle_next_page(actor);
}
static void *direct_next_page(struct squashfs_page_actor *actor)
{
if (actor->pageaddr)
kunmap_atomic(actor->pageaddr);
kunmap_local(actor->pageaddr);
return actor->pageaddr = actor->next_page == actor->pages ? NULL :
kmap_atomic(actor->page[actor->next_page++]);
kfree(actor->tmp_buffer);
actor->pageaddr = actor->tmp_buffer = NULL;
return handle_next_page(actor);
}
static void direct_finish_page(struct squashfs_page_actor *actor)
{
if (actor->pageaddr)
kunmap_atomic(actor->pageaddr);
kunmap_local(actor->pageaddr);
kfree(actor->tmp_buffer);
}
struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
int pages, int length)
struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,
struct page **page, int pages, int length)
{
struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
@ -90,7 +125,11 @@ struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
actor->page = page;
actor->pages = pages;
actor->next_page = 0;
actor->returned_pages = 0;
actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1);
actor->pageaddr = NULL;
actor->tmp_buffer = NULL;
actor->alloc_buffer = msblk->decompressor->alloc_buffer;
actor->squashfs_first_page = direct_first_page;
actor->squashfs_next_page = direct_next_page;
actor->squashfs_finish_page = direct_finish_page;

View File

@ -45,6 +45,11 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
{
/* empty */
}
static inline void squashfs_actor_nobuff(struct squashfs_page_actor *actor)
{
/* empty */
}
#else
struct squashfs_page_actor {
union {
@ -52,17 +57,23 @@ struct squashfs_page_actor {
struct page **page;
};
void *pageaddr;
void *tmp_buffer;
void *(*squashfs_first_page)(struct squashfs_page_actor *);
void *(*squashfs_next_page)(struct squashfs_page_actor *);
void (*squashfs_finish_page)(struct squashfs_page_actor *);
int pages;
int length;
int next_page;
int alloc_buffer;
int returned_pages;
pgoff_t next_index;
};
extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
**, int, int);
extern struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
int pages, int length);
extern struct squashfs_page_actor *squashfs_page_actor_init_special(
struct squashfs_sb_info *msblk,
struct page **page, int pages, int length);
static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
{
return actor->squashfs_first_page(actor);
@ -75,5 +86,9 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
{
actor->squashfs_finish_page(actor);
}
static inline void squashfs_actor_nobuff(struct squashfs_page_actor *actor)
{
actor->alloc_buffer = 0;
}
#endif
#endif

View File

@ -131,6 +131,10 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
stream->buf.out_pos = 0;
stream->buf.out_size = PAGE_SIZE;
stream->buf.out = squashfs_first_page(output);
if (IS_ERR(stream->buf.out)) {
error = PTR_ERR(stream->buf.out);
goto finish;
}
for (;;) {
enum xz_ret xz_err;
@ -156,7 +160,10 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (stream->buf.out_pos == stream->buf.out_size) {
stream->buf.out = squashfs_next_page(output);
if (stream->buf.out != NULL) {
if (IS_ERR(stream->buf.out)) {
error = PTR_ERR(stream->buf.out);
break;
} else if (stream->buf.out != NULL) {
stream->buf.out_pos = 0;
total += PAGE_SIZE;
}
@ -171,6 +178,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
}
}
finish:
squashfs_finish_page(output);
return error ? error : total + stream->buf.out_pos;
@ -183,5 +191,6 @@ const struct squashfs_decompressor squashfs_xz_comp_ops = {
.decompress = squashfs_xz_uncompress,
.id = XZ_COMPRESSION,
.name = "xz",
.alloc_buffer = 1,
.supported = 1
};

View File

@ -62,6 +62,11 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
stream->next_out = squashfs_first_page(output);
stream->avail_in = 0;
if (IS_ERR(stream->next_out)) {
error = PTR_ERR(stream->next_out);
goto finish;
}
for (;;) {
int zlib_err;
@ -85,7 +90,10 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (stream->avail_out == 0) {
stream->next_out = squashfs_next_page(output);
if (stream->next_out != NULL)
if (IS_ERR(stream->next_out)) {
error = PTR_ERR(stream->next_out);
break;
} else if (stream->next_out != NULL)
stream->avail_out = PAGE_SIZE;
}
@ -107,6 +115,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
}
}
finish:
squashfs_finish_page(output);
if (!error)
@ -122,6 +131,7 @@ const struct squashfs_decompressor squashfs_zlib_comp_ops = {
.decompress = zlib_uncompress,
.id = ZLIB_COMPRESSION,
.name = "zlib",
.alloc_buffer = 1,
.supported = 1
};

View File

@ -80,6 +80,10 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
out_buf.size = PAGE_SIZE;
out_buf.dst = squashfs_first_page(output);
if (IS_ERR(out_buf.dst)) {
error = PTR_ERR(out_buf.dst);
goto finish;
}
for (;;) {
size_t zstd_err;
@ -104,7 +108,10 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
if (out_buf.pos == out_buf.size) {
out_buf.dst = squashfs_next_page(output);
if (out_buf.dst == NULL) {
if (IS_ERR(out_buf.dst)) {
error = PTR_ERR(out_buf.dst);
break;
} else if (out_buf.dst == NULL) {
/* Shouldn't run out of pages
* before stream is done.
*/
@ -129,6 +136,8 @@ static int zstd_uncompress(struct squashfs_sb_info *msblk, void *strm,
}
}
finish:
squashfs_finish_page(output);
return error ? error : total_out;
@ -140,5 +149,6 @@ const struct squashfs_decompressor squashfs_zstd_comp_ops = {
.decompress = zstd_uncompress,
.id = ZSTD_COMPRESSION,
.name = "zstd",
.alloc_buffer = 1,
.supported = 1
};

View File

@ -35,21 +35,21 @@ static inline const char *kernel_read_file_id_str(enum kernel_read_file_id id)
return kernel_read_file_str[id];
}
int kernel_read_file(struct file *file, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
int kernel_read_file_from_path(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
int kernel_read_file_from_path_initns(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
int kernel_read_file_from_fd(int fd, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
ssize_t kernel_read_file(struct file *file, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
ssize_t kernel_read_file_from_path(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
ssize_t kernel_read_file_from_path_initns(const char *path, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
ssize_t kernel_read_file_from_fd(int fd, loff_t offset,
void **buf, size_t buf_size,
size_t *file_size,
enum kernel_read_file_id id);
#endif /* _LINUX_KERNEL_READ_FILE_H */

View File

@ -7,6 +7,7 @@
#include <vdso/limits.h>
#define SIZE_MAX (~(size_t)0)
#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
#define PHYS_ADDR_MAX (~(phys_addr_t)0)
#define U8_MAX ((u8)~0U)

View File

@ -17,9 +17,9 @@
#ifndef _LINUX_RBTREE_H
#define _LINUX_RBTREE_H
#include <linux/container_of.h>
#include <linux/rbtree_types.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/rcupdate.h>

View File

@ -102,7 +102,7 @@ static inline __attribute_const__ __u32 __fswahb32(__u32 val)
#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
#else
#define __swab16(x) \
(__builtin_constant_p((__u16)(x)) ? \
(__u16)(__builtin_constant_p(x) ? \
___constant_swab16(x) : \
__fswab16(x))
#endif
@ -115,7 +115,7 @@ static inline __attribute_const__ __u32 __fswahb32(__u32 val)
#define __swab32(x) (__u32)__builtin_bswap32((__u32)(x))
#else
#define __swab32(x) \
(__builtin_constant_p((__u32)(x)) ? \
(__u32)(__builtin_constant_p(x) ? \
___constant_swab32(x) : \
__fswab32(x))
#endif
@ -128,7 +128,7 @@ static inline __attribute_const__ __u32 __fswahb32(__u32 val)
#define __swab64(x) (__u64)__builtin_bswap64((__u64)(x))
#else
#define __swab64(x) \
(__builtin_constant_p((__u64)(x)) ? \
(__u64)(__builtin_constant_p(x) ? \
___constant_swab64(x) : \
__fswab64(x))
#endif

View File

@ -31,6 +31,9 @@
static int kexec_calculate_store_digests(struct kimage *image);
/* Maximum size in bytes for kernel/initrd files. */
#define KEXEC_FILE_SIZE_MAX min_t(s64, 4LL << 30, SSIZE_MAX)
/*
* Currently this is the only default function that is exported as some
* architectures need it to do additional handlings.
@ -189,11 +192,12 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
const char __user *cmdline_ptr,
unsigned long cmdline_len, unsigned flags)
{
int ret;
ssize_t ret;
void *ldata;
ret = kernel_read_file_from_fd(kernel_fd, 0, &image->kernel_buf,
INT_MAX, NULL, READING_KEXEC_IMAGE);
KEXEC_FILE_SIZE_MAX, NULL,
READING_KEXEC_IMAGE);
if (ret < 0)
return ret;
image->kernel_buf_len = ret;
@ -213,7 +217,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
/* It is possible that there no initramfs is being loaded */
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
ret = kernel_read_file_from_fd(initrd_fd, 0, &image->initrd_buf,
INT_MAX, NULL,
KEXEC_FILE_SIZE_MAX, NULL,
READING_KEXEC_INITRAMFS);
if (ret < 0)
goto out;

View File

@ -109,6 +109,13 @@ int __ref profile_init(void)
/* only text is profiled */
prof_len = (_etext - _stext) >> prof_shift;
if (!prof_len) {
pr_warn("profiling shift: %u too large\n", prof_shift);
prof_on = 0;
return -EINVAL;
}
buffer_bytes = prof_len*sizeof(atomic_t);
if (!alloc_cpumask_var(&prof_cpu_mask, GFP_KERNEL))

View File

@ -238,7 +238,7 @@ static int keyzero(struct btree_geo *geo, unsigned long *key)
return 1;
}
void *btree_lookup(struct btree_head *head, struct btree_geo *geo,
static void *btree_lookup_node(struct btree_head *head, struct btree_geo *geo,
unsigned long *key)
{
int i, height = head->height;
@ -257,7 +257,16 @@ void *btree_lookup(struct btree_head *head, struct btree_geo *geo,
if (!node)
return NULL;
}
return node;
}
void *btree_lookup(struct btree_head *head, struct btree_geo *geo,
unsigned long *key)
{
int i;
unsigned long *node;
node = btree_lookup_node(head, geo, key);
if (!node)
return NULL;
@ -271,23 +280,10 @@ EXPORT_SYMBOL_GPL(btree_lookup);
int btree_update(struct btree_head *head, struct btree_geo *geo,
unsigned long *key, void *val)
{
int i, height = head->height;
unsigned long *node = head->node;
if (height == 0)
return -ENOENT;
for ( ; height > 1; height--) {
for (i = 0; i < geo->no_pairs; i++)
if (keycmp(geo, node, i, key) <= 0)
break;
if (i == geo->no_pairs)
return -ENOENT;
node = bval(geo, node, i);
if (!node)
return -ENOENT;
}
int i;
unsigned long *node;
node = btree_lookup_node(head, geo, key);
if (!node)
return -ENOENT;

View File

@ -197,24 +197,14 @@ static int ei_seq_show(struct seq_file *m, void *v)
return 0;
}
static const struct seq_operations ei_seq_ops = {
static const struct seq_operations ei_sops = {
.start = ei_seq_start,
.next = ei_seq_next,
.stop = ei_seq_stop,
.show = ei_seq_show,
};
static int ei_open(struct inode *inode, struct file *filp)
{
return seq_open(filp, &ei_seq_ops);
}
static const struct file_operations debugfs_ei_ops = {
.open = ei_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
DEFINE_SEQ_ATTRIBUTE(ei);
static int __init ei_debugfs_init(void)
{
@ -224,7 +214,7 @@ static int __init ei_debugfs_init(void)
if (!dir)
return -ENOMEM;
file = debugfs_create_file("list", 0444, dir, NULL, &debugfs_ei_ops);
file = debugfs_create_file("list", 0444, dir, NULL, &ei_fops);
if (!file) {
debugfs_remove(dir);
return -ENOMEM;

View File

@ -63,18 +63,13 @@ void fprop_global_destroy(struct fprop_global *p)
*/
bool fprop_new_period(struct fprop_global *p, int periods)
{
s64 events;
unsigned long flags;
s64 events = percpu_counter_sum(&p->events);
local_irq_save(flags);
events = percpu_counter_sum(&p->events);
/*
* Don't do anything if there are no events.
*/
if (events <= 1) {
local_irq_restore(flags);
if (events <= 1)
return false;
}
write_seqcount_begin(&p->sequence);
if (periods < 64)
events -= events >> periods;
@ -82,7 +77,6 @@ bool fprop_new_period(struct fprop_global *p, int periods)
percpu_counter_add(&p->events, -events);
p->period += periods;
write_seqcount_end(&p->sequence);
local_irq_restore(flags);
return true;
}

View File

@ -20,7 +20,11 @@
bool __list_add_valid(struct list_head *new, struct list_head *prev,
struct list_head *next)
{
if (CHECK_DATA_CORRUPTION(next->prev != prev,
if (CHECK_DATA_CORRUPTION(prev == NULL,
"list_add corruption. prev is NULL.\n") ||
CHECK_DATA_CORRUPTION(next == NULL,
"list_add corruption. next is NULL.\n") ||
CHECK_DATA_CORRUPTION(next->prev != prev,
"list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n",
prev, next->prev, next) ||
CHECK_DATA_CORRUPTION(prev->next != next,
@ -42,7 +46,11 @@ bool __list_del_entry_valid(struct list_head *entry)
prev = entry->prev;
next = entry->next;
if (CHECK_DATA_CORRUPTION(next == LIST_POISON1,
if (CHECK_DATA_CORRUPTION(next == NULL,
"list_del corruption, %px->next is NULL\n", entry) ||
CHECK_DATA_CORRUPTION(prev == NULL,
"list_del corruption, %px->prev is NULL\n", entry) ||
CHECK_DATA_CORRUPTION(next == LIST_POISON1,
"list_del corruption, %px->next is LIST_POISON1 (%px)\n",
entry, LIST_POISON1) ||
CHECK_DATA_CORRUPTION(prev == LIST_POISON2,

View File

@ -1042,7 +1042,8 @@ our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
our $declaration_macros = qr{(?x:
(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(
(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(|
(?:$Storage\s+)?(?:XA_STATE|XA_STATE_ORDER)\s*\(
)};
our %allow_repeated_words = (
@ -5720,7 +5721,7 @@ sub process {
$var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ &&
#Ignore some three character SI units explicitly, like MiB and KHz
$var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
while ($var =~ m{($Ident)}g) {
while ($var =~ m{\b($Ident)}g) {
my $word = $1;
next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/);
if ($check) {

View File

@ -45,7 +45,6 @@
exit(code); \
} while (0)
int done;
int rcvbufsz;
char name[100];
int dbg;
@ -285,7 +284,6 @@ int main(int argc, char *argv[])
pid_t rtid = 0;
int fd = 0;
int count = 0;
int write_file = 0;
int maskset = 0;
char *logfile = NULL;
@ -495,7 +493,6 @@ int main(int argc, char *argv[])
len2 = 0;
/* For nested attributes, na follows */
na = (struct nlattr *) NLA_DATA(na);
done = 0;
while (len2 < aggr_len) {
switch (na->nla_type) {
case TASKSTATS_TYPE_PID:
@ -509,7 +506,6 @@ int main(int argc, char *argv[])
printf("TGID\t%d\n", rtid);
break;
case TASKSTATS_TYPE_STATS:
count++;
if (print_delays)
print_delayacct((struct taskstats *) NLA_DATA(na));
if (print_io_accounting)

View File

@ -17,6 +17,7 @@ TARGETS += exec
TARGETS += filesystems
TARGETS += filesystems/binderfs
TARGETS += filesystems/epoll
TARGETS += filesystems/fat
TARGETS += firmware
TARGETS += fpu
TARGETS += ftrace

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
rename_exchange

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := run_fat_tests.sh
TEST_GEN_PROGS_EXTENDED := rename_exchange
CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES)
include ../../lib.mk

View File

@ -0,0 +1,2 @@
CONFIG_BLK_DEV_LOOP=y
CONFIG_VFAT_FS=y

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Program that atomically exchanges two paths using
* the renameat2() system call RENAME_EXCHANGE flag.
*
* Copyright 2022 Red Hat Inc.
* Author: Javier Martinez Canillas <javierm@redhat.com>
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void print_usage(const char *program)
{
printf("Usage: %s [oldpath] [newpath]\n", program);
printf("Atomically exchange oldpath and newpath\n");
}
int main(int argc, char *argv[])
{
int ret;
if (argc != 3) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
ret = renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE);
if (ret) {
perror("rename exchange failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,82 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Run filesystem operations tests on an 1 MiB disk image that is formatted with
# a vfat filesystem and mounted in a temporary directory using a loop device.
#
# Copyright 2022 Red Hat Inc.
# Author: Javier Martinez Canillas <javierm@redhat.com>
set -e
set -u
set -o pipefail
BASE_DIR="$(dirname $0)"
TMP_DIR="$(mktemp -d /tmp/fat_tests_tmp.XXXX)"
IMG_PATH="${TMP_DIR}/fat.img"
MNT_PATH="${TMP_DIR}/mnt"
cleanup()
{
mountpoint -q "${MNT_PATH}" && unmount_image
rm -rf "${TMP_DIR}"
}
trap cleanup SIGINT SIGTERM EXIT
create_loopback()
{
touch "${IMG_PATH}"
chattr +C "${IMG_PATH}" >/dev/null 2>&1 || true
truncate -s 1M "${IMG_PATH}"
mkfs.vfat "${IMG_PATH}" >/dev/null 2>&1
}
mount_image()
{
mkdir -p "${MNT_PATH}"
sudo mount -o loop "${IMG_PATH}" "${MNT_PATH}"
}
rename_exchange_test()
{
local rename_exchange="${BASE_DIR}/rename_exchange"
local old_path="${MNT_PATH}/old_file"
local new_path="${MNT_PATH}/new_file"
echo old | sudo tee "${old_path}" >/dev/null 2>&1
echo new | sudo tee "${new_path}" >/dev/null 2>&1
sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1
sudo sync -f "${MNT_PATH}"
grep new "${old_path}" >/dev/null 2>&1
grep old "${new_path}" >/dev/null 2>&1
}
rename_exchange_subdir_test()
{
local rename_exchange="${BASE_DIR}/rename_exchange"
local dir_path="${MNT_PATH}/subdir"
local old_path="${MNT_PATH}/old_file"
local new_path="${dir_path}/new_file"
sudo mkdir -p "${dir_path}"
echo old | sudo tee "${old_path}" >/dev/null 2>&1
echo new | sudo tee "${new_path}" >/dev/null 2>&1
sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1
sudo sync -f "${MNT_PATH}"
grep new "${old_path}" >/dev/null 2>&1
grep old "${new_path}" >/dev/null 2>&1
}
unmount_image()
{
sudo umount "${MNT_PATH}" &> /dev/null
}
create_loopback
mount_image
rename_exchange_test
rename_exchange_subdir_test
unmount_image
exit 0