#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/time.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/quotaops.h>
#include <linux/buffer_head.h>
#include <linux/bio.h>
#include <linux/iversion.h>
#include <linux/unicode.h>
#include "ext4.h"
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
#include <trace/events/ext4.h>
#define NAMEI_RA_CHUNKS …
#define NAMEI_RA_BLOCKS …
#define NAMEI_RA_SIZE …
static struct buffer_head *ext4_append(handle_t *handle,
struct inode *inode,
ext4_lblk_t *block)
{ … }
static int ext4_dx_csum_verify(struct inode *inode,
struct ext4_dir_entry *dirent);
dirblock_type_t;
#define ext4_read_dirblock(inode, block, type) …
static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
ext4_lblk_t block,
dirblock_type_t type,
const char *func,
unsigned int line)
{ … }
#ifdef DX_DEBUG
#define dxtrace …
#else
#define dxtrace(command) …
#endif
struct fake_dirent
{ … };
struct dx_countlimit
{ … };
struct dx_entry
{ … };
struct dx_root
{ … };
struct dx_node
{ … };
struct dx_frame
{ … };
struct dx_map_entry
{ … };
struct dx_tail { … };
static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);
static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);
static inline unsigned dx_get_hash(struct dx_entry *entry);
static void dx_set_hash(struct dx_entry *entry, unsigned value);
static unsigned dx_get_count(struct dx_entry *entries);
static unsigned dx_get_limit(struct dx_entry *entries);
static void dx_set_count(struct dx_entry *entries, unsigned value);
static void dx_set_limit(struct dx_entry *entries, unsigned value);
static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
static unsigned dx_node_limit(struct inode *dir);
static struct dx_frame *dx_probe(struct ext4_filename *fname,
struct inode *dir,
struct dx_hash_info *hinfo,
struct dx_frame *frame);
static void dx_release(struct dx_frame *frames);
static int dx_make_map(struct inode *dir, struct buffer_head *bh,
struct dx_hash_info *hinfo,
struct dx_map_entry *map_tail);
static void dx_sort_map(struct dx_map_entry *map, unsigned count);
static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from,
char *to, struct dx_map_entry *offsets,
int count, unsigned int blocksize);
static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
unsigned int blocksize);
static void dx_insert_block(struct dx_frame *frame,
u32 hash, ext4_lblk_t block);
static int ext4_htree_next_block(struct inode *dir, __u32 hash,
struct dx_frame *frame,
struct dx_frame *frames,
__u32 *start_hash);
static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir);
static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *dir, struct inode *inode);
void ext4_initialize_dirent_tail(struct buffer_head *bh,
unsigned int blocksize)
{ … }
static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode,
struct buffer_head *bh)
{ … }
static __le32 ext4_dirblock_csum(struct inode *inode, void *dirent, int size)
{ … }
#define warn_no_space_for_csum(inode) …
static void __warn_no_space_for_csum(struct inode *inode, const char *func,
unsigned int line)
{ … }
int ext4_dirblock_csum_verify(struct inode *inode, struct buffer_head *bh)
{ … }
static void ext4_dirblock_csum_set(struct inode *inode,
struct buffer_head *bh)
{ … }
int ext4_handle_dirty_dirblock(handle_t *handle,
struct inode *inode,
struct buffer_head *bh)
{ … }
static struct dx_countlimit *get_dx_countlimit(struct inode *inode,
struct ext4_dir_entry *dirent,
int *offset)
{ … }
static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
int count_offset, int count, struct dx_tail *t)
{ … }
static int ext4_dx_csum_verify(struct inode *inode,
struct ext4_dir_entry *dirent)
{ … }
static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent)
{ … }
static inline int ext4_handle_dirty_dx_node(handle_t *handle,
struct inode *inode,
struct buffer_head *bh)
{ … }
static inline struct ext4_dir_entry_2 *
ext4_next_entry(struct ext4_dir_entry_2 *p, unsigned long blocksize)
{ … }
static inline ext4_lblk_t dx_get_block(struct dx_entry *entry)
{ … }
static inline void dx_set_block(struct dx_entry *entry, ext4_lblk_t value)
{ … }
static inline unsigned dx_get_hash(struct dx_entry *entry)
{ … }
static inline void dx_set_hash(struct dx_entry *entry, unsigned value)
{ … }
static inline unsigned dx_get_count(struct dx_entry *entries)
{ … }
static inline unsigned dx_get_limit(struct dx_entry *entries)
{ … }
static inline void dx_set_count(struct dx_entry *entries, unsigned value)
{ … }
static inline void dx_set_limit(struct dx_entry *entries, unsigned value)
{ … }
static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
{ … }
static inline unsigned dx_node_limit(struct inode *dir)
{ … }
#ifdef DX_DEBUG
static void dx_show_index(char * label, struct dx_entry *entries)
{
int i, n = dx_get_count (entries);
printk(KERN_DEBUG "%s index", label);
for (i = 0; i < n; i++) {
printk(KERN_CONT " %x->%lu",
i ? dx_get_hash(entries + i) : 0,
(unsigned long)dx_get_block(entries + i));
}
printk(KERN_CONT "\n");
}
struct stats
{
unsigned names;
unsigned space;
unsigned bcount;
};
static struct stats dx_show_leaf(struct inode *dir,
struct dx_hash_info *hinfo,
struct ext4_dir_entry_2 *de,
int size, int show_names)
{
unsigned names = 0, space = 0;
char *base = (char *) de;
struct dx_hash_info h = *hinfo;
printk("names: ");
while ((char *) de < base + size)
{
if (de->inode)
{
if (show_names)
{
#ifdef CONFIG_FS_ENCRYPTION
int len;
char *name;
struct fscrypt_str fname_crypto_str =
FSTR_INIT(NULL, 0);
int res = 0;
name = de->name;
len = de->name_len;
if (!IS_ENCRYPTED(dir)) {
(void) ext4fs_dirhash(dir, de->name,
de->name_len, &h);
printk("%*.s:(U)%x.%u ", len,
name, h.hash,
(unsigned) ((char *) de
- base));
} else {
struct fscrypt_str de_name =
FSTR_INIT(name, len);
res = fscrypt_fname_alloc_buffer(
len, &fname_crypto_str);
if (res)
printk(KERN_WARNING "Error "
"allocating crypto "
"buffer--skipping "
"crypto\n");
res = fscrypt_fname_disk_to_usr(dir,
0, 0, &de_name,
&fname_crypto_str);
if (res) {
printk(KERN_WARNING "Error "
"converting filename "
"from disk to usr"
"\n");
name = "??";
len = 2;
} else {
name = fname_crypto_str.name;
len = fname_crypto_str.len;
}
if (IS_CASEFOLDED(dir))
h.hash = EXT4_DIRENT_HASH(de);
else
(void) ext4fs_dirhash(dir,
de->name,
de->name_len, &h);
printk("%*.s:(E)%x.%u ", len, name,
h.hash, (unsigned) ((char *) de
- base));
fscrypt_fname_free_buffer(
&fname_crypto_str);
}
#else
int len = de->name_len;
char *name = de->name;
(void) ext4fs_dirhash(dir, de->name,
de->name_len, &h);
printk("%*.s:%x.%u ", len, name, h.hash,
(unsigned) ((char *) de - base));
#endif
}
space += ext4_dir_rec_len(de->name_len, dir);
names++;
}
de = ext4_next_entry(de, size);
}
printk(KERN_CONT "(%i)\n", names);
return (struct stats) { names, space, 1 };
}
struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
struct dx_entry *entries, int levels)
{
unsigned blocksize = dir->i_sb->s_blocksize;
unsigned count = dx_get_count(entries), names = 0, space = 0, i;
unsigned bcount = 0;
struct buffer_head *bh;
printk("%i indexed blocks...\n", count);
for (i = 0; i < count; i++, entries++)
{
ext4_lblk_t block = dx_get_block(entries);
ext4_lblk_t hash = i ? dx_get_hash(entries): 0;
u32 range = i < count - 1? (dx_get_hash(entries + 1) - hash): ~hash;
struct stats stats;
printk("%s%3u:%03u hash %8x/%8x ",levels?"":" ", i, block, hash, range);
bh = ext4_bread(NULL,dir, block, 0);
if (!bh || IS_ERR(bh))
continue;
stats = levels?
dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *)
bh->b_data, blocksize, 0);
names += stats.names;
space += stats.space;
bcount += stats.bcount;
brelse(bh);
}
if (bcount)
printk(KERN_DEBUG "%snames %u, fullness %u (%u%%)\n",
levels ? "" : " ", names, space/bcount,
(space/bcount)*100/blocksize);
return (struct stats) { names, space, bcount};
}
static inline void htree_rep_invariant_check(struct dx_entry *at,
struct dx_entry *target,
u32 hash, unsigned int n)
{
while (n--) {
dxtrace(printk(KERN_CONT ","));
if (dx_get_hash(++at) > hash) {
at--;
break;
}
}
ASSERT(at == target - 1);
}
#else
static inline void htree_rep_invariant_check(struct dx_entry *at,
struct dx_entry *target,
u32 hash, unsigned int n)
{ … }
#endif
static struct dx_frame *
dx_probe(struct ext4_filename *fname, struct inode *dir,
struct dx_hash_info *hinfo, struct dx_frame *frame_in)
{ … }
static void dx_release(struct dx_frame *frames)
{ … }
static int ext4_htree_next_block(struct inode *dir, __u32 hash,
struct dx_frame *frame,
struct dx_frame *frames,
__u32 *start_hash)
{ … }
static int htree_dirblock_to_tree(struct file *dir_file,
struct inode *dir, ext4_lblk_t block,
struct dx_hash_info *hinfo,
__u32 start_hash, __u32 start_minor_hash)
{ … }
int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
__u32 start_minor_hash, __u32 *next_hash)
{ … }
static inline int search_dirblock(struct buffer_head *bh,
struct inode *dir,
struct ext4_filename *fname,
unsigned int offset,
struct ext4_dir_entry_2 **res_dir)
{ … }
static int dx_make_map(struct inode *dir, struct buffer_head *bh,
struct dx_hash_info *hinfo,
struct dx_map_entry *map_tail)
{ … }
static void dx_sort_map (struct dx_map_entry *map, unsigned count)
{ … }
static void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block)
{ … }
#if IS_ENABLED(CONFIG_UNICODE)
int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
struct ext4_filename *name)
{ … }
#endif
static bool ext4_match(struct inode *parent,
const struct ext4_filename *fname,
struct ext4_dir_entry_2 *de)
{ … }
int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
struct inode *dir, struct ext4_filename *fname,
unsigned int offset, struct ext4_dir_entry_2 **res_dir)
{ … }
static int is_dx_internal_node(struct inode *dir, ext4_lblk_t block,
struct ext4_dir_entry *de)
{ … }
static struct buffer_head *__ext4_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir,
int *inlined)
{ … }
static struct buffer_head *ext4_find_entry(struct inode *dir,
const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir,
int *inlined)
{ … }
static struct buffer_head *ext4_lookup_entry(struct inode *dir,
struct dentry *dentry,
struct ext4_dir_entry_2 **res_dir)
{ … }
static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **res_dir)
{ … }
static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{ … }
struct dentry *ext4_get_parent(struct dentry *child)
{ … }
static struct ext4_dir_entry_2 *
dx_move_dirents(struct inode *dir, char *from, char *to,
struct dx_map_entry *map, int count,
unsigned blocksize)
{ … }
static struct ext4_dir_entry_2 *dx_pack_dirents(struct inode *dir, char *base,
unsigned int blocksize)
{ … }
static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
struct buffer_head **bh,struct dx_frame *frame,
struct dx_hash_info *hinfo)
{ … }
int ext4_find_dest_de(struct inode *dir, struct inode *inode,
struct buffer_head *bh,
void *buf, int buf_size,
struct ext4_filename *fname,
struct ext4_dir_entry_2 **dest_de)
{ … }
void ext4_insert_dentry(struct inode *dir,
struct inode *inode,
struct ext4_dir_entry_2 *de,
int buf_size,
struct ext4_filename *fname)
{ … }
static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
struct inode *dir,
struct inode *inode, struct ext4_dir_entry_2 *de,
struct buffer_head *bh)
{ … }
static bool ext4_check_dx_root(struct inode *dir, struct dx_root *root)
{ … }
static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
struct inode *dir,
struct inode *inode, struct buffer_head *bh)
{ … }
static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
struct inode *inode)
{ … }
static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
struct inode *dir, struct inode *inode)
{ … }
int ext4_generic_delete_entry(struct inode *dir,
struct ext4_dir_entry_2 *de_del,
struct buffer_head *bh,
void *entry_buf,
int buf_size,
int csum_size)
{ … }
static int ext4_delete_entry(handle_t *handle,
struct inode *dir,
struct ext4_dir_entry_2 *de_del,
struct buffer_head *bh)
{ … }
static void ext4_inc_count(struct inode *inode)
{ … }
static void ext4_dec_count(struct inode *inode)
{ … }
static int ext4_add_nondir(handle_t *handle,
struct dentry *dentry, struct inode **inodep)
{ … }
static int ext4_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{ … }
static int ext4_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev)
{ … }
static int ext4_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
struct file *file, umode_t mode)
{ … }
struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode,
struct ext4_dir_entry_2 *de,
int blocksize, int csum_size,
unsigned int parent_ino, int dotdot_real_len)
{ … }
int ext4_init_new_dir(handle_t *handle, struct inode *dir,
struct inode *inode)
{ … }
static int ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode)
{ … }
bool ext4_empty_dir(struct inode *inode)
{ … }
static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
{ … }
int __ext4_unlink(struct inode *dir, const struct qstr *d_name,
struct inode *inode,
struct dentry *dentry )
{ … }
static int ext4_unlink(struct inode *dir, struct dentry *dentry)
{ … }
static int ext4_init_symlink_block(handle_t *handle, struct inode *inode,
struct fscrypt_str *disk_link)
{ … }
static int ext4_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname)
{ … }
int __ext4_link(struct inode *dir, struct inode *inode, struct dentry *dentry)
{ … }
static int ext4_link(struct dentry *old_dentry,
struct inode *dir, struct dentry *dentry)
{ … }
static struct buffer_head *ext4_get_first_dir_block(handle_t *handle,
struct inode *inode,
int *retval,
struct ext4_dir_entry_2 **parent_de,
int *inlined)
{ … }
struct ext4_renament { … };
static int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent, bool is_cross)
{ … }
static int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent,
unsigned dir_ino)
{ … }
static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
unsigned ino, unsigned file_type)
{ … }
static void ext4_resetent(handle_t *handle, struct ext4_renament *ent,
unsigned ino, unsigned file_type)
{ … }
static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
const struct qstr *d_name)
{ … }
static void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent,
int force_reread)
{ … }
static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
{ … }
static struct inode *ext4_whiteout_for_rename(struct mnt_idmap *idmap,
struct ext4_renament *ent,
int credits, handle_t **h)
{ … }
static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
{ … }
static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{ … }
static int ext4_rename2(struct mnt_idmap *idmap,
struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{ … }
const struct inode_operations ext4_dir_inode_operations = …;
const struct inode_operations ext4_special_inode_operations = …;