⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dir.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
		return -ENOMEM;	}	kaddr = kmap_atomic(page, KM_USER0);	memcpy(kaddr, symname, pathlen);	if (pathlen < PAGE_SIZE)		memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);	kunmap_atomic(kaddr, KM_USER0);	error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);	if (error != 0) {		dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",			dir->i_sb->s_id, dir->i_ino,			dentry->d_name.name, symname, error);		d_drop(dentry);		__free_page(page);		unlock_kernel();		return error;	}	/*	 * No big deal if we can't add this page to the page cache here.	 * READLINK will get the missing page from the server if needed.	 */	pagevec_init(&lru_pvec, 0);	if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,							GFP_KERNEL)) {		pagevec_add(&lru_pvec, page);		pagevec_lru_add(&lru_pvec);		SetPageUptodate(page);		unlock_page(page);	} else		__free_page(page);	unlock_kernel();	return 0;}static int nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry){	struct inode *inode = old_dentry->d_inode;	int error;	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,		dentry->d_parent->d_name.name, dentry->d_name.name);	lock_kernel();	d_drop(dentry);	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);	if (error == 0) {		atomic_inc(&inode->i_count);		d_add(dentry, inode);	}	unlock_kernel();	return error;}/* * RENAME * FIXME: Some nfsds, like the Linux user space nfsd, may generate a * different file handle for the same inode after a rename (e.g. when * moving to a different directory). A fail-safe method to do so would * be to look up old_dir/old_name, create a link to new_dir/new_name and * rename the old file using the sillyrename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. * * FIXED. *  * It actually works quite well. One needs to have the possibility for * at least one ".nfs..." file in each directory the file ever gets * moved or linked to which happens automagically with the new * implementation that only depends on the dcache stuff instead of * using the inode layer * * Unfortunately, things are a little more complicated than indicated * above. For a cross-directory move, we want to make sure we can get * rid of the old inode after the operation.  This means there must be * no pending writes (if it's a file), and the use count must be 1. * If these conditions are met, we can drop the dentries before doing * the rename. */static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,		      struct inode *new_dir, struct dentry *new_dentry){	struct inode *old_inode = old_dentry->d_inode;	struct inode *new_inode = new_dentry->d_inode;	struct dentry *dentry = NULL, *rehash = NULL;	int error = -EBUSY;	/*	 * To prevent any new references to the target during the rename,	 * we unhash the dentry and free the inode in advance.	 */	lock_kernel();	if (!d_unhashed(new_dentry)) {		d_drop(new_dentry);		rehash = new_dentry;	}	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",		 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,		 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,		 atomic_read(&new_dentry->d_count));	/*	 * First check whether the target is busy ... we can't	 * safely do _any_ rename if the target is in use.	 *	 * For files, make a copy of the dentry and then do a 	 * silly-rename. If the silly-rename succeeds, the	 * copied dentry is hashed and becomes the new target.	 */	if (!new_inode)		goto go_ahead;	if (S_ISDIR(new_inode->i_mode)) {		error = -EISDIR;		if (!S_ISDIR(old_inode->i_mode))			goto out;	} else if (atomic_read(&new_dentry->d_count) > 2) {		int err;		/* copy the target dentry's name */		dentry = d_alloc(new_dentry->d_parent,				 &new_dentry->d_name);		if (!dentry)			goto out;		/* silly-rename the existing target ... */		err = nfs_sillyrename(new_dir, new_dentry);		if (!err) {			new_dentry = rehash = dentry;			new_inode = NULL;			/* instantiate the replacement target */			d_instantiate(new_dentry, NULL);		} else if (atomic_read(&new_dentry->d_count) > 1)			/* dentry still busy? */			goto out;	} else		drop_nlink(new_inode);go_ahead:	/*	 * ... prune child dentries and writebacks if needed.	 */	if (atomic_read(&old_dentry->d_count) > 1) {		if (S_ISREG(old_inode->i_mode))			nfs_wb_all(old_inode);		shrink_dcache_parent(old_dentry);	}	nfs_inode_return_delegation(old_inode);	if (new_inode != NULL) {		nfs_inode_return_delegation(new_inode);		d_delete(new_dentry);	}	error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,					   new_dir, &new_dentry->d_name);	nfs_mark_for_revalidate(old_inode);out:	if (rehash)		d_rehash(rehash);	if (!error) {		d_move(old_dentry, new_dentry);		nfs_set_verifier(new_dentry,					nfs_save_change_attribute(new_dir));	}	/* new dentry created? */	if (dentry)		dput(dentry);	unlock_kernel();	return error;}static DEFINE_SPINLOCK(nfs_access_lru_lock);static LIST_HEAD(nfs_access_lru_list);static atomic_long_t nfs_access_nr_entries;static void nfs_access_free_entry(struct nfs_access_entry *entry){	put_rpccred(entry->cred);	kfree(entry);	smp_mb__before_atomic_dec();	atomic_long_dec(&nfs_access_nr_entries);	smp_mb__after_atomic_dec();}int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask){	LIST_HEAD(head);	struct nfs_inode *nfsi;	struct nfs_access_entry *cache;restart:	spin_lock(&nfs_access_lru_lock);	list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {		struct inode *inode;		if (nr_to_scan-- == 0)			break;		inode = igrab(&nfsi->vfs_inode);		if (inode == NULL)			continue;		spin_lock(&inode->i_lock);		if (list_empty(&nfsi->access_cache_entry_lru))			goto remove_lru_entry;		cache = list_entry(nfsi->access_cache_entry_lru.next,				struct nfs_access_entry, lru);		list_move(&cache->lru, &head);		rb_erase(&cache->rb_node, &nfsi->access_cache);		if (!list_empty(&nfsi->access_cache_entry_lru))			list_move_tail(&nfsi->access_cache_inode_lru,					&nfs_access_lru_list);		else {remove_lru_entry:			list_del_init(&nfsi->access_cache_inode_lru);			clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);		}		spin_unlock(&inode->i_lock);		spin_unlock(&nfs_access_lru_lock);		iput(inode);		goto restart;	}	spin_unlock(&nfs_access_lru_lock);	while (!list_empty(&head)) {		cache = list_entry(head.next, struct nfs_access_entry, lru);		list_del(&cache->lru);		nfs_access_free_entry(cache);	}	return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure;}static void __nfs_access_zap_cache(struct inode *inode){	struct nfs_inode *nfsi = NFS_I(inode);	struct rb_root *root_node = &nfsi->access_cache;	struct rb_node *n, *dispose = NULL;	struct nfs_access_entry *entry;	/* Unhook entries from the cache */	while ((n = rb_first(root_node)) != NULL) {		entry = rb_entry(n, struct nfs_access_entry, rb_node);		rb_erase(n, root_node);		list_del(&entry->lru);		n->rb_left = dispose;		dispose = n;	}	nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;	spin_unlock(&inode->i_lock);	/* Now kill them all! */	while (dispose != NULL) {		n = dispose;		dispose = n->rb_left;		nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node));	}}void nfs_access_zap_cache(struct inode *inode){	/* Remove from global LRU init */	if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {		spin_lock(&nfs_access_lru_lock);		list_del_init(&NFS_I(inode)->access_cache_inode_lru);		spin_unlock(&nfs_access_lru_lock);	}	spin_lock(&inode->i_lock);	/* This will release the spinlock */	__nfs_access_zap_cache(inode);}static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred){	struct rb_node *n = NFS_I(inode)->access_cache.rb_node;	struct nfs_access_entry *entry;	while (n != NULL) {		entry = rb_entry(n, struct nfs_access_entry, rb_node);		if (cred < entry->cred)			n = n->rb_left;		else if (cred > entry->cred)			n = n->rb_right;		else			return entry;	}	return NULL;}static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res){	struct nfs_inode *nfsi = NFS_I(inode);	struct nfs_access_entry *cache;	int err = -ENOENT;	spin_lock(&inode->i_lock);	if (nfsi->cache_validity & NFS_INO_INVALID_ACCESS)		goto out_zap;	cache = nfs_access_search_rbtree(inode, cred);	if (cache == NULL)		goto out;	if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))		goto out_stale;	res->jiffies = cache->jiffies;	res->cred = cache->cred;	res->mask = cache->mask;	list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);	err = 0;out:	spin_unlock(&inode->i_lock);	return err;out_stale:	rb_erase(&cache->rb_node, &nfsi->access_cache);	list_del(&cache->lru);	spin_unlock(&inode->i_lock);	nfs_access_free_entry(cache);	return -ENOENT;out_zap:	/* This will release the spinlock */	__nfs_access_zap_cache(inode);	return -ENOENT;}static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *set){	struct nfs_inode *nfsi = NFS_I(inode);	struct rb_root *root_node = &nfsi->access_cache;	struct rb_node **p = &root_node->rb_node;	struct rb_node *parent = NULL;	struct nfs_access_entry *entry;	spin_lock(&inode->i_lock);	while (*p != NULL) {		parent = *p;		entry = rb_entry(parent, struct nfs_access_entry, rb_node);		if (set->cred < entry->cred)			p = &parent->rb_left;		else if (set->cred > entry->cred)			p = &parent->rb_right;		else			goto found;	}	rb_link_node(&set->rb_node, parent, p);	rb_insert_color(&set->rb_node, root_node);	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);	spin_unlock(&inode->i_lock);	return;found:	rb_replace_node(parent, &set->rb_node, root_node);	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);	list_del(&entry->lru);	spin_unlock(&inode->i_lock);	nfs_access_free_entry(entry);}static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set){	struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);	if (cache == NULL)		return;	RB_CLEAR_NODE(&cache->rb_node);	cache->jiffies = set->jiffies;	cache->cred = get_rpccred(set->cred);	cache->mask = set->mask;	nfs_access_add_rbtree(inode, cache);	/* Update accounting */	smp_mb__before_atomic_inc();	atomic_long_inc(&nfs_access_nr_entries);	smp_mb__after_atomic_inc();	/* Add inode to global LRU list */	if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {		spin_lock(&nfs_access_lru_lock);		list_add_tail(&NFS_I(inode)->access_cache_inode_lru, &nfs_access_lru_list);		spin_unlock(&nfs_access_lru_lock);	}}static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask){	struct nfs_access_entry cache;	int status;	status = nfs_access_get_cached(inode, cred, &cache);	if (status == 0)		goto out;	/* Be clever: ask server to check for all possible rights */	cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;	cache.cred = cred;	cache.jiffies = jiffies;	status = NFS_PROTO(inode)->access(inode, &cache);	if (status != 0)		return status;	nfs_access_add_cache(inode, &cache);out:	if ((cache.mask & mask) == mask)		return 0;	return -EACCES;}static int nfs_open_permission_mask(int openflags){	int mask = 0;	if (openflags & FMODE_READ)		mask |= MAY_READ;	if (openflags & FMODE_WRITE)		mask |= MAY_WRITE;	if (openflags & FMODE_EXEC)		mask |= MAY_EXEC;	return mask;}int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags){	return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));}int nfs_permission(struct inode *inode, int mask, struct nameidata *nd){	struct rpc_cred *cred;	int res = 0;	nfs_inc_stats(inode, NFSIOS_VFSACCESS);	if (mask == 0)		goto out;	/* Is this sys_access() ? */	if (nd != NULL && (nd->flags & LOOKUP_ACCESS))		goto force_lookup;	switch (inode->i_mode & S_IFMT) {		case S_IFLNK:			goto out;		case S_IFREG:			/* NFSv4 has atomic_open... */			if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)					&& nd != NULL					&& (nd->flags & LOOKUP_OPEN))				goto out;			break;		case S_IFDIR:			/*			 * Optimize away all write operations, since the server			 * will check permissions when we perform the op.			 */			if ((mask & MAY_WRITE) && !(mask & MAY_READ))				goto out;	}force_lookup:	lock_kernel();	if (!NFS_PROTO(inode)->access)		goto out_notsup;	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);	if (!IS_ERR(cred)) {		res = nfs_do_access(inode, cred, mask);		put_rpccred(cred);	} else		res = PTR_ERR(cred);	unlock_kernel();out:	dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n",		inode->i_sb->s_id, inode->i_ino, mask, res);	return res;out_notsup:	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);	if (res == 0)		res = generic_permission(inode, mask, NULL);	unlock_kernel();	goto out;}/* * Local variables: *  version-control: t *  kept-new-versions: 5 * End: */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -