2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 * Dynamic /afs volume support.
15 * afs_IsDynrootMountFid
18 * afs_GetDynrootMountFid
22 * afs_DynrootInvalidate
26 * afs_SetDynrootEnable
27 * afs_GetDynrootEnable
28 * afs_DynrootVOPRemove
29 * afs_DynrootVOPSymlink
33 #include <afsconfig.h>
34 #include "afs/param.h"
37 #include "afs/sysincludes.h" /* Standard vendor system headers */
38 #include "afsincludes.h"
39 #include "afs/afs_osi.h"
43 #include "afs/prs_fs.h"
45 #include "afs/afs_dynroot.h"
47 #define AFS_DYNROOT_CELLNAME "dynroot"
48 #define AFS_DYNROOT_VOLUME 1
49 #define AFS_DYNROOT_VNODE 1
50 #define AFS_DYNROOT_MOUNT_VNODE 3
51 #define AFS_DYNROOT_UNIQUE 1
53 static int afs_dynrootInit = 0;
54 static int afs_dynrootEnable = 0;
55 static int afs_dynrootCell = 0;
57 static afs_rwlock_t afs_dynrootDirLock;
58 /* Start of variables protected by afs_dynrootDirLock */
59 static char *afs_dynrootDir = NULL;
60 static int afs_dynrootDirLen;
61 static char *afs_dynrootMountDir = NULL;
62 static int afs_dynrootMountDirLen;
63 static int afs_dynrootDirLinkcnt;
64 static int afs_dynrootDirVersion;
65 static int afs_dynrootVersion = 1;
66 static int afs_dynrootVersionHigh = 1;
67 /* End of variables protected by afs_dynrootDirLock */
69 /* A dynamically-created symlink in a dynroot /afs */
70 struct afs_dynSymlink {
71 struct afs_dynSymlink *next;
77 static afs_rwlock_t afs_dynSymlinkLock;
78 /* Start of variables protected by afs_dynSymlinkLock */
79 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
80 static int afs_dynSymlinkIndex = 0;
81 /* End of variables protected by afs_dynSymlinkLock */
84 * Set up a cell for dynroot if it's not there yet.
89 if (!afs_dynrootCell) {
90 afs_int32 cellHosts[MAXCELLHOSTS];
94 memset(cellHosts, 0, sizeof(cellHosts));
96 afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
100 tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
103 afs_dynrootCell = tc->cellNum;
104 afs_PutCell(tc, READ_LOCK);
111 * Returns non-zero iff fid corresponds to the top of the dynroot volume.
114 _afs_IsDynrootFid(struct VenusFid *fid)
116 return (fid->Cell == afs_dynrootCell
117 && fid->Fid.Volume == AFS_DYNROOT_VOLUME
118 && fid->Fid.Vnode == AFS_DYNROOT_VNODE
119 && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
123 afs_IsDynrootFid(struct VenusFid *fid)
125 return (afs_dynrootEnable && _afs_IsDynrootFid(fid));
129 afs_IsDynrootMountFid(struct VenusFid *fid)
131 return (fid->Cell == afs_dynrootCell
132 && fid->Fid.Volume == AFS_DYNROOT_VOLUME
133 && fid->Fid.Vnode == AFS_DYNROOT_MOUNT_VNODE
134 && fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
138 afs_IsDynrootAnyFid(struct VenusFid *fid)
140 return (fid->Cell == afs_dynrootCell
141 && fid->Fid.Volume == AFS_DYNROOT_VOLUME);
145 * Obtain the magic dynroot volume Fid.
148 afs_GetDynrootFid(struct VenusFid *fid)
150 fid->Cell = afs_dynrootCell;
151 fid->Fid.Volume = AFS_DYNROOT_VOLUME;
152 fid->Fid.Vnode = AFS_DYNROOT_VNODE;
153 fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
157 afs_GetDynrootMountFid(struct VenusFid *fid)
159 fid->Cell = afs_dynrootCell;
160 fid->Fid.Volume = AFS_DYNROOT_VOLUME;
161 fid->Fid.Vnode = AFS_DYNROOT_MOUNT_VNODE;
162 fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
166 * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
169 afs_IsDynroot(struct vcache *avc)
171 return afs_IsDynrootFid(&avc->fid);
175 afs_IsDynrootMount(struct vcache *avc)
177 return afs_IsDynrootMountFid(&avc->fid);
181 afs_IsDynrootAny(struct vcache *avc)
183 return afs_IsDynrootAnyFid(&avc->fid);
187 * Given the current page and chunk pointers in a directory, adjust them
188 * appropriately so that the given file name can be appended. Used for
189 * computing the size of a directory.
192 afs_dynroot_computeDirEnt(char *name, int *curPageP, int *curChunkP)
196 esize = afs_dir_NameBlobs(name);
197 if (*curChunkP + esize > EPP) {
205 * Add directory entry by given name to a directory. Assumes the
206 * caller has allocated the directory to be large enough to hold
207 * the necessary entry.
210 afs_dynroot_addDirEnt(struct DirHeader *dirHeader, int *curPageP,
211 int *curChunkP, char *name, int vnode)
213 char *dirBase = (char *)dirHeader;
214 struct PageHeader *pageHeader;
215 struct DirEntry *dirEntry;
216 int sizeOfEntry, i, t1, t2;
217 int curPage = *curPageP;
218 int curChunk = *curChunkP;
222 * Check if we need to flip pages.. If so, init the new page.
224 sizeOfEntry = afs_dir_NameBlobs(name);
225 if (curChunk + sizeOfEntry > EPP) {
231 pageHeader = (struct PageHeader *)(dirBase + curPage * AFS_PAGESIZE);
233 pageHeader->pgcount = 0;
234 pageHeader->tag = htons(1234);
235 pageHeader->freecount = 0;
236 pageHeader->freebitmap[0] = 0x01;
237 for (i = 1; i < EPP / 8; i++)
238 pageHeader->freebitmap[i] = 0;
240 dirHeader->alloMap[curPage] = EPP - 1;
243 dirEntry = (struct DirEntry *)(pageHeader + curChunk);
245 dirEntry->length = 0;
247 dirEntry->fid.vnode = htonl(vnode);
248 dirEntry->fid.vunique = htonl(1);
249 strcpy(dirEntry->name, name);
251 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
254 pageHeader->freebitmap[t1] |= (1 << t2);
258 * Add the new entry to the correct hash chain.
261 dirEntry->next = dirHeader->hashTable[i];
262 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
264 curChunk += sizeOfEntry;
265 dirHeader->alloMap[curPage] -= sizeOfEntry;
268 *curChunkP = curChunk;
272 * Invalidate the /afs vnode for dynroot; called when the underlying
273 * directory has changed and needs to be re-read.
276 afs_DynrootInvalidate(void)
280 struct VenusFid tfid;
282 if (!afs_dynrootEnable)
285 ObtainWriteLock(&afs_dynrootDirLock, 687);
286 afs_dynrootVersion++;
287 afs_dynrootVersionHigh = osi_Time();
288 ReleaseWriteLock(&afs_dynrootDirLock);
290 afs_GetDynrootFid(&tfid);
293 ObtainReadLock(&afs_xvcache);
294 tvc = afs_FindVCache(&tfid, &retry, 0);
295 ReleaseReadLock(&afs_xvcache);
298 tvc->states &= ~(CStatd | CUnique);
299 osi_dnlc_purgedp(tvc);
305 * Regenerates the dynroot contents from the current list of
306 * cells. Useful when the list of cells has changed due to
307 * an AFSDB lookup, for instance.
310 afs_RebuildDynroot(void)
312 int cellidx, maxcellidx, i;
313 int aliasidx, maxaliasidx;
315 struct cell_alias *ca;
316 int curChunk, curPage;
318 char *newDir, *dotCell;
319 struct DirHeader *dirHeader;
321 struct afs_dynSymlink *ts;
324 ObtainReadLock(&afs_dynrootDirLock);
325 newVersion = afs_dynrootVersion;
326 ReleaseReadLock(&afs_dynrootDirLock);
329 * Compute the amount of space we need for the fake dir
334 /* Reserve space for "." and ".." */
337 /* Reserve space for the dynamic-mount directory */
338 afs_dynroot_computeDirEnt(AFS_DYNROOT_MOUNTNAME, &curPage, &curChunk);
340 for (cellidx = 0;; cellidx++) {
341 c = afs_GetCellByIndex(cellidx, READ_LOCK);
344 if (c->cellNum == afs_dynrootCell)
347 dotLen = strlen(c->cellName) + 2;
348 dotCell = afs_osi_Alloc(dotLen);
349 strcpy(dotCell, ".");
350 afs_strcat(dotCell, c->cellName);
352 afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
353 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
355 afs_osi_Free(dotCell, dotLen);
356 afs_PutCell(c, READ_LOCK);
358 maxcellidx = cellidx;
360 for (aliasidx = 0;; aliasidx++) {
361 ca = afs_GetCellAlias(aliasidx);
365 dotLen = strlen(ca->alias) + 2;
366 dotCell = afs_osi_Alloc(dotLen);
367 strcpy(dotCell, ".");
368 afs_strcat(dotCell, ca->alias);
370 afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
371 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
373 afs_osi_Free(dotCell, dotLen);
374 afs_PutCellAlias(ca);
376 maxaliasidx = aliasidx;
378 ObtainReadLock(&afs_dynSymlinkLock);
379 ts = afs_dynSymlinkBase;
381 afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
385 dirSize = (curPage + 1) * AFS_PAGESIZE;
386 newDir = afs_osi_Alloc(dirSize);
389 * Now actually construct the directory.
393 dirHeader = (struct DirHeader *)newDir;
395 dirHeader->header.pgcount = 0;
396 dirHeader->header.tag = htons(1234);
397 dirHeader->header.freecount = 0;
399 dirHeader->header.freebitmap[0] = 0xff;
400 dirHeader->header.freebitmap[1] = 0x1f;
401 for (i = 2; i < EPP / 8; i++)
402 dirHeader->header.freebitmap[i] = 0;
403 dirHeader->alloMap[0] = EPP - DHE - 1;
404 for (i = 1; i < MAXPAGES; i++)
405 dirHeader->alloMap[i] = EPP;
406 for (i = 0; i < NHASHENT; i++)
407 dirHeader->hashTable[i] = 0;
409 /* Install ".", "..", and the dynamic mount directory */
410 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
411 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
412 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
413 AFS_DYNROOT_MOUNTNAME, AFS_DYNROOT_MOUNT_VNODE);
416 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
417 c = afs_GetCellByIndex(cellidx, READ_LOCK);
420 if (c->cellNum == afs_dynrootCell)
423 dotLen = strlen(c->cellName) + 2;
424 dotCell = afs_osi_Alloc(dotLen);
425 strcpy(dotCell, ".");
426 afs_strcat(dotCell, c->cellName);
427 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, c->cellName,
428 VNUM_FROM_CIDX_RW(cellidx, 0));
429 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
430 VNUM_FROM_CIDX_RW(cellidx, 1));
431 afs_osi_Free(dotCell, dotLen);
434 afs_PutCell(c, READ_LOCK);
437 for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
438 ca = afs_GetCellAlias(aliasidx);
442 dotLen = strlen(ca->alias) + 2;
443 dotCell = afs_osi_Alloc(dotLen);
444 strcpy(dotCell, ".");
445 afs_strcat(dotCell, ca->alias);
446 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ca->alias,
447 VNUM_FROM_CAIDX_RW(aliasidx, 0));
448 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, dotCell,
449 VNUM_FROM_CAIDX_RW(aliasidx, 1));
450 afs_osi_Free(dotCell, dotLen);
451 afs_PutCellAlias(ca);
454 ts = afs_dynSymlinkBase;
456 int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
457 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ts->name, vnum);
461 ReleaseReadLock(&afs_dynSymlinkLock);
463 ObtainWriteLock(&afs_dynrootDirLock, 549);
465 afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
466 afs_dynrootDir = newDir;
467 afs_dynrootDirLen = dirSize;
468 afs_dynrootDirLinkcnt = linkCount;
469 afs_dynrootDirVersion = newVersion;
470 ReleaseWriteLock(&afs_dynrootDirLock);
474 afs_RebuildDynrootMount(void)
477 int curChunk, curPage;
479 struct DirHeader *dirHeader;
481 newDir = afs_osi_Alloc(AFS_PAGESIZE);
484 * Now actually construct the directory.
488 dirHeader = (struct DirHeader *)newDir;
490 dirHeader->header.pgcount = 0;
491 dirHeader->header.tag = htons(1234);
492 dirHeader->header.freecount = 0;
494 dirHeader->header.freebitmap[0] = 0xff;
495 dirHeader->header.freebitmap[1] = 0x1f;
496 for (i = 2; i < EPP / 8; i++)
497 dirHeader->header.freebitmap[i] = 0;
498 dirHeader->alloMap[0] = EPP - DHE - 1;
499 for (i = 1; i < MAXPAGES; i++)
500 dirHeader->alloMap[i] = EPP;
501 for (i = 0; i < NHASHENT; i++)
502 dirHeader->hashTable[i] = 0;
504 /* Install "." and ".." */
505 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
506 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
508 ObtainWriteLock(&afs_dynrootDirLock, 549);
509 if (afs_dynrootMountDir)
510 afs_osi_Free(afs_dynrootMountDir, afs_dynrootMountDirLen);
511 afs_dynrootMountDir = newDir;
512 afs_dynrootMountDirLen = AFS_PAGESIZE;
513 ReleaseWriteLock(&afs_dynrootDirLock);
517 * Returns a pointer to the base of the dynroot directory in memory,
518 * length thereof, and a FetchStatus.
521 afs_GetDynroot(char **dynrootDir, int *dynrootLen,
522 struct AFSFetchStatus *status)
524 ObtainReadLock(&afs_dynrootDirLock);
525 if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
526 ReleaseReadLock(&afs_dynrootDirLock);
527 afs_RebuildDynroot();
528 ObtainReadLock(&afs_dynrootDirLock);
532 *dynrootDir = afs_dynrootDir;
534 *dynrootLen = afs_dynrootDirLen;
537 memset(status, 0, sizeof(struct AFSFetchStatus));
538 status->FileType = Directory;
539 status->LinkCount = afs_dynrootDirLinkcnt;
540 status->Length = afs_dynrootDirLen;
541 status->DataVersion = afs_dynrootVersion;
542 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
543 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
544 status->UnixModeBits = 0755;
545 status->ParentVnode = 1;
546 status->ParentUnique = 1;
547 status->dataVersionHigh = afs_dynrootVersionHigh;
552 afs_GetDynrootMount(char **dynrootDir, int *dynrootLen,
553 struct AFSFetchStatus *status)
555 ObtainReadLock(&afs_dynrootDirLock);
556 if (!afs_dynrootMountDir) {
557 ReleaseReadLock(&afs_dynrootDirLock);
558 afs_RebuildDynrootMount();
559 ObtainReadLock(&afs_dynrootDirLock);
563 *dynrootDir = afs_dynrootMountDir;
565 *dynrootLen = afs_dynrootMountDirLen;
568 memset(status, 0, sizeof(struct AFSFetchStatus));
569 status->FileType = Directory;
570 status->LinkCount = 1;
571 status->Length = afs_dynrootMountDirLen;
572 status->DataVersion = 1;
573 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
574 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
575 status->UnixModeBits = 0755;
576 status->ParentVnode = 1;
577 status->ParentUnique = 1;
578 status->dataVersionHigh = 0;
583 * Puts back the dynroot read lock.
588 ReleaseReadLock(&afs_dynrootDirLock);
592 * Inform dynroot that a new vnode is being created. Return value
593 * is non-zero if this vnode is handled by dynroot, in which case
594 * FetchStatus will be filled in.
597 afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
599 char *bp, tbuf[CVBS];
601 if (_afs_IsDynrootFid(&avc->fid)) {
602 if (!afs_dynrootEnable)
604 afs_GetDynroot(0, 0, status);
609 if (afs_IsDynrootMount(avc)) {
610 afs_GetDynrootMount(0, 0, status);
616 * Check if this is an entry under /afs, e.g. /afs/cellname.
618 if (avc->fid.Cell == afs_dynrootCell
619 && avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
622 struct cell_alias *ca;
623 int namelen, linklen, cellidx, rw;
625 memset(status, 0, sizeof(struct AFSFetchStatus));
627 status->FileType = SymbolicLink;
628 status->LinkCount = 1;
629 status->DataVersion = 1;
630 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
631 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
632 status->ParentVnode = 1;
633 status->ParentUnique = 1;
635 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
636 struct afs_dynSymlink *ts;
637 int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
639 ObtainReadLock(&afs_dynSymlinkLock);
640 ts = afs_dynSymlinkBase;
642 if (ts->index == index)
648 linklen = strlen(ts->target);
649 avc->linkData = afs_osi_Alloc(linklen + 1);
650 strcpy(avc->linkData, ts->target);
652 status->Length = linklen;
653 status->UnixModeBits = 0755;
655 ReleaseReadLock(&afs_dynSymlinkLock);
660 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL
661 && VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_ALIAS
662 && VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_MOUNT) {
663 afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
664 VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
668 cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
669 rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
671 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_ALIAS) {
674 ca = afs_GetCellAlias(cellidx);
676 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
682 * linkData needs to contain the name of the cell
683 * we're aliasing for.
687 afs_warn("dynroot: alias %s missing real cell name\n",
689 avc->linkData = afs_strdup("unknown");
692 int namelen = strlen(realName);
693 linklen = rw + namelen;
694 avc->linkData = afs_osi_Alloc(linklen + 1);
695 strcpy(avc->linkData, rw ? "." : "");
696 afs_strcat(avc->linkData, realName);
699 status->UnixModeBits = 0755;
700 afs_PutCellAlias(ca);
702 } else if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_MOUNT) {
703 c = afs_GetCellByIndex(cellidx, READ_LOCK);
705 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
711 * linkData needs to contain "%cell:volumeid"
713 namelen = strlen(c->cellName);
714 bp = afs_cv2string(&tbuf[CVBS], avc->fid.Fid.Unique);
715 linklen = 2 + namelen + strlen(bp);
716 avc->linkData = afs_osi_Alloc(linklen + 1);
717 strcpy(avc->linkData, "%");
718 afs_strcat(avc->linkData, c->cellName);
719 afs_strcat(avc->linkData, ":");
720 afs_strcat(avc->linkData, bp);
722 status->UnixModeBits = 0644;
723 status->ParentVnode = AFS_DYNROOT_MOUNT_VNODE;
724 afs_PutCell(c, READ_LOCK);
727 c = afs_GetCellByIndex(cellidx, READ_LOCK);
729 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
735 * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
737 namelen = strlen(c->cellName);
738 linklen = 1 + namelen + 10;
739 avc->linkData = afs_osi_Alloc(linklen + 1);
740 strcpy(avc->linkData, rw ? "%" : "#");
741 afs_strcat(avc->linkData, c->cellName);
742 afs_strcat(avc->linkData, ":root.cell");
744 status->UnixModeBits = 0644;
745 afs_PutCell(c, READ_LOCK);
748 status->Length = linklen;
756 * Make sure dynroot initialization has been done.
759 afs_InitDynroot(void)
763 RWLOCK_INIT(&afs_dynrootDirLock, "afs_dynrootDirLock");
764 RWLOCK_INIT(&afs_dynSymlinkLock, "afs_dynSymlinkLock");
766 return afs_dynrootCellInit();
770 * Enable or disable dynroot. Returns 0 if successful.
773 afs_SetDynrootEnable(int enable)
775 afs_dynrootEnable = enable;
776 return afs_InitDynroot();
780 * Check if dynroot support is enabled.
783 afs_GetDynrootEnable(void)
785 return afs_dynrootEnable;
789 * Remove a temporary symlink entry from /afs.
792 afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred, char *aname)
794 struct afs_dynSymlink **tpps;
795 struct afs_dynSymlink *tps;
801 ObtainWriteLock(&afs_dynSymlinkLock, 97);
802 tpps = &afs_dynSymlinkBase;
805 if (afs_strcasecmp(aname, tps->name) == 0) {
806 afs_osi_Free(tps->name, strlen(tps->name) + 1);
807 afs_osi_Free(tps->target, strlen(tps->target) + 1);
809 afs_osi_Free(tps, sizeof(*tps));
810 afs_dynSymlinkIndex++;
816 ReleaseWriteLock(&afs_dynSymlinkLock);
818 afs_DynrootInvalidate();
822 if (afs_CellOrAliasExists(aname))
829 * Create a temporary symlink entry in /afs.
832 afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred,
833 char *aname, char *atargetName)
835 struct afs_dynSymlink *tps;
839 if (afs_CellOrAliasExists(aname))
842 /* Check if it's already a symlink */
843 ObtainWriteLock(&afs_dynSymlinkLock, 91);
844 tps = afs_dynSymlinkBase;
846 if (afs_strcasecmp(aname, tps->name) == 0) {
847 ReleaseWriteLock(&afs_dynSymlinkLock);
853 /* Doesn't already exist -- go ahead and create it */
854 tps = afs_osi_Alloc(sizeof(*tps));
855 tps->index = afs_dynSymlinkIndex++;
856 tps->next = afs_dynSymlinkBase;
857 tps->name = afs_osi_Alloc(strlen(aname) + 1);
858 strcpy(tps->name, aname);
859 tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
860 strcpy(tps->target, atargetName);
861 afs_dynSymlinkBase = tps;
862 ReleaseWriteLock(&afs_dynSymlinkLock);
864 afs_DynrootInvalidate();