* License. For details, see the LICENSE file in the top-level source
* directory or online at http://www.openafs.org/dl/license10.html
*/
-
+
#include <afsconfig.h>
#include "afs/param.h"
-RCSID("$Header$");
#include "afs/sysincludes.h"
#include "afsincludes.h"
-
-#ifdef AFS_DISCON_ENV
+#include "afs/afs_stats.h" /* statistics */
+#include "afs/lock.h"
+#include "afs/afs_cbqueue.h"
+
+#define dv_match(vc, fstat) \
+ ((vc->f.m.DataVersion.low == fstat.DataVersion) && \
+ (vc->f.m.DataVersion.high == fstat.dataVersionHigh))
+
+/*! Circular queue of dirty vcaches */
+struct afs_q afs_disconDirty;
+
+/*! Circular queue of vcaches with shadow directories */
+struct afs_q afs_disconShadow;
+
+/*! Locks both of these lists. Must be write locked for anything other than
+ * list traversal */
+afs_rwlock_t afs_disconDirtyLock;
+
+extern afs_int32 *afs_dvhashTbl; /*Data cache hash table */
+extern afs_int32 *afs_dchashTbl; /*Data cache hash table */
+extern afs_int32 *afs_dvnextTbl; /*Dcache hash table links */
+extern afs_int32 *afs_dcnextTbl; /*Dcache hash table links */
+extern struct dcache **afs_indexTable; /*Pointers to dcache entries */
+
+/*! Vnode number. On file creation, use the current value and increment it.
+ */
+afs_uint32 afs_DisconVnode = 2;
+
+/*! Conflict policy. */
+enum {
+ CLIENT_WINS = 0,
+ SERVER_WINS,
+ LAST_CLOSER_WINS,
+ ASK
+};
+
+afs_int32 afs_ConflictPolicy = SERVER_WINS;
+
+static void afs_DisconDiscardAllShadows(int, afs_ucred_t *);
+void afs_DbgListDirEntries(struct VenusFid *afid);
+
+
+/*!
+ * Find the first dcache of a file that has the specified fid.
+ * Similar to afs_FindDCache, only that it takes a fid instead
+ * of a vcache and it can get the first dcache.
+ *
+ * \param afid
+ *
+ * \return The found dcache or NULL.
+ */
+struct dcache *
+afs_FindDCacheByFid(struct VenusFid *afid)
+{
+ afs_int32 i, index;
+ struct dcache *tdc = NULL;
+
+ i = DVHash(afid);
+ ObtainWriteLock(&afs_xdcache, 758);
+ for (index = afs_dvhashTbl[i]; index != NULLIDX;) {
+ if (afs_indexUnique[index] == afid->Fid.Unique) {
+ tdc = afs_GetDSlot(index, NULL);
+ ReleaseReadLock(&tdc->tlock);
+ if (!FidCmp(&tdc->f.fid, afid)) {
+ break; /* leaving refCount high for caller */
+ }
+ afs_PutDCache(tdc);
+ }
+ index = afs_dvnextTbl[index];
+ }
+ ReleaseWriteLock(&afs_xdcache);
+
+ if (index == NULLIDX)
+ tdc = NULL;
+ return tdc;
+}
+
+/*!
+ * Generate a store status from a dirty vcache entry.
+ *
+ * \param avc Dirty vcache entry.
+ * \param astat
+ *
+ * \note The vnode must be share locked. It is called only on resync,
+ * where the vnode is write locked locally and and the server.
+ *
+ * \return Mask of operations.
+ */
+int
+afs_GenStoreStatus(struct vcache *avc, struct AFSStoreStatus *astat)
+{
+ if (!avc || !astat || !avc->f.ddirty_flags)
+ return 0;
+
+ /* Clean up store stat. */
+ memset(astat, 0, sizeof(struct AFSStoreStatus));
+
+ if (avc->f.ddirty_flags & VDisconSetTime) {
+ /* Update timestamp. */
+ astat->ClientModTime = avc->f.m.Date;
+ astat->Mask |= AFS_SETMODTIME;
+ }
+
+ if (avc->f.ddirty_flags & VDisconSetMode) {
+ /* Copy the mode bits. */
+ astat->UnixModeBits = avc->f.m.Mode;
+ astat->Mask |= AFS_SETMODE;
+ }
+
+ /* XXX: more to come... ?*/
+
+ return astat->Mask;
+}
+
+/*!
+ * Hook for filtering the local dir fid by searching the "." entry.
+ *
+ * \param hdata The fid to be filled.
+ */
+static int
+get_parent_dir_fid_hook(void *hdata, char *aname, afs_int32 vnode,
+ afs_int32 unique)
+{
+ struct VenusFid *tfid = (struct VenusFid *) hdata;
+
+ if ((aname[0] == '.') && (aname[1] == '.') && !aname[2]) {
+ tfid->Fid.Vnode = vnode;
+ tfid->Fid.Unique = unique;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Get a the dir's fid by looking in the vcache for simple files and
+ * in the ".." entry for directories.
+ *
+ * \param avc The file's vhash entry.
+ * \param afid Put the fid here.
+ *
+ * \return 0 on success, -1 on failure
+ */
+int
+afs_GetParentDirFid(struct vcache *avc, struct VenusFid *afid)
+{
+ struct dcache *tdc;
+
+ afid->Cell = avc->f.fid.Cell;
+ afid->Fid.Volume = avc->f.fid.Fid.Volume;
+
+ switch (vType(avc)) {
+ case VREG:
+ case VLNK:
+ /* Normal files have the dir fid embedded in the vcache. */
+ afid->Fid.Vnode = avc->f.parent.vnode;
+ afid->Fid.Unique = avc->f.parent.unique;
+ break;
+ case VDIR:
+ /* If dir or parent dir created locally*/
+ tdc = afs_FindDCacheByFid(&avc->f.fid);
+ if (tdc) {
+ afid->Fid.Unique = 0;
+ /* Lookup each entry for the fid. It should be the first. */
+ afs_dir_EnumerateDir(tdc, &get_parent_dir_fid_hook, afid);
+ afs_PutDCache(tdc);
+ if (afid->Fid.Unique == 0) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+struct NameAndFid {
+ struct VenusFid *fid;
+ char *name;
+ int name_len;
+};
+
+/*!
+ * Hook that searches a certain fid's name.
+ *
+ * \param hdata NameAndFid structure containin a pointer to a fid
+ * and an allocate name. The name will be filled when hit.
+ */
+static int
+get_vnode_name_hook(void *hdata, char *aname, afs_int32 vnode,
+ afs_int32 unique)
+{
+ struct NameAndFid *nf = (struct NameAndFid *) hdata;
+
+ if ((nf->fid->Fid.Vnode == vnode) &&
+ (nf->fid->Fid.Unique == unique)) {
+ nf->name_len = strlen(aname);
+ memcpy(nf->name, aname, nf->name_len);
+ nf->name[nf->name_len] = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Try to get a vnode's name by comparing all parent dir's entries
+ * to the given fid. It can also return the dir's dcache.
+ *
+ * \param avc The file's vcache.
+ * \param afid The parent dir's fid.
+ * \param aname A preallocated string for the name.
+ * \param deleted Has this file been deleted? If yes, use the shadow
+ * dir for looking up the name.
+ */
+int
+afs_GetVnodeName(struct vcache *avc, struct VenusFid *afid, char *aname,
+ int deleted)
+{
+ int code = 0;
+ struct dcache *tdc;
+ struct vcache *parent_vc;
+ struct NameAndFid tnf;
+ struct VenusFid parent_fid;
+ struct VenusFid shadow_fid;
+
+ /* List dir contents and get it's tdc. */
+ if (deleted) {
+ /* For deleted files, get the shadow dir's tdc: */
+
+ /* Get the parent dir's vcache that contains the shadow fid. */
+ parent_fid.Cell = avc->f.fid.Cell;
+ parent_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+ if (avc->f.ddirty_flags & VDisconRename) {
+ /* For renames the old dir fid is needed. */
+ parent_fid.Fid.Vnode = avc->f.oldParent.vnode;
+ parent_fid.Fid.Unique = avc->f.oldParent.unique;
+ } else {
+ parent_fid.Fid.Vnode = afid->Fid.Vnode;
+ parent_fid.Fid.Unique = afid->Fid.Unique;
+ }
+
+ /* Get the parent dir's vcache that contains the shadow fid. */
+ ObtainSharedLock(&afs_xvcache, 755);
+ parent_vc = afs_FindVCache(&parent_fid, 0, 1);
+ ReleaseSharedLock(&afs_xvcache);
+ if (!parent_vc) {
+ return ENOENT;
+ }
+
+ shadow_fid.Cell = parent_vc->f.fid.Cell;
+ shadow_fid.Fid.Volume = parent_vc->f.fid.Fid.Volume;
+ shadow_fid.Fid.Vnode = parent_vc->f.shadow.vnode;
+ shadow_fid.Fid.Unique = parent_vc->f.shadow.unique;
+
+ afs_PutVCache(parent_vc);
+
+ /* Get shadow dir's dcache. */
+ tdc = afs_FindDCacheByFid(&shadow_fid);
+
+ } else {
+
+ /* For normal files, look into the current dir's entry. */
+ tdc = afs_FindDCacheByFid(afid);
+ } /* if (deleted) */
+
+ if (tdc) {
+ tnf.fid = &avc->f.fid;
+ tnf.name_len = -1;
+ tnf.name = aname;
+ afs_dir_EnumerateDir(tdc, &get_vnode_name_hook, &tnf);
+ afs_PutDCache(tdc);
+ if (tnf.name_len == -1)
+ code = ENOENT;
+ } else {
+ /* printf("Directory dcache not found!\n"); */
+ code = ENOENT;
+ }
+
+ return code;
+}
+
+struct DirtyChildrenCount {
+ struct vcache *vc;
+ afs_uint32 count;
+};
+
+/*!
+ * Lookup dirty deleted vnodes in this dir.
+ */
+static int
+chk_del_children_hook(void *hdata, char *aname, afs_int32 vnode,
+ afs_int32 unique)
+{
+ struct VenusFid tfid;
+ struct DirtyChildrenCount *v = (struct DirtyChildrenCount *) hdata;
+ struct vcache *tvc;
+
+ if ((aname[0] == '.') && !aname[1])
+ /* Skip processing this dir again.
+ * It would result in an endless loop.
+ */
+ return 0;
+
+ if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
+ /* Don't process parent dir. */
+ return 0;
+
+ /* Get this file's vcache. */
+ tfid.Cell = v->vc->f.fid.Cell;
+ tfid.Fid.Volume = v->vc->f.fid.Fid.Volume;
+ tfid.Fid.Vnode = vnode;
+ tfid.Fid.Unique = unique;
+
+ ObtainSharedLock(&afs_xvcache, 757);
+ tvc = afs_FindVCache(&tfid, 0, 1);
+ ReleaseSharedLock(&afs_xvcache);
+
+ /* Count unfinished dirty children. */
+ if (tvc) {
+ ObtainReadLock(&tvc->lock);
+ if (tvc->f.ddirty_flags)
+ v->count++;
+ ReleaseReadLock(&tvc->lock);
+
+ afs_PutVCache(tvc);
+ }
+
+ return 0;
+}
+
+/*!
+ * Check if entries have been deleted in a vnode's shadow
+ * dir.
+ *
+ * \return Returns the number of dirty children.
+ *
+ * \note afs_DDirtyVCListLock must be write locked.
+ */
+int
+afs_CheckDeletedChildren(struct vcache *avc)
+{
+ struct dcache *tdc;
+ struct DirtyChildrenCount dcc;
+ struct VenusFid shadow_fid;
+
+ if (!avc->f.shadow.vnode)
+ /* Empty dir. */
+ return 0;
+
+ shadow_fid.Cell = avc->f.fid.Cell;
+ shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+ shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
+ shadow_fid.Fid.Unique = avc->f.shadow.unique;
+
+ dcc.count = 0;
+
+ /* Get shadow dir's dcache. */
+ tdc = afs_FindDCacheByFid(&shadow_fid);
+ if (tdc) {
+ dcc.vc = avc;
+ afs_dir_EnumerateDir(tdc, &chk_del_children_hook, &dcc);
+ afs_PutDCache(tdc);
+ }
+
+ return dcc.count;
+}
+
+/*!
+ * Changes a file's parent fid references.
+ */
+static int
+fix_children_fids_hook(void *hdata, char *aname, afs_int32 vnode,
+ afs_int32 unique)
+{
+ struct VenusFid tfid;
+ struct VenusFid *afid = (struct VenusFid *) hdata;
+ struct vcache *tvc;
+ struct dcache *tdc = NULL;
+
+ if ((aname[0] == '.') && !aname[1])
+ return 0;
+
+ if ((aname[0] == '.') && (aname[1] == '.') && !aname[2])
+ return 0;
+
+ tfid.Cell = afid->Cell;
+ tfid.Fid.Volume = afid->Fid.Volume;
+ tfid.Fid.Vnode = vnode;
+ tfid.Fid.Unique = unique;
+
+ if (!(vnode % 2)) {
+ /* vnode's parity indicates that it's a file. */
+
+ /* Get the vcache. */
+ ObtainSharedLock(&afs_xvcache, 759);
+ tvc = afs_FindVCache(&tfid, 0, 1);
+ ReleaseSharedLock(&afs_xvcache);
+
+ /* Change the fields. */
+ if (tvc) {
+ tvc->f.parent.vnode = afid->Fid.Vnode;
+ tvc->f.parent.unique = afid->Fid.Unique;
+
+ afs_PutVCache(tvc);
+ }
+ } else {
+ /* It's a dir. Fix this dir's .. entry to contain the new fid. */
+ /* Seek the dir's dcache. */
+ tdc = afs_FindDCacheByFid(&tfid);
+ if (tdc) {
+ /* Change the .. entry fid. */
+ afs_dir_ChangeFid(tdc, "..", NULL, &afid->Fid.Vnode);
+ afs_PutDCache(tdc);
+ }
+ } /* if (!(vnode % 2))*/
+
+ return 0;
+}
+
+/*!
+ * Fixes the parentVnode and parentUnique fields of all
+ * files (not dirs) contained in the directory pointed by
+ * old_fid. This is useful on resync, when a locally created dir
+ * get's a new fid and all the children references must be updated
+ * to reflect the new fid.
+ *
+ * \note The dir's fid hasn't been changed yet, it is still referenced
+ * with the old fid.
+ *
+ * \param old_fid The current dir's fid.
+ * \param new_fid The new dir's fid.
+ */
+void
+afs_FixChildrenFids(struct VenusFid *old_fid, struct VenusFid *new_fid)
+{
+ struct dcache *tdc;
+
+ /* Get shadow dir's dcache. */
+ tdc = afs_FindDCacheByFid(old_fid);
+ /* Change the fids. */
+ if (tdc) {
+ afs_dir_EnumerateDir(tdc, &fix_children_fids_hook, new_fid);
+ afs_PutDCache(tdc);
+ }
+}
+
+static int
+list_dir_hook(void *hdata, char *aname, afs_int32 vnode, afs_int32 unique)
+{
+ /* printf("list_dir_hook: %s v:%u u:%u\n", aname, vnode, unique); */
+ return 0;
+}
+
+void
+afs_DbgListDirEntries(struct VenusFid *afid)
+{
+ struct dcache *tdc;
+
+ /* Get shadow dir's dcache. */
+ tdc = afs_FindDCacheByFid(afid);
+ if (tdc) {
+ afs_dir_EnumerateDir(tdc, &list_dir_hook, NULL);
+ afs_PutDCache(tdc);
+ }
+}
+
+/*!
+ * Find the parent vcache for a given child
+ *
+ * \param avc The vcache whose parent is required
+ * \param afid Fid structure in which parent's fid should be stored
+ * \param aname An AFSNAMEMAX sized buffer to hold the parents name
+ * \param adp A pointer to a struct vcache* which will be set to the
+ * parent vcache
+ *
+ * \return An error code. 0 indicates success, EAGAIN that the vnode should
+ * be deferred to later in the resync process
+ */
+
+int
+afs_GetParentVCache(struct vcache *avc, int deleted, struct VenusFid *afid,
+ char *aname, struct vcache **adp)
+{
+ int code;
+
+ *adp = NULL;
+
+ if (afs_GetParentDirFid(avc, afid)) {
+ /* printf("afs_GetParentVCache: Couldn't find parent dir's FID.\n"); */
+ return ENOENT;
+ }
+
+ code = afs_GetVnodeName(avc, afid, aname, deleted);
+ if (code) {
+ /* printf("afs_GetParentVCache: Couldn't find file name\n"); */
+ goto end;
+ }
+
+ ObtainSharedLock(&afs_xvcache, 766);
+ *adp = afs_FindVCache(afid, 0, 1);
+ ReleaseSharedLock(&afs_xvcache);
+ if (!*adp) {
+ /* printf("afs_GetParentVCache: Couldn't find parent dir's vcache\n"); */
+ code = ENOENT;
+ goto end;
+ }
+
+ if ((*adp)->f.ddirty_flags & VDisconCreate) {
+ /* printf("afs_GetParentVCache: deferring until parent exists\n"); */
+ code = EAGAIN;
+ goto end;
+ }
+
+end:
+ if (code && *adp) {
+ afs_PutVCache(*adp);
+ *adp = NULL;
+ }
+ return code;
+}
+
+
+/*!
+ * Handles file renaming on reconnection:
+ * - Get the old name from the old dir's shadow dir.
+ * - Get the new name from the current dir.
+ * - Old dir fid and new dir fid are collected along the way.
+ * */
+int
+afs_ProcessOpRename(struct vcache *avc, struct vrequest *areq)
+{
+ struct VenusFid old_pdir_fid, new_pdir_fid;
+ char *old_name = NULL, *new_name = NULL;
+ struct AFSFetchStatus OutOldDirStatus, OutNewDirStatus;
+ struct AFSVolSync tsync;
+ struct afs_conn *tc;
+ afs_uint32 code = 0;
+ XSTATS_DECLS;
+
+ /* Get old dir vcache. */
+ old_pdir_fid.Cell = avc->f.fid.Cell;
+ old_pdir_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+ old_pdir_fid.Fid.Vnode = avc->f.oldParent.vnode;
+ old_pdir_fid.Fid.Unique = avc->f.oldParent.unique;
+
+ /* Get old name. */
+ old_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
+ if (!old_name) {
+ /* printf("afs_ProcessOpRename: Couldn't alloc space for old name.\n"); */
+ return ENOMEM;
+ }
+ code = afs_GetVnodeName(avc, &old_pdir_fid, old_name, 1);
+ if (code) {
+ /* printf("afs_ProcessOpRename: Couldn't find old name.\n"); */
+ goto done;
+ }
+
+ /* Alloc data first. */
+ new_name = (char *) afs_osi_Alloc(AFSNAMEMAX);
+ if (!new_name) {
+ /* printf("afs_ProcessOpRename: Couldn't alloc space for new name.\n"); */
+ code = ENOMEM;
+ goto done;
+ }
+
+ if (avc->f.ddirty_flags & VDisconRenameSameDir) {
+ /* If we're in the same dir, don't do the lookups all over again,
+ * just copy fid and vcache from the old dir.
+ */
+ memcpy(&new_pdir_fid, &old_pdir_fid, sizeof(struct VenusFid));
+ } else {
+ /* Get parent dir's FID.*/
+ if (afs_GetParentDirFid(avc, &new_pdir_fid)) {
+ /* printf("afs_ProcessOpRename: Couldn't find new parent dir FID.\n"); */
+ code = ENOENT;
+ goto done;
+ }
+ }
+
+ /* And finally get the new name. */
+ code = afs_GetVnodeName(avc, &new_pdir_fid, new_name, 0);
+ if (code) {
+ /* printf("afs_ProcessOpRename: Couldn't find new name.\n"); */
+ goto done;
+ }
+
+ /* Send to data to server. */
+ do {
+ tc = afs_Conn(&old_pdir_fid, areq, SHARED_LOCK);
+ if (tc) {
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RENAME);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_Rename(tc->id,
+ (struct AFSFid *)&old_pdir_fid.Fid,
+ old_name,
+ (struct AFSFid *)&new_pdir_fid.Fid,
+ new_name,
+ &OutOldDirStatus,
+ &OutNewDirStatus,
+ &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+
+ } while (afs_Analyze(tc,
+ code,
+ &new_pdir_fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_RENAME,
+ SHARED_LOCK,
+ NULL));
+
+ /* if (code) printf("afs_ProcessOpRename: server code=%u\n", code); */
+done:
+ if (new_name)
+ afs_osi_Free(new_name, AFSNAMEMAX);
+ if (old_name)
+ afs_osi_Free(old_name, AFSNAMEMAX);
+ return code;
+}
+
+/*!
+ * Handles all the reconnection details:
+ * - Get all the details about the vnode: name, fid, and parent dir fid.
+ * - Send data to server.
+ * - Handle errors.
+ * - Reorder vhash and dcaches in their hashes, using the newly acquired fid.
+ */
+int
+afs_ProcessOpCreate(struct vcache *avc, struct vrequest *areq,
+ afs_ucred_t *acred)
+{
+ char *tname = NULL, *ttargetName = NULL;
+ struct AFSStoreStatus InStatus;
+ struct AFSFetchStatus OutFidStatus, OutDirStatus;
+ struct VenusFid pdir_fid, newFid;
+ struct AFSCallBack CallBack;
+ struct AFSVolSync tsync;
+ struct vcache *tdp = NULL, *tvc = NULL;
+ struct dcache *tdc = NULL;
+ struct afs_conn *tc;
+ afs_int32 hash, new_hash, index;
+ afs_size_t tlen;
+ int code, op = 0;
+ XSTATS_DECLS;
+
+ tname = afs_osi_Alloc(AFSNAMEMAX);
+ if (!tname)
+ return ENOMEM;
-/* Nothing here any more. Remove from the build unless stuff comes back! */
+ code = afs_GetParentVCache(avc, 0, &pdir_fid, tname, &tdp);
+ if (code)
+ goto end;
+
+ /* This data may also be in linkData, but then we have to deal with
+ * the joy of terminating NULLs and . and file modes. So just get
+ * it from the dcache where it won't have been fiddled with.
+ */
+ if (vType(avc) == VLNK) {
+ afs_size_t offset;
+ struct dcache *tdc;
+ struct osi_file *tfile;
+
+ tdc = afs_GetDCache(avc, 0, areq, &offset, &tlen, 0);
+ if (!tdc) {
+ code = ENOENT;
+ goto end;
+ }
+
+ if (tlen > 1024) {
+ afs_PutDCache(tdc);
+ code = EFAULT;
+ goto end;
+ }
+
+ tlen++; /* space for NULL */
+ ttargetName = afs_osi_Alloc(tlen);
+ if (!ttargetName) {
+ afs_PutDCache(tdc);
+ return ENOMEM;
+ }
+ ObtainReadLock(&tdc->lock);
+ tfile = afs_CFileOpen(&tdc->f.inode);
+ code = afs_CFileRead(tfile, 0, ttargetName, tlen);
+ ttargetName[tlen-1] = '\0';
+ afs_CFileClose(tfile);
+ ReleaseReadLock(&tdc->lock);
+ afs_PutDCache(tdc);
+ }
+
+ /* Set status. */
+ InStatus.Mask = AFS_SETMODTIME | AFS_SETMODE | AFS_SETGROUP;
+ InStatus.ClientModTime = avc->f.m.Date;
+ InStatus.Owner = avc->f.m.Owner;
+ InStatus.Group = (afs_int32) afs_cr_gid(acred);
+ /* Only care about protection bits. */
+ InStatus.UnixModeBits = avc->f.m.Mode & 0xffff;
+
+ do {
+ tc = afs_Conn(&tdp->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ switch (vType(avc)) {
+ case VREG:
+ /* Make file on server. */
+ op = AFS_STATS_FS_RPCIDX_CREATEFILE;
+ XSTATS_START_TIME(op);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_CreateFile(tc->id,
+ (struct AFSFid *)&tdp->f.fid.Fid,
+ tname, &InStatus,
+ (struct AFSFid *) &newFid.Fid,
+ &OutFidStatus, &OutDirStatus,
+ &CallBack, &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ break;
+ case VDIR:
+ /* Make dir on server. */
+ op = AFS_STATS_FS_RPCIDX_MAKEDIR;
+ XSTATS_START_TIME(op);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_MakeDir(tc->id, (struct AFSFid *) &tdp->f.fid.Fid,
+ tname, &InStatus,
+ (struct AFSFid *) &newFid.Fid,
+ &OutFidStatus, &OutDirStatus,
+ &CallBack, &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ break;
+ case VLNK:
+ /* Make symlink on server. */
+ op = AFS_STATS_FS_RPCIDX_SYMLINK;
+ XSTATS_START_TIME(op);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_Symlink(tc->id,
+ (struct AFSFid *) &tdp->f.fid.Fid,
+ tname, ttargetName, &InStatus,
+ (struct AFSFid *) &newFid.Fid,
+ &OutFidStatus, &OutDirStatus, &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ break;
+ default:
+ op = AFS_STATS_FS_RPCIDX_CREATEFILE;
+ code = 1;
+ break;
+ }
+ } else
+ code = -1;
+ } while (afs_Analyze(tc, code, &tdp->f.fid, areq, op, SHARED_LOCK, NULL));
+
+ /* TODO: Handle errors. */
+ if (code) {
+ /* printf("afs_ProcessOpCreate: error while creating vnode on server, code=%d .\n", code); */
+ goto end;
+ }
+
+ /* The rpc doesn't set the cell number. */
+ newFid.Cell = avc->f.fid.Cell;
+
+ /*
+ * Change the fid in the dir entry.
+ */
+
+ /* Seek the dir's dcache. */
+ tdc = afs_FindDCacheByFid(&tdp->f.fid);
+ if (tdc) {
+ /* And now change the fid in the parent dir entry. */
+ afs_dir_ChangeFid(tdc, tname, &avc->f.fid.Fid.Vnode, &newFid.Fid.Vnode);
+ afs_PutDCache(tdc);
+ }
+
+ if (vType(avc) == VDIR) {
+ /* Change fid in the dir for the "." entry. ".." has alredy been
+ * handled by afs_FixChildrenFids when processing the parent dir.
+ */
+ tdc = afs_FindDCacheByFid(&avc->f.fid);
+ if (tdc) {
+ afs_dir_ChangeFid(tdc, ".", &avc->f.fid.Fid.Vnode,
+ &newFid.Fid.Vnode);
+
+ if (avc->f.m.LinkCount >= 2)
+ /* For non empty dirs, fix children's parentVnode and
+ * parentUnique reference.
+ */
+ afs_FixChildrenFids(&avc->f.fid, &newFid);
+
+ afs_PutDCache(tdc);
+ }
+ }
+
+ /* Recompute hash chain positions for vnode and dcaches.
+ * Then change to the new FID.
+ */
+
+ /* The vcache goes first. */
+ ObtainWriteLock(&afs_xvcache, 735);
+
+ /* Old fid hash. */
+ hash = VCHash(&avc->f.fid);
+ /* New fid hash. */
+ new_hash = VCHash(&newFid);
+
+ /* Remove hash from old position. */
+ /* XXX: not checking array element contents. It shouldn't be empty.
+ * If it oopses, then something else might be wrong.
+ */
+ if (afs_vhashT[hash] == avc) {
+ /* First in hash chain (might be the only one). */
+ afs_vhashT[hash] = avc->hnext;
+ } else {
+ /* More elements in hash chain. */
+ for (tvc = afs_vhashT[hash]; tvc; tvc = tvc->hnext) {
+ if (tvc->hnext == avc) {
+ tvc->hnext = avc->hnext;
+ break;
+ }
+ }
+ } /* if (!afs_vhashT[i]->hnext) */
+ QRemove(&avc->vhashq);
+
+ /* Insert hash in new position. */
+ avc->hnext = afs_vhashT[new_hash];
+ afs_vhashT[new_hash] = avc;
+ QAdd(&afs_vhashTV[VCHashV(&newFid)], &avc->vhashq);
+
+ ReleaseWriteLock(&afs_xvcache);
+
+ /* Do the same thing for all dcaches. */
+ hash = DVHash(&avc->f.fid);
+ ObtainWriteLock(&afs_xdcache, 743);
+ for (index = afs_dvhashTbl[hash]; index != NULLIDX; index = hash) {
+ hash = afs_dvnextTbl[index];
+ tdc = afs_GetDSlot(index, NULL);
+ ReleaseReadLock(&tdc->tlock);
+ if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
+ if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
+
+ /* Safer but slower. */
+ afs_HashOutDCache(tdc, 0);
+
+ /* Put dcache in new positions in the dchash and dvhash. */
+ new_hash = DCHash(&newFid, tdc->f.chunk);
+ afs_dcnextTbl[tdc->index] = afs_dchashTbl[new_hash];
+ afs_dchashTbl[new_hash] = tdc->index;
+
+ new_hash = DVHash(&newFid);
+ afs_dvnextTbl[tdc->index] = afs_dvhashTbl[new_hash];
+ afs_dvhashTbl[new_hash] = tdc->index;
+
+ afs_indexUnique[tdc->index] = newFid.Fid.Unique;
+ memcpy(&tdc->f.fid, &newFid, sizeof(struct VenusFid));
+ } /* if fid match */
+ } /* if uniquifier match */
+ if (tdc)
+ afs_PutDCache(tdc);
+ } /* for all dcaches in this hash bucket */
+ ReleaseWriteLock(&afs_xdcache);
+
+ /* Now we can set the new fid. */
+ memcpy(&avc->f.fid, &newFid, sizeof(struct VenusFid));
+
+end:
+ if (tdp)
+ afs_PutVCache(tdp);
+ afs_osi_Free(tname, AFSNAMEMAX);
+ if (ttargetName)
+ afs_osi_Free(ttargetName, tlen);
+ return code;
+}
+
+/*!
+ * Remove a vnode on the server, be it file or directory.
+ * Not much to do here only get the parent dir's fid and call the
+ * removal rpc.
+ *
+ * \param avc The deleted vcache
+ * \param areq
+ *
+ * \note The vcache refcount should be dropped because it points to
+ * a deleted vnode and has served it's purpouse, but we drop refcount
+ * on shadow dir deletio (we still need it for that).
+ *
+ * \note avc must be write locked.
+ */
+int
+afs_ProcessOpRemove(struct vcache *avc, struct vrequest *areq)
+{
+ char *tname = NULL;
+ struct AFSFetchStatus OutDirStatus;
+ struct VenusFid pdir_fid;
+ struct AFSVolSync tsync;
+ struct afs_conn *tc;
+ struct vcache *tdp = NULL;
+ int code = 0;
+ XSTATS_DECLS;
+
+ tname = afs_osi_Alloc(AFSNAMEMAX);
+ if (!tname) {
+ /* printf("afs_ProcessOpRemove: Couldn't alloc space for file name\n"); */
+ return ENOMEM;
+ }
+
+ code = afs_GetParentVCache(avc, 1, &pdir_fid, tname, &tdp);
+ if (code)
+ goto end;
+
+ if ((vType(avc) == VDIR) && (afs_CheckDeletedChildren(avc))) {
+ /* Deleted children of this dir remain unsynchronized.
+ * Defer this vcache.
+ */
+ code = EAGAIN;
+ goto end;
+ }
+
+ if (vType(avc) == VREG || vType(avc) == VLNK) {
+ /* Remove file on server. */
+ do {
+ tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
+ if (tc) {
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEFILE);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_RemoveFile(tc->id,
+ &pdir_fid.Fid,
+ tname,
+ &OutDirStatus,
+ &tsync);
+
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+ } while (afs_Analyze(tc,
+ code,
+ &pdir_fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_REMOVEFILE,
+ SHARED_LOCK,
+ NULL));
+
+ } else if (vType(avc) == VDIR) {
+ /* Remove dir on server. */
+ do {
+ tc = afs_Conn(&pdir_fid, areq, SHARED_LOCK);
+ if (tc) {
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_REMOVEDIR);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_RemoveDir(tc->id,
+ &pdir_fid.Fid,
+ tname,
+ &OutDirStatus,
+ &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+ } while (afs_Analyze(tc,
+ code,
+ &pdir_fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_REMOVEDIR,
+ SHARED_LOCK,
+ NULL));
+
+ } /* if (vType(avc) == VREG) */
+
+ /* if (code) printf("afs_ProcessOpRemove: server returned code=%u\n", code); */
+
+end:
+ afs_osi_Free(tname, AFSNAMEMAX);
+ return code;
+}
+
+/*!
+ * Send disconnected file changes to the server.
+ *
+ * \note Call with vnode locked both locally and on the server.
+ *
+ * \param avc Vnode that gets synchronized to the server.
+ * \param areq Used for obtaining a conn struct.
+ *
+ * \return 0 for success. On failure, other error codes.
+ */
+int
+afs_SendChanges(struct vcache *avc, struct vrequest *areq)
+{
+ struct afs_conn *tc;
+ struct AFSStoreStatus sstat;
+ struct AFSFetchStatus fstat;
+ struct AFSVolSync tsync;
+ int code = 0;
+ int flags = 0;
+ XSTATS_DECLS;
+
+ /* Start multiplexing dirty operations from ddirty_flags field: */
+ if (avc->f.ddirty_flags & VDisconSetAttrMask) {
+ /* Setattr OPS: */
+ /* Turn dirty vc data into a new store status... */
+ if (afs_GenStoreStatus(avc, &sstat) > 0) {
+ do {
+ tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ /* ... and send it. */
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_StoreStatus(tc->id,
+ (struct AFSFid *) &avc->f.fid.Fid,
+ &sstat,
+ &fstat,
+ &tsync);
+
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+
+ } while (afs_Analyze(tc,
+ code,
+ &avc->f.fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_STORESTATUS,
+ SHARED_LOCK,
+ NULL));
+
+ } /* if (afs_GenStoreStatus() > 0)*/
+ } /* disconnected SETATTR */
+
+ if (code)
+ return code;
+
+ if (avc->f.ddirty_flags &
+ (VDisconTrunc
+ | VDisconWriteClose
+ | VDisconWriteFlush
+ | VDisconWriteOsiFlush)) {
+
+ /* Truncate OP: */
+ do {
+ tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ /* Set storing flags. XXX: A tad inefficient ... */
+ if (avc->f.ddirty_flags & VDisconWriteClose)
+ flags |= AFS_LASTSTORE;
+ if (avc->f.ddirty_flags & VDisconWriteOsiFlush)
+ flags |= (AFS_SYNC | AFS_LASTSTORE);
+ if (avc->f.ddirty_flags & VDisconWriteFlush)
+ flags |= AFS_SYNC;
+
+ /* Try to send store to server. */
+ /* XXX: AFS_LASTSTORE for writes? Or just AFS_SYNC for all? */
+ code = afs_StoreAllSegments(avc, areq, flags);
+ } else
+ code = -1;
+
+ } while (afs_Analyze(tc,
+ code,
+ &avc->f.fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_STOREDATA,
+ SHARED_LOCK,
+ NULL));
+
+ } /* disconnected TRUNC | WRITE */
+
+ return code;
+}
+
+/*!
+ * All files that have been dirty before disconnection are going to
+ * be replayed back to the server.
+ *
+ * \param areq Request from the user.
+ * \param acred User credentials.
+ *
+ * \return If all files synchronized succesfully, return 0, otherwise
+ * return error code
+ *
+ * \note For now, it's the request from the PDiscon pioctl.
+ *
+ */
+int
+afs_ResyncDisconFiles(struct vrequest *areq, afs_ucred_t *acred)
+{
+ struct afs_conn *tc;
+ struct vcache *tvc;
+ struct AFSFetchStatus fstat;
+ struct AFSCallBack callback;
+ struct AFSVolSync tsync;
+ int code = 0;
+ afs_int32 start = 0;
+ XSTATS_DECLS;
+ /*AFS_STATCNT(afs_ResyncDisconFiles);*/
+
+ ObtainWriteLock(&afs_disconDirtyLock, 707);
+
+ while (!QEmpty(&afs_disconDirty)) {
+ tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
+
+ /* Can't lock tvc whilst holding the discon dirty lock */
+ ReleaseWriteLock(&afs_disconDirtyLock);
+
+ /* Get local write lock. */
+ ObtainWriteLock(&tvc->lock, 705);
+
+ if (tvc->f.ddirty_flags & VDisconRemove) {
+ /* Delete the file on the server and just move on
+ * to the next file. After all, it has been deleted
+ * we can't replay any other operation it.
+ */
+ code = afs_ProcessOpRemove(tvc, areq);
+ goto next_file;
+
+ } else if (tvc->f.ddirty_flags & VDisconCreate) {
+ /* For newly created files, we don't need a server lock. */
+ code = afs_ProcessOpCreate(tvc, areq, acred);
+ if (code)
+ goto next_file;
+
+ tvc->f.ddirty_flags &= ~VDisconCreate;
+ tvc->f.ddirty_flags |= VDisconCreated;
+ }
+#if 0
+ /* Get server write lock. */
+ do {
+ tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_SETLOCK);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_SetLock(tc->id,
+ (struct AFSFid *)&tvc->f.fid.Fid,
+ LockWrite,
+ &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+
+ } while (afs_Analyze(tc,
+ code,
+ &tvc->f.fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_SETLOCK,
+ SHARED_LOCK,
+ NULL));
+
+ if (code)
+ goto next_file;
+#endif
+ if (tvc->f.ddirty_flags & VDisconRename) {
+ /* If we're renaming the file, do so now */
+ code = afs_ProcessOpRename(tvc, areq);
+ if (code)
+ goto unlock_srv_file;
+ }
+
+ /* Issue a FetchStatus to get info about DV and callbacks. */
+ do {
+ tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ tvc->callback = tc->srvr->server;
+ start = osi_Time();
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
+ RX_AFS_GUNLOCK();
+ code = RXAFS_FetchStatus(tc->id,
+ (struct AFSFid *)&tvc->f.fid.Fid,
+ &fstat,
+ &callback,
+ &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ code = -1;
+
+ } while (afs_Analyze(tc,
+ code,
+ &tvc->f.fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_FETCHSTATUS,
+ SHARED_LOCK,
+ NULL));
+
+ if (code) {
+ goto unlock_srv_file;
+ }
+
+ if ((dv_match(tvc, fstat) && (tvc->f.m.Date == fstat.ServerModTime)) ||
+ (afs_ConflictPolicy == CLIENT_WINS) ||
+ (tvc->f.ddirty_flags & VDisconCreated)) {
+ /*
+ * Send changes to the server if there's data version match, or
+ * client wins policy has been selected or file has been created
+ * but doesn't have it's the contents on to the server yet.
+ */
+ /*
+ * XXX: Checking server attr changes by timestamp might not the
+ * most elegant solution, but it's the most viable one that we could find.
+ */
+ afs_UpdateStatus(tvc, &tvc->f.fid, areq, &fstat, &callback, start);
+ code = afs_SendChanges(tvc, areq);
+
+ } else if (afs_ConflictPolicy == SERVER_WINS) {
+ /* DV mismatch, apply collision resolution policy. */
+ /* Discard this files chunks and remove from current dir. */
+ afs_ResetVCache(tvc, acred);
+ tvc->f.truncPos = AFS_NOTRUNC;
+ } else {
+ /* printf("afs_ResyncDisconFiles: no resolution policy selected.\n"); */
+ } /* if DV match or client wins policy */
+
+unlock_srv_file:
+ /* Release server write lock. */
+#if 0
+ do {
+ tc = afs_Conn(&tvc->f.fid, areq, SHARED_LOCK);
+ if (tc) {
+ XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_RELEASELOCK);
+ RX_AFS_GUNLOCK();
+ ucode = RXAFS_ReleaseLock(tc->id,
+ (struct AFSFid *) &tvc->f.fid.Fid,
+ &tsync);
+ RX_AFS_GLOCK();
+ XSTATS_END_TIME;
+ } else
+ ucode = -1;
+ } while (afs_Analyze(tc,
+ ucode,
+ &tvc->f.fid,
+ areq,
+ AFS_STATS_FS_RPCIDX_RELEASELOCK,
+ SHARED_LOCK,
+ NULL));
#endif
+next_file:
+ ObtainWriteLock(&afs_disconDirtyLock, 710);
+ if (code == 0) {
+ /* Replayed successfully - pull the vcache from the
+ * disconnected list */
+ tvc->f.ddirty_flags = 0;
+ QRemove(&tvc->dirtyq);
+ afs_PutVCache(tvc);
+ } else {
+ if (code == EAGAIN) {
+ /* Operation was deferred. Pull it from the current place in
+ * the list, and stick it at the end again */
+ QRemove(&tvc->dirtyq);
+ QAdd(&afs_disconDirty, &tvc->dirtyq);
+ } else {
+ /* Failed - keep state as is, and let the user know we died */
+
+ ReleaseWriteLock(&tvc->lock);
+ break;
+ }
+ }
+
+ /* Release local write lock. */
+ ReleaseWriteLock(&tvc->lock);
+ } /* while (tvc) */
+
+ if (code) {
+ ReleaseWriteLock(&afs_disconDirtyLock);
+ return code;
+ }
+
+ /* Dispose of all of the shadow directories */
+ afs_DisconDiscardAllShadows(0, acred);
+
+ ReleaseWriteLock(&afs_disconDirtyLock);
+ return code;
+}
+
+/*!
+ * Discard all of our shadow directory copies. If squash is true, then
+ * we also invalidate the vcache holding the shadow directory, to ensure
+ * that any disconnected changes are deleted
+ *
+ * \param squash
+ * \param acred
+ *
+ * \note afs_disconDirtyLock must be held on entry. It will be released
+ * and reobtained
+ */
+
+static void
+afs_DisconDiscardAllShadows(int squash, afs_ucred_t *acred)
+{
+ struct vcache *tvc;
+
+ while (!QEmpty(&afs_disconShadow)) {
+ tvc = QEntry(QNext(&afs_disconShadow), struct vcache, shadowq);
+
+ /* Must release the dirty lock to be able to get a vcache lock */
+ ReleaseWriteLock(&afs_disconDirtyLock);
+ ObtainWriteLock(&tvc->lock, 706);
+
+ if (squash)
+ afs_ResetVCache(tvc, acred);
+
+ afs_DeleteShadowDir(tvc);
+
+ ReleaseWriteLock(&tvc->lock);
+ ObtainWriteLock(&afs_disconDirtyLock, 709);
+ } /* while (tvc) */
+}
+
+/*!
+ * This function throws away the whole disconnected state, allowing
+ * the cache manager to reconnect to a server if we get into a state
+ * where reconiliation is impossible.
+ *
+ * \param acred
+ *
+ */
+void
+afs_DisconDiscardAll(afs_ucred_t *acred)
+{
+ struct vcache *tvc;
+
+ ObtainWriteLock(&afs_disconDirtyLock, 717);
+ while (!QEmpty(&afs_disconDirty)) {
+ tvc = QEntry(QPrev(&afs_disconDirty), struct vcache, dirtyq);
+ QRemove(&tvc->dirtyq);
+ ReleaseWriteLock(&afs_disconDirtyLock);
+
+ ObtainWriteLock(&tvc->lock, 718);
+ afs_ResetVCache(tvc, acred);
+ tvc->f.truncPos = AFS_NOTRUNC;
+ ReleaseWriteLock(&tvc->lock);
+ ObtainWriteLock(&afs_disconDirtyLock, 719);
+ afs_PutVCache(tvc);
+ }
+
+ afs_DisconDiscardAllShadows(1, acred);
+
+ ReleaseWriteLock(&afs_disconDirtyLock);
+}
+
+/*!
+ * Print list of disconnected files.
+ *
+ * \note Call with afs_DDirtyVCListLock read locked.
+ */
+void
+afs_DbgDisconFiles(void)
+{
+ struct vcache *tvc;
+ struct afs_q *q;
+ int i = 0;
+
+ afs_warn("List of dirty files: \n");
+
+ ObtainReadLock(&afs_disconDirtyLock);
+ for (q = QPrev(&afs_disconDirty); q != &afs_disconDirty; q = QPrev(q)) {
+ tvc = QEntry(q, struct vcache, dirtyq);
+
+ afs_warn("Cell=%u Volume=%u VNode=%u Unique=%u\n",
+ tvc->f.fid.Cell,
+ tvc->f.fid.Fid.Volume,
+ tvc->f.fid.Fid.Vnode,
+ tvc->f.fid.Fid.Unique);
+
+ i++;
+ if (i >= 30)
+ osi_Panic("afs_DbgDisconFiles: loop in dirty list\n");
+ }
+ ReleaseReadLock(&afs_disconDirtyLock);
+}
+
+/*!
+ * Generate a fake fid for a disconnected shadow dir.
+ * Similar to afs_GenFakeFid, only that it uses the dhash
+ * to search for a uniquifier because a shadow dir lives only
+ * in the dcache.
+ *
+ * \param afid
+ *
+ * \note Don't forget to fill in afid with Cell and Volume.
+ */
+void
+afs_GenShadowFid(struct VenusFid *afid)
+{
+ afs_uint32 i, index, max_unique = 1;
+ struct vcache *tvc = NULL;
+
+ /* Try generating a fid that isn't used in the vhash. */
+ do {
+ /* Shadow Fids are always directories */
+ afid->Fid.Vnode = afs_DisconVnode + 1;
+
+ i = DVHash(afid);
+ ObtainWriteLock(&afs_xdcache, 737);
+ for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
+ i = afs_dvnextTbl[index];
+ if (afs_indexUnique[index] > max_unique)
+ max_unique = afs_indexUnique[index];
+ }
+
+ ReleaseWriteLock(&afs_xdcache);
+ afid->Fid.Unique = max_unique + 1;
+ afs_DisconVnode += 2;
+ if (!afs_DisconVnode)
+ afs_DisconVnode = 2;
+
+ /* Is this a used vnode? */
+ ObtainSharedLock(&afs_xvcache, 762);
+ tvc = afs_FindVCache(afid, 0, 1);
+ ReleaseSharedLock(&afs_xvcache);
+ if (tvc)
+ afs_PutVCache(tvc);
+ } while (tvc);
+}
+
+/*!
+ * Generate a fake fid (vnode and uniquifier) for a vcache
+ * (either dir or normal file). The vnode is generated via
+ * afs_DisconVNode and the uniquifier by getting the highest
+ * uniquifier on a hash chain and incrementing it by one.
+ *
+ * \param afid The fid structre that will be filled.
+ * \param avtype Vnode type: VDIR/VREG.
+ * \param lock True indicates that xvcache may be obtained,
+ * False that it is already held
+ *
+ * \note The cell number must be completed somewhere else.
+ */
+void
+afs_GenFakeFid(struct VenusFid *afid, afs_uint32 avtype, int lock)
+{
+ struct vcache *tvc;
+ afs_uint32 max_unique = 0, i;
+
+ switch (avtype) {
+ case VDIR:
+ afid->Fid.Vnode = afs_DisconVnode + 1;
+ break;
+ case VREG:
+ case VLNK:
+ afid->Fid.Vnode = afs_DisconVnode;
+ break;
+ }
+
+ if (lock)
+ ObtainWriteLock(&afs_xvcache, 736);
+ i = VCHash(afid);
+ for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
+ if (tvc->f.fid.Fid.Unique > max_unique)
+ max_unique = tvc->f.fid.Fid.Unique;
+ }
+ if (lock)
+ ReleaseWriteLock(&afs_xvcache);
+
+ afid->Fid.Unique = max_unique + 1;
+ afs_DisconVnode += 2;
+ if (!afs_DisconVnode)
+ afs_DisconVnode = 2;
+}
+
+/*!
+ * Fill in stats for a newly created file/directory.
+ *
+ * \param adp The parent dir's vcache.
+ * \param avc The created vnode.
+ * \param afid The new fid.
+ * \param attrs
+ * \param areq
+ * \param file_type Specify if file or directory.
+ *
+ * \note Call with avc write locked.
+ */
+void
+afs_GenDisconStatus(struct vcache *adp, struct vcache *avc,
+ struct VenusFid *afid, struct vattr *attrs,
+ struct vrequest *areq, int file_type)
+{
+ memcpy(&avc->f.fid, afid, sizeof(struct VenusFid));
+ avc->f.m.Mode = attrs->va_mode;
+ /* Used to do this:
+ * avc->f.m.Owner = attrs->va_uid;
+ * But now we use the parent dir's ownership,
+ * there's no other way to get a server owner id.
+ * XXX: Does it really matter?
+ */
+ avc->f.m.Group = adp->f.m.Group;
+ avc->f.m.Owner = adp->f.m.Owner;
+ hset64(avc->f.m.DataVersion, 0, 0);
+ avc->f.m.Length = attrs->va_size;
+ avc->f.m.Date = osi_Time();
+ switch(file_type) {
+ case VREG:
+ vSetType(avc, VREG);
+ avc->f.m.Mode |= S_IFREG;
+ avc->f.m.LinkCount = 1;
+ avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+ avc->f.parent.unique = adp->f.fid.Fid.Unique;
+ break;
+ case VDIR:
+ vSetType(avc, VDIR);
+ avc->f.m.Mode |= S_IFDIR;
+ avc->f.m.LinkCount = 2;
+ break;
+ case VLNK:
+ vSetType(avc, VLNK);
+ avc->f.m.Mode |= S_IFLNK;
+ if ((avc->f.m.Mode & 0111) == 0)
+ avc->mvstat = 1;
+ avc->f.parent.vnode = adp->f.fid.Fid.Vnode;
+ avc->f.parent.unique = adp->f.fid.Fid.Unique;
+ break;
+ default:
+ break;
+ }
+ avc->f.anyAccess = adp->f.anyAccess;
+ afs_AddAxs(avc->Access, areq->uid, adp->Access->axess);
+
+ avc->callback = NULL;
+ avc->f.states |= CStatd;
+ avc->f.states &= ~CBulkFetching;
+}