/*
* Copyright (c) 2013 Reinoud Zandijk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Comments and trivial code from the reference implementation in tmpfs.
*/
/*
* udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is
* its parent.
*
* vp and dvp must be locked and referenced.
*/
static bool
udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
struct vnode *vp, struct vnode *dvp)
{
struct udf_node *udf_node = VTOI(vp);
int error, isempty;
/* create new directory entry for the node */
error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp);
if (error)
return error;
/* unlink old directory entry for the node, if failing, unattach new */
error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
if (error)
goto rollback_attach;
rollback:
/* 'try' to recover from this situation */
udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
rollback_attach:
udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
return error;
}
/*
* udf_gro_remove: rename an object over another link to itself, effectively
* removing just the original link.
*/
static int
udf_gro_remove(struct mount *mp, kauth_cred_t cred,
struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
nlink_t *tvp_nlinkp)
{
struct udf_node *dir_node, *udf_node;
/* lookup filename in the directory; location icb_loc */
name = cnp->cn_nameptr;
namelen = cnp->cn_namelen;
error = udf_lookup_name_in_dir(dvp, name, namelen,
&icb_loc, &found);
if (error)
return error;
if (!found)
return ENOENT;
DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name));
error = udf_get_node(dir_node->ump, &icb_loc, &res_node, LK_EXCLUSIVE);
if (error)
return error;
*vp_ret = res_node->vnode;
VOP_UNLOCK(res_node->vnode);
return 0;
}
/*
* udf_rmdired_p: check whether the directory vp has been rmdired.
*
* vp must be locked and referenced.
*/
static bool
udf_rmdired_p(struct vnode *vp)
{
DPRINTF(CALL, ("udf_rmdired_p called\n"));
/*
* We need to provisionally lock tdvp to keep rmdir from deleting it
* -- or any ancestor -- at an inopportune moment.
*
* XXX WHY is this not in genfs's rename? XXX
*/
error = udf_gro_lock_directory(mp, tdvp);
if (error)
return error;
name = "..";
namelen = 2;
error = 0;
ump = VTOI(tdvp)->ump;
/* if nodes are equal, it is no use looking */
KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0);
/* start at destination vnode and walk up the tree */
vp = tdvp;
vref(vp);
/* go down one level */
error = udf_lookup_name_in_dir(vp, name, namelen,
&parent_loc, &found);
DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, "
"found %d\n", error, found));
if (!found)
error = ENOENT;
if (error) {
vput(vp);
return error;
}
/* did we encounter the root node? i.e. loop back */
if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) {
DPRINTF(NODE, ("ROOT found!\n"));
vput(vp);
*intermediate_node_ret = NULL;
return 0;
}
/* Did we find that fdvp is an ancestor of tdvp? */
if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) {
DPRINTF(NODE, ("fdvp is ancestor of tdvp\n"));
*intermediate_node_ret = vp;
VOP_UNLOCK(vp);
return 0;
}
/*
* Unlock vp so that we can lock the parent, but keep child vp
* referenced until after we have found the parent, so that
* parent_node will not be recycled.
*/
DPRINTF(NODE, ("\tgetting the parent node\n"));
VOP_UNLOCK(vp);
error = udf_get_node(ump, &parent_loc, &parent_node,
LK_EXCLUSIVE);
vrele(vp);
if (error)
return error;
/* sanity check */
if (vp->v_type != VDIR) {
/*
* Odd, but can happen if we lose the race and the
* '..' node has been recycled.
*/
vput(vp);
return ENOTDIR;
}
if (udf_rmdired_p(vp)) {
vput(vp);
return ENOENT;
}
}
}
/*
* udf_gro_lock_directory: lock the directory vp, but fail if it has been
* rmdir'd.
*/
static int
udf_gro_lock_directory(struct mount *mp, struct vnode *vp)
{