Add support for whiteouts on tmpfs.
Right now unionfs only allows filesystems to be mounted on top of another if it supports whiteouts. Even though I have sent a patch to daichi@ to let unionfs work without it, we'd better also add support for whiteouts to tmpfs. This patch implements .vop_whiteout and makes necessary changes to lookup() and readdir() to take them into account. We must also make sure that when adding or removing a file, we honour the componentname's DOWHITEOUT and ISWHITEOUT, to prevent duplicate filenames. MFC after: 1 month
This commit is contained in:
parent
8024e98228
commit
99d57a6bd8
@ -72,7 +72,8 @@ struct tmpfs_dirent {
|
||||
* td_namelen field must always be used when accessing its value. */
|
||||
char * td_name;
|
||||
|
||||
/* Pointer to the node this entry refers to. */
|
||||
/* Pointer to the node this entry refers to. In case this field
|
||||
* is NULL, the node is a whiteout. */
|
||||
struct tmpfs_node * td_node;
|
||||
};
|
||||
|
||||
@ -434,6 +435,8 @@ int tmpfs_dir_getdotdent(struct tmpfs_node *, struct uio *);
|
||||
int tmpfs_dir_getdotdotdent(struct tmpfs_node *, struct uio *);
|
||||
struct tmpfs_dirent * tmpfs_dir_lookupbycookie(struct tmpfs_node *, off_t);
|
||||
int tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, off_t *);
|
||||
int tmpfs_dir_whiteout_add(struct vnode *, struct componentname *);
|
||||
void tmpfs_dir_whiteout_remove(struct vnode *, struct componentname *);
|
||||
int tmpfs_reg_resize(struct vnode *, off_t);
|
||||
int tmpfs_chflags(struct vnode *, int, struct ucred *, struct thread *);
|
||||
int tmpfs_chmod(struct vnode *, mode_t, struct ucred *, struct thread *);
|
||||
|
@ -261,7 +261,8 @@ tmpfs_alloc_dirent(struct tmpfs_mount *tmp, struct tmpfs_node *node,
|
||||
memcpy(nde->td_name, name, len);
|
||||
|
||||
nde->td_node = node;
|
||||
node->tn_links++;
|
||||
if (node != NULL)
|
||||
node->tn_links++;
|
||||
|
||||
*de = nde;
|
||||
|
||||
@ -287,9 +288,10 @@ tmpfs_free_dirent(struct tmpfs_mount *tmp, struct tmpfs_dirent *de,
|
||||
struct tmpfs_node *node;
|
||||
|
||||
node = de->td_node;
|
||||
|
||||
MPASS(node->tn_links > 0);
|
||||
node->tn_links--;
|
||||
if (node != NULL) {
|
||||
MPASS(node->tn_links > 0);
|
||||
node->tn_links--;
|
||||
}
|
||||
}
|
||||
|
||||
free(de->td_name, M_TMPFSNAME);
|
||||
@ -518,6 +520,8 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
|
||||
/* Now that all required items are allocated, we can proceed to
|
||||
* insert the new node into the directory, an operation that
|
||||
* cannot fail. */
|
||||
if (cnp->cn_flags & ISWHITEOUT)
|
||||
tmpfs_dir_whiteout_remove(dvp, cnp);
|
||||
tmpfs_dir_attach(dvp, de);
|
||||
|
||||
out:
|
||||
@ -768,39 +772,44 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
|
||||
|
||||
/* Create a dirent structure representing the current
|
||||
* tmpfs_node and fill it. */
|
||||
d.d_fileno = de->td_node->tn_id;
|
||||
switch (de->td_node->tn_type) {
|
||||
case VBLK:
|
||||
d.d_type = DT_BLK;
|
||||
break;
|
||||
if (de->td_node == NULL) {
|
||||
d.d_fileno = 1;
|
||||
d.d_type = DT_WHT;
|
||||
} else {
|
||||
d.d_fileno = de->td_node->tn_id;
|
||||
switch (de->td_node->tn_type) {
|
||||
case VBLK:
|
||||
d.d_type = DT_BLK;
|
||||
break;
|
||||
|
||||
case VCHR:
|
||||
d.d_type = DT_CHR;
|
||||
break;
|
||||
case VCHR:
|
||||
d.d_type = DT_CHR;
|
||||
break;
|
||||
|
||||
case VDIR:
|
||||
d.d_type = DT_DIR;
|
||||
break;
|
||||
case VDIR:
|
||||
d.d_type = DT_DIR;
|
||||
break;
|
||||
|
||||
case VFIFO:
|
||||
d.d_type = DT_FIFO;
|
||||
break;
|
||||
case VFIFO:
|
||||
d.d_type = DT_FIFO;
|
||||
break;
|
||||
|
||||
case VLNK:
|
||||
d.d_type = DT_LNK;
|
||||
break;
|
||||
case VLNK:
|
||||
d.d_type = DT_LNK;
|
||||
break;
|
||||
|
||||
case VREG:
|
||||
d.d_type = DT_REG;
|
||||
break;
|
||||
case VREG:
|
||||
d.d_type = DT_REG;
|
||||
break;
|
||||
|
||||
case VSOCK:
|
||||
d.d_type = DT_SOCK;
|
||||
break;
|
||||
case VSOCK:
|
||||
d.d_type = DT_SOCK;
|
||||
break;
|
||||
|
||||
default:
|
||||
panic("tmpfs_dir_getdents: type %p %d",
|
||||
de->td_node, (int)de->td_node->tn_type);
|
||||
default:
|
||||
panic("tmpfs_dir_getdents: type %p %d",
|
||||
de->td_node, (int)de->td_node->tn_type);
|
||||
}
|
||||
}
|
||||
d.d_namlen = de->td_namelen;
|
||||
MPASS(de->td_namelen < sizeof(d.d_name));
|
||||
@ -837,6 +846,31 @@ tmpfs_dir_getdents(struct tmpfs_node *node, struct uio *uio, off_t *cntp)
|
||||
return error;
|
||||
}
|
||||
|
||||
int
|
||||
tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
|
||||
{
|
||||
struct tmpfs_dirent *de;
|
||||
int error;
|
||||
|
||||
error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
|
||||
cnp->cn_nameptr, cnp->cn_namelen, &de);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
tmpfs_dir_attach(dvp, de);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
|
||||
{
|
||||
struct tmpfs_dirent *de;
|
||||
|
||||
de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
|
||||
MPASS(de != NULL && de->td_node == NULL);
|
||||
tmpfs_dir_detach(dvp, de);
|
||||
tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de, TRUE);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
|
@ -109,14 +109,19 @@ tmpfs_lookup(struct vop_cachedlookup_args *v)
|
||||
error = 0;
|
||||
} else {
|
||||
de = tmpfs_dir_lookup(dnode, NULL, cnp);
|
||||
if (de == NULL) {
|
||||
if (de != NULL && de->td_node == NULL)
|
||||
cnp->cn_flags |= ISWHITEOUT;
|
||||
if (de == NULL || de->td_node == NULL) {
|
||||
/* The entry was not found in the directory.
|
||||
* This is OK if we are creating or renaming an
|
||||
* entry and are working on the last component of
|
||||
* the path name. */
|
||||
if ((cnp->cn_flags & ISLASTCN) &&
|
||||
(cnp->cn_nameiop == CREATE || \
|
||||
cnp->cn_nameiop == RENAME)) {
|
||||
cnp->cn_nameiop == RENAME ||
|
||||
(cnp->cn_nameiop == DELETE &&
|
||||
cnp->cn_flags & DOWHITEOUT &&
|
||||
cnp->cn_flags & ISWHITEOUT))) {
|
||||
error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
|
||||
cnp->cn_thread);
|
||||
if (error != 0)
|
||||
@ -834,6 +839,8 @@ tmpfs_remove(struct vop_remove_args *v)
|
||||
/* Remove the entry from the directory; as it is a file, we do not
|
||||
* have to change the number of hard links of the directory. */
|
||||
tmpfs_dir_detach(dvp, de);
|
||||
if (v->a_cnp->cn_flags & DOWHITEOUT)
|
||||
tmpfs_dir_whiteout_add(dvp, v->a_cnp);
|
||||
|
||||
/* Free the directory entry we just deleted. Note that the node
|
||||
* referred by it will not be removed until the vnode is really
|
||||
@ -904,6 +911,8 @@ tmpfs_link(struct vop_link_args *v)
|
||||
goto out;
|
||||
|
||||
/* Insert the new directory entry into the appropriate directory. */
|
||||
if (cnp->cn_flags & ISWHITEOUT)
|
||||
tmpfs_dir_whiteout_remove(dvp, cnp);
|
||||
tmpfs_dir_attach(dvp, de);
|
||||
|
||||
/* vp link count has changed, so update node times. */
|
||||
@ -1099,6 +1108,10 @@ tmpfs_rename(struct vop_rename_args *v)
|
||||
/* Do the move: just remove the entry from the source directory
|
||||
* and insert it into the target one. */
|
||||
tmpfs_dir_detach(fdvp, de);
|
||||
if (fcnp->cn_flags & DOWHITEOUT)
|
||||
tmpfs_dir_whiteout_add(fdvp, fcnp);
|
||||
if (tcnp->cn_flags & ISWHITEOUT)
|
||||
tmpfs_dir_whiteout_remove(tdvp, tcnp);
|
||||
tmpfs_dir_attach(tdvp, de);
|
||||
}
|
||||
|
||||
@ -1223,6 +1236,8 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
|
||||
|
||||
/* Detach the directory entry from the directory (dnode). */
|
||||
tmpfs_dir_detach(dvp, de);
|
||||
if (v->a_cnp->cn_flags & DOWHITEOUT)
|
||||
tmpfs_dir_whiteout_add(dvp, v->a_cnp);
|
||||
|
||||
/* No vnode should be allocated for this entry from this point */
|
||||
TMPFS_NODE_LOCK(node);
|
||||
@ -1541,6 +1556,29 @@ tmpfs_vptofh(struct vop_vptofh_args *ap)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
tmpfs_whiteout(struct vop_whiteout_args *ap)
|
||||
{
|
||||
struct vnode *dvp = ap->a_dvp;
|
||||
struct componentname *cnp = ap->a_cnp;
|
||||
struct tmpfs_dirent *de;
|
||||
|
||||
switch (ap->a_flags) {
|
||||
case LOOKUP:
|
||||
return (0);
|
||||
case CREATE:
|
||||
de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
|
||||
if (de != NULL)
|
||||
return (de->td_node == NULL ? 0 : EEXIST);
|
||||
return (tmpfs_dir_whiteout_add(dvp, cnp));
|
||||
case DELETE:
|
||||
tmpfs_dir_whiteout_remove(dvp, cnp);
|
||||
return (0);
|
||||
default:
|
||||
panic("tmpfs_whiteout: unknown op");
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
@ -1573,6 +1611,7 @@ struct vop_vector tmpfs_vnodeop_entries = {
|
||||
.vop_print = tmpfs_print,
|
||||
.vop_pathconf = tmpfs_pathconf,
|
||||
.vop_vptofh = tmpfs_vptofh,
|
||||
.vop_whiteout = tmpfs_whiteout,
|
||||
.vop_bmap = VOP_EOPNOTSUPP,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user