diff options
| -rw-r--r-- | fs/ntfs/ea.c | 67 |
1 files changed, 63 insertions, 4 deletions
diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c index a1f948c4d2ec..9f62056c1d6f 100644 --- a/fs/ntfs/ea.c +++ b/fs/ntfs/ea.c @@ -1053,8 +1053,9 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, { const char *name; size_t size, name_len; - void *value; - int err; + void *value, *old_value = NULL; + int err, old_len = -ENODATA; + bool acl_committed = false; int flags; umode_t mode; @@ -1098,7 +1099,34 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, } mutex_lock(&NTFS_I(inode)->mrec_lock); + if (!init_acl) { + /* Save the old xattr for rollback. */ + old_len = ntfs_get_ea(inode, name, name_len, NULL, 0); + if (old_len >= 0) { + /* kmalloc(0) returns the non-NULL ZERO_SIZE_PTR. */ + old_value = kmalloc(old_len, GFP_NOFS); + if (!old_value) { + err = -ENOMEM; + goto out_unlock; + } + err = ntfs_get_ea(inode, name, name_len, + old_value, old_len); + if (err != old_len) { + kfree(old_value); + old_value = NULL; + if (err >= 0) + err = -EIO; + goto out_unlock; + } + } else if (old_len != -ENODATA) { + err = old_len; + goto out_unlock; + } + } err = ntfs_set_ea(inode, name, name_len, value, size, flags, NULL); + if (!err) + acl_committed = true; +out_unlock: mutex_unlock(&NTFS_I(inode)->mrec_lock); if (err == -ENODATA && !size) err = 0; /* Removing non existed xattr. */ @@ -1110,8 +1138,38 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, mutex_lock(&NTFS_I(inode)->mrec_lock); err = ntfs_ea_set_wsl_inode(inode, 0, &ea_size, NTFS_EA_MODE); if (err) { - ntfs_set_ea(inode, name, name_len, NULL, 0, - XATTR_REPLACE, NULL); + if (acl_committed) { + int undo_err; + + if (old_len >= 0) { + /* + * A removed or zero-length xattr must + * be recreated without XATTR_REPLACE. + */ + int undo_flags = acl && old_len ? + XATTR_REPLACE : 0; + + undo_err = ntfs_set_ea(inode, name, + name_len, old_value, + old_len, undo_flags, + NULL); + } else { + undo_err = ntfs_set_ea(inode, name, + name_len, NULL, 0, + XATTR_REPLACE, NULL); + } + if (undo_err) { + /* + * Record a failed rollback so the next + * persistence point marks the volume + * dirty. Run chkdsk. + */ + NVolSetErrors(NTFS_I(inode)->vol); + ntfs_error(inode->i_sb, + "Failed to undo the ACL xattr: %d", + undo_err); + } + } mutex_unlock(&NTFS_I(inode)->mrec_lock); inode->i_mode = old_mode; goto out; @@ -1124,6 +1182,7 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap, } out: + kfree(old_value); kfree(value); return err; |
