📄 namei.c
字号:
up(&dir->d_inode->i_sem); goto exit; } /* Negative dentry, just create the file */ if (!dentry->d_inode) { error = vfs_create(dir->d_inode, dentry, mode & ~current->fs->umask); up(&dir->d_inode->i_sem); dput(nd->dentry); nd->dentry = dentry; if (error) goto exit; /* Don't check for write permission, don't truncate */ acc_mode = 0; flag &= ~O_TRUNC; goto ok; } /* * It already exists. */ up(&dir->d_inode->i_sem); error = -EEXIST; if (flag & O_EXCL) goto exit_dput; if (d_mountpoint(dentry)) { error = -ELOOP; if (flag & O_NOFOLLOW) goto exit_dput; while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry)); } error = -ENOENT; if (!dentry->d_inode) goto exit_dput; if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link) goto do_link; dput(nd->dentry); nd->dentry = dentry; error = -EISDIR; if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) goto exit;ok: 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 (nd->mnt->mnt_flags & MNT_NODEV) 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; } /* * Ensure there are no outstanding leases on the file. */ error = get_lease(inode, flag); if (error) 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 0;exit_dput: dput(dentry);exit: path_release(nd); return error;do_link: error = -ELOOP; if (flag & O_NOFOLLOW) goto exit_dput; /* * This is subtle. Instead of calling do_follow_link() we do the * thing by hands. The reason is that this way we have zero link_count * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT. * After that we have the parent and last component, i.e. * we are in the same situation as after the first path_walk(). * Well, almost - if the last component is normal we get its copy * stored in nd->last.name and we will have to putname() it when we * are done. Procfs-like symlinks just set LAST_BIND. */ UPDATE_ATIME(dentry->d_inode); error = dentry->d_inode->i_op->follow_link(dentry, nd); dput(dentry); if (error) return error; if (nd->last_type == LAST_BIND) { dentry = nd->dentry; goto ok; } error = -EISDIR; if (nd->last_type != LAST_NORM) goto exit; if (nd->last.name[nd->last.len]) { putname(nd->last.name); goto exit; } error = -ELOOP; if (count++==32) { putname(nd->last.name); goto exit; } dir = nd->dentry; down(&dir->d_inode->i_sem); dentry = lookup_hash(&nd->last, nd->dentry); putname(nd->last.name); goto do_last;}/* SMP-safe */static struct dentry *lookup_create(struct nameidata *nd, int is_dir){ struct dentry *dentry; down(&nd->dentry->d_inode->i_sem); dentry = ERR_PTR(-EEXIST); if (nd->last_type != LAST_NORM) goto fail; dentry = lookup_hash(&nd->last, nd->dentry); if (IS_ERR(dentry)) goto fail; if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode) goto enoent; return dentry;enoent: dput(dentry); dentry = ERR_PTR(-ENOENT);fail: return dentry;}int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev){ int error = -EPERM; down(&dir->i_zombie); if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) goto exit_lock; error = may_create(dir, dentry); if (error) goto exit_lock; error = -EPERM; if (!dir->i_op || !dir->i_op->mknod) goto exit_lock; DQUOT_INIT(dir); lock_kernel(); error = dir->i_op->mknod(dir, dentry, mode, dev); unlock_kernel();exit_lock: up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error;}asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev){ int error = 0; char * tmp; struct dentry * dentry; struct nameidata nd; if (S_ISDIR(mode)) return -EPERM; tmp = getname(filename); if (IS_ERR(tmp)) return PTR_ERR(tmp); if (path_init(tmp, LOOKUP_PARENT, &nd)) error = path_walk(tmp, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); mode &= ~current->fs->umask; if (!IS_ERR(dentry)) { switch (mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(nd.dentry->d_inode,dentry,mode); break; case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: error = vfs_mknod(nd.dentry->d_inode,dentry,mode,dev); break; case S_IFDIR: error = -EPERM; break; default: error = -EINVAL; } dput(dentry); } up(&nd.dentry->d_inode->i_sem); path_release(&nd);out: putname(tmp); return error;}int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode){ int error; down(&dir->i_zombie); error = may_create(dir, dentry); if (error) goto exit_lock; error = -EPERM; if (!dir->i_op || !dir->i_op->mkdir) goto exit_lock; DQUOT_INIT(dir); mode &= (S_IRWXUGO|S_ISVTX); lock_kernel(); error = dir->i_op->mkdir(dir, dentry, mode); unlock_kernel();exit_lock: up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error;}asmlinkage long sys_mkdir(const char * pathname, int mode){ int error = 0; char * tmp; tmp = getname(pathname); error = PTR_ERR(tmp); if (!IS_ERR(tmp)) { struct dentry *dentry; struct nameidata nd; if (path_init(tmp, LOOKUP_PARENT, &nd)) error = path_walk(tmp, &nd); if (error) goto out; dentry = lookup_create(&nd, 1); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = vfs_mkdir(nd.dentry->d_inode, dentry, mode & ~current->fs->umask); dput(dentry); } up(&nd.dentry->d_inode->i_sem); path_release(&nd);out: putname(tmp); } return error;}/* * We try to drop the dentry early: we should have * a usage count of 2 if we're the only user of this * dentry, and if that is true (possibly after pruning * the dcache), then we drop the dentry now. * * A low-level filesystem can, if it choses, legally * do a * * if (!d_unhashed(dentry)) * return -EBUSY; * * if it cannot handle the case of removing a directory * that is still in use by something else.. */static void d_unhash(struct dentry *dentry){ dget(dentry); switch (atomic_read(&dentry->d_count)) { default: shrink_dcache_parent(dentry); if (atomic_read(&dentry->d_count) != 2) break; case 2: d_drop(dentry); }}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); double_down(&dir->i_zombie, &dentry->d_inode->i_zombie); d_unhash(dentry); if (IS_DEADDIR(dir)) error = -ENOENT; else if (d_mountpoint(dentry)) error = -EBUSY; else { lock_kernel(); error = dir->i_op->rmdir(dir, dentry); unlock_kernel(); if (!error) dentry->d_inode->i_flags |= S_DEAD; } double_up(&dir->i_zombie, &dentry->d_inode->i_zombie); if (!error) { inode_dir_notify(dir, DN_DELETE); d_delete(dentry); } dput(dentry); return error;}asmlinkage long sys_rmdir(const char * pathname){ int error = 0; char * name; struct dentry *dentry; struct nameidata nd; name = getname(pathname); if(IS_ERR(name)) return PTR_ERR(name); if (path_init(name, LOOKUP_PARENT, &nd)) error = path_walk(name, &nd); if (error) goto exit; switch(nd.last_type) { case LAST_DOTDOT: error = -ENOTEMPTY; goto exit1; case LAST_DOT: error = -EINVAL; goto exit1; case LAST_ROOT: error = -EBUSY; goto exit1; } down(&nd.dentry->d_inode->i_sem); dentry = lookup_hash(&nd.last, nd.dentry); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = vfs_rmdir(nd.dentry->d_inode, dentry); dput(dentry); } up(&nd.dentry->d_inode->i_sem);exit1: path_release(&nd);exit: putname(name); return error;}int vfs_unlink(struct inode *dir, struct dentry *dentry){ int error; down(&dir->i_zombie); error = may_delete(dir, dentry, 0); if (!error) { error = -EPERM; if (dir->i_op && dir->i_op->unlink) { DQUOT_INIT(dir); if (d_mountpoint(dentry)) error = -EBUSY; else { lock_kernel(); error = dir->i_op->unlink(dir, dentry); unlock_kernel(); if (!error) d_delete(dentry); } } } up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_DELETE); return error;}asmlinkage long sys_unlink(const char * pathname){ int error = 0; char * name; struct dentry *dentry; struct nameidata nd; name = getname(pathname); if(IS_ERR(name)) return PTR_ERR(name); if (path_init(name, LOOKUP_PARENT, &nd)) error = path_walk(name, &nd); if (error) goto exit; error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; down(&nd.dentry->d_inode->i_sem); dentry = lookup_hash(&nd.last, nd.dentry); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /* Why not before? Because we want correct error value */ if (nd.last.name[nd.last.len]) goto slashes; error = vfs_unlink(nd.dentry->d_inode, dentry); exit2: dput(dentry); } up(&nd.dentry->d_inode->i_sem);exit1: path_release(&nd);exit: putname(name); return error;slashes: error = !dentry->d_inode ? -ENOENT : S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; goto exit2;}int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname){ int error; down(&dir->i_zombie); error = may_create(dir, dentry); if (error) goto exit_lock; error = -EPERM; if (!dir->i_op || !dir->i_op->symlink) goto exit_lock;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -