cregit-Linux how code gets into the kernel

Release 4.10 fs/ext4/namei.c

Directory: fs/ext4
/*
 *  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/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 "ext4.h"
#include "ext4_jbd2.h"

#include "xattr.h"
#include "acl.h"

#include <trace/events/ext4.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)


static struct buffer_head *ext4_append(handle_t *handle, struct inode *inode, ext4_lblk_t *block) { struct buffer_head *bh; int err; if (unlikely(EXT4_SB(inode->i_sb)->s_max_dir_size_kb && ((inode->i_size >> 10) >= EXT4_SB(inode->i_sb)->s_max_dir_size_kb))) return ERR_PTR(-ENOSPC); *block = inode->i_size >> inode->i_sb->s_blocksize_bits; bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE); if (IS_ERR(bh)) return bh; inode->i_size += inode->i_sb->s_blocksize; EXT4_I(inode)->i_disksize = inode->i_size; BUFFER_TRACE(bh, "get_write_access"); err = ext4_journal_get_write_access(handle, bh); if (err) { brelse(bh); ext4_std_error(inode->i_sb, err); return ERR_PTR(err); } return bh; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp7845.35%110.00%
theodore tsotheodore tso5833.72%440.00%
akinobu mitaakinobu mita169.30%110.00%
carlos maiolinocarlos maiolino84.65%110.00%
liang xieliang xie74.07%110.00%
mingming caomingming cao42.33%110.00%
aneesh kumaraneesh kumar10.58%110.00%
Total172100.00%10100.00%

static int ext4_dx_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent); typedef enum { EITHER, INDEX, DIRENT } dirblock_type_t; #define ext4_read_dirblock(inode, block, type) \ __ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)
static struct buffer_head *__ext4_read_dirblock(struct inode *inode, ext4_lblk_t block, dirblock_type_t type, const char *func, unsigned int line) { struct buffer_head *bh; struct ext4_dir_entry *dirent; int is_dx_block = 0; bh = ext4_bread(NULL, inode, block, 0); if (IS_ERR(bh)) { __ext4_warning(inode->i_sb, func, line, "inode #%lu: lblock %lu: comm %s: " "error %ld reading directory block", inode->i_ino, (unsigned long)block, current->comm, PTR_ERR(bh)); return bh; } if (!bh) { ext4_error_inode(inode, func, line, block, "Directory hole found"); return ERR_PTR(-EFSCORRUPTED); } dirent = (struct ext4_dir_entry *) bh->b_data; /* Determine whether or not we have an index block */ if (is_dx(inode)) { if (block == 0) is_dx_block = 1; else if (ext4_rec_len_from_disk(dirent->rec_len, inode->i_sb->s_blocksize) == inode->i_sb->s_blocksize) is_dx_block = 1; } if (!is_dx_block && type == INDEX) { ext4_error_inode(inode, func, line, block, "directory leaf block found instead of index block"); return ERR_PTR(-EFSCORRUPTED); } if (!ext4_has_metadata_csum(inode->i_sb) || buffer_verified(bh)) return bh; /* * An empty leaf block can get mistaken for a index block; for * this reason, we can only check the index checksum when the * caller is sure it should be an index block. */ if (is_dx_block && type == INDEX) { if (ext4_dx_csum_verify(inode, dirent)) set_buffer_verified(bh); else { ext4_error_inode(inode, func, line, block, "Directory index failed checksum"); brelse(bh); return ERR_PTR(-EFSBADCRC); } } if (!is_dx_block) { if (ext4_dirent_csum_verify(inode, dirent)) set_buffer_verified(bh); else { ext4_error_inode(inode, func, line, block, "Directory block failed checksum"); brelse(bh); return ERR_PTR(-EFSBADCRC); } } return bh; }

Contributors

PersonTokensPropCommitsCommitProp
theodore tsotheodore tso28283.19%225.00%
andreas dilgerandreas dilger216.19%112.50%
carlos maiolinocarlos maiolino133.83%112.50%
akinobu mitaakinobu mita113.24%112.50%
dave kleikampdave kleikamp72.06%112.50%
darrick j. wongdarrick j. wong41.18%112.50%
dmitriy monakhovdmitriy monakhov10.29%112.50%
Total339100.00%8100.00%

#ifndef assert #define assert(test) J_ASSERT(test) #endif #ifdef DX_DEBUG #define dxtrace(command) command #else #define dxtrace(command) #endif struct 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; }; /* * This goes at the end of each htree block. */ struct dx_tail { u32 dt_reserved; __le32 dt_checksum; /* crc32c(uuid+inum+dirblock) */ }; 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 ext4_dir_entry_2 *de, unsigned blocksize, 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, unsigned blocksize); static struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned 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); /* checksumming functions */
void initialize_dirent_tail(struct ext4_dir_entry_tail *t, unsigned int blocksize) { memset(t, 0, sizeof(struct ext4_dir_entry_tail)); t->det_rec_len = ext4_rec_len_to_disk( sizeof(struct ext4_dir_entry_tail), blocksize); t->det_reserved_ft = EXT4_FT_DIR_CSUM; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong3470.83%133.33%
li zefanli zefan918.75%133.33%
wei yongjunwei yongjun510.42%133.33%
Total48100.00%3100.00%

/* Walk through a dirent block to find a checksum "dirent" at the tail */
static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, struct ext4_dir_entry *de) { struct ext4_dir_entry_tail *t; #ifdef PARANOID struct ext4_dir_entry *d, *top; d = de; top = (struct ext4_dir_entry *)(((void *)de) + (EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct ext4_dir_entry_tail))); while (d < top && d->rec_len) d = (struct ext4_dir_entry *)(((void *)d) + le16_to_cpu(d->rec_len)); if (d != top) return NULL; t = (struct ext4_dir_entry_tail *)d; #else t = EXT4_DIRENT_TAIL(de, EXT4_BLOCK_SIZE(inode->i_sb)); #endif if (t->det_reserved_zero1 || le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) || t->det_reserved_zero2 || t->det_reserved_ft != EXT4_FT_DIR_CSUM) return NULL; return t; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong14582.86%150.00%
dave kleikampdave kleikamp3017.14%150.00%
Total175100.00%2100.00%


static __le32 ext4_dirent_csum(struct inode *inode, struct ext4_dir_entry *dirent, int size) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); __u32 csum; csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); return cpu_to_le32(csum); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong69100.00%1100.00%
Total69100.00%1100.00%

#define warn_no_space_for_csum(inode) \ __warn_no_space_for_csum((inode), __func__, __LINE__)
static void __warn_no_space_for_csum(struct inode *inode, const char *func, unsigned int line) { __ext4_warning_inode(inode, func, line, "No space for directory leaf checksum. Please run e2fsck -D."); }

Contributors

PersonTokensPropCommitsCommitProp
theodore tsotheodore tso1651.61%150.00%
andreas dilgerandreas dilger1548.39%150.00%
Total31100.00%2100.00%


int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent) { struct ext4_dir_entry_tail *t; if (!ext4_has_metadata_csum(inode->i_sb)) return 1; t = get_dirent_tail(inode, dirent); if (!t) { warn_no_space_for_csum(inode); return 0; } if (t->det_checksum != ext4_dirent_csum(inode, dirent, (void *)t - (void *)dirent)) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong8697.73%133.33%
dmitriy monakhovdmitriy monakhov11.14%133.33%
theodore tsotheodore tso11.14%133.33%
Total88100.00%3100.00%


static void ext4_dirent_csum_set(struct inode *inode, struct ext4_dir_entry *dirent) { struct ext4_dir_entry_tail *t; if (!ext4_has_metadata_csum(inode->i_sb)) return; t = get_dirent_tail(inode, dirent); if (!t) { warn_no_space_for_csum(inode); return; } t->det_checksum = ext4_dirent_csum(inode, dirent, (void *)t - (void *)dirent); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong7597.40%133.33%
dmitriy monakhovdmitriy monakhov11.30%133.33%
theodore tsotheodore tso11.30%133.33%
Total77100.00%3100.00%


int ext4_handle_dirty_dirent_node(handle_t *handle, struct inode *inode, struct buffer_head *bh) { ext4_dirent_csum_set(inode, (struct ext4_dir_entry *)bh->b_data); return ext4_handle_dirty_metadata(handle, inode, bh); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong43100.00%1100.00%
Total43100.00%1100.00%


static struct dx_countlimit *get_dx_countlimit(struct inode *inode, struct ext4_dir_entry *dirent, int *offset) { struct ext4_dir_entry *dp; struct dx_root_info *root; int count_offset; if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb)) count_offset = 8; else if (le16_to_cpu(dirent->rec_len) == 12) { dp = (struct ext4_dir_entry *)(((void *)dirent) + 12); if (le16_to_cpu(dp->rec_len) != EXT4_BLOCK_SIZE(inode->i_sb) - 12) return NULL; root = (struct dx_root_info *)(((void *)dp + 12)); if (root->reserved_zero || root->info_length != sizeof(struct dx_root_info)) return NULL; count_offset = 32; } else return NULL; if (offset) *offset = count_offset; return (struct dx_countlimit *)(((void *)dirent) + count_offset); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong182100.00%1100.00%
Total182100.00%1100.00%


static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent, int count_offset, int count, struct dx_tail *t) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); __u32 csum; int size; __u32 dummy_csum = 0; int offset = offsetof(struct dx_tail, dt_checksum); size = count_offset + (count * sizeof(struct dx_entry)); csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); csum = ext4_chksum(sbi, csum, (__u8 *)t, offset); csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); return cpu_to_le32(csum); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong11275.68%133.33%
daeho jeongdaeho jeong3523.65%133.33%
theodore tsotheodore tso10.68%133.33%
Total148100.00%3100.00%


static int ext4_dx_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent) { struct dx_countlimit *c; struct dx_tail *t; int count_offset, limit, count; if (!ext4_has_metadata_csum(inode->i_sb)) return 1; c = get_dx_countlimit(inode, dirent, &count_offset); if (!c) { EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); return 0; } limit = le16_to_cpu(c->limit); count = le16_to_cpu(c->count); if (count_offset + (limit * sizeof(struct dx_entry)) > EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { warn_no_space_for_csum(inode); return 0; } t = (struct dx_tail *)(((struct dx_entry *)c) + limit); if (t->dt_checksum != ext4_dx_csum(inode, dirent, count_offset, count, t)) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong17197.71%125.00%
daeho jeongdaeho jeong21.14%125.00%
dmitriy monakhovdmitriy monakhov10.57%125.00%
theodore tsotheodore tso10.57%125.00%
Total175100.00%4100.00%


static void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent) { struct dx_countlimit *c; struct dx_tail *t; int count_offset, limit, count; if (!ext4_has_metadata_csum(inode->i_sb)) return; c = get_dx_countlimit(inode, dirent, &count_offset); if (!c) { EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); return; } limit = le16_to_cpu(c->limit); count = le16_to_cpu(c->count); if (count_offset + (limit * sizeof(struct dx_entry)) > EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { warn_no_space_for_csum(inode); return; } t = (struct dx_tail *)(((struct dx_entry *)c) + limit); t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong15998.76%133.33%
theodore tsotheodore tso10.62%133.33%
dmitriy monakhovdmitriy monakhov10.62%133.33%
Total161100.00%3100.00%


static inline int ext4_handle_dirty_dx_node(handle_t *handle, struct inode *inode, struct buffer_head *bh) { ext4_dx_csum_set(inode, (struct ext4_dir_entry *)bh->b_data); return ext4_handle_dirty_metadata(handle, inode, bh); }

Contributors

PersonTokensPropCommitsCommitProp
darrick j. wongdarrick j. wong45100.00%1100.00%
Total45100.00%1100.00%

/* * p is at least 6 bytes before the end of page */
static inline struct ext4_dir_entry_2 * ext4_next_entry(struct ext4_dir_entry_2 *p, unsigned long blocksize) { return (struct ext4_dir_entry_2 *)((char *)p + ext4_rec_len_from_disk(p->rec_len, blocksize)); }

Contributors

PersonTokensPropCommitsCommitProp
li zefanli zefan3585.37%150.00%
wei yongjunwei yongjun614.63%150.00%
Total41100.00%2100.00%

/* * Future: use high four bits of block for coalesce-on-delete flags * Mask them off for now. */
static inline ext4_lblk_t dx_get_block(struct dx_entry *entry) { return le32_to_cpu(entry->block) & 0x00ffffff; }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp2195.45%150.00%
aneesh kumaraneesh kumar14.55%150.00%
Total22100.00%2100.00%


static inline void dx_set_block(struct dx_entry *entry, ext4_lblk_t value) { entry->block = cpu_to_le32(value); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp2395.83%150.00%
aneesh kumaraneesh kumar14.17%150.00%
Total24100.00%2100.00%


static inline unsigned dx_get_hash(struct dx_entry *entry) { return le32_to_cpu(entry->hash); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp20100.00%1100.00%
Total20100.00%1100.00%


static inline void dx_set_hash(struct dx_entry *entry, unsigned value) { entry->hash = cpu_to_le32(value); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp24100.00%1100.00%
Total24100.00%1100.00%


static inline unsigned dx_get_count(struct dx_entry *entries) { return le16_to_cpu(((struct dx_countlimit *) entries)->count); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp27100.00%1100.00%
Total27100.00%1100.00%


static inline unsigned dx_get_limit(struct dx_entry *entries) { return le16_to_cpu(((struct dx_countlimit *) entries)->limit); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp27100.00%1100.00%
Total27100.00%1100.00%


static inline void dx_set_count(struct dx_entry *entries, unsigned value) { ((struct dx_countlimit *) entries)->count = cpu_to_le16(value); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp31100.00%1100.00%
Total31100.00%1100.00%


static inline void dx_set_limit(struct dx_entry *entries, unsigned value) { ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp31100.00%1100.00%
Total31100.00%1100.00%


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; if (ext4_has_metadata_csum(dir->i_sb)) entry_space -= sizeof(struct dx_tail); return entry_space / sizeof(struct dx_entry); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp4369.35%125.00%
darrick j. wongdarrick j. wong1625.81%125.00%
mingming caomingming cao23.23%125.00%
dmitriy monakhovdmitriy monakhov11.61%125.00%
Total62100.00%4100.00%


static inline unsigned dx_node_limit(struct inode *dir) { unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0); if (ext4_has_metadata_csum(dir->i_sb)) entry_space -= sizeof(struct dx_tail); return entry_space / sizeof(struct dx_entry); }

Contributors

PersonTokensPropCommitsCommitProp
dave kleikampdave kleikamp3465.38%125.00%
darrick j. wongdarrick j. wong1630.77%125.00%
mingming caomingming cao11.92%125.00%
dmitriy monakhovdmitriy monakhov11.92%125.00%
Total52100.00%4100.00%

/* * Debug */ #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));