在Linux内核源代码情景分析-文件系统的安装,一文中,已经调用sudo mount -t ext2 /dev/sdb1 /mnt/sdb,在/mnt/sdb节点上挂载了文件系统,那么我们接下来访问/mnt/sdb/hello.c节点。我们来看一下path_walk的执行有什么不同?
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; 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 = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE); if (!dentry) { dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE); 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) 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; } while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))//在找到/mnt/sdb节点后,会执行到这里,也是不同的所在 ; 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; }
d_mountpoint,查看是否是挂载点。
static __inline__ int d_mountpoint(struct dentry *dentry) { return !list_empty(&dentry->d_vfsmnt); }
还记得在Linux内核源代码情景分析-文件系统的安装,一文中,最后add_vfsmnt。
list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt);
所以这里非空,返回true,接着执行__follow_down,代码如下:
static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) { struct list_head *p; spin_lock(&dcache_lock); p = (*dentry)->d_vfsmnt.next; while (p != &(*dentry)->d_vfsmnt) {//参考文件系统的安装的add_vfsmnt struct vfsmount *tmp; tmp = list_entry(p, struct vfsmount, mnt_clash); if (tmp->mnt_parent == *mnt) { //参考文件系统的安装的add_vfsmnt *mnt = mntget(tmp);//nd->mnt为被安装设备的vfsmount结构 spin_unlock(&dcache_lock); mntput(tmp->mnt_parent); /* tmp holds the mountpoint, so... */ dput(*dentry); *dentry = dget(tmp->mnt_root);//dentry为被安装设备的根节点的dentry结构 return 1; } p = p->next; } spin_unlock(&dcache_lock); return 0; }
还有一个知识点,就是对于中间节点是dot或者dotdot的处理。
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;
如果是dot,那么就继续下一次循环。如果是dotdot,会调用follow_dotdot,代码如下:
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) {//已到达的nd->dentry就是本进程的根节点,这时不能再往上跑了,所以保持nd->dentry不变 read_unlock(¤t->fs->lock); break; } read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); if (nd->dentry != nd->mnt->mnt_root) {//已到达nd->dentry不等于根节点的dentry结构 dentry = dget(nd->dentry->d_parent); spin_unlock(&dcache_lock); dput(nd->dentry); nd->dentry = dentry;//dentry结构往上走,mnt结构不变 break; } parent=nd->mnt->mnt_parent;//走到这里,说明nd->dentry已经是根节点的dentry结构,只能跑到另一个设备上了,读到父设备的vfsmount结构 if (parent == nd->mnt) {//如果父设备的vfsmount结构和本设备的vmfmount结构一样,那就不能再往父设备上跑了 spin_unlock(&dcache_lock); break; } mntget(parent);//如果父设备的vfsmount结构和本设备的vmfmount结构不一样 dentry=dget(nd->mnt->mnt_mountpoint);//dentry为当前设备的挂载节点的dentry结构 spin_unlock(&dcache_lock); dput(nd->dentry); nd->dentry = dentry; mntput(nd->mnt); nd->mnt = parent;//nd->mnt也被赋值为父设备的vfsmount结构 } }
时间: 2024-12-28 09:26:26