IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux内核文件系统7 -> 正文阅读

[系统运维]Linux内核文件系统7

2021SC@SDUSC

今天来分析ext4_extents.h 、 xattr.h 、 truncate.h 三个头文件,为以后分析inode.c核心文件打下基础。

以下是ext4_extents.h头文件的代码分析

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com
 * Written by Alex Tomas <alex@clusterfs.com>
 */

#ifndef _EXT4_EXTENTS
#define _EXT4_EXTENTS

#include "ext4.h"

/*随着aggressive ve_test的定义,索引/叶块的容量变得非常小,因此索引分裂、深度增长和其他硬变化发生得更频繁。这仅用于调试目的。*/
#define AGGRESSIVE_TEST_

/*定义EXTENTS_STATS后,在截断路径中收集块和区段的数量。它们将在umount时间显示。*/
#define EXTENTS_STATS__

/*如果定义了CHECK_BINSEARCH,那么二进制搜索的结果也将通过线性搜索进行检查。*/
#define CHECK_BINSEARCH__

/*如果定义了EXT_STATS,则统计数据将被收集。这些数字将在umount时显示。*/
#define EXT_STATS_


/** ext4_inode有i_block数组(总共60字节)。

*前12字节存储ext4_extent_header;其余存储ext4_extent数组。对于非inode扩展块,ext4_extent_tail紧跟在数组之后。*/

/**这是磁盘上的extent tail结构。

*所有其他区段结构都是12字节长。结果表明,对于大于512的2的所有幂,block_size % 12 >= 4,这覆盖了所有有效的ext4块大小。因此,这个尾部结构可以被塞到块的末端,而不需要重新平衡树。*/
struct ext4_extent_tail {
	__le32	et_checksum;	/* crc32c(uuid+inum+extent_block) */
};

/*这是磁盘上的extent结构。

它在树的底部使用。*/
struct ext4_extent {
	__le32	ee_block;	/* 首先逻辑块范围覆盖*/
	__le16	ee_len;		/* 范围所覆盖的块数*/
	__le16	ee_start_hi;	/* 高16位的物理块*/
	__le32	ee_start_lo;	/* 低32位物理块*/
};

/*这是磁盘索引结构。

除底部外,所有级别都使用。*/
struct ext4_extent_idx {
	__le32	ei_block;	/* 索引涵盖了来自“block”的逻辑块*/
	__le32	ei_leaf_lo;	/*指针指向下一层的物理块。叶索引或下一个索引可能在那里*/
	__le16	ei_leaf_hi;	/*高16位的物理块 */
	__u16	ei_unused;
};

/*每个块(叶子和索引),即使是inode存储的也有头。*/
struct ext4_extent_header {
	__le16	eh_magic;	/* 可能会支持不同的格式*/
	__le16	eh_entries;	/* 有效条目数量*/
	__le16	eh_max;		/* 入库容量*/
	__le16	eh_depth;	/*树有真正的底层块吗? */
	__le32	eh_generation;	/* 树的各代*/
};

#define EXT4_EXT_MAGIC		cpu_to_le16(0xf30a)
#define EXT4_MAX_EXTENT_DEPTH 5

#define EXT4_EXTENT_TAIL_OFFSET(hdr) \
	(sizeof(struct ext4_extent_header) + \
	 (sizeof(struct ext4_extent) * le16_to_cpu((hdr)->eh_max)))

static inline struct ext4_extent_tail *
find_ext4_extent_tail(struct ext4_extent_header *eh)
{
	return (struct ext4_extent_tail *)(((void *)eh) +
					   EXT4_EXTENT_TAIL_OFFSET(eh));
}

/*ext4_ext_path数组中包含路径。

*创建/查找例程使用它来遍历/分割/等。

* Truncate使用它来模拟递归遍历。

*/
struct ext4_ext_path {
	ext4_fsblk_t			p_block;
	__u16				p_depth;
	__u16				p_maxdepth;
	struct ext4_extent		*p_ext;
	struct ext4_extent_idx		*p_idx;
	struct ext4_extent_header	*p_hdr;
	struct buffer_head		*p_bh;
};

/**用于在空间移除期间遍历区段树时,记录在区段开始或结束时发现的簇的一部分。

*如果一个部分集群中没有与未被删除的区段共享的块(处于空闲状态),可能会被移除。否则,它不能被删除(nofree状态)。*/
struct partial_cluster {
	ext4_fsblk_t pclu;  /* physical cluster number */
	ext4_lblk_t lblk;   /* logical block number within logical cluster */
	enum {initial, tofree, nofree} state;
};

/**结构的外部API*/

/** EXT_INIT_MAX_LEN是在初始化的extent中可以拥有的最大块数。这是2^15而不是(2^16 - 1),因为我们在区段数据结构中使用ee_len字段的MSB来表示这个特定的区段是初始化的还是未写入的(即预分配的)。

* EXT_UNWRITTEN_MAX_LEN是一个未写入区可以拥有的最大块数。如果ee_len <= 0x8000,则它是一个初始化的范围。否则,它是一个不成文的。换句话说,如果设置了ee_len的MSB,则它是一个未写入区,只有一个特殊场景:ee_len = 0x8000。在本例中,我们不能有长度为0的未写入范围,因此我们将其作为长度为0x8000的初始化范围的特殊情况。

*通过这种方式,我们可以更好地对初始化的区段和组进行对齐。

因此,在一个*初始化的*区段中我们可以拥有的最大块数是2^15(32768),而在一个*未写入的*区段中我们可以拥有的最大块数是2^15-1(32767)。

*/
#define EXT_INIT_MAX_LEN	(1UL << 15)
#define EXT_UNWRITTEN_MAX_LEN	(EXT_INIT_MAX_LEN - 1)


#define EXT_FIRST_EXTENT(__hdr__) \
	((struct ext4_extent *) (((char *) (__hdr__)) +		\
				 sizeof(struct ext4_extent_header)))
#define EXT_FIRST_INDEX(__hdr__) \
	((struct ext4_extent_idx *) (((char *) (__hdr__)) +	\
				     sizeof(struct ext4_extent_header)))
#define EXT_HAS_FREE_INDEX(__path__) \
	(le16_to_cpu((__path__)->p_hdr->eh_entries) \
				     < le16_to_cpu((__path__)->p_hdr->eh_max))
#define EXT_LAST_EXTENT(__hdr__) \
	(EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_LAST_INDEX(__hdr__) \
	(EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
#define EXT_MAX_EXTENT(__hdr__)	\
	((le16_to_cpu((__hdr__)->eh_max)) ? \
	((EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)) \
					: 0)
#define EXT_MAX_INDEX(__hdr__) \
	((le16_to_cpu((__hdr__)->eh_max)) ? \
	((EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)) : 0)

static inline struct ext4_extent_header *ext_inode_hdr(struct inode *inode)
{
	return (struct ext4_extent_header *) EXT4_I(inode)->i_data;
}

static inline struct ext4_extent_header *ext_block_hdr(struct buffer_head *bh)
{
	return (struct ext4_extent_header *) bh->b_data;
}

static inline unsigned short ext_depth(struct inode *inode)
{
	return le16_to_cpu(ext_inode_hdr(inode)->eh_depth);
}

static inline void ext4_ext_mark_unwritten(struct ext4_extent *ext)
{
	/* We can not have an unwritten extent of zero length! */
	BUG_ON((le16_to_cpu(ext->ee_len) & ~EXT_INIT_MAX_LEN) == 0);
	ext->ee_len |= cpu_to_le16(EXT_INIT_MAX_LEN);
}

static inline int ext4_ext_is_unwritten(struct ext4_extent *ext)
{
	/* Extent with ee_len of 0x8000 is treated as an initialized extent */
	return (le16_to_cpu(ext->ee_len) > EXT_INIT_MAX_LEN);
}

static inline int ext4_ext_get_actual_len(struct ext4_extent *ext)
{
	return (le16_to_cpu(ext->ee_len) <= EXT_INIT_MAX_LEN ?
		le16_to_cpu(ext->ee_len) :
		(le16_to_cpu(ext->ee_len) - EXT_INIT_MAX_LEN));
}

static inline void ext4_ext_mark_initialized(struct ext4_extent *ext)
{
	ext->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ext));
}

/** ext4_ext_pblock:

*将物理块号的高低部分合并成ext4_fsblk_t

*/
static inline ext4_fsblk_t ext4_ext_pblock(struct ext4_extent *ex)
{
	ext4_fsblk_t block;

	block = le32_to_cpu(ex->ee_start_lo);
	block |= ((ext4_fsblk_t) le16_to_cpu(ex->ee_start_hi) << 31) << 1;
	return block;
}

/** ext4_idx_pblock:

将叶子物理块号的高低部分合并到ext4_fsblk_t中

*/
static inline ext4_fsblk_t ext4_idx_pblock(struct ext4_extent_idx *ix)
{
	ext4_fsblk_t block;

	block = le32_to_cpu(ix->ei_leaf_lo);
	block |= ((ext4_fsblk_t) le16_to_cpu(ix->ei_leaf_hi) << 31) << 1;
	return block;
}

/** ext4_ext_store_pblock:

*将一个大的物理块数存储到一个区段结构中,并将其分解为多个部分

*/
static inline void ext4_ext_store_pblock(struct ext4_extent *ex,
					 ext4_fsblk_t pb)
{
	ex->ee_start_lo = cpu_to_le32((unsigned long) (pb & 0xffffffff));
	ex->ee_start_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) &
				      0xffff);
}

/* ext4_idx_store_pblock:

*将一个大的物理块数存储到一个索引结构中,并将其分解为多个部分*/
static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix,
					 ext4_fsblk_t pb)
{
	ix->ei_leaf_lo = cpu_to_le32((unsigned long) (pb & 0xffffffff));
	ix->ei_leaf_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) &
				     0xffff);
}

#endif /* _EXT4_EXTENTS */

以下是xattr.h头文件的代码分析

// SPDX-License-Identifier: GPL-2.0
/*
  File: fs/ext4/xattr.h

  On-disk format of extended attributes for the ext4 filesystem.

  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/

#include <linux/xattr.h>

/* 属性块中的魔法值 */
#define EXT4_XATTR_MAGIC		0xEA020000

/* 对一个属性块的最大引用数 */
#define EXT4_XATTR_REFCOUNT_MAX		1024

/* 名称索引*/
#define EXT4_XATTR_INDEX_USER			1
#define EXT4_XATTR_INDEX_POSIX_ACL_ACCESS	2
#define EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT	3
#define EXT4_XATTR_INDEX_TRUSTED		4
#define	EXT4_XATTR_INDEX_LUSTRE			5
#define EXT4_XATTR_INDEX_SECURITY	        6
#define EXT4_XATTR_INDEX_SYSTEM			7
#define EXT4_XATTR_INDEX_RICHACL		8
#define EXT4_XATTR_INDEX_ENCRYPTION		9
#define EXT4_XATTR_INDEX_HURD			10 /* 预留给HURD */

struct ext4_xattr_header {
	__le32	h_magic;	/* 用于标识的魔数 */
	__le32	h_refcount;	/* 引用计数 */
	__le32	h_blocks;	/* 已使用的磁盘块数 */
	__le32	h_hash;		/* 所有属性的哈希值 */
	__le32	h_checksum;	/* crc32c(uuid+id+xattrblock) */
				/* 如果refcount=1, Id = inum,否则为blknum */
	__u32	h_reserved[3];	
};

struct ext4_xattr_ibody_header {
	__le32	h_magic;	/* 用于标识的魔数 */
};

struct ext4_xattr_entry {
	__u8	e_name_len;	/* 名字的长度 */
	__u8	e_name_index;	/* 属性名称索引 */
	__le16	e_value_offs;	/* 磁盘块中的偏移值 */
	__le32	e_value_inum;	/* 存储值的索引节点 */
	__le32	e_value_size;	/* 属性值大小 */
	__le32	e_hash;		/* name和value的散列值 */
	char	e_name[];	/* 属性名称 */
};

#define EXT4_XATTR_PAD_BITS		2
#define EXT4_XATTR_PAD		(1<<EXT4_XATTR_PAD_BITS)
#define EXT4_XATTR_ROUND		(EXT4_XATTR_PAD-1)
#define EXT4_XATTR_LEN(name_len) \
	(((name_len) + EXT4_XATTR_ROUND + \
	sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
#define EXT4_XATTR_NEXT(entry) \
	((struct ext4_xattr_entry *)( \
	 (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
#define EXT4_XATTR_SIZE(size) \
	(((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)

#define IHDR(inode, raw_inode) \
	((struct ext4_xattr_ibody_header *) \
		((void *)raw_inode + \
		EXT4_GOOD_OLD_INODE_SIZE + \
		EXT4_I(inode)->i_extra_isize))
#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))

/*
XATTR_SIZE_MAX目前是64k,但是为了检查文件系统一致性错误,我们使用了一个更大的值。
这允许XATTR_SIZE_MAX在未来增长,但是在某些一致性检查中使用它而不是INT_MAX,我们不需要担心算术溢出。
(实际上XATTR_SIZE_MAX是在include/uapi/linux/limits.h中定义的,所以改变它不会是简单的....)
 */
#define EXT4_XATTR_SIZE_MAX (1 << 24)

/*
当您开始将EA值存储在外部索引节点时,
它的最小大小为块大小-头大小- 1个条目大小- 4个空字节
*/
#define EXT4_XATTR_MIN_LARGE_EA_SIZE(b)					\
	((b) - EXT4_XATTR_LEN(3) - sizeof(struct ext4_xattr_header) - 4)

#define BHDR(bh) ((struct ext4_xattr_header *)((bh)->b_data))
#define ENTRY(ptr) ((struct ext4_xattr_entry *)(ptr))
#define BFIRST(bh) ENTRY(BHDR(bh)+1)
#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)

#define EXT4_ZERO_XATTR_VALUE ((void *)-1)

struct ext4_xattr_info {
	const char *name;
	const void *value;
	size_t value_len;
	int name_index;
	int in_inode;
};

struct ext4_xattr_search {
	struct ext4_xattr_entry *first;
	void *base;
	void *end;
	struct ext4_xattr_entry *here;
	int not_found;
};

struct ext4_xattr_ibody_find {
	struct ext4_xattr_search s;
	struct ext4_iloc iloc;
};

struct ext4_xattr_inode_array {
	unsigned int count;		/* 数组中使用的条目的# */
	struct inode *inodes[];
};

extern const struct xattr_handler ext4_xattr_user_handler;
extern const struct xattr_handler ext4_xattr_trusted_handler;
extern const struct xattr_handler ext4_xattr_security_handler;
extern const struct xattr_handler ext4_xattr_hurd_handler;

#define EXT4_XATTR_NAME_ENCRYPTION_CONTEXT "c"

/*
EXT4_STATE_NO_EXPAND是重载的,用于两个目的。
第一个是发出信号,表示内联xattrs和数据占用了太多的空间,所以我们最好不要一直尝试扩展它。
其次,xattr_sem用于写入,因此不应该尝试递归到inode展开中。
对于第二种情况,我们需要确保适当地使用保存和恢复NO_EXPAND状态标志。
 */
static inline void ext4_write_lock_xattr(struct inode *inode, int *save)
{
	down_write(&EXT4_I(inode)->xattr_sem);
	*save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
	ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
}

static inline int ext4_write_trylock_xattr(struct inode *inode, int *save)
{
	if (down_write_trylock(&EXT4_I(inode)->xattr_sem) == 0)
		return 0;
	*save = ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND);
	ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
	return 1;
}

static inline void ext4_write_unlock_xattr(struct inode *inode, int *save)
{
	if (*save == 0)
		ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
	up_write(&EXT4_I(inode)->xattr_sem);
}

extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);

extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_set_credits(struct inode *inode, size_t value_len,
				  bool is_create, int *credits);
extern int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
				struct buffer_head *block_bh, size_t value_len,
				bool is_create);

extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
				   struct ext4_xattr_inode_array **array,
				   int extra_credits);
extern void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *array);

extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
			    struct ext4_inode *raw_inode, handle_t *handle);

extern const struct xattr_handler *ext4_xattr_handlers[];

extern int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
				 struct ext4_xattr_ibody_find *is);
extern int ext4_xattr_ibody_get(struct inode *inode, int name_index,
				const char *name,
				void *buffer, size_t buffer_size);
extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
				struct ext4_xattr_info *i,
				struct ext4_xattr_ibody_find *is);

extern struct mb_cache *ext4_xattr_create_cache(void);
extern void ext4_xattr_destroy_cache(struct mb_cache *);

#ifdef CONFIG_EXT4_FS_SECURITY
extern int ext4_init_security(handle_t *handle, struct inode *inode,
			      struct inode *dir, const struct qstr *qstr);
#else
static inline int ext4_init_security(handle_t *handle, struct inode *inode,
				     struct inode *dir, const struct qstr *qstr)
{
	return 0;
}
#endif

#ifdef CONFIG_LOCKDEP
extern void ext4_xattr_inode_set_class(struct inode *ea_inode);
#else
static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { }
#endif

extern int ext4_get_inode_usage(struct inode *inode, qsize_t *usage);

以下是truncate.h头文件的代码分析:

// SPDX-License-Identifier: GPL-2.0
/*
 * linux/fs/ext4/truncate.h
 *
 * Common inline functions needed for truncate support
 */

/*
截断未被写入使用的块。
我们还必须截断页面缓存,以便正确地取消相应的缓冲区映射。
 */
static inline void ext4_truncate_failed_write(struct inode *inode)
{
	/*
	我们不需要调用ext4_break_layouts(),
	因为我们正在截断的块对用户空间是不可见的。
	 */
	down_write(&EXT4_I(inode)->i_mmap_sem);
	truncate_inode_pages(inode->i_mapping, inode->i_size);
	ext4_truncate(inode);
	up_write(&EXT4_I(inode)->i_mmap_sem);
}

/*
计算出我们需要多少块来处理截断事务的下一个块。
 */
static inline unsigned long ext4_blocks_for_truncate(struct inode *inode)
{
	ext4_lblk_t needed;

	needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);

	/* 
	 给我们足够的空间来处理i_blocks损坏的inode:
	 我们在过去看到过磁盘损坏导致inode中出现随机数据,
	 这些数据看起来很像ext4试图删除的常规文件。
	 如果发生这种情况,事情会变得有点疯狂,
	 但至少我们应该尽量不要让整个内核感到恐慌。
	 */
	if (needed < 2)
		needed = 2;

	/* 
	我们需要对事务进行约束,以避免日记账溢出。
	 */
	if (needed > EXT4_MAX_TRANS_DATA)
		needed = EXT4_MAX_TRANS_DATA;

	return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed;

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-12-06 15:37:23  更:2021-12-06 15:39:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 4:42:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码