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