165934Spendry /*
265934Spendry * Copyright (c) 1994 Jan-Simon Pendry
365934Spendry * Copyright (c) 1994
465934Spendry * The Regents of the University of California. All rights reserved.
565934Spendry *
665934Spendry * This code is derived from software contributed to Berkeley by
765934Spendry * Jan-Simon Pendry.
865934Spendry *
965934Spendry * %sccs.include.redist.c%
1065934Spendry *
11*69585Spendry * @(#)union_subr.c 8.20 (Berkeley) 05/20/95
1265934Spendry */
1365934Spendry
1465934Spendry #include <sys/param.h>
1565934Spendry #include <sys/systm.h>
1665934Spendry #include <sys/time.h>
1765934Spendry #include <sys/kernel.h>
1865934Spendry #include <sys/vnode.h>
1965934Spendry #include <sys/namei.h>
2065934Spendry #include <sys/malloc.h>
2165994Spendry #include <sys/file.h>
2265997Spendry #include <sys/filedesc.h>
2366053Spendry #include <sys/queue.h>
2467065Spendry #include <sys/mount.h>
2567784Spendry #include <sys/stat.h>
2667107Spendry #include <vm/vm.h> /* for vnode_pager_setsize */
2766055Spendry #include <miscfs/union/union.h>
2865934Spendry
2965992Spendry #ifdef DIAGNOSTIC
3065992Spendry #include <sys/proc.h>
3165992Spendry #endif
3265992Spendry
3366053Spendry /* must be power of two, otherwise change UNION_HASH() */
3466053Spendry #define NHASH 32
3565934Spendry
3666053Spendry /* unsigned int ... */
3766053Spendry #define UNION_HASH(u, l) \
3866053Spendry (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
3966053Spendry
LIST_HEAD(unhead,union_node)4066053Spendry static LIST_HEAD(unhead, union_node) unhead[NHASH];
4166053Spendry static int unvplock[NHASH];
4266053Spendry
4365934Spendry int
4465934Spendry union_init()
4565934Spendry {
4666053Spendry int i;
4765934Spendry
4866053Spendry for (i = 0; i < NHASH; i++)
4966053Spendry LIST_INIT(&unhead[i]);
5066053Spendry bzero((caddr_t) unvplock, sizeof(unvplock));
5165934Spendry }
5265934Spendry
5366053Spendry static int
union_list_lock(ix)5466053Spendry union_list_lock(ix)
5566053Spendry int ix;
5666053Spendry {
5766053Spendry
5866053Spendry if (unvplock[ix] & UN_LOCKED) {
5966053Spendry unvplock[ix] |= UN_WANT;
6066053Spendry sleep((caddr_t) &unvplock[ix], PINOD);
6166053Spendry return (1);
6266053Spendry }
6366053Spendry
6466053Spendry unvplock[ix] |= UN_LOCKED;
6566053Spendry
6666053Spendry return (0);
6766053Spendry }
6866053Spendry
6966051Spendry static void
union_list_unlock(ix)7066053Spendry union_list_unlock(ix)
7166053Spendry int ix;
7266053Spendry {
7366053Spendry
7466053Spendry unvplock[ix] &= ~UN_LOCKED;
7566053Spendry
7666053Spendry if (unvplock[ix] & UN_WANT) {
7766053Spendry unvplock[ix] &= ~UN_WANT;
7866053Spendry wakeup((caddr_t) &unvplock[ix]);
7966053Spendry }
8066053Spendry }
8166053Spendry
8266053Spendry void
union_updatevp(un,uppervp,lowervp)8366053Spendry union_updatevp(un, uppervp, lowervp)
8466051Spendry struct union_node *un;
8566053Spendry struct vnode *uppervp;
8666053Spendry struct vnode *lowervp;
8766051Spendry {
8866053Spendry int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
8966053Spendry int nhash = UNION_HASH(uppervp, lowervp);
9067072Spendry int docache = (lowervp != NULLVP || uppervp != NULLVP);
9169390Spendry int lhash, hhash, uhash;
9266051Spendry
9367072Spendry /*
9467072Spendry * Ensure locking is ordered from lower to higher
9567072Spendry * to avoid deadlocks.
9667072Spendry */
9767072Spendry if (nhash < ohash) {
9869390Spendry lhash = nhash;
9969390Spendry uhash = ohash;
10069390Spendry } else {
10169390Spendry lhash = ohash;
10269390Spendry uhash = nhash;
10367072Spendry }
10466053Spendry
10569390Spendry if (lhash != uhash)
10669390Spendry while (union_list_lock(lhash))
10766053Spendry continue;
10866053Spendry
10969390Spendry while (union_list_lock(uhash))
11067072Spendry continue;
11166053Spendry
11267072Spendry if (ohash != nhash || !docache) {
11367072Spendry if (un->un_flags & UN_CACHED) {
11467401Spendry un->un_flags &= ~UN_CACHED;
11567072Spendry LIST_REMOVE(un, un_cache);
11667072Spendry }
11766051Spendry }
11866053Spendry
11967072Spendry if (ohash != nhash)
12067072Spendry union_list_unlock(ohash);
12167072Spendry
12266053Spendry if (un->un_lowervp != lowervp) {
12366053Spendry if (un->un_lowervp) {
12466053Spendry vrele(un->un_lowervp);
12566053Spendry if (un->un_path) {
12666053Spendry free(un->un_path, M_TEMP);
12766053Spendry un->un_path = 0;
12866053Spendry }
12966053Spendry if (un->un_dirvp) {
13066053Spendry vrele(un->un_dirvp);
13166053Spendry un->un_dirvp = NULLVP;
13266053Spendry }
13366053Spendry }
13466053Spendry un->un_lowervp = lowervp;
13567107Spendry un->un_lowersz = VNOVAL;
13666053Spendry }
13766053Spendry
13866053Spendry if (un->un_uppervp != uppervp) {
13966053Spendry if (un->un_uppervp)
14066053Spendry vrele(un->un_uppervp);
14166053Spendry
14266053Spendry un->un_uppervp = uppervp;
14367107Spendry un->un_uppersz = VNOVAL;
14466053Spendry }
14566053Spendry
14667072Spendry if (docache && (ohash != nhash)) {
14766053Spendry LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
14867072Spendry un->un_flags |= UN_CACHED;
14967072Spendry }
15066053Spendry
15166053Spendry union_list_unlock(nhash);
15266051Spendry }
15366051Spendry
15466053Spendry void
union_newlower(un,lowervp)15566053Spendry union_newlower(un, lowervp)
15666053Spendry struct union_node *un;
15766053Spendry struct vnode *lowervp;
15866053Spendry {
15966053Spendry
16066053Spendry union_updatevp(un, un->un_uppervp, lowervp);
16166053Spendry }
16266053Spendry
16366053Spendry void
union_newupper(un,uppervp)16466053Spendry union_newupper(un, uppervp)
16566053Spendry struct union_node *un;
16666053Spendry struct vnode *uppervp;
16766053Spendry {
16866053Spendry
16966053Spendry union_updatevp(un, uppervp, un->un_lowervp);
17066053Spendry }
17166053Spendry
17265934Spendry /*
17367107Spendry * Keep track of size changes in the underlying vnodes.
17467107Spendry * If the size changes, then callback to the vm layer
17567107Spendry * giving priority to the upper layer size.
17667107Spendry */
17767107Spendry void
union_newsize(vp,uppersz,lowersz)17867107Spendry union_newsize(vp, uppersz, lowersz)
17967107Spendry struct vnode *vp;
18067107Spendry off_t uppersz, lowersz;
18167107Spendry {
18267107Spendry struct union_node *un;
18367107Spendry off_t sz;
18467107Spendry
18567107Spendry /* only interested in regular files */
18667107Spendry if (vp->v_type != VREG)
18767107Spendry return;
18867107Spendry
18967107Spendry un = VTOUNION(vp);
19067107Spendry sz = VNOVAL;
19167107Spendry
19267107Spendry if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
19367107Spendry un->un_uppersz = uppersz;
19467107Spendry if (sz == VNOVAL)
19567107Spendry sz = un->un_uppersz;
19667107Spendry }
19767107Spendry
19867107Spendry if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
19967107Spendry un->un_lowersz = lowersz;
20067107Spendry if (sz == VNOVAL)
20167107Spendry sz = un->un_lowersz;
20267107Spendry }
20367107Spendry
20467107Spendry if (sz != VNOVAL) {
20567107Spendry #ifdef UNION_DIAGNOSTIC
20667107Spendry printf("union: %s size now %ld\n",
20767107Spendry uppersz != VNOVAL ? "upper" : "lower", (long) sz);
20867107Spendry #endif
20967107Spendry vnode_pager_setsize(vp, sz);
21067107Spendry }
21167107Spendry }
21267107Spendry
21367107Spendry /*
21465934Spendry * allocate a union_node/vnode pair. the vnode is
21565965Spendry * referenced and locked. the new vnode is returned
21665965Spendry * via (vpp). (mp) is the mountpoint of the union filesystem,
21765965Spendry * (dvp) is the parent directory where the upper layer object
21865965Spendry * should exist (but doesn't) and (cnp) is the componentname
21965965Spendry * information which is partially copied to allow the upper
22065965Spendry * layer object to be created at a later time. (uppervp)
22165965Spendry * and (lowervp) reference the upper and lower layer objects
22265965Spendry * being mapped. either, but not both, can be nil.
22366051Spendry * if supplied, (uppervp) is locked.
22465997Spendry * the reference is either maintained in the new union_node
22565997Spendry * object which is allocated, or they are vrele'd.
22665934Spendry *
22765934Spendry * all union_nodes are maintained on a singly-linked
22865934Spendry * list. new nodes are only allocated when they cannot
22965934Spendry * be found on this list. entries on the list are
23065934Spendry * removed when the vfs reclaim entry is called.
23165934Spendry *
23265934Spendry * a single lock is kept for the entire list. this is
23365934Spendry * needed because the getnewvnode() function can block
23465934Spendry * waiting for a vnode to become free, in which case there
23565934Spendry * may be more than one process trying to get the same
23665934Spendry * vnode. this lock is only taken if we are going to
23765934Spendry * call getnewvnode, since the kernel itself is single-threaded.
23865934Spendry *
23965934Spendry * if an entry is found on the list, then call vget() to
24065934Spendry * take a reference. this is done because there may be
24165934Spendry * zero references to it and so it needs to removed from
24265934Spendry * the vnode free list.
24365934Spendry */
24465934Spendry int
union_allocvp(vpp,mp,undvp,dvp,cnp,uppervp,lowervp,docache)24568078Spendry union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache)
24665934Spendry struct vnode **vpp;
24765934Spendry struct mount *mp;
24867415Spendry struct vnode *undvp; /* parent union vnode */
24965934Spendry struct vnode *dvp; /* may be null */
25065934Spendry struct componentname *cnp; /* may be null */
25165934Spendry struct vnode *uppervp; /* may be null */
25265934Spendry struct vnode *lowervp; /* may be null */
25368078Spendry int docache;
25465934Spendry {
25565934Spendry int error;
25665934Spendry struct union_node *un;
25765934Spendry struct union_node **pp;
25866053Spendry struct vnode *xlowervp = NULLVP;
25967065Spendry struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
26066053Spendry int hash;
26167065Spendry int vflag;
26266053Spendry int try;
26365934Spendry
26466053Spendry if (uppervp == NULLVP && lowervp == NULLVP)
26565965Spendry panic("union: unidentifiable allocation");
26665965Spendry
26765965Spendry if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
26865965Spendry xlowervp = lowervp;
26966053Spendry lowervp = NULLVP;
27065965Spendry }
27165965Spendry
27267065Spendry /* detect the root vnode (and aliases) */
27367065Spendry vflag = 0;
27467065Spendry if ((uppervp == um->um_uppervp) &&
27567065Spendry ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
27667065Spendry if (lowervp == NULLVP) {
27767065Spendry lowervp = um->um_lowervp;
27867167Spendry if (lowervp != NULLVP)
27967167Spendry VREF(lowervp);
28067065Spendry }
28167065Spendry vflag = VROOT;
28267065Spendry }
28367065Spendry
28465934Spendry loop:
28568078Spendry if (!docache) {
28668078Spendry un = 0;
28768078Spendry } else for (try = 0; try < 3; try++) {
28866053Spendry switch (try) {
28966053Spendry case 0:
29066053Spendry if (lowervp == NULLVP)
29166053Spendry continue;
29266053Spendry hash = UNION_HASH(uppervp, lowervp);
29366051Spendry break;
29466053Spendry
29566053Spendry case 1:
29666053Spendry if (uppervp == NULLVP)
29766053Spendry continue;
29866053Spendry hash = UNION_HASH(uppervp, NULLVP);
29966053Spendry break;
30066053Spendry
30166053Spendry case 2:
30266053Spendry if (lowervp == NULLVP)
30366053Spendry continue;
30466053Spendry hash = UNION_HASH(NULLVP, lowervp);
30566053Spendry break;
30666051Spendry }
30766053Spendry
30866053Spendry while (union_list_lock(hash))
30966053Spendry continue;
31066053Spendry
31166053Spendry for (un = unhead[hash].lh_first; un != 0;
31266053Spendry un = un->un_cache.le_next) {
31366053Spendry if ((un->un_lowervp == lowervp ||
31466053Spendry un->un_lowervp == NULLVP) &&
31566053Spendry (un->un_uppervp == uppervp ||
31666053Spendry un->un_uppervp == NULLVP) &&
31766053Spendry (UNIONTOV(un)->v_mount == mp)) {
31869447Smckusick if (vget(UNIONTOV(un), 0,
31969447Smckusick cnp ? cnp->cn_proc : NULL)) {
32066053Spendry union_list_unlock(hash);
32166053Spendry goto loop;
32266053Spendry }
32366053Spendry break;
32466053Spendry }
32566053Spendry }
32666053Spendry
32766053Spendry union_list_unlock(hash);
32866053Spendry
32966053Spendry if (un)
33066053Spendry break;
33166051Spendry }
33266027Spendry
33366051Spendry if (un) {
33466051Spendry /*
33566051Spendry * Obtain a lock on the union_node.
33666051Spendry * uppervp is locked, though un->un_uppervp
33766051Spendry * may not be. this doesn't break the locking
33866051Spendry * hierarchy since in the case that un->un_uppervp
33966051Spendry * is not yet locked it will be vrele'd and replaced
34066051Spendry * with uppervp.
34166051Spendry */
34266051Spendry
34366051Spendry if ((dvp != NULLVP) && (uppervp == dvp)) {
34466027Spendry /*
34566051Spendry * Access ``.'', so (un) will already
34666051Spendry * be locked. Since this process has
34766051Spendry * the lock on (uppervp) no other
34866051Spendry * process can hold the lock on (un).
34966027Spendry */
35066051Spendry #ifdef DIAGNOSTIC
35166051Spendry if ((un->un_flags & UN_LOCKED) == 0)
35266051Spendry panic("union: . not locked");
35366051Spendry else if (curproc && un->un_pid != curproc->p_pid &&
35466051Spendry un->un_pid > -1 && curproc->p_pid > -1)
35566051Spendry panic("union: allocvp not lock owner");
35666051Spendry #endif
35766051Spendry } else {
35866051Spendry if (un->un_flags & UN_LOCKED) {
35966051Spendry vrele(UNIONTOV(un));
36066051Spendry un->un_flags |= UN_WANT;
36166051Spendry sleep((caddr_t) &un->un_flags, PINOD);
36266051Spendry goto loop;
36365992Spendry }
36466051Spendry un->un_flags |= UN_LOCKED;
36566027Spendry
36666051Spendry #ifdef DIAGNOSTIC
36766051Spendry if (curproc)
36866051Spendry un->un_pid = curproc->p_pid;
36966051Spendry else
37066051Spendry un->un_pid = -1;
37166051Spendry #endif
37266051Spendry }
37366051Spendry
37466051Spendry /*
37566051Spendry * At this point, the union_node is locked,
37666051Spendry * un->un_uppervp may not be locked, and uppervp
37766051Spendry * is locked or nil.
37866051Spendry */
37966051Spendry
38066051Spendry /*
38166051Spendry * Save information about the upper layer.
38266051Spendry */
38366051Spendry if (uppervp != un->un_uppervp) {
38466053Spendry union_newupper(un, uppervp);
38566051Spendry } else if (uppervp) {
38666051Spendry vrele(uppervp);
38766051Spendry }
38866051Spendry
38966051Spendry if (un->un_uppervp) {
39066051Spendry un->un_flags |= UN_ULOCK;
39166051Spendry un->un_flags &= ~UN_KLOCK;
39266051Spendry }
39366051Spendry
39466051Spendry /*
39566051Spendry * Save information about the lower layer.
39666051Spendry * This needs to keep track of pathname
39766051Spendry * and directory information which union_vn_create
39866051Spendry * might need.
39966051Spendry */
40066051Spendry if (lowervp != un->un_lowervp) {
40166053Spendry union_newlower(un, lowervp);
40267575Spendry if (cnp && (lowervp != NULLVP)) {
40366051Spendry un->un_hash = cnp->cn_hash;
40466051Spendry un->un_path = malloc(cnp->cn_namelen+1,
40566051Spendry M_TEMP, M_WAITOK);
40666051Spendry bcopy(cnp->cn_nameptr, un->un_path,
40766051Spendry cnp->cn_namelen);
40866051Spendry un->un_path[cnp->cn_namelen] = '\0';
40966051Spendry VREF(dvp);
41066051Spendry un->un_dirvp = dvp;
41166051Spendry }
41266051Spendry } else if (lowervp) {
41366051Spendry vrele(lowervp);
41465934Spendry }
41566051Spendry *vpp = UNIONTOV(un);
41666051Spendry return (0);
41765934Spendry }
41865934Spendry
41968078Spendry if (docache) {
42068078Spendry /*
42168078Spendry * otherwise lock the vp list while we call getnewvnode
42268078Spendry * since that can block.
42368078Spendry */
42468078Spendry hash = UNION_HASH(uppervp, lowervp);
42566053Spendry
42668078Spendry if (union_list_lock(hash))
42768078Spendry goto loop;
42868078Spendry }
42965934Spendry
43065934Spendry error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
43166051Spendry if (error) {
43266051Spendry if (uppervp) {
43366051Spendry if (dvp == uppervp)
43466051Spendry vrele(uppervp);
43566051Spendry else
43666051Spendry vput(uppervp);
43766051Spendry }
43866051Spendry if (lowervp)
43966051Spendry vrele(lowervp);
44066051Spendry
44165934Spendry goto out;
44266051Spendry }
44365934Spendry
44465934Spendry MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
44565934Spendry M_TEMP, M_WAITOK);
44665934Spendry
44767065Spendry (*vpp)->v_flag |= vflag;
44865965Spendry if (uppervp)
44965965Spendry (*vpp)->v_type = uppervp->v_type;
45065965Spendry else
45165965Spendry (*vpp)->v_type = lowervp->v_type;
45265934Spendry un = VTOUNION(*vpp);
45365992Spendry un->un_vnode = *vpp;
45465934Spendry un->un_uppervp = uppervp;
45567107Spendry un->un_uppersz = VNOVAL;
45665934Spendry un->un_lowervp = lowervp;
45767107Spendry un->un_lowersz = VNOVAL;
45867415Spendry un->un_pvp = undvp;
45967415Spendry if (undvp != NULLVP)
46067415Spendry VREF(undvp);
46168078Spendry un->un_dircache = 0;
46266028Spendry un->un_openl = 0;
46366051Spendry un->un_flags = UN_LOCKED;
46466051Spendry if (un->un_uppervp)
46566051Spendry un->un_flags |= UN_ULOCK;
46666051Spendry #ifdef DIAGNOSTIC
46766051Spendry if (curproc)
46866051Spendry un->un_pid = curproc->p_pid;
46966051Spendry else
47066051Spendry un->un_pid = -1;
47166051Spendry #endif
47267575Spendry if (cnp && (lowervp != NULLVP)) {
47366027Spendry un->un_hash = cnp->cn_hash;
47465934Spendry un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
47565934Spendry bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
47665934Spendry un->un_path[cnp->cn_namelen] = '\0';
47765965Spendry VREF(dvp);
47865965Spendry un->un_dirvp = dvp;
47965934Spendry } else {
48066027Spendry un->un_hash = 0;
48165934Spendry un->un_path = 0;
48265965Spendry un->un_dirvp = 0;
48365934Spendry }
48465934Spendry
48568078Spendry if (docache) {
48668078Spendry LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
48768078Spendry un->un_flags |= UN_CACHED;
48868078Spendry }
48965934Spendry
49065965Spendry if (xlowervp)
49165965Spendry vrele(xlowervp);
49265965Spendry
49365934Spendry out:
49468078Spendry if (docache)
49568078Spendry union_list_unlock(hash);
49665934Spendry
49765934Spendry return (error);
49865934Spendry }
49965934Spendry
50065934Spendry int
union_freevp(vp)50165934Spendry union_freevp(vp)
50265934Spendry struct vnode *vp;
50365934Spendry {
50465934Spendry struct union_node *un = VTOUNION(vp);
50565934Spendry
50667072Spendry if (un->un_flags & UN_CACHED) {
50767401Spendry un->un_flags &= ~UN_CACHED;
50867072Spendry LIST_REMOVE(un, un_cache);
50967072Spendry }
51065934Spendry
51167415Spendry if (un->un_pvp != NULLVP)
51267415Spendry vrele(un->un_pvp);
51367072Spendry if (un->un_uppervp != NULLVP)
51466053Spendry vrele(un->un_uppervp);
51567072Spendry if (un->un_lowervp != NULLVP)
51666053Spendry vrele(un->un_lowervp);
51767072Spendry if (un->un_dirvp != NULLVP)
51866053Spendry vrele(un->un_dirvp);
51966053Spendry if (un->un_path)
52066053Spendry free(un->un_path, M_TEMP);
52166053Spendry
52265934Spendry FREE(vp->v_data, M_TEMP);
52365934Spendry vp->v_data = 0;
52466053Spendry
52565934Spendry return (0);
52665934Spendry }
52765994Spendry
52865994Spendry /*
52965994Spendry * copyfile. copy the vnode (fvp) to the vnode (tvp)
53065994Spendry * using a sequence of reads and writes. both (fvp)
53165994Spendry * and (tvp) are locked on entry and exit.
53265994Spendry */
53365994Spendry int
union_copyfile(fvp,tvp,cred,p)53467166Spendry union_copyfile(fvp, tvp, cred, p)
53565994Spendry struct vnode *fvp;
53665994Spendry struct vnode *tvp;
53767166Spendry struct ucred *cred;
53867166Spendry struct proc *p;
53965994Spendry {
54065994Spendry char *buf;
54165994Spendry struct uio uio;
54265994Spendry struct iovec iov;
54365994Spendry int error = 0;
54465994Spendry
54565994Spendry /*
54665994Spendry * strategy:
54765994Spendry * allocate a buffer of size MAXBSIZE.
54865994Spendry * loop doing reads and writes, keeping track
54965994Spendry * of the current uio offset.
55065994Spendry * give up at the first sign of trouble.
55165994Spendry */
55265994Spendry
55365994Spendry uio.uio_procp = p;
55465994Spendry uio.uio_segflg = UIO_SYSSPACE;
55565994Spendry uio.uio_offset = 0;
55665994Spendry
55769447Smckusick VOP_UNLOCK(fvp, 0, p); /* XXX */
55867750Spendry VOP_LEASE(fvp, p, cred, LEASE_READ);
55969447Smckusick vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */
56069447Smckusick VOP_UNLOCK(tvp, 0, p); /* XXX */
56167750Spendry VOP_LEASE(tvp, p, cred, LEASE_WRITE);
56269447Smckusick vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */
56365994Spendry
56465994Spendry buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
56565994Spendry
56665994Spendry /* ugly loop follows... */
56765994Spendry do {
56865994Spendry off_t offset = uio.uio_offset;
56965994Spendry
57065994Spendry uio.uio_iov = &iov;
57165994Spendry uio.uio_iovcnt = 1;
57265994Spendry iov.iov_base = buf;
57365994Spendry iov.iov_len = MAXBSIZE;
57465994Spendry uio.uio_resid = iov.iov_len;
57565994Spendry uio.uio_rw = UIO_READ;
57665994Spendry error = VOP_READ(fvp, &uio, 0, cred);
57765994Spendry
57865994Spendry if (error == 0) {
57965994Spendry uio.uio_iov = &iov;
58065994Spendry uio.uio_iovcnt = 1;
58165994Spendry iov.iov_base = buf;
58265994Spendry iov.iov_len = MAXBSIZE - uio.uio_resid;
58365994Spendry uio.uio_offset = offset;
58465994Spendry uio.uio_rw = UIO_WRITE;
58565994Spendry uio.uio_resid = iov.iov_len;
58665994Spendry
58765994Spendry if (uio.uio_resid == 0)
58865994Spendry break;
58965994Spendry
59065994Spendry do {
59165994Spendry error = VOP_WRITE(tvp, &uio, 0, cred);
59265994Spendry } while ((uio.uio_resid > 0) && (error == 0));
59365994Spendry }
59465994Spendry
59565994Spendry } while (error == 0);
59665994Spendry
59765994Spendry free(buf, M_TEMP);
59865994Spendry return (error);
59965994Spendry }
60065994Spendry
60165994Spendry /*
60267166Spendry * (un) is assumed to be locked on entry and remains
60367166Spendry * locked on exit.
60467166Spendry */
60567166Spendry int
union_copyup(un,docopy,cred,p)60667166Spendry union_copyup(un, docopy, cred, p)
60767166Spendry struct union_node *un;
60867166Spendry int docopy;
60967166Spendry struct ucred *cred;
61067166Spendry struct proc *p;
61167166Spendry {
61267166Spendry int error;
61367166Spendry struct vnode *lvp, *uvp;
61467166Spendry
61567166Spendry error = union_vn_create(&uvp, un, p);
61667166Spendry if (error)
61767166Spendry return (error);
61867166Spendry
61967166Spendry /* at this point, uppervp is locked */
62067166Spendry union_newupper(un, uvp);
62167166Spendry un->un_flags |= UN_ULOCK;
62267166Spendry
62367166Spendry lvp = un->un_lowervp;
62467166Spendry
62567166Spendry if (docopy) {
62667166Spendry /*
62767166Spendry * XX - should not ignore errors
62867166Spendry * from VOP_CLOSE
62967166Spendry */
63069447Smckusick vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, p);
63167166Spendry error = VOP_OPEN(lvp, FREAD, cred, p);
63267166Spendry if (error == 0) {
63367166Spendry error = union_copyfile(lvp, uvp, cred, p);
63469447Smckusick VOP_UNLOCK(lvp, 0, p);
63568514Smckusick (void) VOP_CLOSE(lvp, FREAD, cred, p);
63667166Spendry }
63767166Spendry #ifdef UNION_DIAGNOSTIC
63867166Spendry if (error == 0)
63967166Spendry uprintf("union: copied up %s\n", un->un_path);
64067166Spendry #endif
64167166Spendry
64267166Spendry }
64367166Spendry un->un_flags &= ~UN_ULOCK;
64469447Smckusick VOP_UNLOCK(uvp, 0, p);
64567166Spendry union_vn_close(uvp, FWRITE, cred, p);
64669447Smckusick vn_lock(uvp, LK_EXCLUSIVE | LK_RETRY, p);
64767166Spendry un->un_flags |= UN_ULOCK;
64867166Spendry
64967166Spendry /*
65067166Spendry * Subsequent IOs will go to the top layer, so
65167166Spendry * call close on the lower vnode and open on the
65267166Spendry * upper vnode to ensure that the filesystem keeps
65367166Spendry * its references counts right. This doesn't do
65467166Spendry * the right thing with (cred) and (FREAD) though.
65567166Spendry * Ignoring error returns is not right, either.
65667166Spendry */
65767166Spendry if (error == 0) {
65867166Spendry int i;
65967166Spendry
66067166Spendry for (i = 0; i < un->un_openl; i++) {
66168514Smckusick (void) VOP_CLOSE(lvp, FREAD, cred, p);
66267166Spendry (void) VOP_OPEN(uvp, FREAD, cred, p);
66367166Spendry }
66467166Spendry un->un_openl = 0;
66567166Spendry }
66667166Spendry
66767166Spendry return (error);
66867166Spendry
66967166Spendry }
67067166Spendry
67167575Spendry static int
union_relookup(um,dvp,vpp,cnp,cn,path,pathlen)67267750Spendry union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
67367575Spendry struct union_mount *um;
67467575Spendry struct vnode *dvp;
67567575Spendry struct vnode **vpp;
67667575Spendry struct componentname *cnp;
67767575Spendry struct componentname *cn;
67867575Spendry char *path;
67967750Spendry int pathlen;
68067575Spendry {
68167575Spendry int error;
68267575Spendry
68367575Spendry /*
68467575Spendry * A new componentname structure must be faked up because
68567575Spendry * there is no way to know where the upper level cnp came
68667575Spendry * from or what it is being used for. This must duplicate
68767575Spendry * some of the work done by NDINIT, some of the work done
68867575Spendry * by namei, some of the work done by lookup and some of
68967575Spendry * the work done by VOP_LOOKUP when given a CREATE flag.
69067575Spendry * Conclusion: Horrible.
69167575Spendry *
69267575Spendry * The pathname buffer will be FREEed by VOP_MKDIR.
69367575Spendry */
69467750Spendry cn->cn_namelen = pathlen;
69567575Spendry cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
69667575Spendry bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
69767575Spendry cn->cn_pnbuf[cn->cn_namelen] = '\0';
69867575Spendry
69967575Spendry cn->cn_nameiop = CREATE;
70067575Spendry cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
70167575Spendry cn->cn_proc = cnp->cn_proc;
70267575Spendry if (um->um_op == UNMNT_ABOVE)
70367575Spendry cn->cn_cred = cnp->cn_cred;
70467575Spendry else
70567575Spendry cn->cn_cred = um->um_cred;
70667575Spendry cn->cn_nameptr = cn->cn_pnbuf;
70767575Spendry cn->cn_hash = cnp->cn_hash;
70867575Spendry cn->cn_consume = cnp->cn_consume;
70967575Spendry
71067575Spendry VREF(dvp);
71167575Spendry error = relookup(dvp, vpp, cn);
71267575Spendry if (!error)
71367575Spendry vrele(dvp);
71467575Spendry
71567575Spendry return (error);
71667575Spendry }
71767575Spendry
71867166Spendry /*
71965997Spendry * Create a shadow directory in the upper layer.
72065997Spendry * The new vnode is returned locked.
72165997Spendry *
72265997Spendry * (um) points to the union mount structure for access to the
72365997Spendry * the mounting process's credentials.
72465997Spendry * (dvp) is the directory in which to create the shadow directory.
72565997Spendry * it is unlocked on entry and exit.
72665997Spendry * (cnp) is the componentname to be created.
72765997Spendry * (vpp) is the returned newly created shadow directory, which
72865997Spendry * is returned locked.
72965997Spendry */
73065997Spendry int
union_mkshadow(um,dvp,cnp,vpp)73165997Spendry union_mkshadow(um, dvp, cnp, vpp)
73265997Spendry struct union_mount *um;
73365997Spendry struct vnode *dvp;
73465997Spendry struct componentname *cnp;
73565997Spendry struct vnode **vpp;
73665997Spendry {
73765997Spendry int error;
73865997Spendry struct vattr va;
73965997Spendry struct proc *p = cnp->cn_proc;
74065997Spendry struct componentname cn;
74165997Spendry
74267750Spendry error = union_relookup(um, dvp, vpp, cnp, &cn,
74367750Spendry cnp->cn_nameptr, cnp->cn_namelen);
74467575Spendry if (error)
74567575Spendry return (error);
74667575Spendry
74767575Spendry if (*vpp) {
74867575Spendry VOP_ABORTOP(dvp, &cn);
74969447Smckusick VOP_UNLOCK(dvp, 0, p);
75067575Spendry vrele(*vpp);
75167575Spendry *vpp = NULLVP;
75267575Spendry return (EEXIST);
75367575Spendry }
75467575Spendry
75565997Spendry /*
75665997Spendry * policy: when creating the shadow directory in the
75766034Spendry * upper layer, create it owned by the user who did
75866034Spendry * the mount, group from parent directory, and mode
75966034Spendry * 777 modified by umask (ie mostly identical to the
76066034Spendry * mkdir syscall). (jsp, kb)
76165997Spendry */
76265997Spendry
76367575Spendry VATTR_NULL(&va);
76467575Spendry va.va_type = VDIR;
76567575Spendry va.va_mode = um->um_cmode;
76665997Spendry
76767750Spendry /* VOP_LEASE: dvp is locked */
76867750Spendry VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE);
76965997Spendry
77067575Spendry error = VOP_MKDIR(dvp, vpp, &cn, &va);
77167575Spendry return (error);
77267575Spendry }
77367575Spendry
77467575Spendry /*
77567575Spendry * Create a whiteout entry in the upper layer.
77667575Spendry *
77767575Spendry * (um) points to the union mount structure for access to the
77867575Spendry * the mounting process's credentials.
77967575Spendry * (dvp) is the directory in which to create the whiteout.
78067575Spendry * it is locked on entry and exit.
78167575Spendry * (cnp) is the componentname to be created.
78267575Spendry */
78367575Spendry int
union_mkwhiteout(um,dvp,cnp,path)78467575Spendry union_mkwhiteout(um, dvp, cnp, path)
78567575Spendry struct union_mount *um;
78667575Spendry struct vnode *dvp;
78767575Spendry struct componentname *cnp;
78867575Spendry char *path;
78967575Spendry {
79067575Spendry int error;
79167575Spendry struct vattr va;
79267575Spendry struct proc *p = cnp->cn_proc;
79368022Spendry struct vnode *wvp;
79467575Spendry struct componentname cn;
79567575Spendry
79669447Smckusick VOP_UNLOCK(dvp, 0, p);
79768022Spendry error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
79867750Spendry if (error) {
79969447Smckusick vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
80065997Spendry return (error);
80167750Spendry }
80265997Spendry
80368022Spendry if (wvp) {
80465997Spendry VOP_ABORTOP(dvp, &cn);
80567575Spendry vrele(dvp);
80668022Spendry vrele(wvp);
80765997Spendry return (EEXIST);
80865997Spendry }
80965997Spendry
81067750Spendry /* VOP_LEASE: dvp is locked */
81167750Spendry VOP_LEASE(dvp, p, p->p_ucred, LEASE_WRITE);
81265997Spendry
81367575Spendry error = VOP_WHITEOUT(dvp, &cn, CREATE);
81467750Spendry if (error)
81567575Spendry VOP_ABORTOP(dvp, &cn);
81667575Spendry
81767575Spendry vrele(dvp);
81867575Spendry
81965997Spendry return (error);
82065997Spendry }
82165997Spendry
82265997Spendry /*
82365994Spendry * union_vn_create: creates and opens a new shadow file
82465994Spendry * on the upper union layer. this function is similar
82565994Spendry * in spirit to calling vn_open but it avoids calling namei().
82665994Spendry * the problem with calling namei is that a) it locks too many
82765994Spendry * things, and b) it doesn't start at the "right" directory,
82865994Spendry * whereas relookup is told where to start.
82965994Spendry */
83065994Spendry int
union_vn_create(vpp,un,p)83165997Spendry union_vn_create(vpp, un, p)
83265994Spendry struct vnode **vpp;
83365994Spendry struct union_node *un;
83465994Spendry struct proc *p;
83565994Spendry {
83665994Spendry struct vnode *vp;
83765994Spendry struct ucred *cred = p->p_ucred;
83865994Spendry struct vattr vat;
83965994Spendry struct vattr *vap = &vat;
84065994Spendry int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
84165994Spendry int error;
84266027Spendry int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
84365994Spendry char *cp;
84465994Spendry struct componentname cn;
84565994Spendry
84665994Spendry *vpp = NULLVP;
84765994Spendry
84866034Spendry /*
84966034Spendry * Build a new componentname structure (for the same
85066034Spendry * reasons outlines in union_mkshadow).
85166034Spendry * The difference here is that the file is owned by
85266034Spendry * the current user, rather than by the person who
85366034Spendry * did the mount, since the current user needs to be
85466034Spendry * able to write the file (that's why it is being
85566034Spendry * copied in the first place).
85666034Spendry */
85765994Spendry cn.cn_namelen = strlen(un->un_path);
85865994Spendry cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
85965994Spendry bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
86065994Spendry cn.cn_nameiop = CREATE;
86166060Spendry cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
86265994Spendry cn.cn_proc = p;
86365994Spendry cn.cn_cred = p->p_ucred;
86465994Spendry cn.cn_nameptr = cn.cn_pnbuf;
86566027Spendry cn.cn_hash = un->un_hash;
86665994Spendry cn.cn_consume = 0;
86765994Spendry
86866027Spendry VREF(un->un_dirvp);
86965994Spendry if (error = relookup(un->un_dirvp, &vp, &cn))
87065994Spendry return (error);
87166027Spendry vrele(un->un_dirvp);
87266027Spendry
87366060Spendry if (vp) {
87465994Spendry VOP_ABORTOP(un->un_dirvp, &cn);
87565994Spendry if (un->un_dirvp == vp)
87665994Spendry vrele(un->un_dirvp);
87765994Spendry else
87866060Spendry vput(un->un_dirvp);
87966060Spendry vrele(vp);
88066060Spendry return (EEXIST);
88165994Spendry }
88265994Spendry
88366060Spendry /*
88466060Spendry * Good - there was no race to create the file
88566060Spendry * so go ahead and create it. The permissions
88666060Spendry * on the file will be 0666 modified by the
88766060Spendry * current user's umask. Access to the file, while
88866060Spendry * it is unioned, will require access to the top *and*
88966060Spendry * bottom files. Access when not unioned will simply
89066060Spendry * require access to the top-level file.
89166060Spendry * TODO: confirm choice of access permissions.
89266060Spendry */
89365994Spendry VATTR_NULL(vap);
89466060Spendry vap->va_type = VREG;
89566060Spendry vap->va_mode = cmode;
89667750Spendry VOP_LEASE(un->un_dirvp, p, cred, LEASE_WRITE);
89766060Spendry if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap))
89866060Spendry return (error);
89965994Spendry
90066060Spendry if (error = VOP_OPEN(vp, fmode, cred, p)) {
90166060Spendry vput(vp);
90266060Spendry return (error);
90366060Spendry }
90465994Spendry
90565994Spendry vp->v_writecount++;
90665994Spendry *vpp = vp;
90765994Spendry return (0);
90865994Spendry }
90966027Spendry
91066027Spendry int
union_vn_close(vp,fmode,cred,p)91166028Spendry union_vn_close(vp, fmode, cred, p)
91266027Spendry struct vnode *vp;
91366027Spendry int fmode;
91466028Spendry struct ucred *cred;
91566028Spendry struct proc *p;
91666027Spendry {
91767575Spendry
91866027Spendry if (fmode & FWRITE)
91966027Spendry --vp->v_writecount;
92068514Smckusick return (VOP_CLOSE(vp, fmode, cred, p));
92166027Spendry }
92266027Spendry
92366027Spendry void
union_removed_upper(un)92466027Spendry union_removed_upper(un)
92566027Spendry struct union_node *un;
92666027Spendry {
92769447Smckusick struct proc *p = curproc; /* XXX */
92867401Spendry
92969390Spendry union_newupper(un, NULLVP);
93069390Spendry if (un->un_flags & UN_CACHED) {
93169390Spendry un->un_flags &= ~UN_CACHED;
93269390Spendry LIST_REMOVE(un, un_cache);
93369390Spendry }
93469390Spendry
93566051Spendry if (un->un_flags & UN_ULOCK) {
93666051Spendry un->un_flags &= ~UN_ULOCK;
93769447Smckusick VOP_UNLOCK(un->un_uppervp, 0, p);
93866051Spendry }
93966027Spendry }
94066028Spendry
94168078Spendry #if 0
94266028Spendry struct vnode *
94366028Spendry union_lowervp(vp)
94466028Spendry struct vnode *vp;
94566028Spendry {
94666028Spendry struct union_node *un = VTOUNION(vp);
94766028Spendry
94867166Spendry if ((un->un_lowervp != NULLVP) &&
94967166Spendry (vp->v_type == un->un_lowervp->v_type)) {
95067166Spendry if (vget(un->un_lowervp, 0) == 0)
95167166Spendry return (un->un_lowervp);
95266028Spendry }
95366028Spendry
95467166Spendry return (NULLVP);
95566028Spendry }
95668078Spendry #endif
95767784Spendry
95867784Spendry /*
95967784Spendry * determine whether a whiteout is needed
96067784Spendry * during a remove/rmdir operation.
96167784Spendry */
96267784Spendry int
union_dowhiteout(un,cred,p)96367784Spendry union_dowhiteout(un, cred, p)
96467784Spendry struct union_node *un;
96567784Spendry struct ucred *cred;
96667784Spendry struct proc *p;
96767784Spendry {
96867784Spendry struct vattr va;
96967784Spendry
97067784Spendry if (un->un_lowervp != NULLVP)
97167784Spendry return (1);
97267784Spendry
97367784Spendry if (VOP_GETATTR(un->un_uppervp, &va, cred, p) == 0 &&
97467784Spendry (va.va_flags & OPAQUE))
97567784Spendry return (1);
97667784Spendry
97767784Spendry return (0);
97867784Spendry }
97968078Spendry
98068078Spendry static void
union_dircache_r(vp,vppp,cntp)98168078Spendry union_dircache_r(vp, vppp, cntp)
98268078Spendry struct vnode *vp;
98368078Spendry struct vnode ***vppp;
98468078Spendry int *cntp;
98568078Spendry {
98668078Spendry struct union_node *un;
98768078Spendry
98868078Spendry if (vp->v_op != union_vnodeop_p) {
98968078Spendry if (vppp) {
99068078Spendry VREF(vp);
99168078Spendry *(*vppp)++ = vp;
99268078Spendry if (--(*cntp) == 0)
99368078Spendry panic("union: dircache table too small");
99468078Spendry } else {
99568078Spendry (*cntp)++;
99668078Spendry }
99768078Spendry
99868078Spendry return;
99968078Spendry }
100068078Spendry
100168078Spendry un = VTOUNION(vp);
100268078Spendry if (un->un_uppervp != NULLVP)
100368078Spendry union_dircache_r(un->un_uppervp, vppp, cntp);
100468078Spendry if (un->un_lowervp != NULLVP)
100568078Spendry union_dircache_r(un->un_lowervp, vppp, cntp);
100668078Spendry }
100768078Spendry
100868078Spendry struct vnode *
union_dircache(vp,p)100969447Smckusick union_dircache(vp, p)
101068078Spendry struct vnode *vp;
101169447Smckusick struct proc *p;
101268078Spendry {
101368078Spendry int cnt;
101468078Spendry struct vnode *nvp;
101568078Spendry struct vnode **vpp;
1016*69585Spendry struct vnode **dircache;
101768078Spendry struct union_node *un;
101868078Spendry int error;
101968078Spendry
1020*69585Spendry vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1021*69585Spendry dircache = VTOUNION(vp)->un_dircache;
1022*69585Spendry
1023*69585Spendry nvp = NULLVP;
1024*69585Spendry
102568078Spendry if (dircache == 0) {
102668078Spendry cnt = 0;
102768078Spendry union_dircache_r(vp, 0, &cnt);
102868078Spendry cnt++;
102968078Spendry dircache = (struct vnode **)
103068078Spendry malloc(cnt * sizeof(struct vnode *),
103168078Spendry M_TEMP, M_WAITOK);
103268078Spendry vpp = dircache;
103368078Spendry union_dircache_r(vp, &vpp, &cnt);
103468078Spendry *vpp = NULLVP;
103568078Spendry vpp = dircache + 1;
103668078Spendry } else {
103768078Spendry vpp = dircache;
103868078Spendry do {
103968078Spendry if (*vpp++ == VTOUNION(vp)->un_uppervp)
104068078Spendry break;
104168078Spendry } while (*vpp != NULLVP);
104268078Spendry }
104368078Spendry
104468078Spendry if (*vpp == NULLVP)
1045*69585Spendry goto out;
104668078Spendry
104769447Smckusick vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, p);
104868078Spendry VREF(*vpp);
104968078Spendry error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
105068078Spendry if (error)
1051*69585Spendry goto out;
1052*69585Spendry
105368078Spendry VTOUNION(vp)->un_dircache = 0;
105468078Spendry un = VTOUNION(nvp);
105568078Spendry un->un_dircache = dircache;
105668078Spendry
1057*69585Spendry out:
1058*69585Spendry VOP_UNLOCK(vp, 0, p);
105968078Spendry return (nvp);
106068078Spendry }
1061