📄 namei.c
字号:
static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry, struct ext4_dir_entry_2 **res_dir, int *err){ struct super_block * sb; struct dx_hash_info hinfo; u32 hash; struct dx_frame frames[2], *frame; struct ext4_dir_entry_2 *de, *top; struct buffer_head *bh; unsigned long block; int retval; int namelen = dentry->d_name.len; const u8 *name = dentry->d_name.name; struct inode *dir = dentry->d_parent->d_inode; sb = dir->i_sb; /* NFS may look up ".." - look at dx_root directory block */ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) return NULL; } else { frame = frames; frame->bh = NULL; /* for dx_release() */ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ dx_set_block(frame->at, 0); /* dx_root block is 0 */ } hash = hinfo.hash; do { block = dx_get_block(frame->at); if (!(bh = ext4_bread (NULL,dir, block, 0, err))) goto errout; de = (struct ext4_dir_entry_2 *) bh->b_data; top = (struct ext4_dir_entry_2 *) ((char *) de + sb->s_blocksize - EXT4_DIR_REC_LEN(0)); for (; de < top; de = ext4_next_entry(de)) if (ext4_match (namelen, name, de)) { if (!ext4_check_dir_entry("ext4_find_entry", dir, de, bh, (block<<EXT4_BLOCK_SIZE_BITS(sb)) +((char *)de - bh->b_data))) { brelse (bh); *err = ERR_BAD_DX_DIR; goto errout; } *res_dir = de; dx_release (frames); return bh; } brelse (bh); /* Check to see if we should continue to search */ retval = ext4_htree_next_block(dir, hash, frame, frames, NULL); if (retval < 0) { ext4_warning(sb, __FUNCTION__, "error reading index page in directory #%lu", dir->i_ino); *err = retval; goto errout; } } while (retval == 1); *err = -ENOENT;errout: dxtrace(printk("%s not found\n", name)); dx_release (frames); return NULL;}static struct dentry *ext4_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd){ struct inode * inode; struct ext4_dir_entry_2 * de; struct buffer_head * bh; if (dentry->d_name.len > EXT4_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); bh = ext4_find_entry(dentry, &de); inode = NULL; if (bh) { unsigned long ino = le32_to_cpu(de->inode); brelse (bh); if (!ext4_valid_inum(dir->i_sb, ino)) { ext4_error(dir->i_sb, "ext4_lookup", "bad inode number: %lu", ino); inode = NULL; } else inode = iget(dir->i_sb, ino); if (!inode) return ERR_PTR(-EACCES); if (is_bad_inode(inode)) { iput(inode); return ERR_PTR(-ENOENT); } } return d_splice_alias(inode, dentry);}struct dentry *ext4_get_parent(struct dentry *child){ unsigned long ino; struct dentry *parent; struct inode *inode; struct dentry dotdot; struct ext4_dir_entry_2 * de; struct buffer_head *bh; dotdot.d_name.name = ".."; dotdot.d_name.len = 2; dotdot.d_parent = child; /* confusing, isn't it! */ bh = ext4_find_entry(&dotdot, &de); inode = NULL; if (!bh) return ERR_PTR(-ENOENT); ino = le32_to_cpu(de->inode); brelse(bh); if (!ext4_valid_inum(child->d_inode->i_sb, ino)) { ext4_error(child->d_inode->i_sb, "ext4_get_parent", "bad inode number: %lu", ino); inode = NULL; } else inode = iget(child->d_inode->i_sb, ino); if (!inode) return ERR_PTR(-EACCES); if (is_bad_inode(inode)) { iput(inode); return ERR_PTR(-ENOENT); } parent = d_alloc_anon(inode); if (!parent) { iput(inode); parent = ERR_PTR(-ENOMEM); } return parent;}#define S_SHIFT 12static unsigned char ext4_type_by_mode[S_IFMT >> S_SHIFT] = { [S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE, [S_IFDIR >> S_SHIFT] = EXT4_FT_DIR, [S_IFCHR >> S_SHIFT] = EXT4_FT_CHRDEV, [S_IFBLK >> S_SHIFT] = EXT4_FT_BLKDEV, [S_IFIFO >> S_SHIFT] = EXT4_FT_FIFO, [S_IFSOCK >> S_SHIFT] = EXT4_FT_SOCK, [S_IFLNK >> S_SHIFT] = EXT4_FT_SYMLINK,};static inline void ext4_set_de_type(struct super_block *sb, struct ext4_dir_entry_2 *de, umode_t mode) { if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FILETYPE)) de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT];}/* * Move count entries from end of map between two memory locations. * Returns pointer to last entry moved. */static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count){ unsigned rec_len = 0; while (count--) { struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *) (from + map->offs); rec_len = EXT4_DIR_REC_LEN(de->name_len); memcpy (to, de, rec_len); ((struct ext4_dir_entry_2 *) to)->rec_len = cpu_to_le16(rec_len); de->inode = 0; map++; to += rec_len; } return (struct ext4_dir_entry_2 *) (to - rec_len);}/* * Compact each dir entry in the range to the minimal rec_len. * Returns pointer to last entry in range. */static struct ext4_dir_entry_2* dx_pack_dirents(char *base, int size){ struct ext4_dir_entry_2 *next, *to, *prev, *de = (struct ext4_dir_entry_2 *) base; unsigned rec_len = 0; prev = to = de; while ((char*)de < base + size) { next = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); if (de->inode && de->name_len) { rec_len = EXT4_DIR_REC_LEN(de->name_len); if (de > to) memmove(to, de, rec_len); to->rec_len = cpu_to_le16(rec_len); prev = to; to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len); } de = next; } return prev;}/* * Split a full leaf block to make room for a new dir entry. * Allocate a new block, and move entries so that they are approx. equally full. * Returns pointer to de in block into which the new entry will be inserted. */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 *error){ unsigned blocksize = dir->i_sb->s_blocksize; unsigned count, continued; struct buffer_head *bh2; u32 newblock; u32 hash2; struct dx_map_entry *map; char *data1 = (*bh)->b_data, *data2; unsigned split, move, size, i; struct ext4_dir_entry_2 *de = NULL, *de2; int err = 0; bh2 = ext4_append (handle, dir, &newblock, &err); if (!(bh2)) { brelse(*bh); *bh = NULL; goto errout; } BUFFER_TRACE(*bh, "get_write_access"); err = ext4_journal_get_write_access(handle, *bh); if (err) goto journal_error; BUFFER_TRACE(frame->bh, "get_write_access"); err = ext4_journal_get_write_access(handle, frame->bh); if (err) goto journal_error; data2 = bh2->b_data; /* create map in the end of data2 block */ map = (struct dx_map_entry *) (data2 + blocksize); count = dx_make_map ((struct ext4_dir_entry_2 *) data1, blocksize, hinfo, map); map -= count; dx_sort_map (map, count); /* Split the existing block in the middle, size-wise */ size = 0; move = 0; for (i = count-1; i >= 0; i--) { /* is more than half of this entry in 2nd half of the block? */ if (size + map[i].size/2 > blocksize/2) break; size += map[i].size; move++; } /* map index at which we will split */ split = count - move; hash2 = map[split].hash; continued = hash2 == map[split - 1].hash; dxtrace(printk("Split block %i at %x, %i/%i\n", dx_get_block(frame->at), hash2, split, count-split)); /* Fancy dance to stay within two buffers */ de2 = dx_move_dirents(data1, data2, map + split, count - split); de = dx_pack_dirents(data1,blocksize); de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2); dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1)); dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data2, blocksize, 1)); /* Which block gets the new entry? */ if (hinfo->hash >= hash2) { swap(*bh, bh2); de = de2; } dx_insert_block (frame, hash2 + continued, newblock); err = ext4_journal_dirty_metadata (handle, bh2); if (err) goto journal_error; err = ext4_journal_dirty_metadata (handle, frame->bh); if (err) goto journal_error; brelse (bh2); dxtrace(dx_show_index ("frame", frame->entries)); return de;journal_error: brelse(*bh); brelse(bh2); *bh = NULL; ext4_std_error(dir->i_sb, err);errout: *error = err; return NULL;}/* * Add a new entry into a directory (leaf) block. If de is non-NULL, * it points to a directory entry which is guaranteed to be large * enough for new directory entry. If de is NULL, then * add_dirent_to_buf will attempt search the directory block for * space. It will return -ENOSPC if no space is available, and -EIO * and -EEXIST if directory entry already exists. * * NOTE! bh is NOT released in the case where ENOSPC is returned. In * all other cases bh is released. */static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, struct inode *inode, struct ext4_dir_entry_2 *de, struct buffer_head * bh){ struct inode *dir = dentry->d_parent->d_inode; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; unsigned long offset = 0; unsigned short reclen; int nlen, rlen, err; char *top; reclen = EXT4_DIR_REC_LEN(namelen); if (!de) { de = (struct ext4_dir_entry_2 *)bh->b_data; top = bh->b_data + dir->i_sb->s_blocksize - reclen; while ((char *) de <= top) { if (!ext4_check_dir_entry("ext4_add_entry", dir, de, bh, offset)) { brelse (bh); return -EIO; } if (ext4_match (namelen, name, de)) { brelse (bh); return -EEXIST; } nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = le16_to_cpu(de->rec_len); if ((de->inode? rlen - nlen: rlen) >= reclen) break; de = (struct ext4_dir_entry_2 *)((char *)de + rlen); offset += rlen; } if ((char *) de > top) return -ENOSPC; } BUFFER_TRACE(bh, "get_write_access"); err = ext4_journal_get_write_access(handle, bh); if (err) { ext4_std_error(dir->i_sb, err); brelse(bh); return err; } /* By now the buffer is marked for journaling */ nlen = EXT4_DIR_REC_LEN(de->name_len); rlen = le16_to_cpu(de->rec_len); if (de->inode) { struct ext4_dir_entry_2 *de1 = (struct ext4_dir_entry_2 *)((char *)de + nlen); de1->rec_len = cpu_to_le16(rlen - nlen); de->rec_len = cpu_to_le16(nlen); de = de1; } de->file_type = EXT4_FT_UNKNOWN; if (inode) { de->inode = cpu_to_le32(inode->i_ino); ext4_set_de_type(dir->i_sb, de, inode->i_mode); } else de->inode = 0; de->name_len = namelen; memcpy (de->name, name, namelen); /* * XXX shouldn't update any times until successful * completion of syscall, but too many callers depend * on this. * * XXX similarly, too many callers depend on * ext4_new_inode() setting the times, but error * recovery deletes the inode, so the worst that can * happen is that the times are slightly out of date * and/or different from the directory change time. */ dir->i_mtime = dir->i_ctime = ext4_current_time(dir); ext4_update_dx_flag(dir); dir->i_version++; ext4_mark_inode_dirty(handle, dir); BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata"); err = ext4_journal_dirty_metadata(handle, bh); if (err) ext4_std_error(dir->i_sb, err); brelse(bh); return 0;}/* * This converts a one block unindexed directory to a 3 block indexed * directory, and adds the dentry to the indexed directory. */static int make_indexed_dir(handle_t *handle, struct dentry *dentry, struct inode *inode, struct buffer_head *bh){ struct inode *dir = dentry->d_parent->d_inode; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; struct buffer_head *bh2; struct dx_root *root; struct dx_frame frames[2], *frame; struct dx_entry *entries; struct ext4_dir_entry_2 *de, *de2; char *data1, *top; unsigned len; int retval; unsigned blocksize; struct dx_hash_info hinfo; u32 block; struct fake_dirent *fde; blocksize = dir->i_sb->s_blocksize; dxtrace(printk("Creating index\n")); retval = ext4_journal_get_write_access(handle, bh); if (retval) { ext4_std_error(dir->i_sb, retval); brelse(bh); return retval; } root = (struct dx_root *) bh->b_data; bh2 = ext4_append (handle, dir, &block, &retval); if (!(bh2)) { brelse(bh); return retval; } EXT4_I(dir)->i_flags |= EXT4_INDEX_FL; data1 = bh2->b_data; /* The 0th block becomes the root, move the dirents out */ fde = &root->dotdot; de = (struct ext4_dir_entry_2 *)((char *)fde + le16_to_cpu(fde->rec_len)); len = ((char *) root) + blocksize - (char *) de; memcpy (data1, de, len); de = (struct ext4_dir_entry_2 *) data1; top = data1 + len; while ((char *)(de2=(void*)de+le16_to_cpu(de->rec_len)) < top) de = de2; de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); /* Initialize the root; the dot dirents already exist */ de = (struct ext4_dir_entry_2 *) (&root->dotdot); de->rec_len = cpu_to_le16(blocksize - EXT4_DIR_REC_LEN(2)); memset (&root->info, 0, sizeof(root->info)); root->info.info_length = sizeof(root->info); root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; entries = root->entries; dx_set_block (entries, 1); dx_set_count (entries, 1); dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info))); /* Initialize as for dx_probe */ hinfo.hash_version = root->info.hash_version; hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; ext4fs_dirhash(name, namelen, &hinfo); frame = frames; frame->entries = entries; frame->at = entries; frame->bh = bh; bh = bh2; de = do_split(handle,dir, &bh, frame, &hinfo, &retval); dx_release (frames); if (!(de)) return retval;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -