Linux Fs-path_walk(从路径dir到目标节点entry)
2010-12-19 01:00
519 查看
文件系统 - 从路径名到目标节点
文件系统 dentry inode file 这里有个重要的图片, 还有几个数据结构列在前面, 会对后面的分析有所帮助… 数据结构1 struct nameidata { struct dentry *dentry; struct vfsmount *mnt; struct qstr last; unsigned int flags; int last_type; }; 数据结构 2 struct dentry { // 该结构所代表的是逻辑意义上的文件, 记录的是其逻辑上的属性. atomic_t d_count; unsigned int d_flags; struct inode * d_inode; /* Where the name belongs to - NULL is negative */ struct dentry * d_parent; /* parent directory */ struct list_head d_vfsmnt; struct list_head d_hash; /* lookup hash list */ struct list_head d_lru; /* d_count = 0 LRU list */ struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our children */ struct list_head d_alias; /* inode alias list */ struct qstr d_name; unsigned long d_time; /* used by d_revalidate */ struct dentry_operations *d_op; struct super_block * d_sb; /* The root of the dentry tree */ unsigned long d_reftime; /* last time referenced */ void * d_fsdata; /* fs-specific data */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ }; 数据结构 3 struct inode { // 该结构所代表的是物理上的文件, 记录的是其物理上的属性.(比如说文件的存储位置, 被哪些不同名所指向, 所属用户.组, 所在设备号, 创建 修改时间等等) struct list_head i_hash; struct list_head i_list; struct list_head i_dentry; struct list_head i_dirty_buffers; unsigned long i_ino; atomic_t i_count; kdev_t i_dev; umode_t i_mode; nlink_t i_nlink; uid_t i_uid; gid_t i_gid; kdev_t i_rdev; loff_t i_size; time_t i_atime; time_t i_mtime; time_t i_ctime; unsigned long i_blksize; unsigned long i_blocks; unsigned long i_version; struct semaphore i_sem; struct semaphore i_zombie; struct inode_operations *i_op; struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct super_block *i_sb; wait_queue_head_t i_wait; struct file_lock *i_flock; struct address_space *i_mapping; struct address_space i_data; struct dquot *i_dquot[MAXQUOTAS]; struct pipe_inode_info *i_pipe; struct block_device *i_bdev; unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ unsigned long i_state; unsigned int i_flags; unsigned char i_sock; atomic_t i_writecount; unsigned int i_attr_flags; __u32 i_generation; union { struct ext2_inode_info ext2_i; … } u; }; 数据结构 4 struct qstr { const unsigned char * name; unsigned int len; unsigned int hash; }; 先分析这样一个函数.在path_walk 之前要对 path_walk返回的结构体nameidata指针进行必要的初始化. Fs/namei.c L690-702 int path_init(const char *name, unsigned int flags, struct nameidata *nd) { nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags; if (*name=='/') //如果当前给出路径是以’/’开始, 则说明给出的是绝对路径, 也就是说要从根目录一层一层的找下去, 所以这里nameidata指针中的 dentry 要取当前进程的根目录的 dentry. return walk_init_root(name,nd); //如是取路径. read_lock(¤t->fs->lock); // 否则的话说明当前给出路径是相对路径, 要从当前进程环境中取出当前目录的dentry, 及mnt . nd->mnt = mntget(current->fs->pwdmnt); nd->dentry = dget(current->fs->pwd); read_unlock(¤t->fs->lock); return 1; } Fs/namei.c L672-688 static inline int walk_init_root(const char *name, struct nameidata *nd) { read_lock(¤t->fs->lock); if (current->fs->altroot && !(nd->flags & LOOKUP _NOALT)) { // 这里只读出了, 如果当前路径有替换根目录, 并且对应禁止标志位也无效,那么使用替换 目录. nd->mnt = mntget(current->fs->altrootmnt); nd->dentry = dget(current->fs->altroot); read_unlock(¤t->fs->lock); if (__emul_lookup_dentry(name,nd)) return 0; read_lock(¤t->fs->lock); } //如果没有替换路径那么把当前用户根目录信息赋值到nameidata数据下. nd->mnt = mntget(current->fs->rootmnt); nd->dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); return 1; } 到这里, 在path_walk函数中所要用到的 dentry 及 mnt等数据都以初始化完成. 可以进行查找了. (这里要查找的是目标目录或文件的dentry及 mnt ) /* * Name resolution. * * This is the basic name resolution function, turning a pathname * into the final dentry. * * We expect 'base' to be positive and a directory. */ int path_walk(const char * name, struct nameidata *nd) { struct dentry *dentry; struct inode *inode; int err; unsigned int lookup_flags = nd->flags; while (*name=='/') name++; if (!*name) goto return_base; //以上 去掉给出路径最前面所有 ’/’ 字符.如果只有一个 ‘/’ 或全是 ‘/’ 那么 直接返回当传入的nameidata信息. inode = nd->dentry->d_inode; if (current->link_count) lookup_flags = LOOKUP_FOLLOW; /* At this point we know we have a real path component. */ for(;;) { // 主要的循环, 在这要遍历给出路径中每一个路径或文件. unsigned long hash; struct qstr this; unsigned int c; err = permission(inode, MAY_EXEC); //当前路径有执行的权限吗? dentry = ERR_PTR(err); if (err) break; this.name = name; c = *(const unsigned char *)name; hash = init_name_hash(); //初始化哈希值. do { name++; hash = partial_name_hash(c, hash); //统计(累加)当前节点的哈希值. c = *(const unsigned char *)name; } while (c && (c != '/')); //直到当前下一个目录或文件.路径中是以 ‘/’ 隔开的. this.len = name - (const char *) this.name; this.hash = end_name_hash(hash); //哈希值计算函数. /* remove trailing slashes? */ if (!c) goto last_component; //给定最后一个节点是文件 while (*++name == '/'); if (!*name) goto last_with_slashes; //给定最后一个节点是目录. /* * "." and ".." are special - ".." especially so because it has * to be able to know about the current root directory and * parent relationships. */ // 如果当前节点是 ‘.’ 或 ‘..’ if (this.name[0] == '.') switch (this.len) { default: break; case 2: if (this.name[1] != '.') break; follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: continue; } /* * See if the low-level filesystem might want * to use its own hash.. */ // 如果目标文件系统有自己的哈希函数. 那就用自带的哈希函数计算哈希值. if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { err = nd->dentry->d_op->d_hash(nd->dentry, &this); if (err < 0) break; } /* This does the actual lookups.. */ // 在内存中查找该目录或文件的dentry是否存在(过). dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE); if (!dentry) { dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE); // 到磁盘上去查找该目录或文件 在dentry 是否存在(过). err = PTR_ERR(dentry); if (IS_ERR(dentry))就用自带的哈希函数. break; } /* Check mountpoints.. */ //如果找到目录是一个设备的挂载点, 那么前进到被挂载设备的根目录上去, 直到设备的根目录下没有挂载其它设备为止. while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry)) ; err = -ENOENT; inode = dentry->d_inode; if (!inode) //如果目录或文件已找到, 无论是目录还是文件, 这个inode 一定不为空. 需要这个数据去操作本节点. goto out_dput; err = -ENOTDIR; if (!inode->i_op) // 节点操作函数不能为空. goto out_dput; if (inode->i_op->follow_link) { err = do_follow_link(dentry, nd); //验证当前目录或文件是否是链接. 并前进到真正的节点. dput(dentry); if (err) goto return_err; err = -ENOENT; inode = nd->dentry->d_inode; if (!inode) break; err = -ENOTDIR; if (!inode->i_op) break; } else { dput(nd->dentry); nd->dentry = dentry; } err = -ENOTDIR; if (!inode->i_op->lookup) break; continue; /* here ends the main loop */ last_with_slashes: //最后一个节点是目录. lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; last_component: //最后一个节点是文件. if (lookup_flags & LOOKUP_PARENT) //是找当前目录的根目录吗? goto lookup_parent; if (this.name[0] == '.') switch (this.len) { //最后一级是点或点点. default: break; case 2: if (this.name[1] != '.') break; follow_dotdot(nd); inode = nd->dentry->d_inode; /* fallthrough */ case 1: goto return_base; } //最后一个节点自己有哈希函数. if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { err = nd->dentry->d_op->d_hash(nd->dentry, &this); if (err < 0) break; } // 在内存中查找最后一个节点. dentry = cached_lookup(nd->dentry, &this, 0); if (!dentry) { // 到物理介质上去查找当前节点. dentry = real_lookup(nd->dentry, &this, 0); err = PTR_ERR(dentry); if (IS_ERR(dentry)) break; } //最后一级目录或文件是否其它设备mount点, 如果是那么一直找到新设备根目录没有被其它设备mount 为止. while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry)) ; inode = dentry->d_inode; if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { err = do_follow_link(dentry, nd); //查看最后一级节点是不是其它文件或目录的链接, 如果是则前进到真实目录或文件上去. dput(dentry); if (err) goto return_err; inode = nd->dentry->d_inode; } else { dput(nd->dentry); nd->dentry = dentry; } err = -ENOENT; if (!inode) goto no_inode; if (lookup_flags & LOOKUP_DIRECTORY) { err = -ENOTDIR; if (!inode->i_op || !inode->i_op->lookup) break; } goto return_base; no_inode: err = -ENOENT; if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY)) break; goto return_base; lookup_parent: nd->last = this; nd->last_type = LAST_NORM; if (this.name[0] != '.') goto return_base; if (this.len == 1) nd->last_type = LAST_DOT; else if (this.len == 2 && this.name[1] == '.') nd->last_type = LAST_DOTDOT; return_base: return 0; out_dput: dput(dentry); break; } path_release(nd); return_err: return err; } 举例如: /////root//////share/c/test1/main.c 第一步, 去年root前的 ‘/’ 通过代码 while (*name=='/') name++; 第二步, 计算root的哈希值, 在内存中查找dentry, 看是否存在, 不存在则到物理设备中去查找对应的dentry, 如是其它设备的挂载点, 则前进到其设备的根节点, 一直打到新设备的根节点不再有其它设备被mount为止. 如该节点是链接, 则前进到真正的节点. 第三步, 去年 当前节点与下一个节点之间多余的 ‘/’ 字符. 重复第二步. 相关函数: Path_walk() -> follow_dotdot() ./linux/fs/namei.c L380 - 413 static inline void follow_dotdot(struct nameidata *nd) { while(1) { struct vfsmount *parent; struct dentry *dentry; read_lock(¤t->fs->lock); if (nd->dentry == current->fs->root && nd->mnt == current->fs->rootmnt) { //如果已经是根设备了. 什么也不做. read_unlock(¤t->fs->lock); break; } read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); if (nd->dentry != nd->mnt->mnt_root) { // 如果当前节点dentry 不等于当前节点monut数据结构中的根设备的dentry, 说明当前节点不是做为根节点被mount到其它设备上去的. Dentry中的d_vfsmnt是记录当前目录下有多少个mount设备的, 不能通过它来判断来当前节点是不是有其它设备被 mount ; dentry = dget(nd->dentry->d_parent); //取出父节点. spin_unlock(&dcache_lock); dput(nd->dentry); nd->dentry = dentry; //向父目录前进. break; } // 以下情况是 当前节点是另一个设备mount 后的根目录, 再向上, 就要跳向其它设备了. parent=nd->mnt->mnt_parent; if (parent == nd->mnt) { // 这种情况是不应该出现的. 既然当前节点已经是其它设备的mount 点, 两个mnt对象就不应该为同一个. spin_unlock(&dcache_lock); break; } mntget(parent); dentry=dget(nd->mnt->mnt_mountpoint); // 取 mount 节点的dentry; spin_unlock(&dcache_lock); dput(nd->dentry); nd->dentry = dentry; // 跳回mount点. 这好像有点问题, 现在的dentry 是mount点的, 而现在是要向上级目录跳, 是从新设备的根目录向上跳, 不应该跳到mount点, 而应该跳到 mount 点的上层目录. ??? // 呵呵 , 注意这里是while(1)死循环, 而当前节点是mount点的时候是没有break的, 在下一次循环的时候 会向上跳并 执行break 跳出while(1); mntput(nd->mnt); nd->mnt = parent; } } |
相关文章推荐
- link_path_walk()对于路径名最后一个分量的处理
- 【Python】目录路径之os.walk & os.path.walk
- new File(path)关于这个path的绝对与相对路径问题(ps部分 dos下切换盘符,user.dir解释)
- 知道树节点的路径FullPath,有哪个方法可以通过它找到该树节点
- Linux内核源代码情景分析-从路径名到目标节点
- LeetCode.687 Longest Univalue Path (经典的求相同节点最长路径)
- python使用os.listdir和os.walk获得文件的路径的方法
- MyBatis-- maven自动生成映射文件及classPathEntry 路径修改
- (BFS11.1.1)POJ 3126 Prime Path(计算从初始素数到目标素数的最短路径长度)
- 【文件管理】从路径名到目标节点
- Qt获取程序路径 之 QDir::currentPath与qApp->applicationDirPath
- Linux内核源代码情景分析-从路径名到目标节点
- Build path contains duplicate entry——生成路径包含重复项
- 获取文件路径----os.listdir()和os.walk()
- 4.28 context.getExternalFilesDir(null).getAbsolutePath()得到相册扫描不到的路径
- 测试目标路径的函数targetpath(movieClipObject)的应用
- 求树根节点到目标节点的路径
- golang使用filepath.Walk的时候路径不能使用\
- AtCoder Beginner Contest 070 Transit Tree Path(一道鸡贼的最短路径题)
- VB.NET System.IO.Path 文件名、路径、扩展名 处理