namei.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,434 行 · 第 1/3 页
C
1,434 行
} } return dentry;}/* * 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; if (victim->d_mounts != victim->d_covers) 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; return permission(dir,MAY_WRITE | MAY_EXEC);}static inline struct dentry *get_parent(struct dentry *dentry){ return dget(dentry->d_parent);}static inline void unlock_dir(struct dentry *dir){ up(&dir->d_inode->i_sem); dput(dir);}/* * We need to do a check-parent every time * after we have locked the parent - to verify * that the parent is still our parent and * that we are still hashed onto it.. * * This is requied in case two processes race * on removing (or moving) the same entry: the * parent lock will serialize them, but the * other process will be too late.. */#define check_parent(dir, dentry) \ ((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash))/* * Locking the parent is needed to: * - serialize directory operations * - make sure the parent doesn't change from * under us in the middle of an operation. * * NOTE! Right now we'd rather use a "struct inode" * for this, but as I expect things to move toward * using dentries instead for most things it is * probably better to start with the conceptually * better interface of relying on a path of dentries. */static inline struct dentry *lock_parent(struct dentry *dentry){ struct dentry *dir = dget(dentry->d_parent); down(&dir->d_inode->i_sem); return dir;}/* * Whee.. Deadlock country. Happily there are only two VFS * operations that do this.. */static inline void double_lock(struct dentry *d1, struct dentry *d2){ struct semaphore *s1 = &d1->d_inode->i_sem; struct semaphore *s2 = &d2->d_inode->i_sem; if (s1 != s2) { if ((unsigned long) s1 < (unsigned long) s2) { struct semaphore *tmp = s2; s2 = s1; s1 = tmp; } down(s1); } down(s2);}static inline void double_unlock(struct dentry *d1, struct dentry *d2){ struct semaphore *s1 = &d1->d_inode->i_sem; struct semaphore *s2 = &d2->d_inode->i_sem; up(s1); if (s1 != s2) up(s2); dput(d1); dput(d2);}/* * 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;}/* * 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). */struct dentry * open_namei(const char * pathname, int flag, int mode){ int acc_mode, error; struct inode *inode; struct dentry *dentry; mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; dentry = lookup_dentry(pathname, NULL, lookup_flags(flag)); if (IS_ERR(dentry)) return dentry; acc_mode = ACC_MODE(flag); if (flag & O_CREAT) { struct dentry *dir; if (dentry->d_inode) { if (!(flag & O_EXCL)) goto nocreate; error = -EEXIST; goto exit; } dir = lock_parent(dentry); if (!check_parent(dir, dentry)) { /* * Really nasty race happened. What's the * right error code? We had a dentry, but * before we could use it it was removed * by somebody else. We could just re-try * everything, I guess. * * ENOENT is definitely wrong. */ error = -ENOENT; unlock_dir(dir); goto exit; } /* * Somebody might have created the file while we * waited for the directory lock.. So we have to * re-do the existence test. */ if (dentry->d_inode) { error = 0; if (flag & O_EXCL) error = -EEXIST; } else if ((error = may_create(dir->d_inode, dentry)) == 0) { if (!dir->d_inode->i_op || !dir->d_inode->i_op->create) error = -EACCES; else { DQUOT_INIT(dir->d_inode); error = dir->d_inode->i_op->create(dir->d_inode, dentry, mode); /* Don't check for write permission, don't truncate */ acc_mode = 0; flag &= ~O_TRUNC; } } unlock_dir(dir); if (error) goto exit; }nocreate: error = -ENOENT; inode = dentry->d_inode; if (!inode) goto exit; error = -ELOOP; if (S_ISLNK(inode->i_mode)) goto exit; error = -EISDIR; if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) goto exit; error = permission(inode,acc_mode); if (error) goto exit; /* * FIFO's, sockets and device files are special: they don't * actually live on the filesystem itself, and as such you * can write to them even if the filesystem is read-only. */ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { flag &= ~O_TRUNC; } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { error = -EACCES; if (IS_NODEV(inode)) goto exit; flag &= ~O_TRUNC; } else { error = -EROFS; if (IS_RDONLY(inode) && (flag & 2)) goto exit; } /* * An append-only file must be opened in append mode for writing. */ error = -EPERM; if (IS_APPEND(inode)) { if ((flag & FMODE_WRITE) && !(flag & O_APPEND)) goto exit; if (flag & O_TRUNC) goto exit; } if (flag & O_TRUNC) { error = get_write_access(inode); if (error) goto exit; /* * Refuse to truncate files with mandatory locks held on them. */ error = locks_verify_locked(inode); if (!error) { DQUOT_INIT(inode); error = do_truncate(dentry, 0); } put_write_access(inode); if (error) goto exit; } else if (flag & FMODE_WRITE) DQUOT_INIT(inode); return dentry;exit: dput(dentry); return ERR_PTR(error);}struct dentry * do_mknod(const char * filename, int mode, dev_t dev){ int error; struct dentry *dir; struct dentry *dentry, *retval; mode &= ~current->fs->umask; dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW); if (IS_ERR(dentry)) return dentry; dir = lock_parent(dentry); error = -ENOENT; if (!check_parent(dir, dentry)) goto exit_lock; error = may_create(dir->d_inode, dentry); if (error) goto exit_lock; error = -EPERM; if (!dir->d_inode->i_op || !dir->d_inode->i_op->mknod) goto exit_lock; DQUOT_INIT(dir->d_inode); error = dir->d_inode->i_op->mknod(dir->d_inode, dentry, mode, dev);exit_lock: retval = ERR_PTR(error); if (!error) retval = dget(dentry); unlock_dir(dir); dput(dentry); return retval;}asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev){ int error; char * tmp; lock_kernel(); error = -EPERM; if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !capable(CAP_SYS_ADMIN))) goto out; error = -EINVAL; switch (mode & S_IFMT) { case 0: mode |= S_IFREG; break; case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: break; default: goto out; } tmp = getname(filename); error = PTR_ERR(tmp); if (!IS_ERR(tmp)) { struct dentry * dentry = do_mknod(tmp,mode,dev); putname(tmp); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { dput(dentry); error = 0; } }out: unlock_kernel(); return error;}/* * Look out: this function may change a normal dentry * into a directory dentry (different size).. */#ifdef OSKIT int do_mkdir(const char * pathname, int mode)#elsestatic inline int do_mkdir(const char * pathname, int mode)#endif{ int error; struct dentry *dir; struct dentry *dentry; dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit; /* * EEXIST is kind of a strange error code to * return, but basically if the dentry was moved * or unlinked while we locked the parent, we * do know that it _did_ exist before, and as * such it makes perfect sense.. In contrast, * ENOENT doesn't make sense for mkdir. */ dir = lock_parent(dentry); error = -EEXIST; if (!check_parent(dir, dentry)) goto exit_lock; error = may_create(dir->d_inode, dentry); if (error) goto exit_lock; error = -EPERM; if (!dir->d_inode->i_op || !dir->d_inode->i_op->mkdir) goto exit_lock; DQUOT_INIT(dir->d_inode); mode &= 0777 & ~current->fs->umask; error = dir->d_inode->i_op->mkdir(dir->d_inode, dentry, mode);exit_lock: unlock_dir(dir); dput(dentry);exit: return error;}asmlinkage int sys_mkdir(const char * pathname, int mode){ int error; char * tmp; lock_kernel(); tmp = getname(pathname); error = PTR_ERR(tmp); if (!IS_ERR(tmp)) { error = do_mkdir(tmp,mode); putname(tmp); } unlock_kernel(); return error;}int vfs_rmdir(struct inode *dir, struct dentry *dentry){ int error; error = may_delete(dir, dentry, 1); if (error) return error; if (!dir->i_op || !dir->i_op->rmdir) return -EPERM; DQUOT_INIT(dir);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?