📄 namei.c
字号:
return add_dirent_to_buf(handle, dentry, inode, de, bh);}/* * ext4_add_entry() * * adds a file entry to the specified directory, using the same * semantics as ext4_find_entry(). It returns NULL if it failed. * * NOTE!! The inode part of 'de' is left at 0 - which means you * may not sleep between calling this and putting something into * the entry, as someone else might have used it while you slept. */static int ext4_add_entry (handle_t *handle, struct dentry *dentry, struct inode *inode){ struct inode *dir = dentry->d_parent->d_inode; unsigned long offset; struct buffer_head * bh; struct ext4_dir_entry_2 *de; struct super_block * sb; int retval; int dx_fallback=0; unsigned blocksize; u32 block, blocks; sb = dir->i_sb; blocksize = sb->s_blocksize; if (!dentry->d_name.len) return -EINVAL; if (is_dx(dir)) { retval = ext4_dx_add_entry(handle, dentry, inode); if (!retval || (retval != ERR_BAD_DX_DIR)) return retval; EXT4_I(dir)->i_flags &= ~EXT4_INDEX_FL; dx_fallback++; ext4_mark_inode_dirty(handle, dir); } blocks = dir->i_size >> sb->s_blocksize_bits; for (block = 0, offset = 0; block < blocks; block++) { bh = ext4_bread(handle, dir, block, 0, &retval); if(!bh) return retval; retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); if (retval != -ENOSPC) return retval; if (blocks == 1 && !dx_fallback && EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) return make_indexed_dir(handle, dentry, inode, bh); brelse(bh); } bh = ext4_append(handle, dir, &block, &retval); if (!bh) return retval; de = (struct ext4_dir_entry_2 *) bh->b_data; de->inode = 0; de->rec_len = cpu_to_le16(blocksize); return add_dirent_to_buf(handle, dentry, inode, de, bh);}/* * Returns 0 for success, or a negative error value */static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode){ struct dx_frame frames[2], *frame; struct dx_entry *entries, *at; struct dx_hash_info hinfo; struct buffer_head * bh; struct inode *dir = dentry->d_parent->d_inode; struct super_block * sb = dir->i_sb; struct ext4_dir_entry_2 *de; int err; frame = dx_probe(dentry, NULL, &hinfo, frames, &err); if (!frame) return err; entries = frame->entries; at = frame->at; if (!(bh = ext4_bread(handle,dir, dx_get_block(frame->at), 0, &err))) goto cleanup; BUFFER_TRACE(bh, "get_write_access"); err = ext4_journal_get_write_access(handle, bh); if (err) goto journal_error; err = add_dirent_to_buf(handle, dentry, inode, NULL, bh); if (err != -ENOSPC) { bh = NULL; goto cleanup; } /* Block full, should compress but for now just split */ dxtrace(printk("using %u of %u node entries\n", dx_get_count(entries), dx_get_limit(entries))); /* Need to split index? */ if (dx_get_count(entries) == dx_get_limit(entries)) { u32 newblock; unsigned icount = dx_get_count(entries); int levels = frame - frames; struct dx_entry *entries2; struct dx_node *node2; struct buffer_head *bh2; if (levels && (dx_get_count(frames->entries) == dx_get_limit(frames->entries))) { ext4_warning(sb, __FUNCTION__, "Directory index full!"); err = -ENOSPC; goto cleanup; } bh2 = ext4_append (handle, dir, &newblock, &err); if (!(bh2)) goto cleanup; node2 = (struct dx_node *)(bh2->b_data); entries2 = node2->entries; node2->fake.rec_len = cpu_to_le16(sb->s_blocksize); node2->fake.inode = 0; BUFFER_TRACE(frame->bh, "get_write_access"); err = ext4_journal_get_write_access(handle, frame->bh); if (err) goto journal_error; if (levels) { unsigned icount1 = icount/2, icount2 = icount - icount1; unsigned hash2 = dx_get_hash(entries + icount1); dxtrace(printk("Split index %i/%i\n", icount1, icount2)); BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */ err = ext4_journal_get_write_access(handle, frames[0].bh); if (err) goto journal_error; memcpy ((char *) entries2, (char *) (entries + icount1), icount2 * sizeof(struct dx_entry)); dx_set_count (entries, icount1); dx_set_count (entries2, icount2); dx_set_limit (entries2, dx_node_limit(dir)); /* Which index block gets the new entry? */ if (at - entries >= icount1) { frame->at = at = at - entries - icount1 + entries2; frame->entries = entries = entries2; swap(frame->bh, bh2); } dx_insert_block (frames + 0, hash2, newblock); dxtrace(dx_show_index ("node", frames[1].entries)); dxtrace(dx_show_index ("node", ((struct dx_node *) bh2->b_data)->entries)); err = ext4_journal_dirty_metadata(handle, bh2); if (err) goto journal_error; brelse (bh2); } else { dxtrace(printk("Creating second level index...\n")); memcpy((char *) entries2, (char *) entries, icount * sizeof(struct dx_entry)); dx_set_limit(entries2, dx_node_limit(dir)); /* Set up root */ dx_set_count(entries, 1); dx_set_block(entries + 0, newblock); ((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels = 1; /* Add new access path frame */ frame = frames + 1; frame->at = at = at - entries + entries2; frame->entries = entries = entries2; frame->bh = bh2; err = ext4_journal_get_write_access(handle, frame->bh); if (err) goto journal_error; } ext4_journal_dirty_metadata(handle, frames[0].bh); } de = do_split(handle, dir, &bh, frame, &hinfo, &err); if (!de) goto cleanup; err = add_dirent_to_buf(handle, dentry, inode, de, bh); bh = NULL; goto cleanup;journal_error: ext4_std_error(dir->i_sb, err);cleanup: if (bh) brelse(bh); dx_release(frames); return err;}/* * ext4_delete_entry deletes a directory entry by merging it with the * previous entry */static int ext4_delete_entry (handle_t *handle, struct inode * dir, struct ext4_dir_entry_2 * de_del, struct buffer_head * bh){ struct ext4_dir_entry_2 * de, * pde; int i; i = 0; pde = NULL; de = (struct ext4_dir_entry_2 *) bh->b_data; while (i < bh->b_size) { if (!ext4_check_dir_entry("ext4_delete_entry", dir, de, bh, i)) return -EIO; if (de == de_del) { BUFFER_TRACE(bh, "get_write_access"); ext4_journal_get_write_access(handle, bh); if (pde) pde->rec_len = cpu_to_le16(le16_to_cpu(pde->rec_len) + le16_to_cpu(de->rec_len)); else de->inode = 0; dir->i_version++; BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata"); ext4_journal_dirty_metadata(handle, bh); return 0; } i += le16_to_cpu(de->rec_len); pde = de; de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); } return -ENOENT;}/* * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2, * since this indicates that nlinks count was previously 1. */static void ext4_inc_count(handle_t *handle, struct inode *inode){ inc_nlink(inode); if (is_dx(inode) && inode->i_nlink > 1) { /* limit is 16-bit i_links_count */ if (inode->i_nlink >= EXT4_LINK_MAX || inode->i_nlink == 2) { inode->i_nlink = 1; EXT4_SET_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_DIR_NLINK); } }}/* * If a directory had nlink == 1, then we should let it be 1. This indicates * directory has >EXT4_LINK_MAX subdirs. */static void ext4_dec_count(handle_t *handle, struct inode *inode){ drop_nlink(inode); if (S_ISDIR(inode->i_mode) && inode->i_nlink == 0) inc_nlink(inode);}static int ext4_add_nondir(handle_t *handle, struct dentry *dentry, struct inode *inode){ int err = ext4_add_entry(handle, dentry, inode); if (!err) { ext4_mark_inode_dirty(handle, inode); d_instantiate(dentry, inode); return 0; } drop_nlink(inode); iput(inode); return err;}/* * By the time this is called, we already have created * the directory cache entry for the new file, but it * is so far negative - it has no inode. * * If the create succeeds, we fill in the inode information * with d_instantiate(). */static int ext4_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd){ handle_t *handle; struct inode * inode; int err, retries = 0;retry: handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + 2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); if (IS_DIRSYNC(dir)) handle->h_sync = 1; inode = ext4_new_inode (handle, dir, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); err = ext4_add_nondir(handle, dentry, inode); } ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err;}static int ext4_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev){ handle_t *handle; struct inode *inode; int err, retries = 0; if (!new_valid_dev(rdev)) return -EINVAL;retry: handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + 2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); if (IS_DIRSYNC(dir)) handle->h_sync = 1; inode = ext4_new_inode (handle, dir, mode); err = PTR_ERR(inode); if (!IS_ERR(inode)) { init_special_inode(inode, inode->i_mode, rdev);#ifdef CONFIG_EXT4DEV_FS_XATTR inode->i_op = &ext4_special_inode_operations;#endif err = ext4_add_nondir(handle, dentry, inode); } ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err;}static int ext4_mkdir(struct inode * dir, struct dentry * dentry, int mode){ handle_t *handle; struct inode * inode; struct buffer_head * dir_block; struct ext4_dir_entry_2 * de; int err, retries = 0; if (EXT4_DIR_LINK_MAX(dir)) return -EMLINK;retry: handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + 2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); if (IS_DIRSYNC(dir)) handle->h_sync = 1; inode = ext4_new_inode (handle, dir, S_IFDIR | mode); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; inode->i_op = &ext4_dir_inode_operations; inode->i_fop = &ext4_dir_operations; inode->i_size = EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize; dir_block = ext4_bread (handle, inode, 0, 1, &err); if (!dir_block) { ext4_dec_count(handle, inode); /* is this nlink == 0? */ ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; } BUFFER_TRACE(dir_block, "get_write_access"); ext4_journal_get_write_access(handle, dir_block); de = (struct ext4_dir_entry_2 *) dir_block->b_data; de->inode = cpu_to_le32(inode->i_ino); de->name_len = 1; de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(de->name_len)); strcpy (de->name, "."); ext4_set_de_type(dir->i_sb, de, S_IFDIR); de = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); de->inode = cpu_to_le32(dir->i_ino); de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT4_DIR_REC_LEN(1)); de->name_len = 2; strcpy (de->name, ".."); ext4_set_de_type(dir->i_sb, de, S_IFDIR); inode->i_nlink = 2; BUFFER_TRACE(dir_block, "call ext4_journal_dirty_metadata"); ext4_journal_dirty_metadata(handle, dir_block); brelse (dir_block); ext4_mark_inode_dirty(handle, inode); err = ext4_add_entry (handle, dentry, inode); if (err) { inode->i_nlink = 0; ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; } ext4_inc_count(handle, dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); d_instantiate(dentry, inode);out_stop: ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err;}/* * routine to check that the specified directory is empty (for rmdir) */static int empty_dir (struct inode * inode){ unsigned long offset; struct buffer_head * bh; struct ext4_dir_entry_2 * de, * de1; struct super_block * sb; int err = 0; sb = inode->i_sb; if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) || !(bh = ext4_bread (NULL, inode, 0, 0, &err))) { if (err) ext4_error(inode->i_sb, __FUNCTION__, "error %d reading directory #%lu offset 0", err, inode->i_ino); else ext4_warning(inode->i_sb, __FUNCTION__, "bad directory (dir #%lu) - no data block", inode->i_ino); return 1; } de = (struct ext4_dir_entry_2 *) bh->b_data; de1 = (struct ext4_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || strcmp (".", de->name) || strcmp ("..", de1->name)) { ext4_warning (inode->i_sb, "empty_dir", "bad directory (dir #%lu) - no `.' or `..'", inode->i_ino); brelse (bh); return 1; } offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); de = (struct ext4_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len)); while (offset < inode->i_size ) { if (!bh || (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) { err = 0; brelse (bh); bh = ext4_bread (NULL, inode, offset >> EXT4_BLOCK_SIZE_BITS(sb), 0, &err); if (!bh) { if (err)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -