obj-$(CONFIG_OVERLAY_FS) += overlay.o
 
 overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
-               copy_up.o export.o params.o
+               copy_up.o export.o params.o xattrs.o
 
        return p;
 }
 
-bool ovl_is_private_xattr(struct super_block *sb, const char *name)
-{
-       struct ovl_fs *ofs = OVL_FS(sb);
-
-       if (ofs->config.userxattr)
-               return strncmp(name, OVL_XATTR_USER_PREFIX,
-                              sizeof(OVL_XATTR_USER_PREFIX) - 1) == 0;
-       else
-               return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
-                              sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1) == 0;
-}
-
-int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
-                 const void *value, size_t size, int flags)
-{
-       int err;
-       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
-       struct dentry *upperdentry = ovl_i_dentry_upper(inode);
-       struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
-       struct path realpath;
-       const struct cred *old_cred;
-
-       if (!value && !upperdentry) {
-               ovl_path_lower(dentry, &realpath);
-               old_cred = ovl_override_creds(dentry->d_sb);
-               err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
-               revert_creds(old_cred);
-               if (err < 0)
-                       goto out;
-       }
-
-       if (!upperdentry) {
-               err = ovl_copy_up(dentry);
-               if (err)
-                       goto out;
-
-               realdentry = ovl_dentry_upper(dentry);
-       }
-
-       err = ovl_want_write(dentry);
-       if (err)
-               goto out;
-
-       old_cred = ovl_override_creds(dentry->d_sb);
-       if (value) {
-               err = ovl_do_setxattr(ofs, realdentry, name, value, size,
-                                     flags);
-       } else {
-               WARN_ON(flags != XATTR_REPLACE);
-               err = ovl_do_removexattr(ofs, realdentry, name);
-       }
-       revert_creds(old_cred);
-       ovl_drop_write(dentry);
-
-       /* copy c/mtime */
-       ovl_copyattr(inode);
-out:
-       return err;
-}
-
-int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
-                 void *value, size_t size)
-{
-       ssize_t res;
-       const struct cred *old_cred;
-       struct path realpath;
-
-       ovl_i_path_real(inode, &realpath);
-       old_cred = ovl_override_creds(dentry->d_sb);
-       res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
-       revert_creds(old_cred);
-       return res;
-}
-
-static bool ovl_can_list(struct super_block *sb, const char *s)
-{
-       /* Never list private (.overlay) */
-       if (ovl_is_private_xattr(sb, s))
-               return false;
-
-       /* List all non-trusted xattrs */
-       if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
-               return true;
-
-       /* list other trusted for superuser only */
-       return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
-}
-
-ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
-{
-       struct dentry *realdentry = ovl_dentry_real(dentry);
-       ssize_t res;
-       size_t len;
-       char *s;
-       const struct cred *old_cred;
-
-       old_cred = ovl_override_creds(dentry->d_sb);
-       res = vfs_listxattr(realdentry, list, size);
-       revert_creds(old_cred);
-       if (res <= 0 || size == 0)
-               return res;
-
-       /* filter out private xattrs */
-       for (s = list, len = res; len;) {
-               size_t slen = strnlen(s, len) + 1;
-
-               /* underlying fs providing us with an broken xattr list? */
-               if (WARN_ON(slen > len))
-                       return -EIO;
-
-               len -= slen;
-               if (!ovl_can_list(dentry->d_sb, s)) {
-                       res -= slen;
-                       memmove(s, s + slen, len);
-               } else {
-                       s += slen;
-               }
-       }
-
-       return res;
-}
-
 #ifdef CONFIG_FS_POSIX_ACL
 /*
  * Apply the idmapping of the layer to POSIX ACLs. The caller must pass a clone
 
 unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
                           struct dentry *upperdentry,
                           unsigned int fallback);
-int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
-               struct iattr *attr);
-int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
-               struct kstat *stat, u32 request_mask, unsigned int flags);
 int ovl_permission(struct mnt_idmap *idmap, struct inode *inode,
                   int mask);
-int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
-                 const void *value, size_t size, int flags);
-int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
-                 void *value, size_t size);
-ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
 
 #ifdef CONFIG_FS_POSIX_ACL
 struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
 {
        return (!ovl_upper_mnt(ofs) || !ofs->workdir);
 }
+
+/* xattr.c */
+
+const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs);
+int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+               struct iattr *attr);
+int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
+               struct kstat *stat, u32 request_mask, unsigned int flags);
+ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
 
        return ok;
 }
 
-static int ovl_own_xattr_get(const struct xattr_handler *handler,
-                            struct dentry *dentry, struct inode *inode,
-                            const char *name, void *buffer, size_t size)
-{
-       return -EOPNOTSUPP;
-}
-
-static int ovl_own_xattr_set(const struct xattr_handler *handler,
-                            struct mnt_idmap *idmap,
-                            struct dentry *dentry, struct inode *inode,
-                            const char *name, const void *value,
-                            size_t size, int flags)
-{
-       return -EOPNOTSUPP;
-}
-
-static int ovl_other_xattr_get(const struct xattr_handler *handler,
-                              struct dentry *dentry, struct inode *inode,
-                              const char *name, void *buffer, size_t size)
-{
-       return ovl_xattr_get(dentry, inode, name, buffer, size);
-}
-
-static int ovl_other_xattr_set(const struct xattr_handler *handler,
-                              struct mnt_idmap *idmap,
-                              struct dentry *dentry, struct inode *inode,
-                              const char *name, const void *value,
-                              size_t size, int flags)
-{
-       return ovl_xattr_set(dentry, inode, name, value, size, flags);
-}
-
-static const struct xattr_handler ovl_own_trusted_xattr_handler = {
-       .prefix = OVL_XATTR_TRUSTED_PREFIX,
-       .get = ovl_own_xattr_get,
-       .set = ovl_own_xattr_set,
-};
-
-static const struct xattr_handler ovl_own_user_xattr_handler = {
-       .prefix = OVL_XATTR_USER_PREFIX,
-       .get = ovl_own_xattr_get,
-       .set = ovl_own_xattr_set,
-};
-
-static const struct xattr_handler ovl_other_xattr_handler = {
-       .prefix = "", /* catch all */
-       .get = ovl_other_xattr_get,
-       .set = ovl_other_xattr_set,
-};
-
-static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
-       &ovl_own_trusted_xattr_handler,
-       &ovl_other_xattr_handler,
-       NULL
-};
-
-static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
-       &ovl_own_user_xattr_handler,
-       &ovl_other_xattr_handler,
-       NULL
-};
-
 static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
                          struct inode **ptrap, const char *name)
 {
        cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
 
        sb->s_magic = OVERLAYFS_SUPER_MAGIC;
-       sb->s_xattr = ofs->config.userxattr ? ovl_user_xattr_handlers :
-               ovl_trusted_xattr_handlers;
+       sb->s_xattr = ovl_xattr_handlers(ofs);
        sb->s_fs_info = ofs;
 #ifdef CONFIG_FS_POSIX_ACL
        sb->s_flags |= SB_POSIXACL;
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include "overlayfs.h"
+
+bool ovl_is_private_xattr(struct super_block *sb, const char *name)
+{
+       struct ovl_fs *ofs = OVL_FS(sb);
+
+       if (ofs->config.userxattr)
+               return strncmp(name, OVL_XATTR_USER_PREFIX,
+                              sizeof(OVL_XATTR_USER_PREFIX) - 1) == 0;
+       else
+               return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
+                              sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1) == 0;
+}
+
+static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
+                        const void *value, size_t size, int flags)
+{
+       int err;
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
+       struct dentry *upperdentry = ovl_i_dentry_upper(inode);
+       struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
+       struct path realpath;
+       const struct cred *old_cred;
+
+       if (!value && !upperdentry) {
+               ovl_path_lower(dentry, &realpath);
+               old_cred = ovl_override_creds(dentry->d_sb);
+               err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
+               revert_creds(old_cred);
+               if (err < 0)
+                       goto out;
+       }
+
+       if (!upperdentry) {
+               err = ovl_copy_up(dentry);
+               if (err)
+                       goto out;
+
+               realdentry = ovl_dentry_upper(dentry);
+       }
+
+       err = ovl_want_write(dentry);
+       if (err)
+               goto out;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       if (value) {
+               err = ovl_do_setxattr(ofs, realdentry, name, value, size,
+                                     flags);
+       } else {
+               WARN_ON(flags != XATTR_REPLACE);
+               err = ovl_do_removexattr(ofs, realdentry, name);
+       }
+       revert_creds(old_cred);
+       ovl_drop_write(dentry);
+
+       /* copy c/mtime */
+       ovl_copyattr(inode);
+out:
+       return err;
+}
+
+static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
+                        void *value, size_t size)
+{
+       ssize_t res;
+       const struct cred *old_cred;
+       struct path realpath;
+
+       ovl_i_path_real(inode, &realpath);
+       old_cred = ovl_override_creds(dentry->d_sb);
+       res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
+       revert_creds(old_cred);
+       return res;
+}
+
+static bool ovl_can_list(struct super_block *sb, const char *s)
+{
+       /* Never list private (.overlay) */
+       if (ovl_is_private_xattr(sb, s))
+               return false;
+
+       /* List all non-trusted xattrs */
+       if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
+               return true;
+
+       /* list other trusted for superuser only */
+       return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
+}
+
+ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+       struct dentry *realdentry = ovl_dentry_real(dentry);
+       ssize_t res;
+       size_t len;
+       char *s;
+       const struct cred *old_cred;
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       res = vfs_listxattr(realdentry, list, size);
+       revert_creds(old_cred);
+       if (res <= 0 || size == 0)
+               return res;
+
+       /* filter out private xattrs */
+       for (s = list, len = res; len;) {
+               size_t slen = strnlen(s, len) + 1;
+
+               /* underlying fs providing us with an broken xattr list? */
+               if (WARN_ON(slen > len))
+                       return -EIO;
+
+               len -= slen;
+               if (!ovl_can_list(dentry->d_sb, s)) {
+                       res -= slen;
+                       memmove(s, s + slen, len);
+               } else {
+                       s += slen;
+               }
+       }
+
+       return res;
+}
+
+static int ovl_own_xattr_get(const struct xattr_handler *handler,
+                            struct dentry *dentry, struct inode *inode,
+                            const char *name, void *buffer, size_t size)
+{
+       return -EOPNOTSUPP;
+}
+
+static int ovl_own_xattr_set(const struct xattr_handler *handler,
+                            struct mnt_idmap *idmap,
+                            struct dentry *dentry, struct inode *inode,
+                            const char *name, const void *value,
+                            size_t size, int flags)
+{
+       return -EOPNOTSUPP;
+}
+
+static int ovl_other_xattr_get(const struct xattr_handler *handler,
+                              struct dentry *dentry, struct inode *inode,
+                              const char *name, void *buffer, size_t size)
+{
+       return ovl_xattr_get(dentry, inode, name, buffer, size);
+}
+
+static int ovl_other_xattr_set(const struct xattr_handler *handler,
+                              struct mnt_idmap *idmap,
+                              struct dentry *dentry, struct inode *inode,
+                              const char *name, const void *value,
+                              size_t size, int flags)
+{
+       return ovl_xattr_set(dentry, inode, name, value, size, flags);
+}
+
+static const struct xattr_handler ovl_own_trusted_xattr_handler = {
+       .prefix = OVL_XATTR_TRUSTED_PREFIX,
+       .get = ovl_own_xattr_get,
+       .set = ovl_own_xattr_set,
+};
+
+static const struct xattr_handler ovl_own_user_xattr_handler = {
+       .prefix = OVL_XATTR_USER_PREFIX,
+       .get = ovl_own_xattr_get,
+       .set = ovl_own_xattr_set,
+};
+
+static const struct xattr_handler ovl_other_xattr_handler = {
+       .prefix = "", /* catch all */
+       .get = ovl_other_xattr_get,
+       .set = ovl_other_xattr_set,
+};
+
+static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
+       &ovl_own_trusted_xattr_handler,
+       &ovl_other_xattr_handler,
+       NULL
+};
+
+static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
+       &ovl_own_user_xattr_handler,
+       &ovl_other_xattr_handler,
+       NULL
+};
+
+const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
+{
+       return ofs->config.userxattr ? ovl_user_xattr_handlers :
+               ovl_trusted_xattr_handlers;
+}
+