static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .dirtied_ino = 1 }; struct ubifs_inode *dir_ui = ubifs_inode(dir); /* * Budget request settings: new inode, new direntry, changing the * parent directory inode. */ dbg_gen("dent '%pd', mode %#hx in dir ino %lu", dentry, mode, dir->i_ino); err = ubifs_budget_space(c, &req); if (err) return err; inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_budg; } err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); insert_inode_hash(inode); d_instantiate(dentry, inode); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_budg: ubifs_release_budget(c, &req); ubifs_err(c, "cannot create regular file, error %d", err); return err; } /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type * * This function converts UBIFS directory entry type into VFS directory entry * type. */ static unsigned int vfs_dent_type(uint8_t type) { switch (type) { case UBIFS_ITYPE_REG: return DT_REG; case UBIFS_ITYPE_DIR: return DT_DIR; case UBIFS_ITYPE_LNK: return DT_LNK; case UBIFS_ITYPE_BLK: return DT_BLK; case UBIFS_ITYPE_CHR: return DT_CHR; case UBIFS_ITYPE_FIFO: return DT_FIFO; case UBIFS_ITYPE_SOCK: return DT_SOCK; default: BUG(); } return 0; } /* * The classical Unix view for directory is that it is a linear array of * (name, inode number) entries. Linux/VFS assumes this model as well. * Particularly, 'readdir()' call wants us to return a directory entry offset * which later may be used to continue 'readdir()'ing the directory or to * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this * model because directory entries are identified by keys, which may collide. * * UBIFS uses directory entry hash value for directory offsets, so * 'seekdir()'/'telldir()' may not always work because of possible key * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work * properly by means of saving full directory entry name in the private field * of the file description object. * * This means that UBIFS cannot support NFS which requires full * 'seekdir()'/'telldir()' support. */ static int ubifs_readdir(struct file *file, struct dir_context *ctx) { int err; struct qstr nm; union ubifs_key key; struct ubifs_dent_node *dent; struct inode *dir = file_inode(file); struct ubifs_info *c = dir->i_sb->s_fs_info; dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos); if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2) /* * The directory was seek'ed to a senseless position or there * are no more entries. */ return 0; if (file->f_version == 0) { /* * The file was seek'ed, which means that @file->private_data * is now invalid. This may also be just the first * 'ubifs_readdir()' invocation, in which case * @file->private_data is NULL, and the below code is * basically a no-op. */ kfree(file->private_data); file->private_data = NULL; } /* * 'generic_file_llseek()' unconditionally sets @file->f_version to * zero, and we use this for detecting whether the file was seek'ed. */ file->f_version = 1; /* File positions 0 and 1 correspond to "." and ".." */ if (ctx->pos < 2) { ubifs_assert(!file->private_data); if (!dir_emit_dots(file, ctx)) return 0; /* Find the first entry in TNC and save it */ lowest_dent_key(c, &key, dir->i_ino); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } dent = file->private_data; if (!dent) { /* * The directory was seek'ed to and is now readdir'ed. * Find the entry corresponding to @ctx->pos or the closest one. */ dent_key_init_hash(c, &key, dir->i_ino, ctx->pos); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } while (1) { dbg_gen("feed '%s', ino %llu, new f_pos %#x", dent->name, (unsigned long long)le64_to_cpu(dent->inum), key_hash_flash(c, &dent->key)); ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum); nm.len = le16_to_cpu(dent->nlen); if (!dir_emit(ctx, dent->name, nm.len, le64_to_cpu(dent->inum), vfs_dent_type(dent->type))) return 0; /* Switch to the next entry */ key_read(c, &dent->key, &key); nm.name = dent->name; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } kfree(file->private_data); ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; cond_resched(); } out: if (err != -ENOENT) { ubifs_err(c, "cannot find next direntry, error %d", err); return err; } kfree(file->private_data); file->private_data = NULL; /* 2 is a special value indicating that there are no more direntries */ ctx->pos = 2; return 0; }
static int ubifs_printdir(struct file *file, void *dirent) { int err, over = 0; struct qstr nm; union ubifs_key key; struct ubifs_dent_node *dent; struct inode *dir = file->f_path.dentry->d_inode; struct ubifs_info *c = dir->i_sb->s_fs_info; dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) /* * The directory was seek'ed to a senseless position or there * are no more entries. */ return 0; if (file->f_pos == 1) { /* Find the first entry in TNC and save it */ lowest_dent_key(c, &key, dir->i_ino); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; } dent = file->private_data; if (!dent) { /* * The directory was seek'ed to and is now readdir'ed. * Find the entry corresponding to @file->f_pos or the * closest one. */ dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; } while (1) { dbg_gen("feed '%s', ino %llu, new f_pos %#x", dent->name, (unsigned long long)le64_to_cpu(dent->inum), key_hash_flash(c, &dent->key)); ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum); nm.len = le16_to_cpu(dent->nlen); over = filldir(c, (char *)dent->name, nm.len, le64_to_cpu(dent->inum), dent->type); if (over) return 0; /* Switch to the next entry */ key_read(c, &dent->key, &key); nm.name = (char *)dent->name; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } kfree(file->private_data); file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; cond_resched(); } out: if (err != -ENOENT) { ubifs_err("cannot find next direntry, error %d", err); return err; } kfree(file->private_data); file->private_data = NULL; file->f_pos = 2; return 0; }
static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .dirtied_ino = 1 }; struct ubifs_inode *dir_ui = ubifs_inode(dir); struct fscrypt_name nm; int err, sz_change; /* * Budget request settings: new inode, new direntry, changing the * parent directory inode. */ dbg_gen("dent '%pd', mode %#hx in dir ino %lu", dentry, mode, dir->i_ino); err = ubifs_budget_space(c, &req); if (err) return err; err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) goto out_budg; sz_change = CALC_DENT_SIZE(fname_len(&nm)); inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_fname; } err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); fscrypt_free_filename(&nm); insert_inode_hash(inode); d_instantiate(dentry, inode); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); iput(inode); out_fname: fscrypt_free_filename(&nm); out_budg: ubifs_release_budget(c, &req); ubifs_err(c, "cannot create regular file, error %d", err); return err; } static int do_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode, struct inode **whiteout) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1}; struct ubifs_budget_req ino_req = { .dirtied_ino = 1 }; struct ubifs_inode *ui, *dir_ui = ubifs_inode(dir); int err, instantiated = 0; struct fscrypt_name nm; /* * Budget request settings: new dirty inode, new direntry, * budget for dirtied inode will be released via writeback. */ dbg_gen("dent '%pd', mode %#hx in dir ino %lu", dentry, mode, dir->i_ino); err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); if (err) return err; err = ubifs_budget_space(c, &req); if (err) { fscrypt_free_filename(&nm); return err; } err = ubifs_budget_space(c, &ino_req); if (err) { ubifs_release_budget(c, &req); fscrypt_free_filename(&nm); return err; } inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_budg; } ui = ubifs_inode(inode); if (whiteout) { init_special_inode(inode, inode->i_mode, WHITEOUT_DEV); ubifs_assert(inode->i_op == &ubifs_file_inode_operations); } err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) goto out_inode; mutex_lock(&ui->ui_mutex); insert_inode_hash(inode); if (whiteout) { mark_inode_dirty(inode); drop_nlink(inode); *whiteout = inode; } else { d_tmpfile(dentry, inode); } ubifs_assert(ui->dirty); instantiated = 1; mutex_unlock(&ui->ui_mutex); mutex_lock(&dir_ui->ui_mutex); err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); return 0; out_cancel: mutex_unlock(&dir_ui->ui_mutex); out_inode: make_bad_inode(inode); if (!instantiated) iput(inode); out_budg: ubifs_release_budget(c, &req); if (!instantiated) ubifs_release_budget(c, &ino_req); fscrypt_free_filename(&nm); ubifs_err(c, "cannot create temporary file, error %d", err); return err; } static int ubifs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { return do_tmpfile(dir, dentry, mode, NULL); } /** * vfs_dent_type - get VFS directory entry type. * @type: UBIFS directory entry type * * This function converts UBIFS directory entry type into VFS directory entry * type. */ static unsigned int vfs_dent_type(uint8_t type) { switch (type) { case UBIFS_ITYPE_REG: return DT_REG; case UBIFS_ITYPE_DIR: return DT_DIR; case UBIFS_ITYPE_LNK: return DT_LNK; case UBIFS_ITYPE_BLK: return DT_BLK; case UBIFS_ITYPE_CHR: return DT_CHR; case UBIFS_ITYPE_FIFO: return DT_FIFO; case UBIFS_ITYPE_SOCK: return DT_SOCK; default: BUG(); } return 0; } /* * The classical Unix view for directory is that it is a linear array of * (name, inode number) entries. Linux/VFS assumes this model as well. * Particularly, 'readdir()' call wants us to return a directory entry offset * which later may be used to continue 'readdir()'ing the directory or to * 'seek()' to that specific direntry. Obviously UBIFS does not really fit this * model because directory entries are identified by keys, which may collide. * * UBIFS uses directory entry hash value for directory offsets, so * 'seekdir()'/'telldir()' may not always work because of possible key * collisions. But UBIFS guarantees that consecutive 'readdir()' calls work * properly by means of saving full directory entry name in the private field * of the file description object. * * This means that UBIFS cannot support NFS which requires full * 'seekdir()'/'telldir()' support. */ static int ubifs_readdir(struct file *file, struct dir_context *ctx) { int fstr_real_len = 0, err = 0; struct fscrypt_name nm; struct fscrypt_str fstr = {0}; union ubifs_key key; struct ubifs_dent_node *dent; struct inode *dir = file_inode(file); struct ubifs_info *c = dir->i_sb->s_fs_info; bool encrypted = ubifs_crypt_is_encrypted(dir); dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos); if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2) /* * The directory was seek'ed to a senseless position or there * are no more entries. */ return 0; if (encrypted) { err = fscrypt_get_encryption_info(dir); if (err && err != -ENOKEY) return err; err = fscrypt_fname_alloc_buffer(dir, UBIFS_MAX_NLEN, &fstr); if (err) return err; fstr_real_len = fstr.len; } if (file->f_version == 0) { /* * The file was seek'ed, which means that @file->private_data * is now invalid. This may also be just the first * 'ubifs_readdir()' invocation, in which case * @file->private_data is NULL, and the below code is * basically a no-op. */ kfree(file->private_data); file->private_data = NULL; } /* * 'generic_file_llseek()' unconditionally sets @file->f_version to * zero, and we use this for detecting whether the file was seek'ed. */ file->f_version = 1; /* File positions 0 and 1 correspond to "." and ".." */ if (ctx->pos < 2) { ubifs_assert(!file->private_data); if (!dir_emit_dots(file, ctx)) { if (encrypted) fscrypt_fname_free_buffer(&fstr); return 0; } /* Find the first entry in TNC and save it */ lowest_dent_key(c, &key, dir->i_ino); fname_len(&nm) = 0; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } dent = file->private_data; if (!dent) { /* * The directory was seek'ed to and is now readdir'ed. * Find the entry corresponding to @ctx->pos or the closest one. */ dent_key_init_hash(c, &key, dir->i_ino, ctx->pos); fname_len(&nm) = 0; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; } while (1) { dbg_gen("ino %llu, new f_pos %#x", (unsigned long long)le64_to_cpu(dent->inum), key_hash_flash(c, &dent->key)); ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum); fname_len(&nm) = le16_to_cpu(dent->nlen); fname_name(&nm) = dent->name; if (encrypted) { fstr.len = fstr_real_len; err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c, &dent->key), le32_to_cpu(dent->cookie), &nm.disk_name, &fstr); if (err) goto out; } else { fstr.len = fname_len(&nm); fstr.name = fname_name(&nm); } if (!dir_emit(ctx, fstr.name, fstr.len, le64_to_cpu(dent->inum), vfs_dent_type(dent->type))) { if (encrypted) fscrypt_fname_free_buffer(&fstr); return 0; } /* Switch to the next entry */ key_read(c, &dent->key, &key); dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } kfree(file->private_data); ctx->pos = key_hash_flash(c, &dent->key); file->private_data = dent; cond_resched(); } out: kfree(file->private_data); file->private_data = NULL; if (encrypted) fscrypt_fname_free_buffer(&fstr); if (err != -ENOENT) ubifs_err(c, "cannot find next direntry, error %d", err); else /* * -ENOENT is a non-fatal error in this context, the TNC uses * it to indicate that the cursor moved past the current directory * and readdir() has to stop. */ err = 0; /* 2 is a special value indicating that there are no more direntries */ ctx->pos = 2; return err; } /* Free saved readdir() state when the directory is closed */ static int ubifs_dir_release(struct inode *dir, struct file *file) { kfree(file->private_data); file->private_data = NULL; return 0; } /** * lock_2_inodes - a wrapper for locking two UBIFS inodes. * @inode1: first inode * @inode2: second inode * * We do not implement any tricks to guarantee strict lock ordering, because * VFS has already done it for us on the @i_mutex. So this is just a simple * wrapper function. */ static void lock_2_inodes(struct inode *inode1, struct inode *inode2) { mutex_lock_nested(&ubifs_inode(inode1)->ui_mutex, WB_MUTEX_1); mutex_lock_nested(&ubifs_inode(inode2)->ui_mutex, WB_MUTEX_2); }
static int ubifs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { struct inode *inode; struct ubifs_info *c = dir->i_sb->s_fs_info; int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len); struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1, .dirtied_ino = 1 }; struct ubifs_inode *dir_ui = ubifs_inode(dir); /* * Budget request settings: new inode, new direntry, changing the * parent directory inode. */ dbg_gen("dent '%.*s', mode %#x in dir ino %lu", dentry->d_name.len, dentry->d_name.name, mode, dir->i_ino); err = ubifs_budget_space(c, &req); if (err) return err; inode = ubifs_new_inode(c, dir, mode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_budg; } mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; dir_ui->ui_size = dir->i_size; dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0); if (err) goto out_cancel; mutex_unlock(&dir_ui->ui_mutex); ubifs_release_budget(c, &req); insert_inode_hash(inode); d_instantiate(dentry, inode); return 0; out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); make_bad_inode(inode); iput(inode); out_budg: ubifs_release_budget(c, &req); ubifs_err("cannot create regular file, error %d", err); return err; } static unsigned int vfs_dent_type(uint8_t type) { switch (type) { case UBIFS_ITYPE_REG: return DT_REG; case UBIFS_ITYPE_DIR: return DT_DIR; case UBIFS_ITYPE_LNK: return DT_LNK; case UBIFS_ITYPE_BLK: return DT_BLK; case UBIFS_ITYPE_CHR: return DT_CHR; case UBIFS_ITYPE_FIFO: return DT_FIFO; case UBIFS_ITYPE_SOCK: return DT_SOCK; default: BUG(); } return 0; } static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) { int err, over = 0; struct qstr nm; union ubifs_key key; struct ubifs_dent_node *dent; struct inode *dir = file->f_path.dentry->d_inode; struct ubifs_info *c = dir->i_sb->s_fs_info; dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) /* * The directory was seek'ed to a senseless position or there * are no more entries. */ return 0; /* File positions 0 and 1 correspond to "." and ".." */ if (file->f_pos == 0) { ubifs_assert(!file->private_data); over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR); if (over) return 0; file->f_pos = 1; } if (file->f_pos == 1) { ubifs_assert(!file->private_data); over = filldir(dirent, "..", 2, 1, parent_ino(file->f_path.dentry), DT_DIR); if (over) return 0; /* Find the first entry in TNC and save it */ lowest_dent_key(c, &key, dir->i_ino); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; } dent = file->private_data; if (!dent) { /* * The directory was seek'ed to and is now readdir'ed. * Find the entry corresponding to @file->f_pos or the * closest one. */ dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); nm.name = NULL; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; } while (1) { dbg_gen("feed '%s', ino %llu, new f_pos %#x", dent->name, (unsigned long long)le64_to_cpu(dent->inum), key_hash_flash(c, &dent->key)); ubifs_assert(le64_to_cpu(dent->ch.sqnum) > ubifs_inode(dir)->creat_sqnum); nm.len = le16_to_cpu(dent->nlen); over = filldir(dirent, dent->name, nm.len, file->f_pos, le64_to_cpu(dent->inum), vfs_dent_type(dent->type)); if (over) return 0; /* Switch to the next entry */ key_read(c, &dent->key, &key); nm.name = dent->name; dent = ubifs_tnc_next_ent(c, &key, &nm); if (IS_ERR(dent)) { err = PTR_ERR(dent); goto out; } kfree(file->private_data); file->f_pos = key_hash_flash(c, &dent->key); file->private_data = dent; cond_resched(); } out: if (err != -ENOENT) { ubifs_err("cannot find next direntry, error %d", err); return err; } kfree(file->private_data); file->private_data = NULL; file->f_pos = 2; return 0; }
static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { int err; union ubifs_key key; struct inode *inode = NULL; struct ubifs_dent_node *dent; struct ubifs_info *c = dir->i_sb->s_fs_info; struct fscrypt_name nm; dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino); if (ubifs_crypt_is_encrypted(dir)) { err = fscrypt_get_encryption_info(dir); /* * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is * created while the directory was encrypted and we * have access to the key. */ if (fscrypt_has_encryption_key(dir)) fscrypt_set_encrypted_dentry(dentry); fscrypt_set_d_op(dentry); if (err && err != -ENOKEY) return ERR_PTR(err); } err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm); if (err) return ERR_PTR(err); if (fname_len(&nm) > UBIFS_MAX_NLEN) { err = -ENAMETOOLONG; goto out_fname; } dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); if (!dent) { err = -ENOMEM; goto out_fname; } if (nm.hash) { ubifs_assert(fname_len(&nm) == 0); ubifs_assert(fname_name(&nm) == NULL); dent_key_init_hash(c, &key, dir->i_ino, nm.hash); err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash); } else { dent_key_init(c, &key, dir->i_ino, &nm); err = ubifs_tnc_lookup_nm(c, &key, dent, &nm); } if (err) { if (err == -ENOENT) { dbg_gen("not found"); goto done; } goto out_dent; } if (dbg_check_name(c, dent, &nm)) { err = -EINVAL; goto out_dent; } inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum)); if (IS_ERR(inode)) { /* * This should not happen. Probably the file-system needs * checking. */ err = PTR_ERR(inode); ubifs_err(c, "dead directory entry '%pd', error %d", dentry, err); ubifs_ro_mode(c, err); goto out_dent; } if (ubifs_crypt_is_encrypted(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && !fscrypt_has_permitted_context(dir, inode)) { ubifs_warn(c, "Inconsistent encryption contexts: %lu/%lu", dir->i_ino, inode->i_ino); err = -EPERM; goto out_inode; } done: kfree(dent); fscrypt_free_filename(&nm); /* * Note, d_splice_alias() would be required instead if we supported * NFS. */ d_add(dentry, inode); return NULL; out_inode: iput(inode); out_dent: kfree(dent); out_fname: fscrypt_free_filename(&nm); return ERR_PTR(err); }