* Copyright (c) 1994 Jan-Simon Pendry
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
* @(#)union_subr.c 1.7 (Berkeley) %G%
#include <sys/filedesc.h>
#include "union.h" /*<miscfs/union/union.h>*/
static struct union_node
*unhead
;
* allocate a union_node/vnode pair. the vnode is
* referenced and locked. the new vnode is returned
* via (vpp). (mp) is the mountpoint of the union filesystem,
* (dvp) is the parent directory where the upper layer object
* should exist (but doesn't) and (cnp) is the componentname
* information which is partially copied to allow the upper
* layer object to be created at a later time. (uppervp)
* and (lowervp) reference the upper and lower layer objects
* being mapped. either, but not both, can be nil.
* the reference is either maintained in the new union_node
* object which is allocated, or they are vrele'd.
* all union_nodes are maintained on a singly-linked
* list. new nodes are only allocated when they cannot
* be found on this list. entries on the list are
* removed when the vfs reclaim entry is called.
* a single lock is kept for the entire list. this is
* needed because the getnewvnode() function can block
* waiting for a vnode to become free, in which case there
* may be more than one process trying to get the same
* vnode. this lock is only taken if we are going to
* call getnewvnode, since the kernel itself is single-threaded.
* if an entry is found on the list, then call vget() to
* take a reference. this is done because there may be
* zero references to it and so it needs to removed from
union_allocvp(vpp
, mp
, undvp
, dvp
, cnp
, uppervp
, lowervp
)
struct vnode
*dvp
; /* may be null */
struct componentname
*cnp
; /* may be null */
struct vnode
*uppervp
; /* may be null */
struct vnode
*lowervp
; /* may be null */
struct vnode
*xlowervp
= 0;
if (uppervp
== 0 && lowervp
== 0)
panic("union: unidentifiable allocation");
if (uppervp
&& lowervp
&& (uppervp
->v_type
!= lowervp
->v_type
)) {
for (un
= unhead
; un
!= 0; un
= un
->un_next
) {
if ((un
->un_lowervp
== lowervp
||
(un
->un_uppervp
== uppervp
||
(UNIONTOV(un
)->v_mount
== mp
)) {
if (vget(UNIONTOV(un
), 0))
if (UNIONTOV(un
) != undvp
)
* Save information about the upper layer.
if (uppervp
!= un
->un_uppervp
) {
un
->un_uppervp
= uppervp
;
* Save information about the lower layer.
* This needs to keep track of pathname
* and directory information which union_vn_create
if (lowervp
!= un
->un_lowervp
) {
free(un
->un_path
, M_TEMP
);
un
->un_lowervp
= lowervp
;
if (cnp
&& (lowervp
!= NULLVP
) &&
(lowervp
->v_type
== VREG
)) {
un
->un_hash
= cnp
->cn_hash
;
un
->un_path
= malloc(cnp
->cn_namelen
+1,
bcopy(cnp
->cn_nameptr
, un
->un_path
,
un
->un_path
[cnp
->cn_namelen
] = '\0';
* otherwise lock the vp list while we call getnewvnode
if (unvplock
& UN_LOCKED
) {
sleep((caddr_t
) &unvplock
, PINOD
);
error
= getnewvnode(VT_UNION
, mp
, union_vnodeop_p
, vpp
);
MALLOC((*vpp
)->v_data
, void *, sizeof(struct union_node
),
(*vpp
)->v_type
= uppervp
->v_type
;
(*vpp
)->v_type
= lowervp
->v_type
;
un
->un_uppervp
= uppervp
;
un
->un_lowervp
= lowervp
;
if (cnp
&& (lowervp
!= NULLVP
) && (lowervp
->v_type
== VREG
)) {
un
->un_hash
= cnp
->cn_hash
;
un
->un_path
= malloc(cnp
->cn_namelen
+1, M_TEMP
, M_WAITOK
);
bcopy(cnp
->cn_nameptr
, un
->un_path
, cnp
->cn_namelen
);
un
->un_path
[cnp
->cn_namelen
] = '\0';
/* add to union vnode list */
for (pp
= &unhead
; *pp
; pp
= &(*pp
)->un_next
)
un
->un_flags
|= UN_LOCKED
;
un
->un_pid
= curproc
->p_pid
;
if (unvplock
& UN_WANT
) {
wakeup((caddr_t
) &unvplock
);
struct union_node
**unpp
;
struct union_node
*un
= VTOUNION(vp
);
for (unpp
= &unhead
; *unpp
!= 0; unpp
= &(*unpp
)->un_next
) {
FREE(vp
->v_data
, M_TEMP
);
* copyfile. copy the vnode (fvp) to the vnode (tvp)
* using a sequence of reads and writes. both (fvp)
* and (tvp) are locked on entry and exit.
union_copyfile(p
, cred
, fvp
, tvp
)
* allocate a buffer of size MAXBSIZE.
* loop doing reads and writes, keeping track
* of the current uio offset.
* give up at the first sign of trouble.
uio
.uio_segflg
= UIO_SYSSPACE
;
VOP_UNLOCK(fvp
); /* XXX */
LEASE_CHECK(fvp
, p
, cred
, LEASE_READ
);
VOP_UNLOCK(tvp
); /* XXX */
LEASE_CHECK(tvp
, p
, cred
, LEASE_WRITE
);
buf
= malloc(MAXBSIZE
, M_TEMP
, M_WAITOK
);
/* ugly loop follows... */
off_t offset
= uio
.uio_offset
;
uio
.uio_resid
= iov
.iov_len
;
error
= VOP_READ(fvp
, &uio
, 0, cred
);
iov
.iov_len
= MAXBSIZE
- uio
.uio_resid
;
uio
.uio_resid
= iov
.iov_len
;
error
= VOP_WRITE(tvp
, &uio
, 0, cred
);
} while ((uio
.uio_resid
> 0) && (error
== 0));
* Create a shadow directory in the upper layer.
* The new vnode is returned locked.
* (um) points to the union mount structure for access to the
* the mounting process's credentials.
* (dvp) is the directory in which to create the shadow directory.
* it is unlocked on entry and exit.
* (cnp) is the componentname to be created.
* (vpp) is the returned newly created shadow directory, which
union_mkshadow(um
, dvp
, cnp
, vpp
)
struct componentname
*cnp
;
struct proc
*p
= cnp
->cn_proc
;
* policy: when creating the shadow directory in the
* upper layer, create it owned by the current user,
* group from parent directory, and mode 777 modified
* by umask (ie mostly identical to the mkdir syscall).
* TODO: create the directory owned by the user who
* did the mount (um->um_cred).
* A new componentname structure must be faked up because
* there is no way to know where the upper level cnp came
* from or what it is being used for. This must duplicate
* some of the work done by NDINIT, some of the work done
* by namei, some of the work done by lookup and some of
* the work done by VOP_LOOKUP when given a CREATE flag.
* The pathname buffer will be FREEed by VOP_MKDIR.
cn
.cn_pnbuf
= malloc(cnp
->cn_namelen
+1, M_NAMEI
, M_WAITOK
);
bcopy(cnp
->cn_nameptr
, cn
.cn_pnbuf
, cnp
->cn_namelen
);
cn
.cn_pnbuf
[cnp
->cn_namelen
] = '\0';
cn
.cn_flags
= (LOCKPARENT
|HASBUF
|SAVENAME
|ISLASTCN
);
cn
.cn_proc
= cnp
->cn_proc
;
cn
.cn_cred
= cnp
->cn_cred
;
cn
.cn_nameptr
= cn
.cn_pnbuf
;
cn
.cn_namelen
= cnp
->cn_namelen
;
cn
.cn_hash
= cnp
->cn_hash
;
cn
.cn_consume
= cnp
->cn_consume
;
if (error
= relookup(dvp
, vpp
, &cn
))
va
.va_mode
= UN_DIRMODE
& ~p
->p_fd
->fd_cmask
;
/* LEASE_CHECK: dvp is locked */
LEASE_CHECK(dvp
, p
, p
->p_ucred
, LEASE_WRITE
);
error
= VOP_MKDIR(dvp
, vpp
, &cn
, &va
);
* union_vn_create: creates and opens a new shadow file
* on the upper union layer. this function is similar
* in spirit to calling vn_open but it avoids calling namei().
* the problem with calling namei is that a) it locks too many
* things, and b) it doesn't start at the "right" directory,
* whereas relookup is told where to start.
union_vn_create(vpp
, un
, p
)
struct ucred
*cred
= p
->p_ucred
;
struct vattr
*vap
= &vat
;
int fmode
= FFLAGS(O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
);
int cmode
= UN_FILEMODE
& ~p
->p_fd
->fd_cmask
;
cn
.cn_namelen
= strlen(un
->un_path
);
cn
.cn_pnbuf
= (caddr_t
) malloc(cn
.cn_namelen
, M_NAMEI
, M_WAITOK
);
bcopy(un
->un_path
, cn
.cn_pnbuf
, cn
.cn_namelen
+1);
cn
.cn_flags
= (LOCKLEAF
|LOCKPARENT
|HASBUF
|SAVENAME
|ISLASTCN
);
cn
.cn_nameptr
= cn
.cn_pnbuf
;
cn
.cn_hash
= un
->un_hash
;
if (error
= relookup(un
->un_dirvp
, &vp
, &cn
))
LEASE_CHECK(un
->un_dirvp
, p
, cred
, LEASE_WRITE
);
if (error
= VOP_CREATE(un
->un_dirvp
, &vp
,
VOP_ABORTOP(un
->un_dirvp
, &cn
);
if (vp
->v_type
!= VREG
) {
VOP_UNLOCK(vp
); /* XXX */
LEASE_CHECK(vp
, p
, cred
, LEASE_WRITE
);
if (error
= VOP_SETATTR(vp
, vap
, cred
, p
))
if (error
= VOP_OPEN(vp
, fmode
, cred
, p
))
union_vn_close(vp
, fmode
)
return (VOP_CLOSE(vp
, fmode
));