📄 namei.c
字号:
} /* * 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)) ; 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;}int path_walk(const char * name, struct nameidata *nd){ current->total_link_count = 0; return link_path_walk(name, nd);}/* SMP-safe *//* returns 1 if everything is done */static int __emul_lookup_dentry(const char *name, struct nameidata *nd){ if (path_walk(name, nd)) return 0; /* something went wrong... */ if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) { struct nameidata nd_root; /* * NAME was not found in alternate root or it's a directory. Try to find * it in the normal root: */ nd_root.last_type = LAST_ROOT; nd_root.flags = nd->flags; read_lock(¤t->fs->lock); nd_root.mnt = mntget(current->fs->rootmnt); nd_root.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); if (path_walk(name, &nd_root)) return 1; if (nd_root.dentry->d_inode) { path_release(nd); nd->dentry = nd_root.dentry; nd->mnt = nd_root.mnt; nd->last = nd_root.last; return 1; } path_release(&nd_root); } return 1;}void set_fs_altroot(void){ char *emul = __emul_prefix(); struct nameidata nd; struct vfsmount *mnt = NULL, *oldmnt; struct dentry *dentry = NULL, *olddentry; if (emul) { read_lock(¤t->fs->lock); nd.mnt = mntget(current->fs->rootmnt); nd.dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); nd.flags = LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE; if (path_walk(emul,&nd) == 0) { mnt = nd.mnt; dentry = nd.dentry; } } write_lock(¤t->fs->lock); oldmnt = current->fs->altrootmnt; olddentry = current->fs->altroot; current->fs->altrootmnt = mnt; current->fs->altroot = dentry; write_unlock(¤t->fs->lock); if (olddentry) { dput(olddentry); mntput(oldmnt); }}/* SMP-safe */static inline intwalk_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); } nd->mnt = mntget(current->fs->rootmnt); nd->dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); return 1;}/* SMP-safe */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=='/') return walk_init_root(name,nd); read_lock(¤t->fs->lock); nd->mnt = mntget(current->fs->pwdmnt); nd->dentry = dget(current->fs->pwd); read_unlock(¤t->fs->lock); return 1;}/* * Restricted form of lookup. Doesn't follow links, single-component only, * needs parent already locked. Doesn't follow mounts. * SMP-safe. */struct dentry * lookup_hash(struct qstr *name, struct dentry * base){ struct dentry * dentry; struct inode *inode; int err; inode = base->d_inode; err = permission(inode, MAY_EXEC); dentry = ERR_PTR(err); if (err) goto out; /* * See if the low-level filesystem might want * to use its own hash.. */ if (base->d_op && base->d_op->d_hash) { err = base->d_op->d_hash(base, name); dentry = ERR_PTR(err); if (err < 0) goto out; } dentry = cached_lookup(base, name, 0); if (!dentry) { struct dentry *new = d_alloc(base, name); dentry = ERR_PTR(-ENOMEM); if (!new) goto out; lock_kernel(); dentry = inode->i_op->lookup(inode, new); unlock_kernel(); if (!dentry) dentry = new; else dput(new); }out: return dentry;}/* SMP-safe */struct dentry * lookup_one_len(const char * name, struct dentry * base, int len){ unsigned long hash; struct qstr this; unsigned int c; this.name = name; this.len = len; if (!len) goto access; hash = init_name_hash(); while (len--) { c = *(const unsigned char *)name++; if (c == '/' || c == '\0') goto access; hash = partial_name_hash(c, hash); } this.hash = end_name_hash(hash); return lookup_hash(&this, base);access: return ERR_PTR(-EACCES);}/* * 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. * * namei exists in two versions: namei/lnamei. The only difference is * that namei follows links, while lnamei does not. * SMP-safe */int __user_walk(const char *name, unsigned flags, struct nameidata *nd){ char *tmp; int err; tmp = getname(name); err = PTR_ERR(tmp); if (!IS_ERR(tmp)) { err = 0; if (path_init(tmp, flags, nd)) err = path_walk(tmp, nd); putname(tmp); } return err;}/* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. */static inline int check_sticky(struct inode *dir, struct inode *inode){ if (!(dir->i_mode & S_ISVTX)) return 0; if (inode->i_uid == current->fsuid) return 0; if (dir->i_uid == current->fsuid) return 0; return !capable(CAP_FOWNER);}/* * Check whether we can remove a link victim from directory dir, check * whether the type of victim is right. * 1. We can't do it if dir is read-only (done in permission()) * 2. We should have write and exec permissions on dir * 3. We can't remove anything from append-only dir * 4. We can't do anything with immutable dir (done in permission()) * 5. If the sticky bit on dir is set we should either * a. be owner of dir, or * b. be owner of victim, or * c. have CAP_FOWNER capability * 6. If the victim is append-only or immutable we can't do antyhing with * links pointing to it. * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR. * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR. * 9. We can't remove a root or mountpoint. */static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir){ int error; if (!victim->d_inode || victim->d_parent->d_inode != dir) return -ENOENT; error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) return -EPERM; if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| IS_IMMUTABLE(victim->d_inode)) return -EPERM; if (isdir) { if (!S_ISDIR(victim->d_inode->i_mode)) return -ENOTDIR; if (IS_ROOT(victim)) return -EBUSY; } else if (S_ISDIR(victim->d_inode->i_mode)) return -EISDIR; return 0;}/* Check whether we can create an object with dentry child in directory * dir. * 1. We can't do it if child already exists (open has special treatment for * this case, but since we are inlined it's OK) * 2. We can't do it if dir is read-only (done in permission()) * 3. We should have write and exec permissions on dir * 4. We can't do it if dir is immutable (done in permission()) */static inline int may_create(struct inode *dir, struct dentry *child) { if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; return permission(dir,MAY_WRITE | MAY_EXEC);}/* * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security * reasons. * * O_DIRECTORY translates into forcing a directory lookup. */static inline int lookup_flags(unsigned int f){ unsigned long retval = LOOKUP_FOLLOW; if (f & O_NOFOLLOW) retval &= ~LOOKUP_FOLLOW; if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) retval &= ~LOOKUP_FOLLOW; if (f & O_DIRECTORY) retval |= LOOKUP_DIRECTORY; return retval;}int vfs_create(struct inode *dir, struct dentry *dentry, int mode){ int error; mode &= S_IALLUGO; mode |= S_IFREG; down(&dir->i_zombie); error = may_create(dir, dentry); if (error) goto exit_lock; error = -EACCES; /* shouldn't it be ENOSYS? */ if (!dir->i_op || !dir->i_op->create) goto exit_lock; DQUOT_INIT(dir); lock_kernel(); error = dir->i_op->create(dir, dentry, mode); unlock_kernel();exit_lock: up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error;}/* * open_namei() * * namei for open - this is in fact almost the whole open-routine. * * Note that the low bits of "flag" aren't the same as in the open * system call - they are 00 - no permissions needed * 01 - read permission needed * 10 - write permission needed * 11 - read/write permissions needed * which is a lot more logical, and also allows the "no perm" needed * for symlinks (where the permissions are checked later). * SMP-safe */int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd){ int acc_mode, error = 0; struct inode *inode; struct dentry *dentry; struct dentry *dir; int count = 0; acc_mode = ACC_MODE(flag); /* * The simplest case - just a plain lookup. */ if (!(flag & O_CREAT)) { if (path_init(pathname, lookup_flags(flag), nd)) error = path_walk(pathname, nd); if (error) return error; dentry = nd->dentry; goto ok; } /* * Create - we need to know the parent. */ if (path_init(pathname, LOOKUP_PARENT, nd)) error = path_walk(pathname, nd); if (error) return error; /* * We have the parent and last component. First of all, check * that we are not asked to creat(2) an obvious directory - that * will not do. */ error = -EISDIR; if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len]) goto exit; dir = nd->dentry; down(&dir->d_inode->i_sem); dentry = lookup_hash(&nd->last, nd->dentry);do_last: error = PTR_ERR(dentry); if (IS_ERR(dentry)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -