mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	exportfs: move most of reconnect_path to helper function
Also replace 3 easily-confused three-letter acronyms by more helpful variable names. Just cleanup, no change in functionality, with one exception: the dentry_connected() check in the "out_reconnected" case will now only check the ancestors of the current dentry instead of checking all the way from target_dir. Since we've already verified connectivity up to this dentry, that should be sufficient. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									e4b70ebeeb
								
							
						
					
					
						commit
						bbf7a8a356
					
				
					 1 changed files with 86 additions and 78 deletions
				
			
		| 
						 | 
				
			
			@ -125,6 +125,86 @@ static void clear_disconnected(struct dentry *dentry)
 | 
			
		|||
	dput(dentry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Reconnect a directory dentry with its parent.
 | 
			
		||||
 *
 | 
			
		||||
 * This can return a dentry, or NULL, or an error.
 | 
			
		||||
 *
 | 
			
		||||
 * In the first case the returned dentry is the parent of the given
 | 
			
		||||
 * dentry, and may itself need to be reconnected to its parent.
 | 
			
		||||
 *
 | 
			
		||||
 * In the NULL case, a concurrent VFS operation has either renamed or
 | 
			
		||||
 * removed this directory.  The concurrent operation has reconnected our
 | 
			
		||||
 * dentry, so we no longer need to.
 | 
			
		||||
 */
 | 
			
		||||
static struct dentry *reconnect_one(struct vfsmount *mnt,
 | 
			
		||||
		struct dentry *dentry, char *nbuf)
 | 
			
		||||
{
 | 
			
		||||
	struct dentry *parent;
 | 
			
		||||
	struct dentry *tmp;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	parent = ERR_PTR(-EACCES);
 | 
			
		||||
	mutex_lock(&dentry->d_inode->i_mutex);
 | 
			
		||||
	if (mnt->mnt_sb->s_export_op->get_parent)
 | 
			
		||||
		parent = mnt->mnt_sb->s_export_op->get_parent(dentry);
 | 
			
		||||
	mutex_unlock(&dentry->d_inode->i_mutex);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(parent)) {
 | 
			
		||||
		dprintk("%s: get_parent of %ld failed, err %d\n",
 | 
			
		||||
			__func__, dentry->d_inode->i_ino, PTR_ERR(parent));
 | 
			
		||||
		return parent;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dprintk("%s: find name of %lu in %lu\n", __func__,
 | 
			
		||||
		dentry->d_inode->i_ino, parent->d_inode->i_ino);
 | 
			
		||||
	err = exportfs_get_name(mnt, parent, nbuf, dentry);
 | 
			
		||||
	if (err == -ENOENT)
 | 
			
		||||
		goto out_reconnected;
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto out_err;
 | 
			
		||||
	dprintk("%s: found name: %s\n", __func__, nbuf);
 | 
			
		||||
	mutex_lock(&parent->d_inode->i_mutex);
 | 
			
		||||
	tmp = lookup_one_len(nbuf, parent, strlen(nbuf));
 | 
			
		||||
	mutex_unlock(&parent->d_inode->i_mutex);
 | 
			
		||||
	if (IS_ERR(tmp)) {
 | 
			
		||||
		dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
 | 
			
		||||
		goto out_err;
 | 
			
		||||
	}
 | 
			
		||||
	if (tmp != dentry) {
 | 
			
		||||
		dput(tmp);
 | 
			
		||||
		goto out_reconnected;
 | 
			
		||||
	}
 | 
			
		||||
	dput(tmp);
 | 
			
		||||
	if (IS_ROOT(dentry)) {
 | 
			
		||||
		err = -ESTALE;
 | 
			
		||||
		goto out_err;
 | 
			
		||||
	}
 | 
			
		||||
	return parent;
 | 
			
		||||
 | 
			
		||||
out_err:
 | 
			
		||||
	dput(parent);
 | 
			
		||||
	return ERR_PTR(err);
 | 
			
		||||
out_reconnected:
 | 
			
		||||
	dput(parent);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Someone must have renamed our entry into another parent, in
 | 
			
		||||
	 * which case it has been reconnected by the rename.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Or someone removed it entirely, in which case filehandle
 | 
			
		||||
	 * lookup will succeed but the directory is now IS_DEAD and
 | 
			
		||||
	 * subsequent operations on it will fail.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Alternatively, maybe there was no race at all, and the
 | 
			
		||||
	 * filesystem is just corrupt and gave us a parent that doesn't
 | 
			
		||||
	 * actually contain any entry pointing to this inode.  So,
 | 
			
		||||
	 * double check that this worked and return -ESTALE if not:
 | 
			
		||||
	 */
 | 
			
		||||
	if (!dentry_connected(dentry))
 | 
			
		||||
		return ERR_PTR(-ESTALE);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Make sure target_dir is fully connected to the dentry tree.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -158,76 +238,19 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
 | 
			
		|||
			dput(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		} else {
 | 
			
		||||
			struct dentry *parent;
 | 
			
		||||
			/*
 | 
			
		||||
			 * We have hit the top of a disconnected path, try to
 | 
			
		||||
			 * find parent and connect.
 | 
			
		||||
			 *
 | 
			
		||||
			 * Racing with some other process renaming a directory
 | 
			
		||||
			 * isn't much of a problem here.  If someone renames
 | 
			
		||||
			 * the directory, it will end up properly connected,
 | 
			
		||||
			 * which is what we want
 | 
			
		||||
			 *
 | 
			
		||||
			 * Getting the parent can't be supported generically,
 | 
			
		||||
			 * the locking is too icky.
 | 
			
		||||
			 *
 | 
			
		||||
			 * Instead we just return EACCES.  If server reboots
 | 
			
		||||
			 * or inodes get flushed, you lose
 | 
			
		||||
			 */
 | 
			
		||||
			struct dentry *ppd = ERR_PTR(-EACCES);
 | 
			
		||||
			struct dentry *npd;
 | 
			
		||||
 | 
			
		||||
			mutex_lock(&pd->d_inode->i_mutex);
 | 
			
		||||
			if (mnt->mnt_sb->s_export_op->get_parent)
 | 
			
		||||
				ppd = mnt->mnt_sb->s_export_op->get_parent(pd);
 | 
			
		||||
			mutex_unlock(&pd->d_inode->i_mutex);
 | 
			
		||||
 | 
			
		||||
			if (IS_ERR(ppd)) {
 | 
			
		||||
				err = PTR_ERR(ppd);
 | 
			
		||||
				dprintk("%s: get_parent of %ld failed, err %d\n",
 | 
			
		||||
					__func__, pd->d_inode->i_ino, err);
 | 
			
		||||
				dput(pd);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			dprintk("%s: find name of %lu in %lu\n", __func__,
 | 
			
		||||
				pd->d_inode->i_ino, ppd->d_inode->i_ino);
 | 
			
		||||
			err = exportfs_get_name(mnt, ppd, nbuf, pd);
 | 
			
		||||
			if (err) {
 | 
			
		||||
				dput(ppd);
 | 
			
		||||
				dput(pd);
 | 
			
		||||
				if (err == -ENOENT)
 | 
			
		||||
					/* some race between get_parent and
 | 
			
		||||
					 * get_name?
 | 
			
		||||
					 */
 | 
			
		||||
					goto out_reconnected;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			dprintk("%s: found name: %s\n", __func__, nbuf);
 | 
			
		||||
			mutex_lock(&ppd->d_inode->i_mutex);
 | 
			
		||||
			npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
 | 
			
		||||
			mutex_unlock(&ppd->d_inode->i_mutex);
 | 
			
		||||
			if (IS_ERR(npd)) {
 | 
			
		||||
				err = PTR_ERR(npd);
 | 
			
		||||
				dprintk("%s: lookup failed: %d\n",
 | 
			
		||||
					__func__, err);
 | 
			
		||||
				dput(ppd);
 | 
			
		||||
				dput(pd);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			/* we didn't really want npd, we really wanted
 | 
			
		||||
			 * a side-effect of the lookup.
 | 
			
		||||
			 * hopefully, npd == pd, though it isn't really
 | 
			
		||||
			 * a problem if it isn't
 | 
			
		||||
			 */
 | 
			
		||||
			dput(npd);
 | 
			
		||||
			dput(ppd);
 | 
			
		||||
			if (npd != pd)
 | 
			
		||||
			 parent = reconnect_one(mnt, pd, nbuf);
 | 
			
		||||
			 if (!parent)
 | 
			
		||||
				goto out_reconnected;
 | 
			
		||||
			if (IS_ROOT(pd)) {
 | 
			
		||||
				/* something went wrong, we have to give up */
 | 
			
		||||
				dput(pd);
 | 
			
		||||
			if (IS_ERR(parent)) {
 | 
			
		||||
				err = PTR_ERR(parent);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			dput(parent);
 | 
			
		||||
		}
 | 
			
		||||
		dput(pd);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -241,21 +264,6 @@ reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)
 | 
			
		|||
 | 
			
		||||
	return 0;
 | 
			
		||||
out_reconnected:
 | 
			
		||||
	/*
 | 
			
		||||
	 * Someone must have renamed our entry into another parent, in
 | 
			
		||||
	 * which case it has been reconnected by the rename.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Or someone removed it entirely, in which case filehandle
 | 
			
		||||
	 * lookup will succeed but the directory is now IS_DEAD and
 | 
			
		||||
	 * subsequent operations on it will fail.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Alternatively, maybe there was no race at all, and the
 | 
			
		||||
	 * filesystem is just corrupt and gave us a parent that doesn't
 | 
			
		||||
	 * actually contain any entry pointing to this inode.  So,
 | 
			
		||||
	 * double check that this worked and return -ESTALE if not:
 | 
			
		||||
	 */
 | 
			
		||||
	if (!dentry_connected(target_dir))
 | 
			
		||||
		return -ESTALE;
 | 
			
		||||
	clear_disconnected(target_dir);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue