]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/f2fs/xattr.c
Merge branch 'for-linus' into for-4.12/block
[karo-tx-linux.git] / fs / f2fs / xattr.c
index c47ce2f330a14eba68de0c30c7f0f16358def5b4..7298a4488f7f59cfc68587c4fbc3b1ac6293f9aa 100644 (file)
@@ -217,6 +217,112 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
        return entry;
 }
 
+static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr,
+                                       void **last_addr, int index,
+                                       size_t len, const char *name)
+{
+       struct f2fs_xattr_entry *entry;
+       unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2;
+
+       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 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)
+{
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+       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 = 0;
+       int err = 0;
+
+       inline_size = inline_xattr_size(inode);
+
+       if (!size && !inline_size)
+               return -ENODATA;
+
+       txattr_addr = kzalloc(inline_size + size + sizeof(__u32),
+                                                       GFP_F2FS_ZERO);
+       if (!txattr_addr)
+               return -ENOMEM;
+
+       /* read from inline xattr */
+       if (inline_size) {
+               struct page *page = NULL;
+               void *inline_addr;
+
+               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 out;
+                       }
+                       inline_addr = inline_xattr_addr(page);
+               }
+               memcpy(txattr_addr, inline_addr, inline_size);
+               f2fs_put_page(page, 1);
+
+               *xe = __find_inline_xattr(txattr_addr, &last_addr,
+                                               index, len, name);
+               if (*xe)
+                       goto check;
+       }
+
+       /* read from xattr node block */
+       if (xnid) {
+               struct page *xpage;
+               void *xattr_addr;
+
+               /* The inode already has an extended attribute block. */
+               xpage = get_node_page(sbi, xnid);
+               if (IS_ERR(xpage)) {
+                       err = PTR_ERR(xpage);
+                       goto out;
+               }
+
+               xattr_addr = page_address(xpage);
+               memcpy(txattr_addr + inline_size, xattr_addr, size);
+               f2fs_put_page(xpage, 1);
+       }
+
+       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)
 {
@@ -348,23 +454,20 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize,
        }
 
        xattr_addr = page_address(xpage);
-       memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE -
-                                               sizeof(struct node_footer));
+       memcpy(xattr_addr, txattr_addr + inline_size, MAX_XATTR_BLOCK_SIZE);
        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;
 }
 
 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 +476,16 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
        if (len > F2FS_NAME_LEN)
                return -ERANGE;
 
-       error = read_all_xattrs(inode, ipage, &base_addr);
+       error = lookup_all_xattrs(inode, ipage, index, len, name,
+                               &entry, &base_addr);
        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 +493,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
                memcpy(buffer, pval, size);
        }
        error = size;
-
-cleanup:
+out:
        kzfree(base_addr);
        return error;
 }
@@ -445,6 +542,13 @@ 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 (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 +583,17 @@ static int __f2fs_setxattr(struct inode *inode, int index,
 
        found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
 
-       if ((flags & XATTR_REPLACE) && !found) {
+       if (found) {
+               if ((flags & XATTR_CREATE)) {
+                       error = -EEXIST;
+                       goto exit;
+               }
+
+               if (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;