libafs: api to create and free vattrs
[openafs.git] / src / afs / VNOPS / afs_vnop_attrs.c
index 58b9a00..798e2c1 100644 (file)
  * afs_getattr
  * afs_VAttrToAS
  * afs_setattr
+ * afs_CreateAttr
+ * afs_DestroyAttr
  *
  */
 
 #include <afsconfig.h>
 #include "afs/param.h"
 
-RCSID
-    ("$Header$");
 
 #include "afs/sysincludes.h"   /* Standard vendor system headers */
 #include "afsincludes.h"       /* Afs-based standard headers */
@@ -43,31 +43,33 @@ extern struct vfs *afs_globalVFS;
 
 /* copy out attributes from cache entry */
 int
-afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
+afs_CopyOutAttrs(struct vcache *avc, struct vattr *attrs)
 {
-    register struct volume *tvp;
-    register struct cell *tcell;
+    struct volume *tvp;
+    struct cell *tcell;
+#if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV)
+    struct vnode *vp = AFSTOV(avc);
+#endif
     int fakedir = 0;
 
     AFS_STATCNT(afs_CopyOutAttrs);
     if (afs_fakestat_enable && avc->mvstat == 1)
        fakedir = 1;
     attrs->va_type = fakedir ? VDIR : vType(avc);
-#if defined(AFS_SGI_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_SUN5_ENV)
-    attrs->va_mode = fakedir ? 0755 : (mode_t) (avc->m.Mode & 0xffff);
+#if defined(AFS_SGI_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV)
+    attrs->va_mode = fakedir ? S_IFDIR | 0755 : (mode_t) (avc->f.m.Mode & 0xffff);
 #else
-    attrs->va_mode = fakedir ? VDIR | 0755 : avc->m.Mode;
+    attrs->va_mode = fakedir ? VDIR | 0755 : avc->f.m.Mode;
 #endif
 
-    if (avc->m.Mode & (VSUID | VSGID)) {
+    if (avc->f.m.Mode & (VSUID | VSGID)) {
        /* setuid or setgid, make sure we're allowed to run them from this cell */
-       tcell = afs_GetCell(avc->fid.Cell, 0);
+       tcell = afs_GetCell(avc->f.fid.Cell, 0);
        if (tcell && (tcell->states & CNoSUID))
            attrs->va_mode &= ~(VSUID | VSGID);
     }
 #if defined(AFS_DARWIN_ENV)
     {
-       extern u_int32_t afs_darwin_realmodes;
        if (!afs_darwin_realmodes) {
            /* Mac OS X uses the mode bits to determine whether a file or
             * directory is accessible, and believes them, even though under
@@ -88,40 +90,43 @@ afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
        }
     }
 #endif /* AFS_DARWIN_ENV */
-    attrs->va_uid = fakedir ? 0 : avc->m.Owner;
-    attrs->va_gid = fakedir ? 0 : avc->m.Group;        /* yeah! */
-#if defined(AFS_SUN56_ENV)
+    attrs->va_uid = fakedir ? 0 : avc->f.m.Owner;
+    attrs->va_gid = fakedir ? 0 : avc->f.m.Group;      /* yeah! */
+#if defined(AFS_SUN5_ENV)
     attrs->va_fsid = avc->v.v_vfsp->vfs_fsid.val[0];
-#elif defined(AFS_OSF_ENV)
-    attrs->va_fsid = avc->v.v_mount->m_stat.f_fsid.val[0];
 #elif defined(AFS_DARWIN80_ENV)
     VATTR_RETURN(attrs, va_fsid, vfs_statfs(vnode_mount(AFSTOV(avc)))->f_fsid.val[0]);
-#elif defined(AFS_DARWIN70_ENV)
+#elif defined(AFS_DARWIN_ENV)
     attrs->va_fsid = avc->v->v_mount->mnt_stat.f_fsid.val[0];
-#else /* ! AFS_DARWIN70_ENV */
+#else /* ! AFS_DARWIN_ENV */
     attrs->va_fsid = 1;
 #endif 
     if (avc->mvstat == 2) {
-       tvp = afs_GetVolume(&avc->fid, 0, READ_LOCK);
+       tvp = afs_GetVolume(&avc->f.fid, 0, READ_LOCK);
        /* The mount point's vnode. */
        if (tvp) {
            attrs->va_nodeid =
-             afs_calc_inum (tvp->mtpoint.Fid.Volume,
-                             tvp->mtpoint.Fid.Vnode);
-           if (FidCmp(&afs_rootFid, &avc->fid) && !attrs->va_nodeid)
+             afs_calc_inum(tvp->mtpoint.Cell,
+                           tvp->mtpoint.Fid.Volume,
+                           tvp->mtpoint.Fid.Vnode);
+           if (FidCmp(&afs_rootFid, &avc->f.fid) && !attrs->va_nodeid)
                attrs->va_nodeid = 2;
            afs_PutVolume(tvp, READ_LOCK);
        } else
            attrs->va_nodeid = 2;
     } else
        attrs->va_nodeid = 
-             afs_calc_inum (avc->fid.Fid.Volume,
-                             avc->fid.Fid.Vnode);
+             afs_calc_inum(avc->f.fid.Cell,
+                           avc->f.fid.Fid.Volume,
+                           avc->f.fid.Fid.Vnode);
     attrs->va_nodeid &= 0x7fffffff;    /* Saber C hates negative inode #s! */
-    attrs->va_nlink = fakedir ? 100 : avc->m.LinkCount;
-    attrs->va_size = fakedir ? 4096 : avc->m.Length;
+    attrs->va_nlink = fakedir ? 100 : avc->f.m.LinkCount;
+    attrs->va_size = fakedir ? 4096 : avc->f.m.Length;
+#if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV)
+        vnode_pager_setsize(vp, (u_long) attrs->va_size);
+#endif
     attrs->va_atime.tv_sec = attrs->va_mtime.tv_sec = attrs->va_ctime.tv_sec =
-       fakedir ? 0 : (int)avc->m.Date;
+       fakedir ? 0 : (int)avc->f.m.Date;
     /* set microseconds to be dataversion # so that we approximate NFS-style
      * use of mtime as a dataversion #.  We take it mod 512K because
      * microseconds *must* be less than a million, and 512K is the biggest
@@ -135,16 +140,16 @@ afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
 
     attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec =
        attrs->va_ctime.tv_nsec = 0;
-    attrs->va_gen = hgetlo(avc->m.DataVersion);
-#elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV) || defined(AFS_OBSD_ENV)
+    attrs->va_gen = hgetlo(avc->f.m.DataVersion);
+#elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV) || defined(AFS_OBSD_ENV) || defined(AFS_NBSD_ENV)
     attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec =
        attrs->va_ctime.tv_nsec =
-       (hgetlo(avc->m.DataVersion) & 0x7ffff) * 1000;
+       (hgetlo(avc->f.m.DataVersion) & 0x7ffff) * 1000;
 #else
     attrs->va_atime.tv_usec = attrs->va_mtime.tv_usec =
-       attrs->va_ctime.tv_usec = (hgetlo(avc->m.DataVersion) & 0x7ffff);
+       attrs->va_ctime.tv_usec = (hgetlo(avc->f.m.DataVersion) & 0x7ffff);
 #endif
-#if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) || defined(AFS_OSF_ENV)
+#if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
     attrs->va_flags = 0;
 #endif
 #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)
@@ -162,7 +167,7 @@ afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
      * Below return 0 (and not 1) blocks if the file is zero length. This conforms
      * better with the other filesystems that do return 0.      
      */
-#if defined(AFS_OSF_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
+#if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
     attrs->va_bytes = (attrs->va_size ? (attrs->va_size + 1023) : 1024);
 #ifdef va_bytes_rsv
     attrs->va_bytes_rsv = -1;
@@ -184,37 +189,41 @@ afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
 #if    defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
 int
 afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, int flags,
-           struct AFS_UCRED *acred)
+           afs_ucred_t *acred)
 #else
 int
-afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
+afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, afs_ucred_t *acred)
 #endif
 {
     afs_int32 code;
-    struct vrequest treq;
+    struct vrequest *treq = NULL;
     struct unixuser *au;
     int inited = 0;
     OSI_VC_CONVERT(avc);
 
     AFS_STATCNT(afs_getattr);
     afs_Trace2(afs_iclSetp, CM_TRACE_GETATTR, ICL_TYPE_POINTER, avc,
-              ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->m.Length));
+              ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
 
     if (afs_fakestat_enable && avc->mvstat == 1) {
        struct afs_fakestat_state fakestat;
+       struct vrequest *ureq = NULL;
 
-       code = afs_InitReq(&treq, acred);
-       if (code)
+       code = afs_CreateReq(&ureq, acred);
+       if (code) {
            return code;
+       }
        afs_InitFakeStat(&fakestat);
-       code = afs_TryEvalFakeStat(&avc, &fakestat, &treq);
+       code = afs_TryEvalFakeStat(&avc, &fakestat, ureq);
        if (code) {
            afs_PutFakeStat(&fakestat);
+           afs_DestroyReq(ureq);
            return code;
        }
 
        code = afs_CopyOutAttrs(avc, attrs);
        afs_PutFakeStat(&fakestat);
+       afs_DestroyReq(ureq);
        return code;
     }
 #if defined(AFS_SUN5_ENV)
@@ -224,7 +233,7 @@ afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
     }
 #endif
 #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV)
-    if (avc->states & CUBCinit) {
+    if (avc->f.states & CUBCinit) {
        code = afs_CopyOutAttrs(avc, attrs);
        return code;
     }
@@ -236,20 +245,27 @@ afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
     afs_BozonLock(&avc->pvnLock, avc);
 #endif
 
-    if (afs_shuttingdown)
+    if (afs_shuttingdown) {
+#ifdef AFS_BOZONLOCK_ENV
+       afs_BozonUnlock(&avc->pvnLock, avc);
+#endif
+       AFS_DISCON_UNLOCK();
        return EIO;
+    }
 
-    if (!(avc->states & CStatd)) {
-       if (!(code = afs_InitReq(&treq, acred))) {
-           code = afs_VerifyVCache2(avc, &treq);
+    if (!(avc->f.states & CStatd)) {
+       if (!(code = afs_CreateReq(&treq, acred))) {
+           code = afs_VerifyVCache2(avc, treq);
            inited = 1;
        }
     } else
        code = 0;
 
-#ifdef AFS_BOZONLOCK_ENV
+#if defined(AFS_SUN5_ENV) || defined(AFS_BOZONLOCK_ENV)
     if (code == 0)
        osi_FlushPages(avc, acred);
+#endif
+#ifdef AFS_BOZONLOCK_ENV
     afs_BozonUnlock(&avc->pvnLock, avc);
 #endif
 
@@ -260,20 +276,22 @@ afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
 
        if (afs_nfsexporter) {
            if (!inited) {
-               if ((code = afs_InitReq(&treq, acred)))
+               if ((code = afs_CreateReq(&treq, acred))) {
                    return code;
+               }
                inited = 1;
            }
            if (AFS_NFSXLATORREQ(acred)) {
                if ((vType(avc) != VDIR)
-                   && !afs_AccessOK(avc, PRSFS_READ, &treq,
+                   && !afs_AccessOK(avc, PRSFS_READ, treq,
                                     CHECK_MODE_BITS |
                                     CMB_ALLOW_EXEC_AS_READ)) {
+                   afs_DestroyReq(treq);
                    return EACCES;
                }
            }
-           if ((au = afs_FindUser(treq.uid, -1, READ_LOCK))) {
-               register struct afs_exporter *exporter = au->exporter;
+           if ((au = afs_FindUser(treq->uid, -1, READ_LOCK))) {
+               struct afs_exporter *exporter = au->exporter;
 
                if (exporter && !(afs_nfsexporter->exp_states & EXP_UNIXMODE)) {
                    unsigned int ubits;
@@ -298,8 +316,10 @@ afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
                    }
 #else
                    if (
-#ifdef AFS_DARWIN_ENV              
+#if defined(AFS_DARWIN_ENV)
                        vnode_isvroot(AFSTOV(avc))
+#elif defined(AFS_NBSD50_ENV)
+                       AFSTOV(avc)->v_vflag & VV_ROOT
 #else
                        AFSTOV(avc)->v_flag & VROOT
 #endif
@@ -333,18 +353,21 @@ afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, struct AFS_UCRED *acred)
 
     AFS_DISCON_UNLOCK();
 
-    if (!code)
+    if (!code) {
+       afs_DestroyReq(treq);
        return 0;
-    code = afs_CheckCode(code, &treq, 14);
+    }
+    code = afs_CheckCode(code, treq, 14);
+    afs_DestroyReq(treq);
     return code;
 }
 
 /* convert a Unix request into a status store request */
 int
-afs_VAttrToAS(register struct vcache *avc, register struct vattr *av,
-             register struct AFSStoreStatus *as)
+afs_VAttrToAS(struct vcache *avc, struct vattr *av,
+             struct AFSStoreStatus *as)
 {
-    register int mask;
+    int mask;
     mask = 0;
     AFS_STATCNT(afs_VAttrToAS);
 #if     defined(AFS_DARWIN80_ENV)
@@ -363,7 +386,7 @@ afs_VAttrToAS(register struct vcache *avc, register struct vattr *av,
 #endif
        mask |= AFS_SETMODE;
        as->UnixModeBits = av->va_mode & 0xffff;
-       if (avc->states & CForeign) {
+       if (avc->f.states & CForeign) {
            ObtainWriteLock(&avc->lock, 127);
            afs_FreeAllAxs(&(avc->Access));
            ReleaseWriteLock(&avc->lock);
@@ -434,22 +457,25 @@ afs_VAttrToAS(register struct vcache *avc, register struct vattr *av,
     return 0;
 }
 
-/* We don't set CDirty bit in avc->states because setattr calls WriteVCache
+/* We don't set CDirty bit in avc->f.states because setattr calls WriteVCache
  * synchronously, therefore, it's not needed.
  */
 #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
 int
-afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs, int flags,
-           struct AFS_UCRED *acred)
+afs_setattr(OSI_VC_DECL(avc), struct vattr *attrs, int flags,
+           afs_ucred_t *acred)
 #else
 int
-afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
-           struct AFS_UCRED *acred)
+afs_setattr(OSI_VC_DECL(avc), struct vattr *attrs,
+           afs_ucred_t *acred)
 #endif
 {
-    struct vrequest treq;
+    struct vrequest *treq = NULL;
     struct AFSStoreStatus astat;
-    register afs_int32 code;
+    afs_int32 code;
+#if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV)
+    struct vnode *vp = AFSTOV(avc);
+#endif
     struct afs_fakestat_state fakestate;
     OSI_VC_CONVERT(avc);
 
@@ -458,24 +484,24 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
     afs_Trace4(afs_iclSetp, CM_TRACE_SETATTR, ICL_TYPE_POINTER, avc,
               ICL_TYPE_INT32, attrs->va_mask, ICL_TYPE_OFFSET,
               ICL_HANDLE_OFFSET(attrs->va_size), ICL_TYPE_OFFSET,
-              ICL_HANDLE_OFFSET(avc->m.Length));
+              ICL_HANDLE_OFFSET(avc->f.m.Length));
 #else
     afs_Trace4(afs_iclSetp, CM_TRACE_SETATTR, ICL_TYPE_POINTER, avc,
               ICL_TYPE_INT32, attrs->va_mode, ICL_TYPE_OFFSET,
               ICL_HANDLE_OFFSET(attrs->va_size), ICL_TYPE_OFFSET,
-              ICL_HANDLE_OFFSET(avc->m.Length));
+              ICL_HANDLE_OFFSET(avc->f.m.Length));
 #endif
-    if ((code = afs_InitReq(&treq, acred)))
+    if ((code = afs_CreateReq(&treq, acred)))
        return code;
 
     AFS_DISCON_LOCK();
 
     afs_InitFakeStat(&fakestate);
-    code = afs_EvalFakeStat(&avc, &fakestate, &treq);
+    code = afs_EvalFakeStat(&avc, &fakestate, treq);
     if (code)
        goto done;
 
-    if (avc->states & CRO) {
+    if (avc->f.states & CRO) {
        code = EROFS;
        goto done;
     }
@@ -496,14 +522,14 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
     if (attrs->va_mask & ATTR_SIZE) {
 #elif  defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
     if (attrs->va_mask & AT_SIZE) {
-#elif  defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
+#elif  defined(AFS_XBSD_ENV)
     if (attrs->va_size != VNOVAL) {
 #elif  defined(AFS_AIX41_ENV)
     if (attrs->va_size != -1) {
 #else
     if (attrs->va_size != ~0) {
 #endif
-       if (!afs_AccessOK(avc, PRSFS_WRITE, &treq, DONT_CHECK_MODE_BITS)) {
+       if (!afs_AccessOK(avc, PRSFS_WRITE, treq, DONT_CHECK_MODE_BITS)) {
            code = EACCES;
            goto done;
        }
@@ -534,7 +560,7 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
     if (attrs->va_mask & ATTR_SIZE) {
 #elif  defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
     if (attrs->va_mask & AT_SIZE) {
-#elif  defined(AFS_OSF_ENV) || defined(AFS_XBSD_ENV)
+#elif  defined(AFS_XBSD_ENV)
     if (attrs->va_size != VNOVAL) {
 #elif  defined(AFS_AIX41_ENV)
     if (attrs->va_size != -1) {
@@ -543,16 +569,25 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
 #endif
        afs_size_t tsize = attrs->va_size;
        ObtainWriteLock(&avc->lock, 128);
-       avc->states |= CDirty;
-
-       code = afs_TruncateAllSegments(avc, tsize, &treq, acred);
-#ifdef AFS_LINUX_26_ENV
+       avc->f.states |= CDirty;
+
+        if (AFS_IS_DISCONNECTED && tsize >=avc->f.m.Length) {
+           /* If we're growing the file, and we're disconnected, we need
+            * to make the relevant dcache chunks appear ourselves. */
+           code = afs_ExtendSegments(avc, tsize, treq);
+       } else {
+           code = afs_TruncateAllSegments(avc, tsize, treq, acred);
+       }
+#ifdef AFS_LINUX26_ENV
        /* We must update the Linux kernel's idea of file size as soon as
         * possible, to avoid racing with delayed writepages delivered by
         * pdflush */
-       if (code == 0) 
+       if (code == 0)
            i_size_write(AFSTOV(avc), tsize);
 #endif
+#if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV)
+        vnode_pager_setsize(vp, (u_long) tsize);
+#endif
        /* if date not explicitly set by this call, set it ourselves, since we
         * changed the data */
        if (!(astat.Mask & AFS_SETMODTIME)) {
@@ -561,19 +596,19 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
        }
 
        if (code == 0) {
-           if (((avc->execsOrWriters <= 0) && (avc->states & CCreating) == 0)
+           if (((avc->execsOrWriters <= 0) && (avc->f.states & CCreating) == 0)
                || (avc->execsOrWriters == 1 && AFS_NFSXLATORREQ(acred))) {
 
                /* Store files now if not disconnected. */
                /* XXX: AFS_IS_DISCON_RW handled. */
                if (!AFS_IS_DISCONNECTED) {
-                       code = afs_StoreAllSegments(avc, &treq, AFS_ASYNC);
+                       code = afs_StoreAllSegments(avc, treq, AFS_ASYNC);
                        if (!code)
-                               avc->states &= ~CDirty;
+                               avc->f.states &= ~CDirty;
                }
            }
        } else
-           avc->states &= ~CDirty;
+           avc->f.states &= ~CDirty;
 
        ReleaseWriteLock(&avc->lock);
        hzero(avc->flushDV);
@@ -583,27 +618,23 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
     if (!AFS_IS_DISCONNECTED) {
         if (code == 0) {
            ObtainSharedLock(&avc->lock, 16);   /* lock entry */
-           code = afs_WriteVCache(avc, &astat, &treq); /* send request */
+           code = afs_WriteVCache(avc, &astat, treq);  /* send request */
            ReleaseSharedLock(&avc->lock);      /* release lock */
         }
         if (code) {
            ObtainWriteLock(&afs_xcbhash, 487);
            afs_DequeueCallback(avc);
-           avc->states &= ~CStatd;
+           avc->f.states &= ~CStatd;
            ReleaseWriteLock(&afs_xcbhash);
-           if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
+           if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
                osi_dnlc_purgedp(avc);
            /* error?  erase any changes we made to vcache entry */
         }
-
-#if defined(AFS_DISCON_ENV)
     } else {
-
        ObtainSharedLock(&avc->lock, 712);
        /* Write changes locally. */
        code = afs_WriteVCacheDiscon(avc, &astat, attrs);
        ReleaseSharedLock(&avc->lock);
-#endif
     }          /* if (!AFS_IS_DISCONNECTED) */
 
 #if    defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV)
@@ -621,6 +652,53 @@ afs_setattr(OSI_VC_DECL(avc), register struct vattr *attrs,
     afs_PutFakeStat(&fakestate);
 
     AFS_DISCON_UNLOCK();
-    code = afs_CheckCode(code, &treq, 15);
+    code = afs_CheckCode(code, treq, 15);
+    afs_DestroyReq(treq);
     return code;
 }
+
+/*!
+ * Allocate a vattr.
+ *
+ * \note The caller must free the allocated vattr with
+ *       afs_DestroyAttr() if this function returns successfully (zero).
+ *
+ * \note The GLOCK must be held on platforms which require the GLOCK
+ *       for osi_AllocSmallSpace() and osi_FreeSmallSpace().
+ *
+ * \param[out] out   address of the vattr pointer
+ * \return     0 on success
+ */
+int
+afs_CreateAttr(struct vattr **out)
+{
+    struct vattr *vattr = NULL;
+
+    if (!out) {
+       return EINVAL;
+    }
+    vattr = osi_AllocSmallSpace(sizeof(struct vattr));
+    if (!vattr) {
+       return ENOMEM;
+    }
+    memset(vattr, 0, sizeof(struct vattr));
+    *out = vattr;
+    return 0;
+}
+
+/*!
+ * Deallocate a vattr.
+ *
+ * \note The GLOCK must be held on platforms which require the GLOCK
+ *       for osi_FreeSmallSpace().
+ *
+ * \param[in]  vattr  pointer to the vattr to free; may be NULL
+ */
+void
+afs_DestroyAttr(struct vattr *vattr)
+{
+    if (vattr) {
+       osi_FreeSmallSpace(vattr);
+    }
+}
+