文件系统源码分析之文件和目录
发布日期:2021-05-09 16:03:02 浏览次数:14 分类:精选文章

本文共 10232 字,大约阅读时间需要 34 分钟。

在这里插入图片描述

#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])/* * comment out this line if you want names > NAME_LEN chars to be * truncated. Else they will be disallowed. *//* #define NO_TRUNCATE */#define MAY_EXEC 1#define MAY_WRITE 2#define MAY_READ 4/* *	permission() * * is used to check for read/write/execute permissions on a file. * I don't know if we should look at just the euid or both euid and * uid, but that should be easily changed. */// 判断有没有mask指定的权限static int permission(struct m_inode * inode,int mask){   	int mode = inode->i_mode;/* special case: not even root can read/write a deleted file */	if (inode->i_dev && !inode->i_nlinks) // 没有被引用		return 0;	else if (current->euid==inode->i_uid) // 是文件的属主则取判断属主的权限		mode >>= 6;	else if (current->egid==inode->i_gid) // 判断组属性		mode >>= 3;	if (((mode & mask & 0007) == mask) || suser()) // 否则判断other的属性,是超级用户则肯定有权限		return 1;	return 0;}/* * ok, we cannot use strncmp, as the name is not in our data space. * Thus we'll have to use match. No big problem. Match also makes * some sanity tests. * * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. */static int match(int len,const char * name,struct dir_entry * de){   	register int same __asm__("ax"); 	if (!de || !de->inode || len > NAME_LEN)		return 0;	if (len < NAME_LEN && de->name[len])		return 0;	__asm__("cld\n\t"		"fs ; repe ; cmpsb\n\t"		"setz %%al"		:"=a" (same)		:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)		:"cx","di","si");	return same;}/* *	find_entry() * * finds an entry in the specified directory with the wanted name. It * returns the cache buffer in which the entry was found, and the entry * itself (as a parameter - res_dir). It does NOT read the inode of the * entry - you'll have to do that yourself if you want to. * * This also takes care of the few special cases due to '..'-traversal * over a pseudo-ro	ot and a mount point. */// 在dir目录项中找到名称等于name的目录项,存放到res_dir中static struct buffer_head * find_entry(struct m_inode ** dir,	const char * name, int namelen, struct dir_entry ** res_dir){   	int entries;	int block,i;	struct buffer_head * bh;	struct dir_entry * de;	struct super_block * sb;#ifdef NO_TRUNCATE	if (namelen > NAME_LEN)		return NULL;#else	if (namelen > NAME_LEN)		namelen = NAME_LEN;#endif	// 当前节点(目录)的大小除以每个目录项的大小,得出目录项的数量	entries = (*dir)->i_size / (sizeof (struct dir_entry));// 该目录下的目录项总数	*res_dir = NULL;	if (!namelen)		return NULL;/* check for '..', as we might have to do some "magic" for it */	// 形如../a/b,当前的目录名是..	if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {   /* '..' in a pseudo-root results in a faked '.' (just change namelen) */		// 当前目录是..并且当前所在的目录是根节点则重写namelen等于1即当前目录,因为无法再回退		if ((*dir) == current->root)			namelen=1;		/*  如果inode在硬盘中的节点号是1说明是文件系统的根节点,			这时候不是回退到该根节点的上级,而是从挂载的文件系统的根节点开始找		*/		else if ((*dir)->i_num == ROOT_INO) {   /* '..' over a mount-point results in 'dir' being exchanged for the mounted   directory-inode. NOTE! We set mounted, so that we can iput the new dir */			// 取出该inode对应的超级块			sb=get_super((*dir)->i_dev);			// 判断该超级块是否挂载在某inode上,是的话重新赋值查找的起点inode			if (sb->s_imount) {   				iput(*dir);				(*dir)=sb->s_imount;				(*dir)->i_count++;			}		}	}	// 取出该目录在硬盘中对应的第一块数据块,然后读进来	if (!(block = (*dir)->i_zone[0]))		return NULL;	if (!(bh = bread((*dir)->i_dev,block)))		return NULL;	i = 0;	// 指向第一个目录项	de = (struct dir_entry *) bh->b_data;	// entries为总目录项,等于很多块的目录项组成,遍历所有的目录项	while (i < entries) {   		// 读完目录对应inode节点中的第一块数据,没找到,继续读下一块的目录项数据		if ((char *)de >= BLOCK_SIZE+bh->b_data) {   			brelse(bh);			bh = NULL;			// 算出i/DIR_ENTRIES_PER_BLOCK,即得到属于7+512+512*512个块中的哪一块,然后得到当前目录项对应硬盘里的块数,然后读进来			if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||			    !(bh = bread((*dir)->i_dev,block))) {   				// 读取失败则跳过该数据块,i+DIR_ENTRIES_PER_BLOCK表示继续读取下一块数据块				i += DIR_ENTRIES_PER_BLOCK;				continue;			}			// 指向新的数据块中的第一个目录项			de = (struct dir_entry *) bh->b_data;		}		// 遍历每个目录项,比较名字是否相等,是的话说明找到了,每个目录项里都存着..和.对应的项		if (match(namelen,name,de)) {   			*res_dir = de;			return bh;		}		// 没找到指向下一个目录项		de++;		// 累计已经遍历的项数		i++;	}	brelse(bh);	return NULL;}/* *	add_entry() * * adds a file entry to the specified directory, using the same * semantics as 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 struct buffer_head * add_entry(struct m_inode * dir,	const char * name, int namelen, struct dir_entry ** res_dir){   	int block,i;	struct buffer_head * bh;	struct dir_entry * de;	*res_dir = NULL;#ifdef NO_TRUNCATE	if (namelen > NAME_LEN)		return NULL;#else	if (namelen > NAME_LEN)		namelen = NAME_LEN;#endif	if (!namelen)		return NULL;	if (!(block = dir->i_zone[0]))		return NULL;	if (!(bh = bread(dir->i_dev,block)))		return NULL;	i = 0;	// 该目录在硬盘中的第一块数据块	de = (struct dir_entry *) bh->b_data;	while (1) {   		// 如果当前块已经查找完毕,读入下一块,如果没有下一块则创建一个		if ((char *)de >= BLOCK_SIZE+bh->b_data) {   			brelse(bh);			bh = NULL;			block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);			if (!block)				return NULL;			// 读取失败则跳过			if (!(bh = bread(dir->i_dev,block))) {   				i += DIR_ENTRIES_PER_BLOCK;				continue;			}			de = (struct dir_entry *) bh->b_data;		}		// 找到了全部块的末尾,即找到了追加entry的位置,更新大小等属性		if (i*sizeof(struct dir_entry) >= dir->i_size) {   			de->inode=0;			dir->i_size = (i+1)*sizeof(struct dir_entry);			dir->i_dirt = 1;			dir->i_ctime = CURRENT_TIME;		}		// 在每一块中找第一项还没使用的目录项		if (!de->inode) {   			dir->i_mtime = CURRENT_TIME;			for (i=0; i < NAME_LEN ; i++)				de->name[i]=(i
b_dirt = 1; *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL;}/* * get_dir() * * Getdir traverses the pathname until it hits the topmost directory. * It returns NULL on failure. */// 找出给定路径最后一级目录的inode节点static struct m_inode * get_dir(const char * pathname){ char c; const char * thisname; struct m_inode * inode; struct buffer_head * bh; int namelen,inr,idev; struct dir_entry * de; if (!current->root || !current->root->i_count) panic("No root inode"); if (!current->pwd || !current->pwd->i_count) panic("No cwd inode"); // 路径名以/开头说明是绝对路径,则搜索节点是该进程的根节点,否则是当前工作路径 if ((c=get_fs_byte(pathname))=='/') { inode = current->root; pathname++; } else if (c) inode = current->pwd; else return NULL; /* empty name is bad */ inode->i_count++; // 逐个目录查找 while (1) { thisname = pathname; // 不是目录或者没有执行的权限 if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { iput(inode); return NULL; } // 获取某一级目录名 for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) /* nothing */ ; // 跑到这里说明c是null或者c等于/,如果是空说明查找完毕,直接返回,否则根据目录名找到对应的目录项内容 if (!c) return inode; // 在inode节点下查找,thisname为当前级的目录名,namelen为当前级目录名长度 if (!(bh = find_entry(&inode,thisname,namelen,&de))) { iput(inode); return NULL; } // 找到当前目录对应的dir_entry结构, inr = de->inode; idev = inode->i_dev; brelse(bh); iput(inode); // 取出设备中inode号为inr的目录项数据 if (!(inode = iget(idev,inr))) return NULL; }}/* * dir_namei() * * dir_namei() returns the inode of the directory of the * specified name, and the name within that directory. */// 找出路径的最后一级目录的inode和路径中的文件名static struct m_inode * dir_namei(const char * pathname, int * namelen, const char ** name){ char c; const char * basename; struct m_inode * dir; if (!(dir = get_dir(pathname))) return NULL; // 初始化值等于pathname,比如传入的是1.txt basename = pathname; // 取路径中最后一个/的剩余部分作为文件名 while (c=get_fs_byte(pathname++)) if (c=='/') basename=pathname; *namelen = pathname-basename-1; *name = basename; return dir;}/* * namei() * * is used by most simple commands to get the inode of a specified name. * Open, link etc use their own routines, but this is enough for things * like 'chmod' etc. */// 根据路径找到文件的inode节点struct m_inode * namei(const char * pathname){ const char * basename; int inr,dev,namelen; struct m_inode * dir; struct buffer_head * bh; struct dir_entry * de; // 找到pathname最后一级目录的inode和解析出路径中的文件名 if (!(dir = dir_namei(pathname,&namelen,&basename))) return NULL; if (!namelen) /* special case: '/usr/' etc */ return dir; // 在最后一级目录中查找basename的文件名的目录项,存在de中 bh = find_entry(&dir,basename,namelen,&de); if (!bh) { iput(dir); return NULL; } inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); // 读取设备dev中inode节点号为inr的inode结构 dir=iget(dev,inr); if (dir) { dir->i_atime=CURRENT_TIME; dir->i_dirt=1; } return dir;}/* * open_namei() * * namei for open - this is in fact almost the whole open-routine. */int open_namei(const char * pathname, int flag, int mode, struct m_inode ** res_inode){ const char * basename; int inr,dev,namelen; struct m_inode * dir, *inode; struct buffer_head * bh; struct dir_entry * de; // 设置了trunc但没有设置读写标记位则设置成只写 if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) flag |= O_WRONLY; mode &= 0777 & ~current->umask; mode |= I_REGULAR; // 根据pathname找出最后一级目录的inode节点,并返回文件名和长度 if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; // 文件名长度是0说明最后一级是目录 if (!namelen) { /* special case: '/usr/' etc */ if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { *res_inode=dir; return 0; } iput(dir); return -EISDIR; } // 从最后一级目录的目录项中查找等于basename的目录项,存储到de中 bh = find_entry(&dir,basename,namelen,&de); if (!bh) { if (!(flag & O_CREAT)) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; } inode = new_inode(dir->i_dev); if (!inode) { iput(dir); return -ENOSPC; } inode->i_uid = current->euid; inode->i_mode = mode; inode->i_dirt = 1; bh = add_entry(dir,basename,namelen,&de); if (!bh) { inode->i_nlinks--; iput(inode); iput(dir); return -ENOSPC; } // 保存新建的inode节点的节点号 de->inode = inode->i_num; bh->b_dirt = 1; brelse(bh); iput(dir); *res_inode = inode; return 0; } inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); // O_EXCL表示该文件必须是由当前进程创建 if (flag & O_EXCL) return -EEXIST; // 获取文件内容 if (!(inode=iget(dev,inr))) return -EACCES; if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || !permission(inode,ACC_MODE(flag))) { iput(inode); return -EPERM; } inode->i_atime = CURRENT_TIME; if (flag & O_TRUNC) truncate(inode); *res_inode = inode; return 0;}
上一篇:tinyhttpd-0.1.0源码解析
下一篇:文件系统源码分析之普通文件读写

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年04月23日 22时07分03秒