bcachefs: Errcodes can now subtype standard error codes
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 18 Sep 2022 19:43:50 +0000 (15:43 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:40 +0000 (17:09 -0400)
The next patch is going to be adding private error codes for all the
places we return -ENOSPC.

Additionally, this patch updates return paths at all module boundaries
to call bch2_err_class(), to return the standard error code.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/errcode.c
fs/bcachefs/errcode.h
fs/bcachefs/fs-io.c
fs/bcachefs/fs-ioctl.c
fs/bcachefs/fs.c
fs/bcachefs/sysfs.c
fs/bcachefs/xattr.c

index 9da8a5973af06ceaf7624d929b9a86c0ee387be1..cc9ce0be356e20931a44df8e6a0ce0944be799a0 100644 (file)
@@ -15,7 +15,7 @@ static const char * const bch2_errcode_strs[] = {
 #define BCH_ERR_0      0
 
 static unsigned bch2_errcode_parents[] = {
-#define x(class, err) [BCH_ERR_##err - BCH_ERR_START] = BCH_ERR_##class,
+#define x(class, err) [BCH_ERR_##err - BCH_ERR_START] = class,
        BCH_ERRCODES()
 #undef x
 };
@@ -49,3 +49,14 @@ bool __bch2_err_matches(int err, int class)
 
        return err == class;
 }
+
+int __bch2_err_class(int err)
+{
+       err = -err;
+       BUG_ON((unsigned) err >= BCH_ERR_MAX);
+
+       while (err >= BCH_ERR_START && bch2_errcode_parents[err - BCH_ERR_START])
+               err = bch2_errcode_parents[err - BCH_ERR_START];
+
+       return -err;
+}
index 15a1be2fcc84e68df6205bc8560fffda72e2a4d1..2088cc5a4f3c1d23f9343eec43ff4d06a3f7ca76 100644 (file)
@@ -3,51 +3,51 @@
 #define _BCACHEFS_ERRCODE_H
 
 #define BCH_ERRCODES()                                                 \
-       x(0,                    open_buckets_empty)                     \
-       x(0,                    freelist_empty)                         \
-       x(freelist_empty,       no_buckets_found)                       \
-       x(0,                    insufficient_devices)                   \
-       x(0,                    transaction_restart)                    \
-       x(transaction_restart,  transaction_restart_fault_inject)       \
-       x(transaction_restart,  transaction_restart_relock)             \
-       x(transaction_restart,  transaction_restart_relock_path)        \
-       x(transaction_restart,  transaction_restart_relock_path_intent) \
-       x(transaction_restart,  transaction_restart_relock_after_fill)  \
-       x(transaction_restart,  transaction_restart_too_many_iters)     \
-       x(transaction_restart,  transaction_restart_lock_node_reused)   \
-       x(transaction_restart,  transaction_restart_fill_relock)        \
-       x(transaction_restart,  transaction_restart_fill_mem_alloc_fail)\
-       x(transaction_restart,  transaction_restart_mem_realloced)      \
-       x(transaction_restart,  transaction_restart_in_traverse_all)    \
-       x(transaction_restart,  transaction_restart_would_deadlock)     \
-       x(transaction_restart,  transaction_restart_would_deadlock_write)\
-       x(transaction_restart,  transaction_restart_upgrade)            \
-       x(transaction_restart,  transaction_restart_key_cache_upgrade)  \
-       x(transaction_restart,  transaction_restart_key_cache_fill)     \
-       x(transaction_restart,  transaction_restart_key_cache_raced)    \
-       x(transaction_restart,  transaction_restart_key_cache_realloced)\
-       x(transaction_restart,  transaction_restart_journal_preres_get) \
-       x(transaction_restart,  transaction_restart_nested)             \
-       x(0,                    no_btree_node)                          \
-       x(no_btree_node,        no_btree_node_relock)                   \
-       x(no_btree_node,        no_btree_node_upgrade)                  \
-       x(no_btree_node,        no_btree_node_drop)                     \
-       x(no_btree_node,        no_btree_node_lock_root)                \
-       x(no_btree_node,        no_btree_node_up)                       \
-       x(no_btree_node,        no_btree_node_down)                     \
-       x(no_btree_node,        no_btree_node_init)                     \
-       x(no_btree_node,        no_btree_node_cached)                   \
-       x(0,                    lock_fail_node_reused)                  \
-       x(0,                    lock_fail_root_changed)                 \
-       x(0,                    journal_reclaim_would_deadlock)         \
-       x(0,                    fsck)                                   \
-       x(fsck,                 fsck_fix)                               \
-       x(fsck,                 fsck_ignore)                            \
-       x(fsck,                 fsck_errors_not_fixed)                  \
-       x(fsck,                 fsck_repair_unimplemented)              \
-       x(fsck,                 fsck_repair_impossible)                 \
-       x(0,                    need_snapshot_cleanup)                  \
-       x(0,                    need_topology_repair)
+       x(0,                            open_buckets_empty)                     \
+       x(0,                            freelist_empty)                         \
+       x(BCH_ERR_freelist_empty,       no_buckets_found)                       \
+       x(0,                            insufficient_devices)                   \
+       x(0,                            transaction_restart)                    \
+       x(BCH_ERR_transaction_restart,  transaction_restart_fault_inject)       \
+       x(BCH_ERR_transaction_restart,  transaction_restart_relock)             \
+       x(BCH_ERR_transaction_restart,  transaction_restart_relock_path)        \
+       x(BCH_ERR_transaction_restart,  transaction_restart_relock_path_intent) \
+       x(BCH_ERR_transaction_restart,  transaction_restart_relock_after_fill)  \
+       x(BCH_ERR_transaction_restart,  transaction_restart_too_many_iters)     \
+       x(BCH_ERR_transaction_restart,  transaction_restart_lock_node_reused)   \
+       x(BCH_ERR_transaction_restart,  transaction_restart_fill_relock)        \
+       x(BCH_ERR_transaction_restart,  transaction_restart_fill_mem_alloc_fail)\
+       x(BCH_ERR_transaction_restart,  transaction_restart_mem_realloced)      \
+       x(BCH_ERR_transaction_restart,  transaction_restart_in_traverse_all)    \
+       x(BCH_ERR_transaction_restart,  transaction_restart_would_deadlock)     \
+       x(BCH_ERR_transaction_restart,  transaction_restart_would_deadlock_write)\
+       x(BCH_ERR_transaction_restart,  transaction_restart_upgrade)            \
+       x(BCH_ERR_transaction_restart,  transaction_restart_key_cache_upgrade)  \
+       x(BCH_ERR_transaction_restart,  transaction_restart_key_cache_fill)     \
+       x(BCH_ERR_transaction_restart,  transaction_restart_key_cache_raced)    \
+       x(BCH_ERR_transaction_restart,  transaction_restart_key_cache_realloced)\
+       x(BCH_ERR_transaction_restart,  transaction_restart_journal_preres_get) \
+       x(BCH_ERR_transaction_restart,  transaction_restart_nested)             \
+       x(0,                            no_btree_node)                          \
+       x(BCH_ERR_no_btree_node,        no_btree_node_relock)                   \
+       x(BCH_ERR_no_btree_node,        no_btree_node_upgrade)                  \
+       x(BCH_ERR_no_btree_node,        no_btree_node_drop)                     \
+       x(BCH_ERR_no_btree_node,        no_btree_node_lock_root)                \
+       x(BCH_ERR_no_btree_node,        no_btree_node_up)                       \
+       x(BCH_ERR_no_btree_node,        no_btree_node_down)                     \
+       x(BCH_ERR_no_btree_node,        no_btree_node_init)                     \
+       x(BCH_ERR_no_btree_node,        no_btree_node_cached)                   \
+       x(0,                            lock_fail_node_reused)                  \
+       x(0,                            lock_fail_root_changed)                 \
+       x(0,                            journal_reclaim_would_deadlock)         \
+       x(0,                            fsck)                                   \
+       x(BCH_ERR_fsck,                 fsck_fix)                               \
+       x(BCH_ERR_fsck,                 fsck_ignore)                            \
+       x(BCH_ERR_fsck,                 fsck_errors_not_fixed)                  \
+       x(BCH_ERR_fsck,                 fsck_repair_unimplemented)              \
+       x(BCH_ERR_fsck,                 fsck_repair_impossible)                 \
+       x(0,                            need_snapshot_cleanup)                  \
+       x(0,                            need_topology_repair)
 
 enum bch_errcode {
        BCH_ERR_START           = 2048,
@@ -71,4 +71,11 @@ static inline bool _bch2_err_matches(int err, int class)
        _bch2_err_matches(_err, _class);                \
 })
 
+int __bch2_err_class(int);
+
+static inline long bch2_err_class(long err)
+{
+       return err < 0 ? __bch2_err_class(err) : err;
+}
+
 #endif /* _BCACHFES_ERRCODE_H */
index 9f1ecb8d7b3bdb39fce2b9f76f24d70c57af9ff0..c83e1de9a39a275040ed546debf25f2943bd7822 100644 (file)
@@ -1186,7 +1186,7 @@ int bch2_read_folio(struct file *file, struct folio *folio)
 
        ret = bch2_read_single_page(page, page->mapping);
        folio_unlock(folio);
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* writepages: */
@@ -1465,7 +1465,7 @@ int bch2_writepages(struct address_space *mapping, struct writeback_control *wbc
        if (w.io)
                bch2_writepage_do_io(&w);
        blk_finish_plug(&plug);
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* buffered writes: */
@@ -1550,7 +1550,7 @@ err_unlock:
        bch2_pagecache_add_put(&inode->ei_pagecache_lock);
        kfree(res);
        *fsdata = NULL;
-       return ret;
+       return bch2_err_class(ret);
 }
 
 int bch2_write_end(struct file *file, struct address_space *mapping,
@@ -1975,7 +1975,7 @@ ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                                                iocb->ki_pos,
                                                iocb->ki_pos + count - 1);
                        if (ret < 0)
-                               return ret;
+                               goto out;
                }
 
                file_accessed(file);
@@ -1991,8 +1991,8 @@ ssize_t bch2_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                ret = generic_file_read_iter(iocb, iter);
                bch2_pagecache_add_put(&inode->ei_pagecache_lock);
        }
-
-       return ret;
+out:
+       return bch2_err_class(ret);
 }
 
 /* O_DIRECT writes */
@@ -2224,6 +2224,9 @@ err:
        /* inode->i_dio_count is our ref on inode and thus bch_fs */
        inode_dio_end(&inode->v);
 
+       if (ret < 0)
+               ret = bch2_err_class(ret);
+
        if (!sync) {
                req->ki_complete(req, ret);
                ret = -EIOCBQUEUED;
@@ -2332,8 +2335,10 @@ ssize_t bch2_write_iter(struct kiocb *iocb, struct iov_iter *from)
        struct bch_inode_info *inode = file_bch_inode(file);
        ssize_t ret;
 
-       if (iocb->ki_flags & IOCB_DIRECT)
-               return bch2_direct_write(iocb, from);
+       if (iocb->ki_flags & IOCB_DIRECT) {
+               ret = bch2_direct_write(iocb, from);
+               goto out;
+       }
 
        inode_lock(&inode->v);
 
@@ -2357,8 +2362,8 @@ unlock:
 
        if (ret > 0)
                ret = generic_write_sync(iocb, ret);
-
-       return ret;
+out:
+       return bch2_err_class(ret);
 }
 
 /* fsync: */
@@ -2392,7 +2397,7 @@ int bch2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        ret2 = sync_inode_metadata(&inode->v, 1);
        ret3 = bch2_flush_inode(c, inode_inum(inode));
 
-       return ret ?: ret2 ?: ret3;
+       return bch2_err_class(ret ?: ret2 ?: ret3);
 }
 
 /* truncate: */
@@ -2698,7 +2703,7 @@ int bch2_truncate(struct mnt_idmap *idmap,
        ret = bch2_setattr_nonsize(idmap, inode, iattr);
 err:
        bch2_pagecache_block_put(&inode->ei_pagecache_lock);
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* fallocate: */
@@ -3128,7 +3133,7 @@ long bch2_fallocate_dispatch(struct file *file, int mode,
        inode_unlock(&inode->v);
        percpu_ref_put(&c->writes);
 
-       return ret;
+       return bch2_err_class(ret);
 }
 
 loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
@@ -3206,7 +3211,7 @@ loff_t bch2_remap_file_range(struct file *file_src, loff_t pos_src,
 err:
        bch2_unlock_inodes(INODE_LOCK|INODE_PAGECACHE_BLOCK, src, dst);
 
-       return ret;
+       return bch2_err_class(ret);
 }
 
 /* fseek: */
@@ -3431,18 +3436,26 @@ err:
 
 loff_t bch2_llseek(struct file *file, loff_t offset, int whence)
 {
+       loff_t ret;
+
        switch (whence) {
        case SEEK_SET:
        case SEEK_CUR:
        case SEEK_END:
-               return generic_file_llseek(file, offset, whence);
+               ret = generic_file_llseek(file, offset, whence);
+               break;
        case SEEK_DATA:
-               return bch2_seek_data(file, offset);
+               ret = bch2_seek_data(file, offset);
+               break;
        case SEEK_HOLE:
-               return bch2_seek_hole(file, offset);
+               ret = bch2_seek_hole(file, offset);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
        }
 
-       return -EINVAL;
+       return bch2_err_class(ret);
 }
 
 void bch2_fs_fsio_exit(struct bch_fs *c)
index de94895ace9f9896e7c06bdfda0adf5827987c13..3df2f5f3d1ea5e90252e6c4015b95281a725e0a9 100644 (file)
@@ -455,51 +455,67 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
        struct bch_inode_info *inode = file_bch_inode(file);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
+       long ret;
 
        switch (cmd) {
        case FS_IOC_GETFLAGS:
-               return bch2_ioc_getflags(inode, (int __user *) arg);
+               ret = bch2_ioc_getflags(inode, (int __user *) arg);
+               break;
 
        case FS_IOC_SETFLAGS:
-               return bch2_ioc_setflags(c, file, inode, (int __user *) arg);
+               ret = bch2_ioc_setflags(c, file, inode, (int __user *) arg);
+               break;
 
        case FS_IOC_FSGETXATTR:
-               return bch2_ioc_fsgetxattr(inode, (void __user *) arg);
+               ret = bch2_ioc_fsgetxattr(inode, (void __user *) arg);
+               break;
+
        case FS_IOC_FSSETXATTR:
-               return bch2_ioc_fssetxattr(c, file, inode,
-                                          (void __user *) arg);
+               ret = bch2_ioc_fssetxattr(c, file, inode,
+                                         (void __user *) arg);
+               break;
 
        case BCHFS_IOC_REINHERIT_ATTRS:
-               return bch2_ioc_reinherit_attrs(c, file, inode,
-                                               (void __user *) arg);
+               ret = bch2_ioc_reinherit_attrs(c, file, inode,
+                                              (void __user *) arg);
+               break;
 
        case FS_IOC_GETVERSION:
-               return -ENOTTY;
+               ret = -ENOTTY;
+               break;
+
        case FS_IOC_SETVERSION:
-               return -ENOTTY;
+               ret = -ENOTTY;
+               break;
 
        case FS_IOC_GOINGDOWN:
-               return bch2_ioc_goingdown(c, (u32 __user *) arg);
+               ret = bch2_ioc_goingdown(c, (u32 __user *) arg);
+               break;
 
        case BCH_IOCTL_SUBVOLUME_CREATE: {
                struct bch_ioctl_subvolume i;
 
-               if (copy_from_user(&i, (void __user *) arg, sizeof(i)))
-                       return -EFAULT;
-               return bch2_ioctl_subvolume_create(c, file, i);
+               ret = copy_from_user(&i, (void __user *) arg, sizeof(i))
+                       ? -EFAULT
+                       : bch2_ioctl_subvolume_create(c, file, i);
+               break;
        }
 
        case BCH_IOCTL_SUBVOLUME_DESTROY: {
                struct bch_ioctl_subvolume i;
 
-               if (copy_from_user(&i, (void __user *) arg, sizeof(i)))
-                       return -EFAULT;
-               return bch2_ioctl_subvolume_destroy(c, file, i);
+               ret = copy_from_user(&i, (void __user *) arg, sizeof(i))
+                       ? -EFAULT
+                       : bch2_ioctl_subvolume_destroy(c, file, i);
+               break;
        }
 
        default:
-               return bch2_fs_ioctl(c, cmd, (void __user *) arg);
+               ret = bch2_fs_ioctl(c, cmd, (void __user *) arg);
+               break;
        }
+
+       return bch2_err_class(ret);
 }
 
 #ifdef CONFIG_COMPAT
index af4941862187900053e971772b0fe7bdda5495be..66fcd3e28e0c870cdecf544a523de5822d1a3775 100644 (file)
@@ -419,7 +419,7 @@ static int bch2_mknod(struct mnt_idmap *idmap,
                              (subvol_inum) { 0 }, 0);
 
        if (IS_ERR(inode))
-               return PTR_ERR(inode);
+               return bch2_err_class(PTR_ERR(inode));
 
        d_instantiate(dentry, &inode->v);
        return 0;
@@ -529,7 +529,7 @@ static int bch2_symlink(struct mnt_idmap *idmap,
        inode = __bch2_create(idmap, dir, dentry, S_IFLNK|S_IRWXUGO, 0,
                              (subvol_inum) { 0 }, BCH_CREATE_TMPFILE);
        if (unlikely(IS_ERR(inode)))
-               return PTR_ERR(inode);
+               return bch2_err_class(PTR_ERR(inode));
 
        inode_lock(&inode->v);
        ret = page_symlink(&inode->v, symname, strlen(symname) + 1);
@@ -769,7 +769,7 @@ err_trans:
 err:
        mutex_unlock(&inode->ei_update_lock);
 
-       return ret;
+       return bch2_err_class(ret);
 }
 
 static int bch2_getattr(struct mnt_idmap *idmap,
@@ -839,7 +839,7 @@ static int bch2_tmpfile(struct mnt_idmap *idmap,
                              (subvol_inum) { 0 }, BCH_CREATE_TMPFILE);
 
        if (IS_ERR(inode))
-               return PTR_ERR(inode);
+               return bch2_err_class(PTR_ERR(inode));
 
        d_mark_tmpfile(file, &inode->v);
        d_instantiate(file->f_path.dentry, &inode->v);
@@ -1454,7 +1454,7 @@ static int bch2_vfs_write_inode(struct inode *vinode,
                               ATTR_ATIME|ATTR_MTIME|ATTR_CTIME);
        mutex_unlock(&inode->ei_update_lock);
 
-       return ret;
+       return bch2_err_class(ret);
 }
 
 static void bch2_evict_inode(struct inode *vinode)
@@ -1558,6 +1558,7 @@ static int bch2_statfs(struct dentry *dentry, struct kstatfs *buf)
 static int bch2_sync_fs(struct super_block *sb, int wait)
 {
        struct bch_fs *c = sb->s_fs_info;
+       int ret;
 
        if (c->opts.journal_flush_disabled)
                return 0;
@@ -1567,7 +1568,8 @@ static int bch2_sync_fs(struct super_block *sb, int wait)
                return 0;
        }
 
-       return bch2_journal_flush(&c->journal);
+       ret = bch2_journal_flush(&c->journal);
+       return bch2_err_class(ret);
 }
 
 static struct bch_fs *bch2_path_to_fs(const char *path)
@@ -1623,7 +1625,7 @@ static int bch2_remount(struct super_block *sb, int *flags, char *data)
 
        ret = bch2_parse_mount_opts(c, &opts, data);
        if (ret)
-               return ret;
+               goto err;
 
        if (opts.read_only != c->opts.read_only) {
                down_write(&c->state_lock);
@@ -1637,7 +1639,8 @@ static int bch2_remount(struct super_block *sb, int *flags, char *data)
                        if (ret) {
                                bch_err(c, "error going rw: %i", ret);
                                up_write(&c->state_lock);
-                               return -EINVAL;
+                               ret = -EINVAL;
+                               goto err;
                        }
 
                        sb->s_flags &= ~SB_RDONLY;
@@ -1650,8 +1653,8 @@ static int bch2_remount(struct super_block *sb, int *flags, char *data)
 
        if (opts.errors >= 0)
                c->opts.errors = opts.errors;
-
-       return ret;
+err:
+       return bch2_err_class(ret);
 }
 
 static int bch2_show_devname(struct seq_file *seq, struct dentry *root)
index 96c107e0508e7c86a1b66d6b8c35a0199c4ea66a..50b3ba92c5ae9e77732be33cdab4a0a6eb1f4685 100644 (file)
 #include "util.h"
 
 #define SYSFS_OPS(type)                                                        \
-const struct sysfs_ops type ## _sysfs_ops = {                                  \
+const struct sysfs_ops type ## _sysfs_ops = {                          \
        .show   = type ## _show,                                        \
        .store  = type ## _store                                        \
 }
 
 #define SHOW(fn)                                                       \
 static ssize_t fn ## _to_text(struct printbuf *,                       \
-                             struct kobject *, struct attribute *);\
+                             struct kobject *, struct attribute *);    \
                                                                        \
 static ssize_t fn ## _show(struct kobject *kobj, struct attribute *attr,\
                           char *buf)                                   \
@@ -66,15 +66,24 @@ static ssize_t fn ## _show(struct kobject *kobj, struct attribute *attr,\
                memcpy(buf, out.buf, ret);                              \
        }                                                               \
        printbuf_exit(&out);                                            \
-       return ret;                                                     \
+       return bch2_err_class(ret);                                     \
 }                                                                      \
                                                                        \
 static ssize_t fn ## _to_text(struct printbuf *out, struct kobject *kobj,\
                              struct attribute *attr)
 
 #define STORE(fn)                                                      \
+static ssize_t fn ## _store_inner(struct kobject *, struct attribute *,\
+                           const char *, size_t);                      \
+                                                                       \
 static ssize_t fn ## _store(struct kobject *kobj, struct attribute *attr,\
                            const char *buf, size_t size)               \
+{                                                                      \
+       return bch2_err_class(fn##_store_inner(kobj, attr, buf, size)); \
+}                                                                      \
+                                                                       \
+static ssize_t fn ## _store_inner(struct kobject *kobj, struct attribute *attr,\
+                                 const char *buf, size_t size)
 
 #define __sysfs_attribute(_name, _mode)                                        \
        static struct attribute sysfs_##_name =                         \
index 37793b3357d331c0aca827ee5ec7d8ffdb2f8318..2b9fb4941e9f7253a6e891cda2a202ebaa437f93 100644 (file)
@@ -350,17 +350,19 @@ err:
        bch2_trans_exit(&trans);
 
        if (ret)
-               return ret;
+               goto out;
 
        ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, false);
        if (ret)
-               return ret;
+               goto out;
 
        ret = bch2_xattr_list_bcachefs(c, &inode->ei_inode, &buf, true);
        if (ret)
-               return ret;
+               goto out;
 
        return buf.used;
+out:
+       return bch2_err_class(ret);
 }
 
 static int bch2_xattr_get_handler(const struct xattr_handler *handler,
@@ -369,8 +371,10 @@ static int bch2_xattr_get_handler(const struct xattr_handler *handler,
 {
        struct bch_inode_info *inode = to_bch_ei(vinode);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
+       int ret;
 
-       return bch2_xattr_get(c, inode, name, buffer, size, handler->flags);
+       ret = bch2_xattr_get(c, inode, name, buffer, size, handler->flags);
+       return bch2_err_class(ret);
 }
 
 static int bch2_xattr_set_handler(const struct xattr_handler *handler,
@@ -382,11 +386,13 @@ static int bch2_xattr_set_handler(const struct xattr_handler *handler,
        struct bch_inode_info *inode = to_bch_ei(vinode);
        struct bch_fs *c = inode->v.i_sb->s_fs_info;
        struct bch_hash_info hash = bch2_hash_info_init(c, &inode->ei_inode);
+       int ret;
 
-       return bch2_trans_do(c, NULL, NULL, 0,
+       ret = bch2_trans_do(c, NULL, NULL, 0,
                        bch2_xattr_set(&trans, inode_inum(inode), &hash,
                                       name, value, size,
                                       handler->flags, flags));
+       return bch2_err_class(ret);
 }
 
 static const struct xattr_handler bch_xattr_user_handler = {