next up previous contents
Next: 3.3 Using Links Among Up: 3. Mechanisms for Interposition Previous: 3.1 Interposition API

3.2 Creating Links Among Vnodes

For each open file or directory, a vnode is allocated for each level of interposition. If there have been N interpositions, then an open file will have N+1 associated vnodes, one for the ``base'' file system and one for each file system that has been interposed.

Each vnode of an interposing file system must have access to the vnode(s) that it interposes upon, and this must be accomplished without changing the vnode definition. Fortunately, the vnode contains a pointer (called v_data) to an opaque private area. For each type of file system (e.g., NFS, UFS) this pointer may point to a structure that includes extra information needed by that type of file system.


  
Figure: Private Data of an Interposing Vnode

Figure: Private Data of an Interposing Vnode


typedef struct fist_wrapnode {
  vnode_t *  fwn_vnodep[];   /* pointers to interposed-on vnodes */
  int        count;          /* # of pointers; >1 indicates fanout */
  /* additional per-vnode data here */
} fist_wrapnode_t;





5in


typedef struct fist_wrapnode {
  vnode_t *  fwn_vnodep[];   /* pointers to interposed-on vnodes */
  int        count;          /* # of pointers; >1 indicates fanout */
  /* additional per-vnode data here */
} fist_wrapnode_t;

I have created a new ``wrap'' vnode type for interposing vnodes. For this type of vnode, v_data points to the private fist_wrapnode_t data structure shown in Figure fig-struct-fist-wrapnode. This structure contains pointers to the vnodes it interposes upon. Pointers link vnodes from the top down, representing the DAG of interposed file systems for each open file. Figure fig-wrapnode shows an example of a caching file system, and the relationship between the vnodes in use and their respective private data, especially that of the interposing file system.


  
Figure: Data Structures Set for a Caching File System

Figure: Data Structures Set for a Caching File System


\epsfig{file=figures/wrapnode.eps}




5in


\epsfig{file=figures/wrapnode.eps}


These pointers are established when a vnode is created. There are only five vnode-creating operations, one of which is ``create.'' I have altered the code for each operation as suggested by Figures fig-wrapfs-create and fig-wrapfs-interpose. (The other functions that create new vnodes, and therefore use the interposition code in Figure fig-wrapfs-interpose are lookup, mkdir, open, and the utility routine realvp. The VFS function vfs_mount also creates a new vnode, the root vnode of the file system.)


  
Figure: Skeleton Create Operation for the ``Wrap'' File System Type

Figure: Skeleton Create Operation for the ``Wrap'' File System Type


static int fist_wrap_create(vnode_t *dvp, char *name, vattr_t *vap,
                            vcexcl_t excl, int mode, vnode_t **vpp,
                            cred_t *cr)
{
  int error = 0;
  vnode_t *hidden_vp;

  /* get interposed-on vnode */
  hidden_vp = vntofwn(dvp)->fwn_vnodep[0];

  /* pass operation down to interposed-on file system */
  error = VOP_CREATE(hidden_vp, name, vap, excl, mode, vpp, cr);

  /* if no error, interpose vnode */
  if (!error)
    *vpp = fist_wrap_interpose(*vpp, (*vpp)->v_vfsp);

  return(error);
}





5in


static int fist_wrap_create(vnode_t *dvp, char *name, vattr_t *vap,
                            vcexcl_t excl, int mode, vnode_t **vpp,
                            cred_t *cr)
{
  int error = 0;
  vnode_t *hidden_vp;

  /* get interposed-on vnode */
  hidden_vp = vntofwn(dvp)->fwn_vnodep[0];

  /* pass operation down to interposed-on file system */
  error = VOP_CREATE(hidden_vp, name, vap, excl, mode, vpp, cr);

  /* if no error, interpose vnode */
  if (!error)
    *vpp = fist_wrap_interpose(*vpp, (*vpp)->v_vfsp);

  return(error);
}

Figure fig-wrapfs-create is a skeleton of the actual code for the vnode operation create in Wrapfs.7 It shows what happens when creating an entry called name in the directory represented by the vnode *dvp, assuming that there is no fanout. The first line obtains a pointer to the vnode that *dvp interposes upon. The second line calls the VOP_CREATE macro. (Solaris and most VFS implementations have VOP_* macros whose purpose is to hide the type of underlying file system that is performing the operation.) The macro expands to invoke the create operation in the operations vector for whatever type of vnode *hidden_vp is. If it is another interposing vnode of the Wrapfs file system, then fist_wrap_create will be called recursively; otherwise, the create operation for the appropriate type of file system will be called. The third line calls fist_wrap_interpose(), which sets up three important pointer fields. This logic can be extended simply when there is fanout.

Vnode destruction is handled similarly. Vnodes are deallocated when their reference count reaches zero; then, the inactive operation is called. The interposing file system simply calls the inactive operation on the interposed file system and then deallocates the current vnode.


  
Figure: Wrapfs Vnode Interposition and Composition Code

Figure: Wrapfs Vnode Interposition and Composition Code


/* interpose on an old vnode and return new one */
static vnode_t *fist_wrap_interpose(vnode_t *old_vp, vfs_t *old_vfsp)
{
  vnode_t *new_vp;
  fist_wrapnode_t *wp;          /* private area for vnode_vp */
  fist_wrapinfo_t *ip;          /* private area for vfs */

  /* allocate new vnode */
  new_vp = kmem_alloc(sizeof(vnode_t), KM_SLEEP);
  if (!new_vp)
    return(NULL);
  /* VN_INIT2 is like VN_INIT but reuses v_lock field of interposed vnode */
  VN_INIT2(vp, old_vfsp, old_vp->v_type, (dev_t) NULL, old_vp->v_lock);

  /* allocate vnode's private area */
  wp = (fist_wrapnode_t *) kmem_alloc(sizeof(fist_wrapnode_t), KM_SLEEP);
  if (!wp)
    return(NULL);

  /* set pointers and operations vector */
  new_vp->v_data = (caddr_t) wp;
  wp->fwn_vnodep[0] = old_vp;
  new_vp->v_op = &fist_wrap_vnodeops;

  /* see "Private VFS State" Section for explanation */
  ip = vfstofwi(old_vfsp);
  ip->fwi_num_vnodes++;  

  /* return new vnode */
  return(new_vp);
}





5.6in


/* interpose on an old vnode and return new one */
static vnode_t *fist_wrap_interpose(vnode_t *old_vp, vfs_t *old_vfsp)
{
  vnode_t *new_vp;
  fist_wrapnode_t *wp;          /* private area for vnode_vp */
  fist_wrapinfo_t *ip;          /* private area for vfs */

  /* allocate new vnode */
  new_vp = kmem_alloc(sizeof(vnode_t), KM_SLEEP);
  if (!new_vp)
    return(NULL);
  /* VN_INIT2 is like VN_INIT but reuses v_lock field of interposed vnode */
  VN_INIT2(vp, old_vfsp, old_vp->v_type, (dev_t) NULL, old_vp->v_lock);

  /* allocate vnode's private area */
  wp = (fist_wrapnode_t *) kmem_alloc(sizeof(fist_wrapnode_t), KM_SLEEP);
  if (!wp)
    return(NULL);

  /* set pointers and operations vector */
  new_vp->v_data = (caddr_t) wp;
  wp->fwn_vnodep[0] = old_vp;
  new_vp->v_op = &fist_wrap_vnodeops;

  /* see "Private VFS State" Section for explanation */
  ip = vfstofwi(old_vfsp);
  ip->fwi_num_vnodes++;  

  /* return new vnode */
  return(new_vp);
}

The three pointer fields set up by fist_wrap_interpose() are:

1.
The interposing vnode's pointer to its private area.
2.
The private area's pointer to the vnode that is being interposed upon.
3.
The interposing vnode's operation vector.

Just as there are specialized operation vectors for vnodes of types NFS and UFS (nfs_vnodeops and ufs_vnodeops, respectively), there is also a specialized vector for interposing vnodes. As shown in Figure fig-wrapfs-interpose, an interposing vnode has its operations vector field (v_op) set to &fist_wrap_vnodeops, a set of pointers to functions that provide ``wrapping'' implementations of vnode interface functions. These functions define a file system that I call Wrapfs.


next up previous contents
Next: 3.3 Using Links Among Up: 3. Mechanisms for Interposition Previous: 3.1 Interposition API
Erez Zadok
1999-12-07