Skip to content

Commit

Permalink
Merge branch 'mm-nonmm-stable' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/akpm/mm
  • Loading branch information
Stephen Rothwell committed Jul 14, 2022
2 parents c29c49b + ee56c3e commit 0e60525
Show file tree
Hide file tree
Showing 33 changed files with 569 additions and 235 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -21217,6 +21217,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>
Expand Down
28 changes: 14 additions & 14 deletions arch/ia64/include/uapi/asm/cmpxchg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand All @@ -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) \
Expand Down
231 changes: 192 additions & 39 deletions fs/fat/namei_vfat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 0e60525

Please sign in to comment.