summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsacl.c2
-rw-r--r--fs/nfs/nfs4idmap.c2
-rw-r--r--fs/ubifs/Kconfig24
-rw-r--r--fs/ubifs/Makefile3
-rw-r--r--fs/ubifs/dir.c17
-rw-r--r--fs/ubifs/file.c4
-rw-r--r--fs/ubifs/journal.c78
-rw-r--r--fs/ubifs/misc.h8
-rw-r--r--fs/ubifs/orphan.c208
-rw-r--r--fs/ubifs/super.c2
-rw-r--r--fs/ubifs/ubifs.h19
-rw-r--r--fs/ubifs/xattr.c73
12 files changed, 349 insertions, 91 deletions
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index f5b87a8f75c4..b59b0ccb61e9 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -360,7 +360,7 @@ init_cifs_idmap(void)
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ,
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto failed_put_cred;
diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
index c99a887100db..3d4602d66068 100644
--- a/fs/nfs/nfs4idmap.c
+++ b/fs/nfs/nfs4idmap.c
@@ -201,7 +201,7 @@ int nfs_idmap_init(void)
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ,
- KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto failed_put_cred;
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 7ff7712f284e..87ec3c28e61f 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -50,3 +50,27 @@ config UBIFS_ATIME_SUPPORT
strictatime is the "heavy", relatime is "lighter", etc.
If unsure, say 'N'
+
+config UBIFS_FS_XATTR
+ bool "UBIFS XATTR support"
+ depends on UBIFS_FS
+ default y
+ help
+ Saying Y here includes support for extended attributes (xattrs).
+ Xattrs are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page).
+
+ If unsure, say Y.
+
+config UBIFS_FS_SECURITY
+ bool "UBIFS Security Labels"
+ depends on UBIFS_FS && UBIFS_FS_XATTR
+ default y
+ help
+ Security labels provide an access control facility to support Linux
+ Security Models (LSMs) accepted by AppArmor, SELinux, Smack and TOMOYO
+ Linux. This option enables an extended attribute handler for file
+ security labels in the ubifs filesystem, so that it requires enabling
+ the extended attribute support in advance.
+
+ If you are not using a security module, say N.
diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile
index 2c6f0cb816b4..29b5c0d6488c 100644
--- a/fs/ubifs/Makefile
+++ b/fs/ubifs/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_UBIFS_FS) += ubifs.o
ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o
ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
-ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o xattr.o debug.o
+ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o debug.o
+ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index f5d5ee43ae6e..a52871ee4d16 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -578,6 +578,11 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu",
dentry, inode->i_ino,
inode->i_nlink, dir->i_ino);
+
+ err = ubifs_purge_xattrs(inode);
+ if (err)
+ return err;
+
ubifs_assert(mutex_is_locked(&dir->i_mutex));
ubifs_assert(mutex_is_locked(&inode->i_mutex));
err = dbg_check_synced_i_size(c, inode);
@@ -673,6 +678,10 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
if (err)
return err;
+ err = ubifs_purge_xattrs(inode);
+ if (err)
+ return err;
+
err = ubifs_budget_space(c, &req);
if (err) {
if (err != -ENOSPC)
@@ -1004,9 +1013,13 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
new_dentry, new_dir->i_ino);
ubifs_assert(mutex_is_locked(&old_dir->i_mutex));
ubifs_assert(mutex_is_locked(&new_dir->i_mutex));
- if (unlink)
+ if (unlink) {
ubifs_assert(mutex_is_locked(&new_inode->i_mutex));
+ err = ubifs_purge_xattrs(new_inode);
+ if (err)
+ return err;
+ }
if (unlink && is_dir) {
err = check_dir_empty(c, new_inode);
@@ -1188,10 +1201,12 @@ const struct inode_operations ubifs_dir_inode_operations = {
.rename = ubifs_rename,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
.setxattr = ubifs_setxattr,
.getxattr = ubifs_getxattr,
.listxattr = ubifs_listxattr,
.removexattr = ubifs_removexattr,
+#endif
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index e6d0a7df341d..e385d62fbc65 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1622,10 +1622,12 @@ const struct address_space_operations ubifs_file_address_operations = {
const struct inode_operations ubifs_file_inode_operations = {
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
.setxattr = ubifs_setxattr,
.getxattr = ubifs_getxattr,
.listxattr = ubifs_listxattr,
.removexattr = ubifs_removexattr,
+#endif
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
@@ -1636,10 +1638,12 @@ const struct inode_operations ubifs_symlink_inode_operations = {
.follow_link = simple_follow_link,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
+#ifdef CONFIG_UBIFS_FS_XATTR
.setxattr = ubifs_setxattr,
.getxattr = ubifs_getxattr,
.listxattr = ubifs_listxattr,
.removexattr = ubifs_removexattr,
+#endif
#ifdef CONFIG_UBIFS_ATIME_SUPPORT
.update_time = ubifs_update_time,
#endif
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 539fa934ed93..3f04fb2d7995 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -789,9 +789,11 @@ out_free:
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
{
int err, lnum, offs;
- struct ubifs_ino_node *ino;
+ struct ubifs_ino_node *ino, *ino_start;
struct ubifs_inode *ui = ubifs_inode(inode);
- int sync = 0, len = UBIFS_INO_NODE_SZ, last_reference = !inode->i_nlink;
+ int sync = 0, write_len = 0, ilen = UBIFS_INO_NODE_SZ;
+ int last_reference = !inode->i_nlink;
+ int kill_xattrs = ui->xattr_cnt && last_reference;
dbg_jnl("ino %lu, nlink %u", inode->i_ino, inode->i_nlink);
@@ -800,20 +802,72 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
* need to synchronize the write-buffer either.
*/
if (!last_reference) {
- len += ui->data_len;
+ ilen += ui->data_len;
sync = IS_SYNC(inode);
+ } else if (kill_xattrs) {
+ write_len += UBIFS_INO_NODE_SZ * ui->xattr_cnt;
}
- ino = kmalloc(len, GFP_NOFS);
+
+ write_len += ilen;
+
+ ino_start = ino = kmalloc(write_len, GFP_NOFS);
if (!ino)
return -ENOMEM;
/* Make reservation before allocating sequence numbers */
- err = make_reservation(c, BASEHD, len);
+ err = make_reservation(c, BASEHD, ilen);
if (err)
goto out_free;
+ if (kill_xattrs) {
+ union ubifs_key key;
+ struct qstr nm = { 0 };
+ struct inode *xino;
+ struct ubifs_dent_node *xent, *pxent = NULL;
+
+ if (ui->xattr_cnt >= ubifs_xattr_max_cnt(c)) {
+ ubifs_err(c, "Cannot delete inode, it has too much xattrs!");
+ goto out_release;
+ }
+
+ lowest_xent_key(c, &key, inode->i_ino);
+ while (1) {
+ xent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(xent)) {
+ err = PTR_ERR(xent);
+ if (err == -ENOENT)
+ break;
+
+ goto out_release;
+ }
+
+ nm.name = xent->name;
+ nm.len = le16_to_cpu(xent->nlen);
+
+ xino = ubifs_iget(c->vfs_sb, 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);
+ goto out_release;
+ }
+ ubifs_assert(ubifs_inode(xino)->xattr);
+
+ clear_nlink(xino);
+ pack_inode(c, ino, xino, 0);
+ ino = (void *)ino + UBIFS_INO_NODE_SZ;
+ iput(xino);
+
+ kfree(pxent);
+ pxent = xent;
+ key_read(c, &xent->key, &key);
+ }
+ kfree(pxent);
+ }
+
pack_inode(c, ino, inode, 1);
- err = write_head(c, BASEHD, ino, len, &lnum, &offs, sync);
+ err = write_head(c, BASEHD, ino_start, write_len, &lnum, &offs, sync);
if (err)
goto out_release;
if (!sync)
@@ -826,12 +880,12 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
if (err)
goto out_ro;
ubifs_delete_orphan(c, inode->i_ino);
- err = ubifs_add_dirt(c, lnum, len);
+ err = ubifs_add_dirt(c, lnum, write_len);
} else {
union ubifs_key key;
ino_key_init(c, &key, inode->i_ino);
- err = ubifs_tnc_add(c, &key, lnum, offs, len);
+ err = ubifs_tnc_add(c, &key, lnum, offs, ilen);
}
if (err)
goto out_ro;
@@ -840,7 +894,7 @@ int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode)
spin_lock(&ui->ui_lock);
ui->synced_i_size = ui->ui_size;
spin_unlock(&ui->ui_lock);
- kfree(ino);
+ kfree(ino_start);
return 0;
out_release:
@@ -849,7 +903,7 @@ out_ro:
ubifs_ro_mode(c, err);
finish_reservation(c);
out_free:
- kfree(ino);
+ kfree(ino_start);
return err;
}
@@ -889,8 +943,8 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
ubifs_assert(inode->i_nlink == 0);
- if (ui->del_cmtno != c->cmt_no)
- /* A commit happened for sure */
+ if (ui->xattr_cnt || ui->del_cmtno != c->cmt_no)
+ /* A commit happened for sure or inode hosts xattrs */
return ubifs_jnl_write_inode(c, inode);
down_read(&c->commit_sem);
diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h
index 8ece6ca58c0b..2a39e3c2766f 100644
--- a/fs/ubifs/misc.h
+++ b/fs/ubifs/misc.h
@@ -295,4 +295,12 @@ static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum)
return lnum;
}
+static inline int ubifs_xattr_max_cnt(struct ubifs_info *c)
+{
+ int max_xattrs = (c->leb_size / 2) / UBIFS_INO_NODE_SZ;
+
+ ubifs_assert(max_xattrs < c->max_orphans);
+ return max_xattrs;
+}
+
#endif /* __UBIFS_MISC_H__ */
diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c
index caf2d123e9ee..8b3543e8baf5 100644
--- a/fs/ubifs/orphan.c
+++ b/fs/ubifs/orphan.c
@@ -54,30 +54,24 @@
static int dbg_check_orphans(struct ubifs_info *c);
-/**
- * ubifs_add_orphan - add an orphan.
- * @c: UBIFS file-system description object
- * @inum: orphan inode number
- *
- * Add an orphan. This function is called when an inodes link count drops to
- * zero.
- */
-int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
+static struct ubifs_orphan *orphan_add(struct ubifs_info *c, ino_t inum,
+ struct ubifs_orphan *parent_orphan)
{
struct ubifs_orphan *orphan, *o;
struct rb_node **p, *parent = NULL;
orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS);
if (!orphan)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
orphan->inum = inum;
orphan->new = 1;
+ INIT_LIST_HEAD(&orphan->child_list);
spin_lock(&c->orphan_lock);
if (c->tot_orphans >= c->max_orphans) {
spin_unlock(&c->orphan_lock);
kfree(orphan);
- return -ENFILE;
+ return ERR_PTR(-ENFILE);
}
p = &c->orph_tree.rb_node;
while (*p) {
@@ -91,7 +85,7 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
ubifs_err(c, "orphaned twice");
spin_unlock(&c->orphan_lock);
kfree(orphan);
- return 0;
+ return ERR_PTR(-EINVAL);
}
}
c->tot_orphans += 1;
@@ -100,24 +94,22 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
rb_insert_color(&orphan->rb, &c->orph_tree);
list_add_tail(&orphan->list, &c->orph_list);
list_add_tail(&orphan->new_list, &c->orph_new);
+
+ if (parent_orphan) {
+ list_add_tail(&orphan->child_list,
+ &parent_orphan->child_list);
+ }
+
spin_unlock(&c->orphan_lock);
dbg_gen("ino %lu", (unsigned long)inum);
- return 0;
+ return orphan;
}
-/**
- * ubifs_delete_orphan - delete an orphan.
- * @c: UBIFS file-system description object
- * @inum: orphan inode number
- *
- * Delete an orphan. This function is called when an inode is deleted.
- */
-void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+static struct ubifs_orphan *lookup_orphan(struct ubifs_info *c, ino_t inum)
{
struct ubifs_orphan *o;
struct rb_node *p;
- spin_lock(&c->orphan_lock);
p = c->orph_tree.rb_node;
while (p) {
o = rb_entry(p, struct ubifs_orphan, rb);
@@ -126,37 +118,124 @@ void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
else if (inum > o->inum)
p = p->rb_right;
else {
- if (o->del) {
- spin_unlock(&c->orphan_lock);
- dbg_gen("deleted twice ino %lu",
- (unsigned long)inum);
- return;
- }
- if (o->cmt) {
- o->del = 1;
- o->dnext = c->orph_dnext;
- c->orph_dnext = o;
- spin_unlock(&c->orphan_lock);
- dbg_gen("delete later ino %lu",
- (unsigned long)inum);
- return;
- }
- rb_erase(p, &c->orph_tree);
- list_del(&o->list);
- c->tot_orphans -= 1;
- if (o->new) {
- list_del(&o->new_list);
- c->new_orphans -= 1;
- }
- spin_unlock(&c->orphan_lock);
- kfree(o);
- dbg_gen("inum %lu", (unsigned long)inum);
- return;
+ return o;
}
}
+ return NULL;
+}
+
+static void __orphan_drop(struct ubifs_info *c, struct ubifs_orphan *o)
+{
+ rb_erase(&o->rb, &c->orph_tree);
+ list_del(&o->list);
+ c->tot_orphans -= 1;
+
+ if (o->new) {
+ list_del(&o->new_list);
+ c->new_orphans -= 1;
+ }
+
+ kfree(o);
+}
+
+static void orphan_delete(struct ubifs_info *c, ino_t inum)
+{
+ struct ubifs_orphan *orph, *child_orph, *tmp_o;
+
+ spin_lock(&c->orphan_lock);
+
+ orph = lookup_orphan(c, inum);
+ if (!orph) {
+ spin_unlock(&c->orphan_lock);
+ ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
+ dump_stack();
+
+ return;
+ }
+
+ if (orph->del) {
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("deleted twice ino %lu",
+ (unsigned long)inum);
+ return;
+ }
+
+ if (orph->cmt) {
+ orph->del = 1;
+ orph->dnext = c->orph_dnext;
+ c->orph_dnext = orph;
+ spin_unlock(&c->orphan_lock);
+ dbg_gen("delete later ino %lu",
+ (unsigned long)inum);
+ return;
+ }
+
+ list_for_each_entry_safe(child_orph, tmp_o, &orph->child_list, child_list) {
+ list_del(&child_orph->child_list);
+ __orphan_drop(c, child_orph);
+ }
+
+ __orphan_drop(c, orph);
+
spin_unlock(&c->orphan_lock);
- ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum);
- dump_stack();
+}
+
+/**
+ * ubifs_add_orphan - add an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Add an orphan. This function is called when an inodes link count drops to
+ * zero.
+ */
+int ubifs_add_orphan(struct ubifs_info *c, ino_t inum)
+{
+ int err = 0;
+ ino_t xattr_inum;
+ union ubifs_key key;
+ struct ubifs_dent_node *xent;
+ struct qstr nm = {0};
+ struct ubifs_orphan *xattr_orphan;
+ struct ubifs_orphan *orphan;
+
+ orphan = orphan_add(c, inum, NULL);
+ if (IS_ERR(orphan))
+ return PTR_ERR(orphan);
+
+ lowest_xent_key(c, &key, inum);
+ while (1) {
+ xent = ubifs_tnc_next_ent(c, &key, &nm);
+ if (IS_ERR(xent)) {
+ err = PTR_ERR(xent);
+ if (err == -ENOENT)
+ break;
+ return err;
+ }
+
+ nm.name = xent->name;
+ nm.len = le16_to_cpu(xent->nlen);
+ xattr_inum = le64_to_cpu(xent->inum);
+
+ xattr_orphan = orphan_add(c, xattr_inum, orphan);
+ if (IS_ERR(xattr_orphan))
+ return PTR_ERR(xattr_orphan);
+
+ key_read(c, &xent->key, &key);
+ }
+
+ return 0;
+}
+
+/**
+ * ubifs_delete_orphan - delete an orphan.
+ * @c: UBIFS file-system description object
+ * @inum: orphan inode number
+ *
+ * Delete an orphan. This function is called when an inode is deleted.
+ */
+void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum)
+{
+ orphan_delete(c, inum);
}
/**
@@ -611,10 +690,16 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb,
n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3;
for (i = 0; i < n; i++) {
+ union ubifs_key key1, key2;
+
inum = le64_to_cpu(orph->inos[i]);
dbg_rcvry("deleting orphaned inode %lu",
(unsigned long)inum);
- err = ubifs_tnc_remove_ino(c, inum);
+
+ lowest_ino_key(c, &key1, inum);
+ highest_ino_key(c, &key2, inum);
+
+ err = ubifs_tnc_remove_range(c, &key1, &key2);
if (err)
return err;
err = insert_dead_orphan(c, inum);
@@ -744,26 +829,15 @@ struct check_info {
struct rb_root root;
};
-static int dbg_find_orphan(struct ubifs_info *c, ino_t inum)
+static bool dbg_find_orphan(struct ubifs_info *c, ino_t inum)
{
- struct ubifs_orphan *o;
- struct rb_node *p;
+ bool found = false;
spin_lock(&c->orphan_lock);
- p = c->orph_tree.rb_node;
- while (p) {
- o = rb_entry(p, struct ubifs_orphan, rb);
- if (inum < o->inum)
- p = p->rb_left;
- else if (inum > o->inum)
- p = p->rb_right;
- else {
- spin_unlock(&c->orphan_lock);
- return 1;
- }
- }
+ found = !!lookup_orphan(c, inum);
spin_unlock(&c->orphan_lock);
- return 0;
+
+ return found;
}
static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum)
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 7968b7a5e787..6ca3d89932af 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1465,6 +1465,8 @@ static int mount_ubifs(struct ubifs_info *c)
c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20);
dbg_gen("max. seq. number: %llu", c->max_sqnum);
dbg_gen("commit number: %llu", c->cmt_no);
+ dbg_gen("max. xattrs per inode: %d", ubifs_xattr_max_cnt(c));
+ dbg_gen("max orphans: %d", c->max_orphans);
return 0;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index a5697de763f5..2cdf3c3f03fc 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -912,6 +912,8 @@ struct ubifs_budget_req {
* @rb: rb-tree node of rb-tree of orphans sorted by inode number
* @list: list head of list of orphans in order added
* @new_list: list head of list of orphans added since the last commit
+ * @child_list: list of xattr childs if this orphan hosts xattrs, list head
+ * if this orphan is a xattr, not used otherwise.
* @cnext: next orphan to commit
* @dnext: next orphan to delete
* @inum: inode number
@@ -923,6 +925,7 @@ struct ubifs_orphan {
struct rb_node rb;
struct list_head list;
struct list_head new_list;
+ struct list_head child_list;
struct ubifs_orphan *cnext;
struct ubifs_orphan *dnext;
ino_t inum;
@@ -1761,9 +1764,23 @@ int ubifs_setxattr(struct dentry *dentry, const char *name,
ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf,
size_t size);
ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size);
+int ubifs_purge_xattrs(struct inode *host);
+
+#ifdef CONFIG_UBIFS_FS_XATTR
int ubifs_removexattr(struct dentry *dentry, const char *name);
-int ubifs_init_security(struct inode *dentry, struct inode *inode,
+#endif
+
+#ifdef CONFIG_UBIFS_FS_SECURITY
+extern int ubifs_init_security(struct inode *dentry, struct inode *inode,
const struct qstr *qstr);
+#else
+static inline int ubifs_init_security(struct inode *dentry,
+ struct inode *inode, const struct qstr *qstr)
+{
+ return 0;
+}
+#endif
+
/* super.c */
struct inode *ubifs_iget(struct super_block *sb, unsigned long inum);
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index b5bf23b34241..1283867988dc 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -62,12 +62,6 @@
#include <linux/posix_acl_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.
*
* USER_XATTR: user extended attribute ("user.*")
@@ -107,7 +101,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;
@@ -541,6 +535,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 qstr 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;
+ }
+
+ nm.name = xent->name;
+ nm.len = le16_to_cpu(xent->nlen);
+
+ xino = ubifs_iget(c->vfs_sb, 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(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;
+}
+
int ubifs_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode, *host = d_inode(dentry);
@@ -590,6 +647,7 @@ out_free:
return err;
}
+#ifdef CONFIG_UBIFS_FS_SECURITY
static int init_xattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
@@ -629,3 +687,4 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode,
}
return err;
}
+#endif