openbsd-20021030
[openafs.git] / src / afs / afs_dynroot.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 /*
11  * Dynamic /afs volume support.
12  *
13  * Implements:
14  * afs_IsDynrootFid
15  * afs_GetDynrootFid
16  * afs_IsDynroot
17  * afs_DynrootInvalidate
18  * afs_GetDynroot
19  * afs_PutDynroot
20  * afs_DynrootNewVnode
21  * afs_SetDynrootEnable
22  * afs_GetDynrootEnable
23  * afs_DynrootVOPRemove
24  * afs_DynrootVOPSymlink
25  *
26  */
27
28 #include <afsconfig.h>
29 #include "afs/param.h"
30
31 #include "afs/stds.h"
32 #include "afs/sysincludes.h" /* Standard vendor system headers */
33 #include "afsincludes.h"
34 #include "afs/afs_osi.h"
35 #include "afsint.h"
36 #include "afs/lock.h"
37
38 #include "afs/prs_fs.h"
39 #include "afs/dir.h"
40
41 #define AFS_DYNROOT_CELLNAME    "dynroot"
42 #define AFS_DYNROOT_VOLUME      1
43 #define AFS_DYNROOT_VNODE       1
44 #define AFS_DYNROOT_UNIQUE      1
45
46 /*
47  * Vnode numbers in dynroot are composed of a type field (upper 8 bits)
48  * and a type-specific identifier in the lower 24 bits.
49  */
50 #define VN_TYPE_CELL            0x01    /* Corresponds to a struct cell */
51 #define VN_TYPE_ALIAS           0x02    /* Corresponds to a struct cell_alias */
52 #define VN_TYPE_SYMLINK         0x03    /* User-created symlink in /afs */
53
54 #define VNUM_TO_VNTYPE(vnum)    ((vnum) >> 24)
55 #define VNUM_TO_VNID(vnum)      ((vnum) & 0x00ffffff)
56 #define VNUM_FROM_TYPEID(type, id) \
57                                 ((type) << 24 | (id))
58 #define VNUM_TO_CIDX(vnum)      (VNUM_TO_VNID(vnum) >> 2)
59 #define VNUM_TO_RW(vnum)        (VNUM_TO_VNID(vnum) >> 1 & 1)
60 #define VNUM_FROM_CIDX_RW(cidx, rw) \
61                                 VNUM_FROM_TYPEID(VN_TYPE_CELL, \
62                                                  ((cidx) << 2 | (rw) << 1))
63 #define VNUM_FROM_CAIDX_RW(caidx, rw) \
64                                 VNUM_FROM_TYPEID(VN_TYPE_ALIAS, \
65                                                  ((caidx) << 2 | (rw) << 1))
66
67 static int afs_dynrootEnable = 0;
68 static int afs_dynrootCell = 0;
69
70 static afs_rwlock_t afs_dynrootDirLock;
71 /* Start of variables protected by afs_dynrootDirLock */
72 static char *afs_dynrootDir = NULL;
73 static int afs_dynrootDirLen;
74 static int afs_dynrootDirLinkcnt;
75 static int afs_dynrootDirVersion;
76 static int afs_dynrootVersion = 1;
77 static int afs_dynrootVersionHigh = 1;
78 /* End of variables protected by afs_dynrootDirLock */
79
80 /* A dynamically-created symlink in a dynroot /afs */
81 struct afs_dynSymlink {
82     struct afs_dynSymlink *next;
83     int index;
84     char *name;
85     char *target;
86 };
87
88 static afs_rwlock_t afs_dynSymlinkLock;
89 /* Start of variables protected by afs_dynSymlinkLock */
90 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
91 static int afs_dynSymlinkIndex = 0;
92 /* End of variables protected by afs_dynSymlinkLock */
93
94 /*
95  * Set up a cell for dynroot if it's not there yet.
96  */
97 static int afs_dynrootCellInit()
98 {
99     if (afs_dynrootEnable && !afs_dynrootCell) {
100         afs_int32 cellHosts[MAXCELLHOSTS];
101         struct cell *tc;
102         int code;
103
104         memset(cellHosts, 0, sizeof(cellHosts));
105         code = afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
106                            NULL, 0, 0, 0);
107         if (code)
108             return code;
109         tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
110         if (!tc)
111             return ENODEV;
112         afs_dynrootCell = tc->cellNum;
113         afs_PutCell(tc, READ_LOCK);
114     }
115
116     return 0;
117 }
118
119 /*
120  * Returns non-zero iff fid corresponds to the top of the dynroot volume.
121  */
122 int afs_IsDynrootFid(struct VenusFid *fid)
123 {
124     return
125         (afs_dynrootEnable &&
126          fid->Cell       == afs_dynrootCell    &&
127          fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
128          fid->Fid.Vnode  == AFS_DYNROOT_VNODE  &&
129          fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
130 }
131
132 /*
133  * Obtain the magic dynroot volume Fid.
134  */
135 void afs_GetDynrootFid(struct VenusFid *fid) 
136 {
137     fid->Cell       = afs_dynrootCell;
138     fid->Fid.Volume = AFS_DYNROOT_VOLUME;
139     fid->Fid.Vnode  = AFS_DYNROOT_VNODE;
140     fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
141 }
142
143 /*
144  * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
145  */
146 int afs_IsDynroot(struct vcache *avc)
147 {
148     return afs_IsDynrootFid(&avc->fid);
149 }
150
151 /*
152  * Add directory entry by given name to a directory.  Assumes the
153  * caller has allocated the directory to be large enough to hold
154  * the necessary entry.
155  */
156 static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader, 
157         int *curPageP, int *curChunkP, char *name, int vnode)
158 {
159     char *dirBase = (char *) dirHeader;
160     struct PageHeader *pageHeader;
161     struct DirEntry *dirEntry;
162     int sizeOfEntry, i, t1, t2;
163     int curPage = *curPageP;
164     int curChunk = *curChunkP;
165     int didNewPage = 0;
166
167     /*
168      * Check if we need to flip pages..  If so, init the new page.
169      */
170     sizeOfEntry = afs_dir_NameBlobs(name);
171     if (curChunk + sizeOfEntry > EPP) {
172         curPage++;
173         curChunk = 1;
174         didNewPage = 1;
175     }
176
177     pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
178     if (didNewPage) {
179         pageHeader->pgcount = 0;
180         pageHeader->tag = htons(1234);
181         pageHeader->freecount = 0;
182         pageHeader->freebitmap[0] = 0x01;
183         for (i = 1; i < EPP/8; i++)
184             pageHeader->freebitmap[i] = 0;
185
186         dirHeader->alloMap[curPage] = EPP - 1;
187     }
188
189     dirEntry = (struct DirEntry *) (pageHeader + curChunk);
190     dirEntry->flag        = 1;
191     dirEntry->length      = 0;
192     dirEntry->next        = 0;
193     dirEntry->fid.vnode   = htonl(vnode);
194     dirEntry->fid.vunique = htonl(1);
195     strcpy(dirEntry->name, name);
196
197     for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
198         t1 = i / 8;
199         t2 = i % 8;
200         pageHeader->freebitmap[t1] |= (1 << t2);
201     }
202
203     /*
204      * Add the new entry to the correct hash chain.
205      */
206     i = DirHash(name);
207     dirEntry->next = dirHeader->hashTable[i];
208     dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
209
210     curChunk += sizeOfEntry;
211     dirHeader->alloMap[curPage] -= sizeOfEntry;
212
213     *curPageP = curPage;
214     *curChunkP = curChunk;
215 }
216
217 /*
218  * Invalidate the /afs vnode for dynroot; called when the underlying
219  * directory has changed and needs to be re-read.
220  */
221 void afs_DynrootInvalidate(void)
222 {
223     afs_int32 retry;
224     struct vcache *tvc;
225     struct VenusFid tfid;
226
227     if (!afs_dynrootEnable)
228         return;
229
230     ObtainWriteLock(&afs_dynrootDirLock, 687);
231     afs_dynrootVersion++;
232     afs_dynrootVersionHigh = osi_Time();
233     ReleaseWriteLock(&afs_dynrootDirLock);
234
235     afs_GetDynrootFid(&tfid);
236     do {
237         retry = 0;
238         ObtainReadLock(&afs_xvcache);
239         tvc = afs_FindVCache(&tfid, &retry, 0);
240         ReleaseReadLock(&afs_xvcache);
241     } while (retry);
242     if (tvc) {
243         tvc->states &= ~(CStatd | CUnique);
244         osi_dnlc_purgedp(tvc);
245         afs_PutVCache(tvc);
246     }
247 }
248
249 /*
250  * Regenerates the dynroot contents from the current list of
251  * cells.  Useful when the list of cells has changed due to
252  * an AFSDB lookup, for instance.
253  */
254 static void afs_RebuildDynroot(void)
255 {
256     int cellidx, maxcellidx, i;
257     int aliasidx, maxaliasidx;
258     struct cell *c;
259     struct cell_alias *ca;
260     int curChunk, curPage;
261     int dirSize, sizeOfCurEntry;
262     char *newDir, *dotCell;
263     struct DirHeader *dirHeader;
264     int linkCount = 0;
265     struct afs_dynSymlink *ts;
266     int newVersion;
267
268     ObtainReadLock(&afs_dynrootDirLock);
269     newVersion = afs_dynrootVersion;
270     ReleaseReadLock(&afs_dynrootDirLock);
271
272     /*
273      * Compute the amount of space we need for the fake dir
274      */
275     curChunk = 13;
276     curPage = 0;
277
278     /* Reserve space for "." and ".." */
279     curChunk += 2;
280
281     for (cellidx = 0; ; cellidx++) {
282         c = afs_GetCellByIndex(cellidx, READ_LOCK);
283         if (!c) break;
284         if (c->cellNum == afs_dynrootCell) continue;
285
286         sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
287         if (curChunk + sizeOfCurEntry > EPP) {
288             curPage++;
289             curChunk = 1;
290         }
291         curChunk += sizeOfCurEntry;
292
293         dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
294         strcpy(dotCell, ".");
295         strcat(dotCell, c->cellName);
296         sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
297         if (curChunk + sizeOfCurEntry > EPP) {
298             curPage++;
299             curChunk = 1;
300         }
301         curChunk += sizeOfCurEntry;
302
303         afs_PutCell(c, READ_LOCK);
304     }
305     maxcellidx = cellidx;
306
307     for (aliasidx = 0; ; aliasidx++) {
308         ca = afs_GetCellAlias(aliasidx);
309         if (!ca) break;
310
311         sizeOfCurEntry = afs_dir_NameBlobs(ca->alias);
312         if (curChunk + sizeOfCurEntry > EPP) {
313             curPage++;
314             curChunk = 1;
315         }
316         curChunk += sizeOfCurEntry;
317
318         dotCell = afs_osi_Alloc(strlen(ca->alias) + 2);
319         strcpy(dotCell, ".");
320         strcat(dotCell, ca->alias);
321         sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
322         if (curChunk + sizeOfCurEntry > EPP) {
323             curPage++;
324             curChunk = 1;
325         }
326         curChunk += sizeOfCurEntry;
327
328         afs_PutCellAlias(ca);
329     }
330     maxaliasidx = aliasidx;
331
332     ObtainReadLock(&afs_dynSymlinkLock);
333     ts = afs_dynSymlinkBase;
334     while (ts) {
335         sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
336         if (curChunk + sizeOfCurEntry > EPP) {
337             curPage++;
338             curChunk = 1;
339         }
340         curChunk += sizeOfCurEntry;
341         ts = ts->next;
342     }
343
344     dirSize = (curPage + 1) * AFS_PAGESIZE;
345     newDir = afs_osi_Alloc(dirSize);
346
347     /*
348      * Now actually construct the directory.
349      */
350     curChunk = 13;
351     curPage = 0;
352     dirHeader = (struct DirHeader *) newDir;
353
354     dirHeader->header.pgcount = 0;
355     dirHeader->header.tag = htons(1234);
356     dirHeader->header.freecount = 0;
357
358     dirHeader->header.freebitmap[0] = 0xff;
359     dirHeader->header.freebitmap[1] = 0x1f;
360     for (i = 2; i < EPP/8; i++)
361         dirHeader->header.freebitmap[i] = 0;
362     dirHeader->alloMap[0] = EPP - DHE - 1;
363     for (i = 1; i < MAXPAGES; i++)
364         dirHeader->alloMap[i] = EPP;
365     for (i = 0; i < NHASHENT; i++)
366         dirHeader->hashTable[i] = 0;
367
368     /* Install "." and ".." */
369     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
370     afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
371     linkCount += 2;
372
373     for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
374         c = afs_GetCellByIndex(cellidx, READ_LOCK);
375         if (!c) continue;
376         if (c->cellNum == afs_dynrootCell) continue;
377
378         dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
379         strcpy(dotCell, ".");
380         strcat(dotCell, c->cellName);
381         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
382                               c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
383         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
384                               dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
385
386         linkCount += 2;
387         afs_PutCell(c, READ_LOCK);
388     }
389
390     for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
391         ca = afs_GetCellAlias(aliasidx);
392         if (!ca) continue;
393
394         dotCell = afs_osi_Alloc(strlen(ca->alias) + 2);
395         strcpy(dotCell, ".");
396         strcat(dotCell, ca->alias);
397         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
398                               ca->alias, VNUM_FROM_CAIDX_RW(aliasidx, 0));
399         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
400                               dotCell, VNUM_FROM_CAIDX_RW(aliasidx, 1));
401         afs_PutCellAlias(ca);
402     }
403
404     ts = afs_dynSymlinkBase;
405     while (ts) {
406         int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
407         afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
408                               ts->name, vnum);
409         ts = ts->next;
410     }
411
412     ReleaseReadLock(&afs_dynSymlinkLock);
413
414     ObtainWriteLock(&afs_dynrootDirLock, 549);
415     if (afs_dynrootDir) afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
416     afs_dynrootDir = newDir;
417     afs_dynrootDirLen = dirSize;
418     afs_dynrootDirLinkcnt = linkCount;
419     afs_dynrootDirVersion = newVersion;
420     ReleaseWriteLock(&afs_dynrootDirLock);
421 }
422
423 /*
424  * Returns a pointer to the base of the dynroot directory in memory,
425  * length thereof, and a FetchStatus.
426  */
427 void afs_GetDynroot(char **dynrootDir, int *dynrootLen,
428         struct AFSFetchStatus *status)
429 {
430     ObtainReadLock(&afs_dynrootDirLock);
431     if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
432         ReleaseReadLock(&afs_dynrootDirLock);
433         afs_RebuildDynroot();
434         ObtainReadLock(&afs_dynrootDirLock);
435     }
436
437     if (dynrootDir) *dynrootDir = afs_dynrootDir;
438     if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
439
440     if (status) {
441         memset(status, 0, sizeof(struct AFSFetchStatus));
442         status->FileType        = Directory;
443         status->LinkCount       = afs_dynrootDirLinkcnt;
444         status->Length          = afs_dynrootDirLen;
445         status->DataVersion     = afs_dynrootVersion;
446         status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
447         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
448         status->UnixModeBits    = 0755;
449         status->ParentVnode     = 1;
450         status->ParentUnique    = 1;
451         status->dataVersionHigh = afs_dynrootVersionHigh;
452     }
453 }
454
455 /*
456  * Puts back the dynroot read lock.
457  */
458 void afs_PutDynroot(void)
459 {
460     ReleaseReadLock(&afs_dynrootDirLock);
461 }
462
463 /*
464  * Inform dynroot that a new vnode is being created.  Return value
465  * is non-zero if this vnode is handled by dynroot, in which case
466  * FetchStatus will be filled in.
467  */
468 int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
469 {
470     if (!afs_dynrootEnable) return 0;
471
472     if (afs_IsDynroot(avc)) {
473         afs_GetDynroot(0, 0, status);
474         afs_PutDynroot();
475         return 1;
476     }
477
478     /*
479      * Check if this is an entry under /afs, e.g. /afs/cellname.
480      */
481     if (avc->fid.Cell       == afs_dynrootCell &&
482         avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
483
484         struct cell *c;
485         struct cell_alias *ca;
486         int namelen, linklen, cellidx, rw;
487
488         memset(status, 0, sizeof(struct AFSFetchStatus));
489
490         status->FileType        = SymbolicLink;
491         status->LinkCount       = 1;
492         status->DataVersion     = 1;
493         status->CallerAccess    = PRSFS_LOOKUP | PRSFS_READ;
494         status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
495         status->ParentVnode     = 1;
496         status->ParentUnique    = 1;
497
498         if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
499             struct afs_dynSymlink *ts;
500             int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
501
502             ObtainReadLock(&afs_dynSymlinkLock);
503             ts = afs_dynSymlinkBase;
504             while (ts) {
505                 if (ts->index == index) break;
506                 ts = ts->next;
507             }
508
509             if (ts) {
510                 linklen = strlen(ts->target);
511                 avc->linkData = afs_osi_Alloc(linklen + 1);
512                 strcpy(avc->linkData, ts->target);
513
514                 status->Length       = linklen;
515                 status->UnixModeBits = 0755;
516             }
517             ReleaseReadLock(&afs_dynSymlinkLock);
518
519             return ts ? 1 : 0;
520         }
521
522         if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL &&
523             VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_ALIAS) {
524             afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
525                      VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
526             return 0;
527         }
528
529         cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
530         rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
531
532         if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_ALIAS) {
533             char *realName;
534
535             ca = afs_GetCellAlias(cellidx);
536             if (!ca) {
537                 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
538                          cellidx);
539                 return 0;
540             }
541
542             /*
543              * linkData needs to contain the name of the cell
544              * we're aliasing for.
545              */
546             realName = ca->cell;
547             if (!realName) {
548                 afs_warn("dynroot: alias %s missing real cell name\n",
549                          ca->alias);
550                 avc->linkData = afs_strdup("unknown");
551                 linklen = 7;
552             } else {
553                 int namelen = strlen(realName);
554                 linklen = rw + namelen;
555                 avc->linkData = afs_osi_Alloc(linklen + 1);
556                 strcpy(avc->linkData, rw ? "." : "");
557                 strcat(avc->linkData, realName);
558             }
559
560             status->UnixModeBits = 0755;
561             afs_PutCellAlias(ca);
562         } else {
563             c = afs_GetCellByIndex(cellidx, READ_LOCK);
564             if (!c) {
565                 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
566                          cellidx);
567                 return 0;
568             }
569
570             /*
571              * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
572              */
573             namelen = strlen(c->cellName);
574             linklen = 1 + namelen + 10;
575             avc->linkData = afs_osi_Alloc(linklen + 1);
576             strcpy(avc->linkData, rw ? "%" : "#");
577             strcat(avc->linkData, c->cellName);
578             strcat(avc->linkData, ":root.cell");
579
580             status->UnixModeBits = 0644;
581             afs_PutCell(c, READ_LOCK);
582         }
583
584         status->Length = linklen;
585         return 1;
586     }
587
588     return 0;
589 }
590
591 /*
592  * Enable or disable dynroot.  Returns 0 if successful.
593  */
594 int afs_SetDynrootEnable(int enable)
595 {
596     afs_dynrootEnable = enable;
597     return afs_dynrootCellInit();
598 }
599
600 /*
601  * Check if dynroot support is enabled.
602  */
603 int afs_GetDynrootEnable(void)
604 {
605     return afs_dynrootEnable;
606 }
607
608 /*
609  * Remove a temporary symlink entry from /afs.
610  */
611 int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred,
612         char *aname)
613 {
614     struct afs_dynSymlink **tpps;
615     struct afs_dynSymlink *tps;
616     int found = 0;
617
618     if (acred->cr_uid)
619         return EPERM;
620
621     ObtainWriteLock(&afs_dynSymlinkLock, 97);
622     tpps = &afs_dynSymlinkBase;
623     while (*tpps) {
624         tps = *tpps;
625         if (afs_strcasecmp(aname, tps->name) == 0) {
626             afs_osi_Free(tps->name, strlen(tps->name) + 1);
627             afs_osi_Free(tps->target, strlen(tps->target) + 1);
628             *tpps = tps->next;
629             afs_osi_Free(tps, sizeof(*tps));
630             afs_dynSymlinkIndex++;
631             found = 1;
632             break;
633         }
634         tpps = &(tps->next);
635     }
636     ReleaseWriteLock(&afs_dynSymlinkLock);
637     if (found) {
638         afs_DynrootInvalidate();
639         return 0;
640     }
641
642     if (afs_CellOrAliasExists(aname))
643         return EROFS;
644     else
645         return ENOENT;
646 }
647
648 /*
649  * Create a temporary symlink entry in /afs.
650  */
651 int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred, 
652         char *aname, char *atargetName)
653 {
654     struct afs_dynSymlink *tps;
655
656     if (acred->cr_uid)
657         return EPERM;
658     if (afs_CellOrAliasExists(aname))
659         return EEXIST;
660
661     /* Check if it's already a symlink */
662     ObtainWriteLock(&afs_dynSymlinkLock, 91);
663     tps = afs_dynSymlinkBase;
664     while (tps) {
665         if (afs_strcasecmp(aname, tps->name) == 0) {
666             ReleaseWriteLock(&afs_dynSymlinkLock);
667             return EEXIST;
668         }
669         tps = tps->next;
670     }
671
672     /* Doesn't already exist -- go ahead and create it */
673     tps = afs_osi_Alloc(sizeof(*tps));
674     tps->index = afs_dynSymlinkIndex++;
675     tps->next = afs_dynSymlinkBase;
676     tps->name = afs_osi_Alloc(strlen(aname) + 1);
677     strcpy(tps->name, aname);
678     tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
679     strcpy(tps->target, atargetName);
680     afs_dynSymlinkBase = tps;
681     ReleaseWriteLock(&afs_dynSymlinkLock);
682
683     afs_DynrootInvalidate();
684     return 0;
685 }