📄 namei.c
字号:
/* * linux/fs/ext4/namei.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/namei.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 * Directory entry file type support and forward compatibility hooks * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998 * Hash Tree Directory indexing (c) * Daniel Phillips, 2001 * Hash Tree Directory indexing porting * Christopher Li, 2002 * Hash Tree Directory indexing cleanup * Theodore Ts'o, 2002 */#include <linux/fs.h>#include <linux/pagemap.h>#include <linux/jbd2.h>#include <linux/time.h>#include <linux/ext4_fs.h>#include <linux/ext4_jbd2.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 "namei.h"#include "xattr.h"#include "acl.h"/* * define how far ahead to read directories while searching them. */#define NAMEI_RA_CHUNKS 2#define NAMEI_RA_BLOCKS 4#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b))static struct buffer_head *ext4_append(handle_t *handle, struct inode *inode, u32 *block, int *err){ struct buffer_head *bh; *block = inode->i_size >> inode->i_sb->s_blocksize_bits; if ((bh = ext4_bread(handle, inode, *block, 1, err))) { inode->i_size += inode->i_sb->s_blocksize; EXT4_I(inode)->i_disksize = inode->i_size; ext4_journal_get_write_access(handle,bh); } return bh;}#ifndef assert#define assert(test) J_ASSERT(test)#endif#ifndef swap#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)#endif#ifdef DX_DEBUG#define dxtrace(command) command#else#define dxtrace(command)#endifstruct fake_dirent{ __le32 inode; __le16 rec_len; u8 name_len; u8 file_type;};struct dx_countlimit{ __le16 limit; __le16 count;};struct dx_entry{ __le32 hash; __le32 block;};/* * dx_root_info is laid out so that if it should somehow get overlaid by a * dirent the two low bits of the hash version will be zero. Therefore, the * hash version mod 4 should never be 0. Sincerely, the paranoia department. */struct dx_root{ struct fake_dirent dot; char dot_name[4]; struct fake_dirent dotdot; char dotdot_name[4]; struct dx_root_info { __le32 reserved_zero; u8 hash_version; u8 info_length; /* 8 */ u8 indirect_levels; u8 unused_flags; } info; struct dx_entry entries[0];};struct dx_node{ struct fake_dirent fake; struct dx_entry entries[0];};struct dx_frame{ struct buffer_head *bh; struct dx_entry *entries; struct dx_entry *at;};struct dx_map_entry{ u32 hash; u16 offs; u16 size;};static inline unsigned dx_get_block (struct dx_entry *entry);static void dx_set_block (struct dx_entry *entry, unsigned 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 dentry *dentry, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame, int *err);static void dx_release (struct dx_frame *frames);static int dx_make_map (struct ext4_dir_entry_2 *de, int size, struct dx_hash_info *hinfo, struct dx_map_entry map[]);static void dx_sort_map(struct dx_map_entry *map, unsigned count);static struct ext4_dir_entry_2 *dx_move_dirents (char *from, char *to, struct dx_map_entry *offsets, int count);static struct ext4_dir_entry_2* dx_pack_dirents (char *base, int size);static void dx_insert_block (struct dx_frame *frame, u32 hash, u32 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 dentry *dentry, struct ext4_dir_entry_2 **res_dir, int *err);static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode);/* * Future: use high four bits of block for coalesce-on-delete flags * Mask them off for now. */static inline unsigned dx_get_block (struct dx_entry *entry){ return le32_to_cpu(entry->block) & 0x00ffffff;}static inline void dx_set_block (struct dx_entry *entry, unsigned value){ entry->block = cpu_to_le32(value);}static inline unsigned dx_get_hash (struct dx_entry *entry){ return le32_to_cpu(entry->hash);}static inline void dx_set_hash (struct dx_entry *entry, unsigned value){ entry->hash = cpu_to_le32(value);}static inline unsigned dx_get_count (struct dx_entry *entries){ return le16_to_cpu(((struct dx_countlimit *) entries)->count);}static inline unsigned dx_get_limit (struct dx_entry *entries){ return le16_to_cpu(((struct dx_countlimit *) entries)->limit);}static inline void dx_set_count (struct dx_entry *entries, unsigned value){ ((struct dx_countlimit *) entries)->count = cpu_to_le16(value);}static inline void dx_set_limit (struct dx_entry *entries, unsigned value){ ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value);}static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize){ unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) - EXT4_DIR_REC_LEN(2) - infosize; return 0? 20: entry_space / sizeof(struct dx_entry);}static inline unsigned dx_node_limit (struct inode *dir){ unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0); return 0? 22: entry_space / sizeof(struct dx_entry);}/* * Debug */#ifdef DX_DEBUGstatic void dx_show_index (char * label, struct dx_entry *entries){ int i, n = dx_get_count (entries); printk("%s index ", label); for (i = 0; i < n; i++) { printk("%x->%u ", i? dx_get_hash(entries + i) : 0, dx_get_block(entries + i)); } printk("\n");}struct stats{ unsigned names; unsigned space; unsigned bcount;};static struct stats dx_show_leaf(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) { int len = de->name_len; char *name = de->name; while (len--) printk("%c", *name++); ext4fs_dirhash(de->name, de->name_len, &h); printk(":%x.%u ", h.hash, ((char *) de - base)); } space += EXT4_DIR_REC_LEN(de->name_len); names++; } de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } printk("(%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; int err; printk("%i indexed blocks...\n", count); for (i = 0; i < count; i++, entries++) { u32 block = dx_get_block(entries), 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); if (!(bh = ext4_bread (NULL,dir, block, 0,&err))) continue; stats = levels? dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1): dx_show_leaf(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("%snames %u, fullness %u (%u%%)\n", levels?"":" ", names, space/bcount,(space/bcount)*100/blocksize); return (struct stats) { names, space, bcount};}#endif /* DX_DEBUG *//* * Probe for a directory leaf block to search. * * dx_probe can return ERR_BAD_DX_DIR, which means there was a format * error in the directory index, and the caller should fall back to * searching the directory normally. The callers of dx_probe **MUST** * check for this error code, and make sure it never gets reflected * back to userspace. */static struct dx_frame *dx_probe(struct dentry *dentry, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err){ unsigned count, indirect; struct dx_entry *at, *entries, *p, *q, *m; struct dx_root *root; struct buffer_head *bh; struct dx_frame *frame = frame_in; u32 hash; frame->bh = NULL; if (dentry) dir = dentry->d_parent->d_inode; if (!(bh = ext4_bread (NULL,dir, 0, 0, err))) goto fail; root = (struct dx_root *) bh->b_data; if (root->info.hash_version != DX_HASH_TEA && root->info.hash_version != DX_HASH_HALF_MD4 && root->info.hash_version != DX_HASH_LEGACY) { ext4_warning(dir->i_sb, __FUNCTION__, "Unrecognised inode hash code %d", root->info.hash_version); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail; } hinfo->hash_version = root->info.hash_version; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; if (dentry) ext4fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo); hash = hinfo->hash; if (root->info.unused_flags & 1) { ext4_warning(dir->i_sb, __FUNCTION__, "Unimplemented inode hash flags: %#06x", root->info.unused_flags); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail; } if ((indirect = root->info.indirect_levels) > 1) { ext4_warning(dir->i_sb, __FUNCTION__, "Unimplemented inode hash depth: %#06x", root->info.indirect_levels); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail; } entries = (struct dx_entry *) (((char *)&root->info) + root->info.info_length); if (dx_get_limit(entries) != dx_root_limit(dir, root->info.info_length)) { ext4_warning(dir->i_sb, __FUNCTION__, "dx entry: limit != root limit"); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail; } dxtrace (printk("Look up %x", hash)); while (1) { count = dx_get_count(entries); if (!count || count > dx_get_limit(entries)) { ext4_warning(dir->i_sb, __FUNCTION__, "dx entry: no count or count > limit"); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail2; } p = entries + 1; q = entries + count - 1; while (p <= q) { m = p + (q - p)/2; dxtrace(printk(".")); if (dx_get_hash(m) > hash) q = m - 1; else p = m + 1; } if (0) // linear search cross check { unsigned n = count - 1; at = entries; while (n--) { dxtrace(printk(",")); if (dx_get_hash(++at) > hash) { at--; break; } } assert (at == p - 1); } at = p - 1; dxtrace(printk(" %x->%u\n", at == entries? 0: dx_get_hash(at), dx_get_block(at))); frame->bh = bh; frame->entries = entries; frame->at = at; if (!indirect--) return frame; if (!(bh = ext4_bread (NULL,dir, dx_get_block(at), 0, err))) goto fail2; at = entries = ((struct dx_node *) bh->b_data)->entries; if (dx_get_limit(entries) != dx_node_limit (dir)) { ext4_warning(dir->i_sb, __FUNCTION__, "dx entry: limit != node limit"); brelse(bh); *err = ERR_BAD_DX_DIR; goto fail2; } frame++; frame->bh = NULL; }fail2: while (frame >= frame_in) { brelse(frame->bh); frame--; }fail: if (*err == ERR_BAD_DX_DIR) ext4_warning(dir->i_sb, __FUNCTION__, "Corrupt dir inode %ld, running e2fsck is " "recommended.", dir->i_ino); return NULL;}static void dx_release (struct dx_frame *frames){ if (frames[0].bh == NULL) return; if (((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels) brelse(frames[1].bh); brelse(frames[0].bh);}/* * This function increments the frame pointer to search the next leaf * block, and reads in the necessary intervening nodes if the search * should be necessary. Whether or not the search is necessary is
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -