⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 namei.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
					ext4_error(sb, __FUNCTION__,						   "error %d reading directory"						   " #%lu offset %lu",						   err, inode->i_ino, offset);				offset += sb->s_blocksize;				continue;			}			de = (struct ext4_dir_entry_2 *) bh->b_data;		}		if (!ext4_check_dir_entry("empty_dir", inode, de, bh, offset)) {			de = (struct ext4_dir_entry_2 *)(bh->b_data +							 sb->s_blocksize);			offset = (offset | (sb->s_blocksize - 1)) + 1;			continue;		}		if (le32_to_cpu(de->inode)) {			brelse (bh);			return 0;		}		offset += le16_to_cpu(de->rec_len);		de = (struct ext4_dir_entry_2 *)				((char *) de + le16_to_cpu(de->rec_len));	}	brelse (bh);	return 1;}/* ext4_orphan_add() links an unlinked or truncated inode into a list of * such inodes, starting at the superblock, in case we crash before the * file is closed/deleted, or in case the inode truncate spans multiple * transactions and the last transaction is not recovered after a crash. * * At filesystem recovery time, we walk this list deleting unlinked * inodes and truncating linked inodes in ext4_orphan_cleanup(). */int ext4_orphan_add(handle_t *handle, struct inode *inode){	struct super_block *sb = inode->i_sb;	struct ext4_iloc iloc;	int err = 0, rc;	lock_super(sb);	if (!list_empty(&EXT4_I(inode)->i_orphan))		goto out_unlock;	/* Orphan handling is only valid for files with data blocks	 * being truncated, or files being unlinked. */	/* @@@ FIXME: Observation from aviro:	 * I think I can trigger J_ASSERT in ext4_orphan_add().  We block	 * here (on lock_super()), so race with ext4_link() which might bump	 * ->i_nlink. For, say it, character device. Not a regular file,	 * not a directory, not a symlink and ->i_nlink > 0.	 */	J_ASSERT ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||		S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);	BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");	err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);	if (err)		goto out_unlock;	err = ext4_reserve_inode_write(handle, inode, &iloc);	if (err)		goto out_unlock;	/* Insert this inode at the head of the on-disk orphan list... */	NEXT_ORPHAN(inode) = le32_to_cpu(EXT4_SB(sb)->s_es->s_last_orphan);	EXT4_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);	err = ext4_journal_dirty_metadata(handle, EXT4_SB(sb)->s_sbh);	rc = ext4_mark_iloc_dirty(handle, inode, &iloc);	if (!err)		err = rc;	/* Only add to the head of the in-memory list if all the	 * previous operations succeeded.  If the orphan_add is going to	 * fail (possibly taking the journal offline), we can't risk	 * leaving the inode on the orphan list: stray orphan-list	 * entries can cause panics at unmount time.	 *	 * This is safe: on error we're going to ignore the orphan list	 * anyway on the next recovery. */	if (!err)		list_add(&EXT4_I(inode)->i_orphan, &EXT4_SB(sb)->s_orphan);	jbd_debug(4, "superblock will point to %lu\n", inode->i_ino);	jbd_debug(4, "orphan inode %lu will point to %d\n",			inode->i_ino, NEXT_ORPHAN(inode));out_unlock:	unlock_super(sb);	ext4_std_error(inode->i_sb, err);	return err;}/* * ext4_orphan_del() removes an unlinked or truncated inode from the list * of such inodes stored on disk, because it is finally being cleaned up. */int ext4_orphan_del(handle_t *handle, struct inode *inode){	struct list_head *prev;	struct ext4_inode_info *ei = EXT4_I(inode);	struct ext4_sb_info *sbi;	unsigned long ino_next;	struct ext4_iloc iloc;	int err = 0;	lock_super(inode->i_sb);	if (list_empty(&ei->i_orphan)) {		unlock_super(inode->i_sb);		return 0;	}	ino_next = NEXT_ORPHAN(inode);	prev = ei->i_orphan.prev;	sbi = EXT4_SB(inode->i_sb);	jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino);	list_del_init(&ei->i_orphan);	/* If we're on an error path, we may not have a valid	 * transaction handle with which to update the orphan list on	 * disk, but we still need to remove the inode from the linked	 * list in memory. */	if (!handle)		goto out;	err = ext4_reserve_inode_write(handle, inode, &iloc);	if (err)		goto out_err;	if (prev == &sbi->s_orphan) {		jbd_debug(4, "superblock will point to %lu\n", ino_next);		BUFFER_TRACE(sbi->s_sbh, "get_write_access");		err = ext4_journal_get_write_access(handle, sbi->s_sbh);		if (err)			goto out_brelse;		sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);		err = ext4_journal_dirty_metadata(handle, sbi->s_sbh);	} else {		struct ext4_iloc iloc2;		struct inode *i_prev =			&list_entry(prev, struct ext4_inode_info, i_orphan)->vfs_inode;		jbd_debug(4, "orphan inode %lu will point to %lu\n",			  i_prev->i_ino, ino_next);		err = ext4_reserve_inode_write(handle, i_prev, &iloc2);		if (err)			goto out_brelse;		NEXT_ORPHAN(i_prev) = ino_next;		err = ext4_mark_iloc_dirty(handle, i_prev, &iloc2);	}	if (err)		goto out_brelse;	NEXT_ORPHAN(inode) = 0;	err = ext4_mark_iloc_dirty(handle, inode, &iloc);out_err:	ext4_std_error(inode->i_sb, err);out:	unlock_super(inode->i_sb);	return err;out_brelse:	brelse(iloc.bh);	goto out_err;}static int ext4_rmdir (struct inode * dir, struct dentry *dentry){	int retval;	struct inode * inode;	struct buffer_head * bh;	struct ext4_dir_entry_2 * de;	handle_t *handle;	/* Initialize quotas before so that eventual writes go in	 * separate transaction */	DQUOT_INIT(dentry->d_inode);	handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb));	if (IS_ERR(handle))		return PTR_ERR(handle);	retval = -ENOENT;	bh = ext4_find_entry (dentry, &de);	if (!bh)		goto end_rmdir;	if (IS_DIRSYNC(dir))		handle->h_sync = 1;	inode = dentry->d_inode;	retval = -EIO;	if (le32_to_cpu(de->inode) != inode->i_ino)		goto end_rmdir;	retval = -ENOTEMPTY;	if (!empty_dir (inode))		goto end_rmdir;	retval = ext4_delete_entry(handle, dir, de, bh);	if (retval)		goto end_rmdir;	if (!EXT4_DIR_LINK_EMPTY(inode))		ext4_warning (inode->i_sb, "ext4_rmdir",			      "empty directory has too many links (%d)",			      inode->i_nlink);	inode->i_version++;	clear_nlink(inode);	/* There's no need to set i_disksize: the fact that i_nlink is	 * zero will ensure that the right thing happens during any	 * recovery. */	inode->i_size = 0;	ext4_orphan_add(handle, inode);	inode->i_ctime = dir->i_ctime = dir->i_mtime = ext4_current_time(inode);	ext4_mark_inode_dirty(handle, inode);	ext4_dec_count(handle, dir);	ext4_update_dx_flag(dir);	ext4_mark_inode_dirty(handle, dir);end_rmdir:	ext4_journal_stop(handle);	brelse (bh);	return retval;}static int ext4_unlink(struct inode * dir, struct dentry *dentry){	int retval;	struct inode * inode;	struct buffer_head * bh;	struct ext4_dir_entry_2 * de;	handle_t *handle;	/* Initialize quotas before so that eventual writes go	 * in separate transaction */	DQUOT_INIT(dentry->d_inode);	handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb));	if (IS_ERR(handle))		return PTR_ERR(handle);	if (IS_DIRSYNC(dir))		handle->h_sync = 1;	retval = -ENOENT;	bh = ext4_find_entry (dentry, &de);	if (!bh)		goto end_unlink;	inode = dentry->d_inode;	retval = -EIO;	if (le32_to_cpu(de->inode) != inode->i_ino)		goto end_unlink;	if (!inode->i_nlink) {		ext4_warning (inode->i_sb, "ext4_unlink",			      "Deleting nonexistent file (%lu), %d",			      inode->i_ino, inode->i_nlink);		inode->i_nlink = 1;	}	retval = ext4_delete_entry(handle, dir, de, bh);	if (retval)		goto end_unlink;	dir->i_ctime = dir->i_mtime = ext4_current_time(dir);	ext4_update_dx_flag(dir);	ext4_mark_inode_dirty(handle, dir);	ext4_dec_count(handle, inode);	if (!inode->i_nlink)		ext4_orphan_add(handle, inode);	inode->i_ctime = ext4_current_time(inode);	ext4_mark_inode_dirty(handle, inode);	retval = 0;end_unlink:	ext4_journal_stop(handle);	brelse (bh);	return retval;}static int ext4_symlink (struct inode * dir,		struct dentry *dentry, const char * symname){	handle_t *handle;	struct inode * inode;	int l, err, retries = 0;	l = strlen(symname)+1;	if (l > dir->i_sb->s_blocksize)		return -ENAMETOOLONG;retry:	handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +					EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +					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_IFLNK|S_IRWXUGO);	err = PTR_ERR(inode);	if (IS_ERR(inode))		goto out_stop;	if (l > sizeof (EXT4_I(inode)->i_data)) {		inode->i_op = &ext4_symlink_inode_operations;		ext4_set_aops(inode);		/*		 * page_symlink() calls into ext4_prepare/commit_write.		 * We have a transaction open.  All is sweetness.  It also sets		 * i_size in generic_commit_write().		 */		err = __page_symlink(inode, symname, l,				mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);		if (err) {			ext4_dec_count(handle, inode);			ext4_mark_inode_dirty(handle, inode);			iput (inode);			goto out_stop;		}	} else {		inode->i_op = &ext4_fast_symlink_inode_operations;		memcpy((char*)&EXT4_I(inode)->i_data,symname,l);		inode->i_size = l-1;	}	EXT4_I(inode)->i_disksize = inode->i_size;	err = ext4_add_nondir(handle, dentry, inode);out_stop:	ext4_journal_stop(handle);	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))		goto retry;	return err;}static int ext4_link (struct dentry * old_dentry,		struct inode * dir, struct dentry *dentry){	handle_t *handle;	struct inode *inode = old_dentry->d_inode;	int err, retries = 0;	if (EXT4_DIR_LINK_MAX(inode))		return -EMLINK;	/*	 * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing	 * otherwise has the potential to corrupt the orphan inode list.	 */	if (inode->i_nlink == 0)		return -ENOENT;retry:	handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +					EXT4_INDEX_EXTRA_TRANS_BLOCKS);	if (IS_ERR(handle))		return PTR_ERR(handle);	if (IS_DIRSYNC(dir))		handle->h_sync = 1;	inode->i_ctime = ext4_current_time(inode);	ext4_inc_count(handle, inode);	atomic_inc(&inode->i_count);	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;}#define PARENT_INO(buffer) \	((struct ext4_dir_entry_2 *) ((char *) buffer + \	le16_to_cpu(((struct ext4_dir_entry_2 *) buffer)->rec_len)))->inode/* * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry,			   struct inode * new_dir,struct dentry *new_dentry){	handle_t *handle;	struct inode * old_inode, * new_inode;	struct buffer_head * old_bh, * new_bh, * dir_bh;	struct ext4_dir_entry_2 * old_de, * new_de;	int retval;	old_bh = new_bh = dir_bh = NULL;	/* Initialize quotas before so that eventual writes go	 * in separate transaction */	if (new_dentry->d_inode)		DQUOT_INIT(new_dentry->d_inode);	handle = ext4_journal_start(old_dir, 2 *					EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) +					EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2);	if (IS_ERR(handle))		return PTR_ERR(handle);	if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))		handle->h_sync = 1;	old_bh = ext4_find_entry (old_dentry, &old_de);	/*	 *  Check for inode number is _not_ due to possible IO errors.	 *  We might rmdir the source, keep it as pwd of some process	 *  and merrily kill the link to whatever was created under the	 *  same name. Goodbye sticky bit ;-<	 */	old_inode = old_dentry->d_inode;	retval = -ENOENT;	if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino)		goto end_rename;	new_inode = new_dentry->d_inode;	new_bh = ext4_find_entry (new_dentry, &new_de);	if (new_bh) {		if (!new_inode) {			brelse (new_bh);			new_bh = NULL;		}	}	if (S_ISDIR(old_inode->i_mode)) {		if (new_inode) {			retval = -ENOTEMPTY;			if (!empty_dir (new_inode))				goto end_rename;		}		retval = -EIO;		dir_bh = ext4_bread (handle, old_inode, 0, 0, &retval);		if (!dir_bh)			goto end_rename;		if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)			goto end_rename;		retval = -EMLINK;		if (!new_inode && new_dir!=old_dir &&				new_dir->i_nlink >= EXT4_LINK_MAX)			goto end_rename;	}	if (!new_bh) {		retval = ext4_add_entry (handle, new_dentry, old_inode);		if (retval)			goto end_rename;	} else {		BUFFER_TRACE(new_bh, "get write access");		ext4_journal_get_write_access(handle, new_bh);		new_de->inode = cpu_to_le32(old_inode->i_ino);		if (EXT4_HAS_INCOMPAT_FEATURE(new_dir->i_sb,					      EXT4_FEATURE_INCOMPAT_FILETYPE))			new_de->file_type = old_de->file_type;		new_dir->i_version++;		BUFFER_TRACE(new_bh, "call ext4_journal_dirty_metadata");		ext4_journal_dirty_metadata(handle, new_bh);		brelse(new_bh);		new_bh = NULL;	}	/*	 * Like most other Unix systems, set the ctime for inodes on a	 * rename.	 */	old_inode->i_ctime = ext4_current_time(old_inode);	ext4_mark_inode_dirty(handle, old_inode);	/*	 * ok, that's it	 */	if (le32_to_cpu(old_de->inode) != old_inode->i_ino ||	    old_de->name_len != old_dentry->

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -