📄 namei.c
字号:
DQUOT_INIT(dir); lock_kernel(); error = dir->i_op->symlink(dir, dentry, oldname); unlock_kernel();exit_lock: up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error;}asmlinkage long sys_symlink(const char * oldname, const char * newname){ int error = 0; char * from; char * to; from = getname(oldname); if(IS_ERR(from)) return PTR_ERR(from); to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { struct dentry *dentry; struct nameidata nd; if (path_init(to, LOOKUP_PARENT, &nd)) error = path_walk(to, &nd); if (error) goto out; dentry = lookup_create(&nd, 0); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = vfs_symlink(nd.dentry->d_inode, dentry, from); dput(dentry); } up(&nd.dentry->d_inode->i_sem); path_release(&nd);out: putname(to); } putname(from); return error;}int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry){ struct inode *inode; int error; down(&dir->i_zombie); error = -ENOENT; inode = old_dentry->d_inode; if (!inode) goto exit_lock; error = may_create(dir, new_dentry); if (error) goto exit_lock; error = -EXDEV; if (dir->i_dev != inode->i_dev) goto exit_lock; /* * A link to an append-only or immutable file cannot be created. */ error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto exit_lock; if (!dir->i_op || !dir->i_op->link) goto exit_lock; DQUOT_INIT(dir); lock_kernel(); error = dir->i_op->link(old_dentry, dir, new_dentry); unlock_kernel();exit_lock: up(&dir->i_zombie); if (!error) inode_dir_notify(dir, DN_CREATE); return error;}/* * Hardlinks are often used in delicate situations. We avoid * security-related surprises by not following symlinks on the * newname. --KAB * * We don't follow them on the oldname either to be compatible * with linux 2.0, and to avoid hard-linking to directories * and other special files. --ADM */asmlinkage long sys_link(const char * oldname, const char * newname){ int error; char * from; char * to; from = getname(oldname); if(IS_ERR(from)) return PTR_ERR(from); to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { struct dentry *new_dentry; struct nameidata nd, old_nd; error = 0; if (path_init(from, LOOKUP_POSITIVE, &old_nd)) error = path_walk(from, &old_nd); if (error) goto exit; if (path_init(to, LOOKUP_PARENT, &nd)) error = path_walk(to, &nd); if (error) goto out; error = -EXDEV; if (old_nd.mnt != nd.mnt) goto out_release; new_dentry = lookup_create(&nd, 0); error = PTR_ERR(new_dentry); if (!IS_ERR(new_dentry)) { error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); dput(new_dentry); } up(&nd.dentry->d_inode->i_sem);out_release: path_release(&nd);out: path_release(&old_nd);exit: putname(to); } putname(from); return error;}/* * The worst of all namespace operations - renaming directory. "Perverted" * doesn't even start to describe it. Somebody in UCB had a heck of a trip... * Problems: * a) we can get into loop creation. Check is done in is_subdir(). * b) race potential - two innocent renames can create a loop together. * That's where 4.4 screws up. Current fix: serialization on * sb->s_vfs_rename_sem. We might be more accurate, but that's another * story. * c) we have to lock _three_ objects - parents and victim (if it exists). * And that - after we got ->i_sem on parents (until then we don't know * whether the target exists at all, let alone whether it is a directory * or not). Solution: ->i_zombie. Taken only after ->i_sem. Always taken * on link creation/removal of any kind. And taken (without ->i_sem) on * directory that will be removed (both in rmdir() and here). * d) some filesystems don't support opened-but-unlinked directories, * either because of layout or because they are not ready to deal with * all cases correctly. The latter will be fixed (taking this sort of * stuff into VFS), but the former is not going away. Solution: the same * trick as in rmdir(). * e) conversion from fhandle to dentry may come in the wrong moment - when * we are removing the target. Solution: we will have to grab ->i_zombie * in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on * ->i_sem on parents, which works but leads to some truely excessive * locking]. */int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry){ int error; struct inode *target; if (old_dentry->d_inode == new_dentry->d_inode) return 0; error = may_delete(old_dir, old_dentry, 1); if (error) return error; if (new_dir->i_dev != old_dir->i_dev) return -EXDEV; if (!new_dentry->d_inode) error = may_create(new_dir, new_dentry); else error = may_delete(new_dir, new_dentry, 1); if (error) return error; if (!old_dir->i_op || !old_dir->i_op->rename) return -EPERM; /* * If we are going to change the parent - check write permissions, * we'll need to flip '..'. */ if (new_dir != old_dir) { error = permission(old_dentry->d_inode, MAY_WRITE); } if (error) return error; DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); down(&old_dir->i_sb->s_vfs_rename_sem); error = -EINVAL; if (is_subdir(new_dentry, old_dentry)) goto out_unlock; /* Don't eat your daddy, dear... */ /* This also avoids locking issues */ if (old_dentry->d_parent == new_dentry) goto out_unlock; target = new_dentry->d_inode; if (target) { /* Hastur! Hastur! Hastur! */ triple_down(&old_dir->i_zombie, &new_dir->i_zombie, &target->i_zombie); d_unhash(new_dentry); } else double_down(&old_dir->i_zombie, &new_dir->i_zombie); if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir)) error = -ENOENT; else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) error = -EBUSY; else error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); if (target) { if (!error) target->i_flags |= S_DEAD; triple_up(&old_dir->i_zombie, &new_dir->i_zombie, &target->i_zombie); if (d_unhashed(new_dentry)) d_rehash(new_dentry); dput(new_dentry); } else double_up(&old_dir->i_zombie, &new_dir->i_zombie); if (!error) d_move(old_dentry,new_dentry);out_unlock: up(&old_dir->i_sb->s_vfs_rename_sem); return error;}int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry){ int error; if (old_dentry->d_inode == new_dentry->d_inode) return 0; error = may_delete(old_dir, old_dentry, 0); if (error) return error; if (new_dir->i_dev != old_dir->i_dev) return -EXDEV; if (!new_dentry->d_inode) error = may_create(new_dir, new_dentry); else error = may_delete(new_dir, new_dentry, 0); if (error) return error; if (!old_dir->i_op || !old_dir->i_op->rename) return -EPERM; DQUOT_INIT(old_dir); DQUOT_INIT(new_dir); double_down(&old_dir->i_zombie, &new_dir->i_zombie); if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) error = -EBUSY; else error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); double_up(&old_dir->i_zombie, &new_dir->i_zombie); if (error) return error; /* The following d_move() should become unconditional */ if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) { d_move(old_dentry, new_dentry); } return 0;}int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry){ int error; if (S_ISDIR(old_dentry->d_inode->i_mode)) error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); else error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); if (!error) { if (old_dir == new_dir) inode_dir_notify(old_dir, DN_RENAME); else { inode_dir_notify(old_dir, DN_DELETE); inode_dir_notify(new_dir, DN_CREATE); } } return error;}static inline int do_rename(const char * oldname, const char * newname){ int error = 0; struct dentry * old_dir, * new_dir; struct dentry * old_dentry, *new_dentry; struct nameidata oldnd, newnd; if (path_init(oldname, LOOKUP_PARENT, &oldnd)) error = path_walk(oldname, &oldnd); if (error) goto exit; if (path_init(newname, LOOKUP_PARENT, &newnd)) error = path_walk(newname, &newnd); if (error) goto exit1; error = -EXDEV; if (oldnd.mnt != newnd.mnt) goto exit2; old_dir = oldnd.dentry; error = -EBUSY; if (oldnd.last_type != LAST_NORM) goto exit2; new_dir = newnd.dentry; if (newnd.last_type != LAST_NORM) goto exit2; double_lock(new_dir, old_dir); old_dentry = lookup_hash(&oldnd.last, old_dir); error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) goto exit3; /* source must exist */ error = -ENOENT; if (!old_dentry->d_inode) goto exit4; /* unless the source is a directory trailing slashes give -ENOTDIR */ if (!S_ISDIR(old_dentry->d_inode->i_mode)) { error = -ENOTDIR; if (oldnd.last.name[oldnd.last.len]) goto exit4; if (newnd.last.name[newnd.last.len]) goto exit4; } new_dentry = lookup_hash(&newnd.last, new_dir); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit4; lock_kernel(); error = vfs_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); unlock_kernel(); dput(new_dentry);exit4: dput(old_dentry);exit3: double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);exit2: path_release(&newnd);exit1: path_release(&oldnd);exit: return error;}asmlinkage long sys_rename(const char * oldname, const char * newname){ int error; char * from; char * to; from = getname(oldname); if(IS_ERR(from)) return PTR_ERR(from); to = getname(newname); error = PTR_ERR(to); if (!IS_ERR(to)) { error = do_rename(from,to); putname(to); } putname(from); return error;}int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, const char *link){ int len; len = PTR_ERR(link); if (IS_ERR(link)) goto out; len = strlen(link); if (len > (unsigned) buflen) len = buflen; if (copy_to_user(buffer, link, len)) len = -EFAULT;out: return len;}static inline int__vfs_follow_link(struct nameidata *nd, const char *link){ int res = 0; char *name; if (IS_ERR(link)) goto fail; if (*link == '/') { path_release(nd); if (!walk_init_root(link, nd)) /* weird __emul_prefix() stuff did it */ goto out; } res = link_path_walk(link, nd);out: if (current->link_count || res || nd->last_type!=LAST_NORM) return res; /* * If it is an iterative symlinks resolution in open_namei() we * have to copy the last component. And all that crap because of * bloody create() on broken symlinks. Furrfu... */ name = __getname(); if (!name) return -ENOMEM; strcpy(name, nd->last.name); nd->last.name = name; return 0;fail: path_release(nd); return PTR_ERR(link);}int vfs_follow_link(struct nameidata *nd, const char *link){ return __vfs_follow_link(nd, link);}/* get the link contents into pagecache */static char *page_getlink(struct dentry * dentry, struct page **ppage){ struct page * page; struct address_space *mapping = dentry->d_inode->i_mapping; page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); if (IS_ERR(page)) goto sync_fail; wait_on_page(page); if (!Page_Uptodate(page)) goto async_fail; *ppage = page; return kmap(page);async_fail: page_cache_release(page); return ERR_PTR(-EIO);sync_fail: return (char*)page;}int page_readlink(struct dentry *dentry, char *buffer, int buflen){ struct page *page = NULL; char *s = page_getlink(dentry, &page); int res = vfs_readlink(dentry,buffer,buflen,s); if (page) { kunmap(page); page_cache_release(page); } return res;}int page_follow_link(struct dentry *dentry, struct nameidata *nd){ struct page *page = NULL; char *s = page_getlink(dentry, &page); int res = __vfs_follow_link(nd, s); if (page) { kunmap(page); page_cache_release(page); } return res;}struct inode_operations page_symlink_inode_operations = { readlink: page_readlink, follow_link: page_follow_link,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -