mirror of https://github.com/OpenIPC/firmware.git
443 lines
12 KiB
Diff
443 lines
12 KiB
Diff
diff -drupN a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
|
|
--- a/fs/f2fs/xattr.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/fs/f2fs/xattr.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -106,7 +106,7 @@ static int f2fs_xattr_advise_set(const s
|
|
return -EINVAL;
|
|
|
|
F2FS_I(inode)->i_advise |= *(char *)value;
|
|
- f2fs_mark_inode_dirty_sync(inode);
|
|
+ f2fs_mark_inode_dirty_sync(inode, true);
|
|
return 0;
|
|
}
|
|
|
|
@@ -217,55 +217,157 @@ static struct f2fs_xattr_entry *__find_x
|
|
return entry;
|
|
}
|
|
|
|
-static int read_all_xattrs(struct inode *inode, struct page *ipage,
|
|
- void **base_addr)
|
|
+static struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode,
|
|
+ void *base_addr, void **last_addr, int index,
|
|
+ size_t len, const char *name)
|
|
+{
|
|
+ struct f2fs_xattr_entry *entry;
|
|
+ unsigned int inline_size = inline_xattr_size(inode);
|
|
+
|
|
+ list_for_each_xattr(entry, base_addr) {
|
|
+ if ((void *)entry + sizeof(__u32) > base_addr + inline_size ||
|
|
+ (void *)XATTR_NEXT_ENTRY(entry) + sizeof(__u32) >
|
|
+ base_addr + inline_size) {
|
|
+ *last_addr = entry;
|
|
+ return NULL;
|
|
+ }
|
|
+ if (entry->e_name_index != index)
|
|
+ continue;
|
|
+ if (entry->e_name_len != len)
|
|
+ continue;
|
|
+ if (!memcmp(entry->e_name, name, len))
|
|
+ break;
|
|
+ }
|
|
+ return entry;
|
|
+}
|
|
+
|
|
+static int read_inline_xattr(struct inode *inode, struct page *ipage,
|
|
+ void *txattr_addr)
|
|
{
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
|
- struct f2fs_xattr_header *header;
|
|
- size_t size = PAGE_SIZE, inline_size = 0;
|
|
- void *txattr_addr;
|
|
- int err;
|
|
+ unsigned int inline_size = inline_xattr_size(inode);
|
|
+ struct page *page = NULL;
|
|
+ void *inline_addr;
|
|
|
|
- inline_size = inline_xattr_size(inode);
|
|
+ if (ipage) {
|
|
+ inline_addr = inline_xattr_addr(inode, ipage);
|
|
+ } else {
|
|
+ page = get_node_page(sbi, inode->i_ino);
|
|
+ if (IS_ERR(page))
|
|
+ return PTR_ERR(page);
|
|
|
|
- txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO);
|
|
+ inline_addr = inline_xattr_addr(inode, page);
|
|
+ }
|
|
+ memcpy(txattr_addr, inline_addr, inline_size);
|
|
+ f2fs_put_page(page, 1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int read_xattr_block(struct inode *inode, void *txattr_addr)
|
|
+{
|
|
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
|
+ nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
|
+ unsigned int inline_size = inline_xattr_size(inode);
|
|
+ struct page *xpage;
|
|
+ void *xattr_addr;
|
|
+
|
|
+ /* The inode already has an extended attribute block. */
|
|
+ xpage = get_node_page(sbi, xnid);
|
|
+ if (IS_ERR(xpage))
|
|
+ return PTR_ERR(xpage);
|
|
+
|
|
+ xattr_addr = page_address(xpage);
|
|
+ memcpy(txattr_addr + inline_size, xattr_addr, VALID_XATTR_BLOCK_SIZE);
|
|
+ f2fs_put_page(xpage, 1);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
|
|
+ unsigned int index, unsigned int len,
|
|
+ const char *name, struct f2fs_xattr_entry **xe,
|
|
+ void **base_addr)
|
|
+{
|
|
+ void *cur_addr, *txattr_addr, *last_addr = NULL;
|
|
+ nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
|
+ unsigned int size = xnid ? VALID_XATTR_BLOCK_SIZE : 0;
|
|
+ unsigned int inline_size = inline_xattr_size(inode);
|
|
+ int err = 0;
|
|
+
|
|
+ if (!size && !inline_size)
|
|
+ return -ENODATA;
|
|
+
|
|
+ txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode),
|
|
+ inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS);
|
|
if (!txattr_addr)
|
|
return -ENOMEM;
|
|
|
|
/* read from inline xattr */
|
|
if (inline_size) {
|
|
- struct page *page = NULL;
|
|
- void *inline_addr;
|
|
+ err = read_inline_xattr(inode, ipage, txattr_addr);
|
|
+ if (err)
|
|
+ goto out;
|
|
|
|
- if (ipage) {
|
|
- inline_addr = inline_xattr_addr(ipage);
|
|
- } else {
|
|
- page = get_node_page(sbi, inode->i_ino);
|
|
- if (IS_ERR(page)) {
|
|
- err = PTR_ERR(page);
|
|
- goto fail;
|
|
- }
|
|
- inline_addr = inline_xattr_addr(page);
|
|
- }
|
|
- memcpy(txattr_addr, inline_addr, inline_size);
|
|
- f2fs_put_page(page, 1);
|
|
+ *xe = __find_inline_xattr(inode, txattr_addr, &last_addr,
|
|
+ index, len, name);
|
|
+ if (*xe)
|
|
+ goto check;
|
|
}
|
|
|
|
/* read from xattr node block */
|
|
- if (F2FS_I(inode)->i_xattr_nid) {
|
|
- struct page *xpage;
|
|
- void *xattr_addr;
|
|
+ if (xnid) {
|
|
+ err = read_xattr_block(inode, txattr_addr);
|
|
+ if (err)
|
|
+ goto out;
|
|
+ }
|
|
|
|
- /* The inode already has an extended attribute block. */
|
|
- xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
|
|
- if (IS_ERR(xpage)) {
|
|
- err = PTR_ERR(xpage);
|
|
+ if (last_addr)
|
|
+ cur_addr = XATTR_HDR(last_addr) - 1;
|
|
+ else
|
|
+ cur_addr = txattr_addr;
|
|
+
|
|
+ *xe = __find_xattr(cur_addr, index, len, name);
|
|
+check:
|
|
+ if (IS_XATTR_LAST_ENTRY(*xe)) {
|
|
+ err = -ENODATA;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ *base_addr = txattr_addr;
|
|
+ return 0;
|
|
+out:
|
|
+ kzfree(txattr_addr);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static int read_all_xattrs(struct inode *inode, struct page *ipage,
|
|
+ void **base_addr)
|
|
+{
|
|
+ struct f2fs_xattr_header *header;
|
|
+ nid_t xnid = F2FS_I(inode)->i_xattr_nid;
|
|
+ unsigned int size = VALID_XATTR_BLOCK_SIZE;
|
|
+ unsigned int inline_size = inline_xattr_size(inode);
|
|
+ void *txattr_addr;
|
|
+ int err;
|
|
+
|
|
+ txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode),
|
|
+ inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS);
|
|
+ if (!txattr_addr)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* read from inline xattr */
|
|
+ if (inline_size) {
|
|
+ err = read_inline_xattr(inode, ipage, txattr_addr);
|
|
+ if (err)
|
|
goto fail;
|
|
- }
|
|
+ }
|
|
|
|
- xattr_addr = page_address(xpage);
|
|
- memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE);
|
|
- f2fs_put_page(xpage, 1);
|
|
+ /* read from xattr node block */
|
|
+ if (xnid) {
|
|
+ err = read_xattr_block(inode, txattr_addr);
|
|
+ if (err)
|
|
+ goto fail;
|
|
}
|
|
|
|
header = XATTR_HDR(txattr_addr);
|
|
@@ -286,13 +388,13 @@ static inline int write_all_xattrs(struc
|
|
void *txattr_addr, struct page *ipage)
|
|
{
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
|
- size_t inline_size = 0;
|
|
+ size_t inline_size = inline_xattr_size(inode);
|
|
+ struct page *in_page = NULL;
|
|
void *xattr_addr;
|
|
+ void *inline_addr = NULL;
|
|
struct page *xpage;
|
|
nid_t new_nid = 0;
|
|
- int err;
|
|
-
|
|
- inline_size = inline_xattr_size(inode);
|
|
+ int err = 0;
|
|
|
|
if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid)
|
|
if (!alloc_nid(sbi, &new_nid))
|
|
@@ -300,30 +402,30 @@ static inline int write_all_xattrs(struc
|
|
|
|
/* write to inline xattr */
|
|
if (inline_size) {
|
|
- struct page *page = NULL;
|
|
- void *inline_addr;
|
|
-
|
|
if (ipage) {
|
|
- inline_addr = inline_xattr_addr(ipage);
|
|
- f2fs_wait_on_page_writeback(ipage, NODE, true);
|
|
- set_page_dirty(ipage);
|
|
+ inline_addr = inline_xattr_addr(inode, ipage);
|
|
} else {
|
|
- page = get_node_page(sbi, inode->i_ino);
|
|
- if (IS_ERR(page)) {
|
|
+ in_page = get_node_page(sbi, inode->i_ino);
|
|
+ if (IS_ERR(in_page)) {
|
|
alloc_nid_failed(sbi, new_nid);
|
|
- return PTR_ERR(page);
|
|
+ return PTR_ERR(in_page);
|
|
}
|
|
- inline_addr = inline_xattr_addr(page);
|
|
- f2fs_wait_on_page_writeback(page, NODE, true);
|
|
+ inline_addr = inline_xattr_addr(inode, in_page);
|
|
}
|
|
- memcpy(inline_addr, txattr_addr, inline_size);
|
|
- f2fs_put_page(page, 1);
|
|
|
|
+ f2fs_wait_on_page_writeback(ipage ? ipage : in_page,
|
|
+ NODE, true);
|
|
/* no need to use xattr node block */
|
|
if (hsize <= inline_size) {
|
|
- err = truncate_xattr_node(inode, ipage);
|
|
+ err = truncate_xattr_node(inode);
|
|
alloc_nid_failed(sbi, new_nid);
|
|
- return err;
|
|
+ if (err) {
|
|
+ f2fs_put_page(in_page, 1);
|
|
+ return err;
|
|
+ }
|
|
+ memcpy(inline_addr, txattr_addr, inline_size);
|
|
+ set_page_dirty(ipage ? ipage : in_page);
|
|
+ goto in_page_out;
|
|
}
|
|
}
|
|
|
|
@@ -331,40 +433,46 @@ static inline int write_all_xattrs(struc
|
|
if (F2FS_I(inode)->i_xattr_nid) {
|
|
xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid);
|
|
if (IS_ERR(xpage)) {
|
|
+ err = PTR_ERR(xpage);
|
|
alloc_nid_failed(sbi, new_nid);
|
|
- return PTR_ERR(xpage);
|
|
+ goto in_page_out;
|
|
}
|
|
f2fs_bug_on(sbi, new_nid);
|
|
f2fs_wait_on_page_writeback(xpage, NODE, true);
|
|
} else {
|
|
struct dnode_of_data dn;
|
|
set_new_dnode(&dn, inode, NULL, NULL, new_nid);
|
|
- xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage);
|
|
+ xpage = new_node_page(&dn, XATTR_NODE_OFFSET);
|
|
if (IS_ERR(xpage)) {
|
|
+ err = PTR_ERR(xpage);
|
|
alloc_nid_failed(sbi, new_nid);
|
|
- return PTR_ERR(xpage);
|
|
+ goto in_page_out;
|
|
}
|
|
alloc_nid_done(sbi, new_nid);
|
|
}
|
|
-
|
|
xattr_addr = page_address(xpage);
|
|
- memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE -
|
|
- sizeof(struct node_footer));
|
|
+
|
|
+ if (inline_size)
|
|
+ memcpy(inline_addr, txattr_addr, inline_size);
|
|
+ memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE);
|
|
+
|
|
+ if (inline_size)
|
|
+ set_page_dirty(ipage ? ipage : in_page);
|
|
set_page_dirty(xpage);
|
|
- f2fs_put_page(xpage, 1);
|
|
|
|
- /* need to checkpoint during fsync */
|
|
- F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi));
|
|
- return 0;
|
|
+ f2fs_put_page(xpage, 1);
|
|
+in_page_out:
|
|
+ f2fs_put_page(in_page, 1);
|
|
+ return err;
|
|
}
|
|
|
|
int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
|
void *buffer, size_t buffer_size, struct page *ipage)
|
|
{
|
|
- struct f2fs_xattr_entry *entry;
|
|
- void *base_addr;
|
|
+ struct f2fs_xattr_entry *entry = NULL;
|
|
int error = 0;
|
|
- size_t size, len;
|
|
+ unsigned int size, len;
|
|
+ void *base_addr = NULL;
|
|
|
|
if (name == NULL)
|
|
return -EINVAL;
|
|
@@ -373,21 +481,18 @@ int f2fs_getxattr(struct inode *inode, i
|
|
if (len > F2FS_NAME_LEN)
|
|
return -ERANGE;
|
|
|
|
- error = read_all_xattrs(inode, ipage, &base_addr);
|
|
+ down_read(&F2FS_I(inode)->i_xattr_sem);
|
|
+ error = lookup_all_xattrs(inode, ipage, index, len, name,
|
|
+ &entry, &base_addr);
|
|
+ up_read(&F2FS_I(inode)->i_xattr_sem);
|
|
if (error)
|
|
return error;
|
|
|
|
- entry = __find_xattr(base_addr, index, len, name);
|
|
- if (IS_XATTR_LAST_ENTRY(entry)) {
|
|
- error = -ENODATA;
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
size = le16_to_cpu(entry->e_value_size);
|
|
|
|
if (buffer && size > buffer_size) {
|
|
error = -ERANGE;
|
|
- goto cleanup;
|
|
+ goto out;
|
|
}
|
|
|
|
if (buffer) {
|
|
@@ -395,8 +500,7 @@ int f2fs_getxattr(struct inode *inode, i
|
|
memcpy(buffer, pval, size);
|
|
}
|
|
error = size;
|
|
-
|
|
-cleanup:
|
|
+out:
|
|
kzfree(base_addr);
|
|
return error;
|
|
}
|
|
@@ -409,7 +513,9 @@ ssize_t f2fs_listxattr(struct dentry *de
|
|
int error = 0;
|
|
size_t rest = buffer_size;
|
|
|
|
+ down_read(&F2FS_I(inode)->i_xattr_sem);
|
|
error = read_all_xattrs(inode, NULL, &base_addr);
|
|
+ up_read(&F2FS_I(inode)->i_xattr_sem);
|
|
if (error)
|
|
return error;
|
|
|
|
@@ -445,6 +551,15 @@ cleanup:
|
|
return error;
|
|
}
|
|
|
|
+static bool f2fs_xattr_value_same(struct f2fs_xattr_entry *entry,
|
|
+ const void *value, size_t size)
|
|
+{
|
|
+ void *pval = entry->e_name + entry->e_name_len;
|
|
+
|
|
+ return (le16_to_cpu(entry->e_value_size) == size) &&
|
|
+ !memcmp(pval, value, size);
|
|
+}
|
|
+
|
|
static int __f2fs_setxattr(struct inode *inode, int index,
|
|
const char *name, const void *value, size_t size,
|
|
struct page *ipage, int flags)
|
|
@@ -479,12 +594,17 @@ static int __f2fs_setxattr(struct inode
|
|
|
|
found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
|
|
|
|
- if ((flags & XATTR_REPLACE) && !found) {
|
|
+ if (found) {
|
|
+ if ((flags & XATTR_CREATE)) {
|
|
+ error = -EEXIST;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ if (value && f2fs_xattr_value_same(here, value, size))
|
|
+ goto exit;
|
|
+ } else if ((flags & XATTR_REPLACE)) {
|
|
error = -ENODATA;
|
|
goto exit;
|
|
- } else if ((flags & XATTR_CREATE) && found) {
|
|
- error = -EEXIST;
|
|
- goto exit;
|
|
}
|
|
|
|
last = here;
|
|
@@ -554,7 +674,7 @@ static int __f2fs_setxattr(struct inode
|
|
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
|
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
|
f2fs_set_encrypted_inode(inode);
|
|
- f2fs_mark_inode_dirty_sync(inode);
|
|
+ f2fs_mark_inode_dirty_sync(inode, true);
|
|
if (!error && S_ISDIR(inode->i_mode))
|
|
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP);
|
|
exit:
|
|
@@ -569,6 +689,10 @@ int f2fs_setxattr(struct inode *inode, i
|
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
|
int err;
|
|
|
|
+ err = dquot_initialize(inode);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
/* this case is only from init_inode_metadata */
|
|
if (ipage)
|
|
return __f2fs_setxattr(inode, index, name, value,
|
|
@@ -578,7 +702,9 @@ int f2fs_setxattr(struct inode *inode, i
|
|
f2fs_lock_op(sbi);
|
|
/* protect xattr_ver */
|
|
down_write(&F2FS_I(inode)->i_sem);
|
|
+ down_write(&F2FS_I(inode)->i_xattr_sem);
|
|
err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags);
|
|
+ up_write(&F2FS_I(inode)->i_xattr_sem);
|
|
up_write(&F2FS_I(inode)->i_sem);
|
|
f2fs_unlock_op(sbi);
|
|
|