diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-15 10:46:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-15 10:46:14 -0700 |
commit | 966859b9f73de9bcc14dece604ced6c0c562075b (patch) | |
tree | 501b0e17f494dd1642bfbb406795d0d369f32e04 /fs | |
parent | e37a07e0c29cd2cef4633b1e6db5579cc99ba4cd (diff) | |
parent | a6664433d383eeb71cbdeb9aea2c66eeea76e742 (diff) | |
download | linux-stable-966859b9f73de9bcc14dece604ced6c0c562075b.tar.gz linux-stable-966859b9f73de9bcc14dece604ced6c0c562075b.tar.bz2 linux-stable-966859b9f73de9bcc14dece604ced6c0c562075b.zip |
Merge tag 'upstream-4.13-rc1' of git://git.infradead.org/linux-ubifs
Pull UBIFS updates from Richard Weinberger:
- Updates and fixes for the file encryption mode
- Minor improvements
- Random fixes
* tag 'upstream-4.13-rc1' of git://git.infradead.org/linux-ubifs:
ubifs: Set double hash cookie also for RENAME_EXCHANGE
ubifs: Massage assert in ubifs_xattr_set() wrt. init_xattrs
ubifs: Don't leak kernel memory to the MTD
ubifs: Change gfp flags in page allocation for bulk read
ubifs: Fix oops when remounting with no_bulk_read.
ubifs: Fail commit if TNC is obviously inconsistent
ubifs: allow userspace to map mounts to volumes
ubifs: Wire-up statx() support
ubifs: Remove dead code from ubifs_get_link()
ubifs: Massage debug prints wrt. fscrypt
ubifs: Add assert to dent_key_init()
ubifs: Fix unlink code wrt. double hash lookups
ubifs: Fix data node size for truncating uncompressed nodes
ubifs: Don't encrypt special files on creation
ubifs: Fix memory leak in RENAME_WHITEOUT error path in do_rename
ubifs: Fix inode data budget in ubifs_mknod
ubifs: Correctly evict xattr inodes
ubifs: Unexport ubifs_inode_slab
ubifs: don't bother checking for encryption key in ->mmap()
ubifs: require key for truncate(2) of encrypted file
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ubifs/crypto.c | 7 | ||||
-rw-r--r-- | fs/ubifs/dir.c | 32 | ||||
-rw-r--r-- | fs/ubifs/file.c | 27 | ||||
-rw-r--r-- | fs/ubifs/journal.c | 36 | ||||
-rw-r--r-- | fs/ubifs/key.h | 1 | ||||
-rw-r--r-- | fs/ubifs/super.c | 11 | ||||
-rw-r--r-- | fs/ubifs/tnc.c | 140 | ||||
-rw-r--r-- | fs/ubifs/tnc_commit.c | 4 | ||||
-rw-r--r-- | fs/ubifs/ubifs.h | 6 | ||||
-rw-r--r-- | fs/ubifs/xattr.c | 39 |
10 files changed, 218 insertions, 85 deletions
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c index 382ed428cfd2..114ba455bac3 100644 --- a/fs/ubifs/crypto.c +++ b/fs/ubifs/crypto.c @@ -9,8 +9,13 @@ static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len) static int ubifs_crypt_set_context(struct inode *inode, const void *ctx, size_t len, void *fs_data) { + /* + * Creating an encryption context is done unlocked since we + * operate on a new inode which is not visible to other users + * at this point. So, no need to check whether inode is locked. + */ return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT, - ctx, len, 0); + ctx, len, 0, false); } static bool ubifs_crypt_empty_dir(struct inode *inode) diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 566079d9b402..417fe0b29f23 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -143,6 +143,7 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir, case S_IFBLK: case S_IFCHR: inode->i_op = &ubifs_file_inode_operations; + encrypted = false; break; default: BUG(); @@ -1061,7 +1062,6 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry, int sz_change; int err, devlen = 0; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, - .new_ino_d = ALIGN(devlen, 8), .dirtied_ino = 1 }; struct fscrypt_name nm; @@ -1079,6 +1079,7 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry, devlen = ubifs_encode_dev(dev, rdev); } + req.new_ino_d = ALIGN(devlen, 8); err = ubifs_budget_space(c, &req); if (err) { kfree(dev); @@ -1396,17 +1397,14 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry, dev = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); if (!dev) { - ubifs_release_budget(c, &req); - ubifs_release_budget(c, &ino_req); - return -ENOMEM; + err = -ENOMEM; + goto out_release; } err = do_tmpfile(old_dir, old_dentry, S_IFCHR | WHITEOUT_MODE, &whiteout); if (err) { - ubifs_release_budget(c, &req); - ubifs_release_budget(c, &ino_req); kfree(dev); - return err; + goto out_release; } whiteout->i_state |= I_LINKABLE; @@ -1494,12 +1492,10 @@ static int do_rename(struct inode *old_dir, struct dentry *old_dentry, err = ubifs_budget_space(c, &wht_req); if (err) { - ubifs_release_budget(c, &req); - ubifs_release_budget(c, &ino_req); kfree(whiteout_ui->data); whiteout_ui->data_len = 0; iput(whiteout); - return err; + goto out_release; } inc_nlink(whiteout); @@ -1554,6 +1550,7 @@ out_cancel: iput(whiteout); } unlock_4_inodes(old_dir, new_dir, new_inode, whiteout); +out_release: ubifs_release_budget(c, &ino_req); ubifs_release_budget(c, &req); fscrypt_free_filename(&old_nm); @@ -1647,6 +1644,21 @@ int ubifs_getattr(const struct path *path, struct kstat *stat, struct ubifs_inode *ui = ubifs_inode(inode); mutex_lock(&ui->ui_mutex); + + if (ui->flags & UBIFS_APPEND_FL) + stat->attributes |= STATX_ATTR_APPEND; + if (ui->flags & UBIFS_COMPR_FL) + stat->attributes |= STATX_ATTR_COMPRESSED; + if (ui->flags & UBIFS_CRYPT_FL) + stat->attributes |= STATX_ATTR_ENCRYPTED; + if (ui->flags & UBIFS_IMMUTABLE_FL) + stat->attributes |= STATX_ATTR_IMMUTABLE; + + stat->attributes_mask |= (STATX_ATTR_APPEND | + STATX_ATTR_COMPRESSED | + STATX_ATTR_ENCRYPTED | + STATX_ATTR_IMMUTABLE); + generic_fillattr(inode, stat); stat->blksize = UBIFS_BLOCK_SIZE; stat->size = ui->ui_size; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 2cda3d67e2d0..8cad0b19b404 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -735,6 +735,7 @@ static int ubifs_do_bulk_read(struct ubifs_info *c, struct bu_info *bu, int err, page_idx, page_cnt, ret = 0, n = 0; int allocate = bu->buf ? 0 : 1; loff_t isize; + gfp_t ra_gfp_mask = readahead_gfp_mask(mapping) & ~__GFP_FS; err = ubifs_tnc_get_bu_keys(c, bu); if (err) @@ -796,8 +797,7 @@ static int ubifs_do_bulk_read(struct ubifs_info *c, struct bu_info *bu, if (page_offset > end_index) break; - page = find_or_create_page(mapping, page_offset, - GFP_NOFS | __GFP_COLD); + page = find_or_create_page(mapping, page_offset, ra_gfp_mask); if (!page) break; if (!PageUptodate(page)) @@ -1284,6 +1284,14 @@ int ubifs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; + if (ubifs_crypt_is_encrypted(inode) && (attr->ia_valid & ATTR_SIZE)) { + err = fscrypt_get_encryption_info(inode); + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } + if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size < inode->i_size) /* Truncation to a smaller size */ err = do_truncation(c, inode, attr); @@ -1607,15 +1615,6 @@ static const struct vm_operations_struct ubifs_file_vm_ops = { static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma) { int err; - struct inode *inode = file->f_mapping->host; - - if (ubifs_crypt_is_encrypted(inode)) { - err = fscrypt_get_encryption_info(inode); - if (err) - return -EACCES; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } err = generic_file_mmap(file, vma); if (err) @@ -1698,12 +1697,6 @@ static const char *ubifs_get_link(struct dentry *dentry, pstr.name[pstr.len] = '\0'; - // XXX this probably won't happen anymore... - if (pstr.name[0] == '\0') { - fscrypt_fname_free_buffer(&pstr); - return ERR_PTR(-ENOENT); - } - set_delayed_call(done, kfree_link, pstr.name); return pstr.name; } diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index 294519b98874..04c4ec6483e5 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -549,8 +549,6 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, struct ubifs_ino_node *ino; union ubifs_key dent_key, ino_key; - //dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu", - // inode->i_ino, nm->len, nm->name, ui->data_len, dir->i_ino); ubifs_assert(mutex_is_locked(&host_ui->ui_mutex)); dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1; @@ -574,7 +572,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, /* Make sure to also account for extended attributes */ len += host_ui->data_len; - dent = kmalloc(len, GFP_NOFS); + dent = kzalloc(len, GFP_NOFS); if (!dent) return -ENOMEM; @@ -585,7 +583,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, if (!xent) { dent->ch.node_type = UBIFS_DENT_NODE; - dent_key_init(c, &dent_key, dir->i_ino, nm); + if (nm->hash) + dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash); + else + dent_key_init(c, &dent_key, dir->i_ino, nm); } else { dent->ch.node_type = UBIFS_XENT_NODE; xent_key_init(c, &dent_key, dir->i_ino, nm); @@ -629,7 +630,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, kfree(dent); if (deletion) { - err = ubifs_tnc_remove_nm(c, &dent_key, nm); + if (nm->hash) + err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash); + else + err = ubifs_tnc_remove_nm(c, &dent_key, nm); if (err) goto out_ro; err = ubifs_add_dirt(c, lnum, dlen); @@ -950,9 +954,6 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, int twoparents = (fst_dir != snd_dir); void *p; - //dbg_jnl("dent '%pd' in dir ino %lu between dent '%pd' in dir ino %lu", - // fst_dentry, fst_dir->i_ino, snd_dentry, snd_dir->i_ino); - ubifs_assert(ubifs_inode(fst_dir)->data_len == 0); ubifs_assert(ubifs_inode(snd_dir)->data_len == 0); ubifs_assert(mutex_is_locked(&ubifs_inode(fst_dir)->ui_mutex)); @@ -967,7 +968,7 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, if (twoparents) len += plen; - dent1 = kmalloc(len, GFP_NOFS); + dent1 = kzalloc(len, GFP_NOFS); if (!dent1) return -ENOMEM; @@ -984,6 +985,7 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, dent1->nlen = cpu_to_le16(fname_len(snd_nm)); memcpy(dent1->name, fname_name(snd_nm), fname_len(snd_nm)); dent1->name[fname_len(snd_nm)] = '\0'; + set_dent_cookie(c, dent1); zero_dent_node_unused(dent1); ubifs_prep_grp_node(c, dent1, dlen1, 0); @@ -996,6 +998,7 @@ int ubifs_jnl_xrename(struct ubifs_info *c, const struct inode *fst_dir, dent2->nlen = cpu_to_le16(fname_len(fst_nm)); memcpy(dent2->name, fname_name(fst_nm), fname_len(fst_nm)); dent2->name[fname_len(fst_nm)] = '\0'; + set_dent_cookie(c, dent2); zero_dent_node_unused(dent2); ubifs_prep_grp_node(c, dent2, dlen2, 0); @@ -1094,8 +1097,6 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, int move = (old_dir != new_dir); struct ubifs_inode *uninitialized_var(new_ui); - //dbg_jnl("dent '%pd' in dir ino %lu to dent '%pd' in dir ino %lu", - // old_dentry, old_dir->i_ino, new_dentry, new_dir->i_ino); ubifs_assert(ubifs_inode(old_dir)->data_len == 0); ubifs_assert(ubifs_inode(new_dir)->data_len == 0); ubifs_assert(mutex_is_locked(&ubifs_inode(old_dir)->ui_mutex)); @@ -1117,7 +1118,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir, len = aligned_dlen1 + aligned_dlen2 + ALIGN(ilen, 8) + ALIGN(plen, 8); if (move) len += plen; - dent = kmalloc(len, GFP_NOFS); + dent = kzalloc(len, GFP_NOFS); if (!dent) return -ENOMEM; @@ -1298,7 +1299,9 @@ static int truncate_data_node(const struct ubifs_info *c, const struct inode *in goto out; } - if (compr_type != UBIFS_COMPR_NONE) { + if (compr_type == UBIFS_COMPR_NONE) { + out_len = *new_len; + } else { err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type); if (err) goto out; @@ -1485,9 +1488,6 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, int sync = IS_DIRSYNC(host); struct ubifs_inode *host_ui = ubifs_inode(host); - //dbg_jnl("host %lu, xattr ino %lu, name '%s', data len %d", - // host->i_ino, inode->i_ino, nm->name, - // ubifs_inode(inode)->data_len); ubifs_assert(inode->i_nlink == 0); ubifs_assert(mutex_is_locked(&host_ui->ui_mutex)); @@ -1500,7 +1500,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host, hlen = host_ui->data_len + UBIFS_INO_NODE_SZ; len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8); - xent = kmalloc(len, GFP_NOFS); + xent = kzalloc(len, GFP_NOFS); if (!xent) return -ENOMEM; @@ -1607,7 +1607,7 @@ int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode, aligned_len1 = ALIGN(len1, 8); aligned_len = aligned_len1 + ALIGN(len2, 8); - ino = kmalloc(aligned_len, GFP_NOFS); + ino = kzalloc(aligned_len, GFP_NOFS); if (!ino) return -ENOMEM; diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index 7547be512db2..b1f7c0caa3ac 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h @@ -162,6 +162,7 @@ static inline void dent_key_init(const struct ubifs_info *c, uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm)); ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); + ubifs_assert(!nm->hash && !nm->minor_hash); key->u32[0] = inum; key->u32[1] = hash | (UBIFS_DENT_KEY << UBIFS_S_KEY_HASH_BITS); } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index cf4cc99b75b5..bffadbb67e47 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -45,7 +45,7 @@ #define UBIFS_KMALLOC_OK (128*1024) /* Slab cache for UBIFS inodes */ -struct kmem_cache *ubifs_inode_slab; +static struct kmem_cache *ubifs_inode_slab; /* UBIFS TNC shrinker description */ static struct shrinker ubifs_shrinker_info = { @@ -446,6 +446,8 @@ static int ubifs_show_options(struct seq_file *s, struct dentry *root) ubifs_compr_name(c->mount_opts.compr_type)); } + seq_printf(s, ",ubi=%d,vol=%d", c->vi.ubi_num, c->vi.vol_id); + return 0; } @@ -931,6 +933,7 @@ enum { Opt_chk_data_crc, Opt_no_chk_data_crc, Opt_override_compr, + Opt_ignore, Opt_err, }; @@ -942,6 +945,8 @@ static const match_table_t tokens = { {Opt_chk_data_crc, "chk_data_crc"}, {Opt_no_chk_data_crc, "no_chk_data_crc"}, {Opt_override_compr, "compr=%s"}, + {Opt_ignore, "ubi=%s"}, + {Opt_ignore, "vol=%s"}, {Opt_err, NULL}, }; @@ -1042,6 +1047,8 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, c->default_compr = c->mount_opts.compr_type; break; } + case Opt_ignore: + break; default: { unsigned long flag; @@ -1869,8 +1876,10 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) bu_init(c); else { dbg_gen("disable bulk-read"); + mutex_lock(&c->bu_mutex); kfree(c->bu.buf); c->bu.buf = NULL; + mutex_unlock(&c->bu_mutex); } ubifs_assert(c->lst.taken_empty_lebs > 0); diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 709aa098dd46..0a213dcba2a1 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -1812,7 +1812,7 @@ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, int found, n, err; struct ubifs_znode *znode; - //dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name); + dbg_tnck(key, "key "); mutex_lock(&c->tnc_mutex); found = ubifs_lookup_level0(c, key, &znode, &n); if (!found) { @@ -1880,48 +1880,65 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, return do_lookup_nm(c, key, node, nm); } -static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, - struct ubifs_dent_node *dent, uint32_t cookie) +static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_dent_node *dent, uint32_t cookie, + struct ubifs_znode **zn, int *n) { - int n, err, type = key_type(c, key); - struct ubifs_znode *znode; + int err; + struct ubifs_znode *znode = *zn; struct ubifs_zbranch *zbr; - union ubifs_key *dkey, start_key; - - ubifs_assert(is_hash_key(c, key)); - - lowest_dent_key(c, &start_key, key_inum(c, key)); - - mutex_lock(&c->tnc_mutex); - err = ubifs_lookup_level0(c, &start_key, &znode, &n); - if (unlikely(err < 0)) - goto out_unlock; + union ubifs_key *dkey; for (;;) { if (!err) { - err = tnc_next(c, &znode, &n); + err = tnc_next(c, &znode, n); if (err) - goto out_unlock; + goto out; } - zbr = &znode->zbranch[n]; + zbr = &znode->zbranch[*n]; dkey = &zbr->key; if (key_inum(c, dkey) != key_inum(c, key) || - key_type(c, dkey) != type) { + key_type(c, dkey) != key_type(c, key)) { err = -ENOENT; - goto out_unlock; + goto out; } err = tnc_read_hashed_node(c, zbr, dent); if (err) - goto out_unlock; + goto out; if (key_hash(c, key) == key_hash(c, dkey) && - le32_to_cpu(dent->cookie) == cookie) - goto out_unlock; + le32_to_cpu(dent->cookie) == cookie) { + *zn = znode; + goto out; + } } +out: + + return err; +} + +static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, + struct ubifs_dent_node *dent, uint32_t cookie) +{ + int n, err; + struct ubifs_znode *znode; + union ubifs_key start_key; + + ubifs_assert(is_hash_key(c, key)); + + lowest_dent_key(c, &start_key, key_inum(c, key)); + + mutex_lock(&c->tnc_mutex); + err = ubifs_lookup_level0(c, &start_key, &znode, &n); + if (unlikely(err < 0)) + goto out_unlock; + + err = search_dh_cookie(c, key, dent, cookie, &znode, &n); + out_unlock: mutex_unlock(&c->tnc_mutex); return err; @@ -2393,8 +2410,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - //dbg_tnck(key, "LEB %d:%d, name '%.*s', key ", - // lnum, offs, nm->len, nm->name); + dbg_tnck(key, "LEB %d:%d, key ", lnum, offs); found = lookup_level0_dirty(c, key, &znode, &n); if (found < 0) { err = found; @@ -2628,7 +2644,7 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - //dbg_tnck(key, "%.*s, key ", nm->len, nm->name); + dbg_tnck(key, "key "); err = lookup_level0_dirty(c, key, &znode, &n); if (err < 0) goto out_unlock; @@ -2663,6 +2679,74 @@ out_unlock: } /** + * ubifs_tnc_remove_dh - remove an index entry for a "double hashed" node. + * @c: UBIFS file-system description object + * @key: key of node + * @cookie: node cookie for collision resolution + * + * Returns %0 on success or negative error code on failure. + */ +int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key, + uint32_t cookie) +{ + int n, err; + struct ubifs_znode *znode; + struct ubifs_dent_node *dent; + struct ubifs_zbranch *zbr; + + if (!c->double_hash) + return -EOPNOTSUPP; + + mutex_lock(&c->tnc_mutex); + err = lookup_level0_dirty(c, key, &znode, &n); + if (err <= 0) + goto out_unlock; + + zbr = &znode->zbranch[n]; + dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); + if (!dent) { + err = -ENOMEM; + goto out_unlock; + } + + err = tnc_read_hashed_node(c, zbr, dent); + if (err) + goto out_free; + + /* If the cookie does not match, we're facing a hash collision. */ + if (le32_to_cpu(dent->cookie) != cookie) { + union ubifs_key start_key; + + lowest_dent_key(c, &start_key, key_inum(c, key)); + + err = ubifs_lookup_level0(c, &start_key, &znode, &n); + if (unlikely(err < 0)) + goto out_free; + + err = search_dh_cookie(c, key, dent, cookie, &znode, &n); + if (err) + goto out_free; + } + + if (znode->cnext || !ubifs_zn_dirty(znode)) { + znode = dirty_cow_bottom_up(c, znode); + if (IS_ERR(znode)) { + err = PTR_ERR(znode); + goto out_free; + } + } + err = tnc_delete(c, znode, n); + +out_free: + kfree(dent); +out_unlock: + if (!err) + err = dbg_check_tnc(c, 0); + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** * key_in_range - determine if a key falls within a range of keys. * @c: UBIFS file-system description object * @key: key to check @@ -2802,6 +2886,8 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) dbg_tnc("xent '%s', ino %lu", xent->name, (unsigned long)xattr_inum); + ubifs_evict_xattr_inode(c, xattr_inum); + fname_name(&nm) = xent->name; fname_len(&nm) = le16_to_cpu(xent->nlen); err = ubifs_tnc_remove_nm(c, &key1, &nm); @@ -2863,7 +2949,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, struct ubifs_zbranch *zbr; union ubifs_key *dkey; - //dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)"); + dbg_tnck(key, "key "); ubifs_assert(is_hash_key(c, key)); mutex_lock(&c->tnc_mutex); diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c index 51157da3f76e..aa31f60220ef 100644 --- a/fs/ubifs/tnc_commit.c +++ b/fs/ubifs/tnc_commit.c @@ -57,6 +57,8 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, ubifs_dump_znode(c, znode); if (zbr->znode) ubifs_dump_znode(c, zbr->znode); + + return -EINVAL; } } ubifs_prepare_node(c, idx, len, 0); @@ -859,6 +861,8 @@ static int write_index(struct ubifs_info *c) ubifs_dump_znode(c, znode); if (zbr->znode) ubifs_dump_znode(c, zbr->znode); + + return -EINVAL; } } len = ubifs_idx_node_sz(c, znode->child_cnt); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 298b4d89eee9..cd43651f1731 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1451,7 +1451,6 @@ struct ubifs_info { extern struct list_head ubifs_infos; extern spinlock_t ubifs_infos_lock; extern atomic_long_t ubifs_clean_zn_cnt; -extern struct kmem_cache *ubifs_inode_slab; extern const struct super_operations ubifs_super_operations; extern const struct address_space_operations ubifs_file_address_operations; extern const struct file_operations ubifs_file_operations; @@ -1590,6 +1589,8 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key); int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, const struct fscrypt_name *nm); +int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key, + uint32_t cookie); int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key, union ubifs_key *to_key); int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum); @@ -1754,9 +1755,10 @@ int ubifs_check_dir_empty(struct inode *dir); extern const struct xattr_handler *ubifs_xattr_handlers[]; ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size); int ubifs_xattr_set(struct inode *host, const char *name, const void *value, - size_t size, int flags); + size_t size, int flags, bool check_lock); ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf, size_t size); +void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum); #ifdef CONFIG_UBIFS_FS_SECURITY extern int ubifs_init_security(struct inode *dentry, struct inode *inode, diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index 6c9e62c2ef55..c13eae819cbc 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -280,7 +280,7 @@ static struct inode *iget_xattr(struct ubifs_info *c, ino_t inum) } int ubifs_xattr_set(struct inode *host, const char *name, const void *value, - size_t size, int flags) + size_t size, int flags, bool check_lock) { struct inode *inode; struct ubifs_info *c = host->i_sb->s_fs_info; @@ -289,12 +289,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value, union ubifs_key key; int err; - /* - * Creating an encryption context is done unlocked since we - * operate on a new inode which is not visible to other users - * at this point. - */ - if (strcmp(name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) != 0) + if (check_lock) ubifs_assert(inode_is_locked(host)); if (size > UBIFS_MAX_INO_DATA) @@ -513,6 +508,28 @@ out_cancel: return err; } +/** + * ubifs_evict_xattr_inode - Evict an xattr inode. + * @c: UBIFS file-system description object + * @xattr_inum: xattr inode number + * + * When an inode that hosts xattrs is being removed we have to make sure + * that cached inodes of the xattrs also get removed from the inode cache + * otherwise we'd waste memory. This function looks up an inode from the + * inode cache and clears the link counter such that iput() will evict + * the inode. + */ +void ubifs_evict_xattr_inode(struct ubifs_info *c, ino_t xattr_inum) +{ + struct inode *inode; + + inode = ilookup(c->vfs_sb, xattr_inum); + if (inode) { + clear_nlink(inode); + iput(inode); + } +} + static int ubifs_xattr_remove(struct inode *host, const char *name) { struct inode *inode; @@ -576,8 +593,12 @@ static int init_xattrs(struct inode *inode, const struct xattr *xattr_array, } strcpy(name, XATTR_SECURITY_PREFIX); strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name); + /* + * creating a new inode without holding the inode rwsem, + * no need to check whether inode is locked. + */ err = ubifs_xattr_set(inode, name, xattr->value, - xattr->value_len, 0); + xattr->value_len, 0, false); kfree(name); if (err < 0) break; @@ -624,7 +645,7 @@ static int xattr_set(const struct xattr_handler *handler, name = xattr_full_name(handler, name); if (value) - return ubifs_xattr_set(inode, name, value, size, flags); + return ubifs_xattr_set(inode, name, value, size, flags, true); else return ubifs_xattr_remove(inode, name); } |