Пример #1
0
/*
 * create a symlink in an AFS filesystem
 */
static int afs_symlink(struct inode *dir, struct dentry *dentry,
		       const char *content)
{
	struct afs_file_status status;
	struct afs_server *server;
	struct afs_vnode *dvnode, *vnode;
	struct afs_fid fid;
	struct inode *inode;
	struct key *key;
	int ret;

	dvnode = AFS_FS_I(dir);

	_enter("{%x:%u},{%pd},%s",
	       dvnode->fid.vid, dvnode->fid.vnode, dentry,
	       content);

	ret = -EINVAL;
	if (strlen(content) >= AFSPATHMAX)
		goto error;

	key = afs_request_key(dvnode->volume->cell);
	if (IS_ERR(key)) {
		ret = PTR_ERR(key);
		goto error;
	}

	ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content,
				&fid, &status, &server);
	if (ret < 0)
		goto create_error;

	inode = afs_iget(dir->i_sb, key, &fid, &status, NULL);
	if (IS_ERR(inode)) {
		/* ENOMEM at a really inconvenient time - just abandon the new
		 * directory on the server */
		ret = PTR_ERR(inode);
		goto iget_error;
	}

	/* apply the status report we've got for the new vnode */
	vnode = AFS_FS_I(inode);
	spin_lock(&vnode->lock);
	vnode->update_cnt++;
	spin_unlock(&vnode->lock);
	afs_vnode_finalise_status_update(vnode, server);
	afs_put_server(server);

	d_instantiate(dentry, inode);
	if (d_unhashed(dentry)) {
		_debug("not hashed");
		d_rehash(dentry);
	}
	key_put(key);
	_leave(" = 0");
	return 0;

iget_error:
	afs_put_server(server);
create_error:
	key_put(key);
error:
	d_drop(dentry);
	_leave(" = %d", ret);
	return ret;
}
Пример #2
0
/*
 * fetch file status from the volume
 * - don't issue a fetch if:
 *   - the changed bit is not set and there's a valid callback
 *   - there are any outstanding ops that will fetch the status
 * - TODO implement local caching
 */
int afs_vnode_fetch_status(afs_vnode_t *vnode)
{
	afs_server_t *server;
	int ret;

	DECLARE_WAITQUEUE(myself,current);

	_enter("%s,{%u,%u,%u}",vnode->volume->vlocation->vldb.name,
	       vnode->fid.vid,vnode->fid.vnode,vnode->fid.unique);

	if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
		_leave(" [unchanged]");
		return 0;
	}

	if (vnode->flags & AFS_VNODE_DELETED) {
		_leave(" [deleted]");
		return -ENOENT;
	}

	spin_lock(&vnode->lock);

	if (!(vnode->flags & AFS_VNODE_CHANGED)) {
		spin_unlock(&vnode->lock);
		_leave(" [unchanged]");
		return 0;
	}

	if (vnode->update_cnt>0) {
		/* someone else started a fetch */
		set_current_state(TASK_UNINTERRUPTIBLE);
		add_wait_queue(&vnode->update_waitq,&myself);

		/* wait for the status to be updated */
		for (;;) {
			if (!(vnode->flags & AFS_VNODE_CHANGED))	break;
			if (vnode->flags & AFS_VNODE_DELETED)		break;

			/* it got updated and invalidated all before we saw it */
			if (vnode->update_cnt==0) {
				remove_wait_queue(&vnode->update_waitq,&myself);
				set_current_state(TASK_RUNNING);
				goto get_anyway;
			}

			spin_unlock(&vnode->lock);

			schedule();
			set_current_state(TASK_UNINTERRUPTIBLE);

			spin_lock(&vnode->lock);
		}

		remove_wait_queue(&vnode->update_waitq,&myself);
		spin_unlock(&vnode->lock);
		set_current_state(TASK_RUNNING);

		return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0;
	}

 get_anyway:
	/* okay... we're going to have to initiate the op */
	vnode->update_cnt++;

	spin_unlock(&vnode->lock);

	/* merge AFS status fetches and clear outstanding callback on this vnode */
	do {
		/* pick a server to query */
		ret = afs_volume_pick_fileserver(vnode->volume,&server);
		if (ret<0)
			return ret;

		_debug("USING SERVER: %08x\n",ntohl(server->addr.s_addr));

		ret = afs_rxfs_fetch_file_status(server,vnode,NULL);

	} while (!afs_volume_release_fileserver(vnode->volume,server,ret));

	/* adjust the flags */
	afs_vnode_finalise_status_update(vnode,server,ret);

	_leave(" = %d",ret);
	return ret;
} /* end afs_vnode_fetch_status() */
Пример #3
0
/*
 * rename a file
 */
int afs_vnode_rename(struct afs_vnode *orig_dvnode,
		     struct afs_vnode *new_dvnode,
		     struct key *key,
		     const char *orig_name,
		     const char *new_name)
{
	struct afs_server *server;
	int ret;

	_enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s",
	       orig_dvnode->volume->vlocation->vldb.name,
	       orig_dvnode->fid.vid,
	       orig_dvnode->fid.vnode,
	       orig_dvnode->fid.unique,
	       new_dvnode->volume->vlocation->vldb.name,
	       new_dvnode->fid.vid,
	       new_dvnode->fid.vnode,
	       new_dvnode->fid.unique,
	       key_serial(key),
	       orig_name,
	       new_name);

	/* this op will fetch the status on both the directories we're dealing
	 * with */
	spin_lock(&orig_dvnode->lock);
	orig_dvnode->update_cnt++;
	spin_unlock(&orig_dvnode->lock);
	if (new_dvnode != orig_dvnode) {
		spin_lock(&new_dvnode->lock);
		new_dvnode->update_cnt++;
		spin_unlock(&new_dvnode->lock);
	}

	do {
		/* pick a server to query */
		server = afs_volume_pick_fileserver(orig_dvnode);
		if (IS_ERR(server))
			goto no_server;

		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));

		ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
				    new_dvnode, new_name, &afs_sync_call);

	} while (!afs_volume_release_fileserver(orig_dvnode, server, ret));

	/* adjust the flags */
	if (ret == 0) {
		afs_vnode_finalise_status_update(orig_dvnode, server);
		if (new_dvnode != orig_dvnode)
			afs_vnode_finalise_status_update(new_dvnode, server);
		afs_put_server(server);
	} else {
		afs_vnode_status_update_failed(orig_dvnode, ret);
		if (new_dvnode != orig_dvnode)
			afs_vnode_status_update_failed(new_dvnode, ret);
	}

	_leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
	return ret;

no_server:
	spin_lock(&orig_dvnode->lock);
	orig_dvnode->update_cnt--;
	ASSERTCMP(orig_dvnode->update_cnt, >=, 0);
	spin_unlock(&orig_dvnode->lock);
	if (new_dvnode != orig_dvnode) {
		spin_lock(&new_dvnode->lock);
		new_dvnode->update_cnt--;
		ASSERTCMP(new_dvnode->update_cnt, >=, 0);
		spin_unlock(&new_dvnode->lock);
	}
Пример #4
0
/*
 * create a hard link
 */
int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
			  struct key *key, const char *name)
{
	struct afs_server *server;
	int ret;

	_enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s",
	       dvnode->volume->vlocation->vldb.name,
	       dvnode->fid.vid,
	       dvnode->fid.vnode,
	       dvnode->fid.unique,
	       vnode->volume->vlocation->vldb.name,
	       vnode->fid.vid,
	       vnode->fid.vnode,
	       vnode->fid.unique,
	       key_serial(key),
	       name);

	/* this op will fetch the status on the directory we're removing from */
	spin_lock(&vnode->lock);
	vnode->update_cnt++;
	spin_unlock(&vnode->lock);
	spin_lock(&dvnode->lock);
	dvnode->update_cnt++;
	spin_unlock(&dvnode->lock);

	do {
		/* pick a server to query */
		server = afs_volume_pick_fileserver(dvnode);
		if (IS_ERR(server))
			goto no_server;

		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));

		ret = afs_fs_link(server, key, dvnode, vnode, name,
				  &afs_sync_call);

	} while (!afs_volume_release_fileserver(dvnode, server, ret));

	/* adjust the flags */
	if (ret == 0) {
		afs_vnode_finalise_status_update(vnode, server);
		afs_vnode_finalise_status_update(dvnode, server);
		afs_put_server(server);
	} else {
		afs_vnode_status_update_failed(vnode, ret);
		afs_vnode_status_update_failed(dvnode, ret);
	}

	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
	return ret;

no_server:
	spin_lock(&vnode->lock);
	vnode->update_cnt--;
	ASSERTCMP(vnode->update_cnt, >=, 0);
	spin_unlock(&vnode->lock);
	spin_lock(&dvnode->lock);
	dvnode->update_cnt--;
	ASSERTCMP(dvnode->update_cnt, >=, 0);
	spin_unlock(&dvnode->lock);
	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
	return PTR_ERR(server);
}
Пример #5
0
/*
 * fetch file status from the volume
 * - don't issue a fetch if:
 *   - the changed bit is not set and there's a valid callback
 *   - there are any outstanding ops that will fetch the status
 * - TODO implement local caching
 */
int afs_vnode_fetch_status(struct afs_vnode *vnode,
			   struct afs_vnode *auth_vnode, struct key *key)
{
	struct afs_server *server;
	unsigned long acl_order;
	int ret;

	DECLARE_WAITQUEUE(myself, current);

	_enter("%s,{%x:%u.%u}",
	       vnode->volume->vlocation->vldb.name,
	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);

	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
	    vnode->cb_promised) {
		_leave(" [unchanged]");
		return 0;
	}

	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
		_leave(" [deleted]");
		return -ENOENT;
	}

	acl_order = 0;
	if (auth_vnode)
		acl_order = auth_vnode->acl_order;

	spin_lock(&vnode->lock);

	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
	    vnode->cb_promised) {
		spin_unlock(&vnode->lock);
		_leave(" [unchanged]");
		return 0;
	}

	ASSERTCMP(vnode->update_cnt, >=, 0);

	if (vnode->update_cnt > 0) {
		/* someone else started a fetch */
		_debug("wait on fetch %d", vnode->update_cnt);

		set_current_state(TASK_UNINTERRUPTIBLE);
		ASSERT(myself.func != NULL);
		add_wait_queue(&vnode->update_waitq, &myself);

		/* wait for the status to be updated */
		for (;;) {
			if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
				break;
			if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
				break;

			/* check to see if it got updated and invalidated all
			 * before we saw it */
			if (vnode->update_cnt == 0) {
				remove_wait_queue(&vnode->update_waitq,
						  &myself);
				set_current_state(TASK_RUNNING);
				goto get_anyway;
			}

			spin_unlock(&vnode->lock);

			schedule();
			set_current_state(TASK_UNINTERRUPTIBLE);

			spin_lock(&vnode->lock);
		}

		remove_wait_queue(&vnode->update_waitq, &myself);
		spin_unlock(&vnode->lock);
		set_current_state(TASK_RUNNING);

		return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
			-ENOENT : 0;
	}

get_anyway:
	/* okay... we're going to have to initiate the op */
	vnode->update_cnt++;

	spin_unlock(&vnode->lock);

	/* merge AFS status fetches and clear outstanding callback on this
	 * vnode */
	do {
		/* pick a server to query */
		server = afs_volume_pick_fileserver(vnode);
		if (IS_ERR(server))
			goto no_server;

		_debug("USING SERVER: %p{%08x}",
		       server, ntohl(server->addr.s_addr));

		ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
					       &afs_sync_call);

	} while (!afs_volume_release_fileserver(vnode, server, ret));

	/* adjust the flags */
	if (ret == 0) {
		_debug("adjust");
		if (auth_vnode)
			afs_cache_permit(vnode, key, acl_order);
		afs_vnode_finalise_status_update(vnode, server);
		afs_put_server(server);
	} else {
		_debug("failed [%d]", ret);
		afs_vnode_status_update_failed(vnode, ret);
	}

	ASSERTCMP(vnode->update_cnt, >=, 0);

	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
	return ret;

no_server:
	spin_lock(&vnode->lock);
	vnode->update_cnt--;
	ASSERTCMP(vnode->update_cnt, >=, 0);
	spin_unlock(&vnode->lock);
	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
	return PTR_ERR(server);
}
Пример #6
0
static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
	struct afs_file_status status;
	struct afs_callback cb;
	struct afs_server *server;
	struct afs_vnode *dvnode, *vnode;
	struct afs_fid fid;
	struct inode *inode;
	struct key *key;
	int ret;

	dvnode = AFS_FS_I(dir);

	_enter("{%x:%u},{%s},%o",
	       dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode);

	ret = -ENAMETOOLONG;
	if (dentry->d_name.len >= AFSNAMEMAX)
		goto error;

	key = afs_request_key(dvnode->volume->cell);
	if (IS_ERR(key)) {
		ret = PTR_ERR(key);
		goto error;
	}

	mode |= S_IFDIR;
	ret = afs_vnode_create(dvnode, key, dentry->d_name.name,
			       mode, &fid, &status, &cb, &server);
	if (ret < 0)
		goto mkdir_error;

	inode = afs_iget(dir->i_sb, key, &fid, &status, &cb);
	if (IS_ERR(inode)) {
		/* ENOMEM at a really inconvenient time - just abandon the new
		 * directory on the server */
		ret = PTR_ERR(inode);
		goto iget_error;
	}

	/* apply the status report we've got for the new vnode */
	vnode = AFS_FS_I(inode);
	spin_lock(&vnode->lock);
	vnode->update_cnt++;
	spin_unlock(&vnode->lock);
	afs_vnode_finalise_status_update(vnode, server);
	afs_put_server(server);

	d_instantiate(dentry, inode);
	if (d_unhashed(dentry)) {
		_debug("not hashed");
		d_rehash(dentry);
	}
	key_put(key);
	_leave(" = 0");
	return 0;

iget_error:
	afs_put_server(server);
mkdir_error:
	key_put(key);
error:
	d_drop(dentry);
	_leave(" = %d", ret);
	return ret;
}