Merge tag 'for-linus-20190524' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / fs / ubifs / xattr.c
index f5ad1ed..bcfed27 100644 (file)
 #include <linux/slab.h>
 #include <linux/xattr.h>
 
-/*
- * Limit the number of extended attributes per inode so that the total size
- * (@xattr_size) is guaranteeded to fit in an 'unsigned int'.
- */
-#define MAX_XATTRS_PER_INODE 65535
-
 /*
  * Extended attribute type constants.
  *
@@ -106,7 +100,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
                                .new_ino_d = ALIGN(size, 8), .dirtied_ino = 1,
                                .dirtied_ino_d = ALIGN(host_ui->data_len, 8) };
 
-       if (host_ui->xattr_cnt >= MAX_XATTRS_PER_INODE) {
+       if (host_ui->xattr_cnt >= ubifs_xattr_max_cnt(c)) {
                ubifs_err(c, "inode %lu already has too many xattrs (%d), cannot create more",
                          host->i_ino, host_ui->xattr_cnt);
                return -ENOSPC;
@@ -507,6 +501,69 @@ out_cancel:
        return err;
 }
 
+int ubifs_purge_xattrs(struct inode *host)
+{
+       union ubifs_key key;
+       struct ubifs_info *c = host->i_sb->s_fs_info;
+       struct ubifs_dent_node *xent, *pxent = NULL;
+       struct inode *xino;
+       struct fscrypt_name nm = {0};
+       int err;
+
+       if (ubifs_inode(host)->xattr_cnt < ubifs_xattr_max_cnt(c))
+               return 0;
+
+       ubifs_warn(c, "inode %lu has too many xattrs, doing a non-atomic deletion",
+                  host->i_ino);
+
+       lowest_xent_key(c, &key, host->i_ino);
+       while (1) {
+               xent = ubifs_tnc_next_ent(c, &key, &nm);
+               if (IS_ERR(xent)) {
+                       err = PTR_ERR(xent);
+                       break;
+               }
+
+               fname_name(&nm) = xent->name;
+               fname_len(&nm) = le16_to_cpu(xent->nlen);
+
+               xino = ubifs_iget(c->vfs_sb, le64_to_cpu(xent->inum));
+               if (IS_ERR(xino)) {
+                       err = PTR_ERR(xino);
+                       ubifs_err(c, "dead directory entry '%s', error %d",
+                                 xent->name, err);
+                       ubifs_ro_mode(c, err);
+                       kfree(pxent);
+                       return err;
+               }
+
+               ubifs_assert(c, ubifs_inode(xino)->xattr);
+
+               clear_nlink(xino);
+               err = remove_xattr(c, host, xino, &nm);
+               if (err) {
+                       kfree(pxent);
+                       iput(xino);
+                       ubifs_err(c, "cannot remove xattr, error %d", err);
+                       return err;
+               }
+
+               iput(xino);
+
+               kfree(pxent);
+               pxent = xent;
+               key_read(c, &xent->key, &key);
+       }
+
+       kfree(pxent);
+       if (err != -ENOENT) {
+               ubifs_err(c, "cannot find next direntry, error %d", err);
+               return err;
+       }
+
+       return 0;
+}
+
 /**
  * ubifs_evict_xattr_inode - Evict an xattr inode.
  * @c: UBIFS file-system description object