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.
21 * afs_SetDynrootEnable
22 * afs_GetDynrootEnable
23 * afs_DynrootVOPRemove
24 * afs_DynrootVOPSymlink
28 #include <afsconfig.h>
29 #include "../afs/param.h"
31 #include "../afs/stds.h"
32 #include "../afs/sysincludes.h" /* Standard vendor system headers */
33 #include "../afs/afsincludes.h"
34 #include "../afs/afs_osi.h"
35 #include "../afsint/afsint.h"
36 #include "../afs/lock.h"
38 #include "../afs/prs_fs.h"
39 #include "../afs/dir.h"
41 #define AFS_DYNROOT_CELL 1
42 #define AFS_DYNROOT_VOLUME 1
43 #define AFS_DYNROOT_VNODE 1
44 #define AFS_DYNROOT_UNIQUE 1
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.
50 #define VN_TYPE_CELL 0x01 /* Corresponds to a struct cell */
51 #define VN_TYPE_SYMLINK 0x02 /* User-created symlink in /afs */
53 #define VNUM_TO_VNTYPE(vnum) ((vnum) >> 24)
54 #define VNUM_TO_VNID(vnum) ((vnum) & 0x00ffffff)
55 #define VNUM_FROM_TYPEID(type, id) \
57 #define VNUM_TO_CIDX(vnum) (VNUM_TO_VNID(vnum) >> 2)
58 #define VNUM_TO_RW(vnum) (VNUM_TO_VNID(vnum) >> 1 & 1)
59 #define VNUM_FROM_CIDX_RW(cidx, rw) \
60 VNUM_FROM_TYPEID(VN_TYPE_CELL, \
61 ((cidx) << 2 | (rw) << 1))
63 static int afs_dynrootEnable = 0;
65 static afs_rwlock_t afs_dynrootDirLock;
66 /* Start of variables protected by afs_dynrootDirLock */
67 static char *afs_dynrootDir = NULL;
68 static int afs_dynrootDirLen;
69 static int afs_dynrootDirLinkcnt;
70 static int afs_dynrootCellCount;
71 static int afs_dynrootVersion = 1;
72 static int afs_dynrootVersionHigh = 1;
73 /* End of variables protected by afs_dynrootDirLock */
75 /* A dynamically-created symlink in a dynroot /afs */
76 struct afs_dynSymlink {
77 struct afs_dynSymlink *next;
83 static afs_rwlock_t afs_dynSymlinkLock;
84 /* Start of variables protected by afs_dynSymlinkLock */
85 static struct afs_dynSymlink *afs_dynSymlinkBase = NULL;
86 static int afs_dynSymlinkIndex = 0;
87 /* End of variables protected by afs_dynSymlinkLock */
90 * Returns non-zero iff fid corresponds to the top of the dynroot volume.
92 int afs_IsDynrootFid(struct VenusFid *fid)
96 fid->Cell == AFS_DYNROOT_CELL &&
97 fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
98 fid->Fid.Vnode == AFS_DYNROOT_VNODE &&
99 fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
103 * Obtain the magic dynroot volume Fid.
105 void afs_GetDynrootFid(struct VenusFid *fid)
107 fid->Cell = AFS_DYNROOT_CELL;
108 fid->Fid.Volume = AFS_DYNROOT_VOLUME;
109 fid->Fid.Vnode = AFS_DYNROOT_VNODE;
110 fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
114 * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
116 int afs_IsDynroot(struct vcache *avc)
118 return afs_IsDynrootFid(&avc->fid);
122 * Add directory entry by given name to a directory. Assumes the
123 * caller has allocated the directory to be large enough to hold
124 * the necessary entry.
126 static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
127 int *curPageP, int *curChunkP, char *name, int vnode)
129 char *dirBase = (char *) dirHeader;
130 struct PageHeader *pageHeader;
131 struct DirEntry *dirEntry;
132 int sizeOfEntry, i, t1, t2;
133 int curPage = *curPageP;
134 int curChunk = *curChunkP;
138 * Check if we need to flip pages.. If so, init the new page.
140 sizeOfEntry = afs_dir_NameBlobs(name);
141 if (curChunk + sizeOfEntry > EPP) {
147 pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
149 pageHeader->pgcount = 0;
150 pageHeader->tag = htons(1234);
151 pageHeader->freecount = 0;
152 pageHeader->freebitmap[0] = 0x01;
153 for (i = 1; i < EPP/8; i++)
154 pageHeader->freebitmap[i] = 0;
156 dirHeader->alloMap[curPage] = EPP - 1;
159 dirEntry = (struct DirEntry *) (pageHeader + curChunk);
161 dirEntry->length = 0;
163 dirEntry->fid.vnode = htonl(vnode);
164 dirEntry->fid.vunique = htonl(1);
165 strcpy(dirEntry->name, name);
167 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
170 pageHeader->freebitmap[t1] |= (1 << t2);
174 * Add the new entry to the correct hash chain.
177 dirEntry->next = dirHeader->hashTable[i];
178 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
180 curChunk += sizeOfEntry;
181 dirHeader->alloMap[curPage] -= sizeOfEntry;
184 *curChunkP = curChunk;
188 * Regenerates the dynroot contents from the current list of
189 * cells. Useful when the list of cells has changed due to
190 * an AFSDB lookup, for instance.
192 void afs_RefreshDynroot(void)
194 int cellidx, maxcellidx, i;
196 int curChunk, curPage;
197 int dirSize, sizeOfCurEntry;
198 char *newDir, *dotCell;
199 struct DirHeader *dirHeader;
202 struct afs_dynSymlink *ts;
206 * Save afs_cellindex here, in case it changes between the
209 maxcellidx = afs_cellindex;
212 * Compute the amount of space we need for the fake dir
217 /* Reserve space for "." and ".." */
220 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
221 c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
224 sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
225 if (curChunk + sizeOfCurEntry > EPP) {
229 curChunk += sizeOfCurEntry;
231 dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
232 strcpy(dotCell, ".");
233 strcat(dotCell, c->cellName);
234 sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
235 if (curChunk + sizeOfCurEntry > EPP) {
239 curChunk += sizeOfCurEntry;
241 afs_PutCell(c, READ_LOCK);
244 ObtainReadLock(&afs_dynSymlinkLock);
245 ts = afs_dynSymlinkBase;
247 sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
248 if (curChunk + sizeOfCurEntry > EPP) {
252 curChunk += sizeOfCurEntry;
256 dirSize = (curPage + 1) * AFS_PAGESIZE;
257 newDir = afs_osi_Alloc(dirSize);
260 * Now actually construct the directory.
264 dirHeader = (struct DirHeader *) newDir;
266 dirHeader->header.pgcount = 0;
267 dirHeader->header.tag = htons(1234);
268 dirHeader->header.freecount = 0;
270 dirHeader->header.freebitmap[0] = 0xff;
271 dirHeader->header.freebitmap[1] = 0x1f;
272 for (i = 2; i < EPP/8; i++)
273 dirHeader->header.freebitmap[i] = 0;
274 dirHeader->alloMap[0] = EPP - DHE - 1;
275 for (i = 1; i < MAXPAGES; i++)
276 dirHeader->alloMap[i] = EPP;
277 for (i = 0; i < NHASHENT; i++)
278 dirHeader->hashTable[i] = 0;
280 /* Install "." and ".." */
281 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
282 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
285 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
286 c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
289 dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
290 strcpy(dotCell, ".");
291 strcat(dotCell, c->cellName);
292 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
293 c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
294 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
295 dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
297 if (!(c->states & CAlias)) linkCount += 2;
298 afs_PutCell(c, READ_LOCK);
301 ts = afs_dynSymlinkBase;
303 int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
304 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
309 newCellCount = maxcellidx + afs_dynSymlinkIndex;
310 ReleaseReadLock(&afs_dynSymlinkLock);
312 ObtainWriteLock(&afs_dynrootDirLock, 549);
313 if (afs_dynrootDir) afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
314 afs_dynrootDir = newDir;
315 afs_dynrootDirLen = dirSize;
316 afs_dynrootDirLinkcnt = linkCount;
317 if (afs_dynrootCellCount != newCellCount) {
319 * New cells/symlinks added -- bump data version, invalidate vcache.
321 afs_dynrootCellCount = newCellCount;
322 afs_dynrootVersion++;
323 afs_dynrootVersionHigh = osi_Time();
326 ReleaseWriteLock(&afs_dynrootDirLock);
331 struct VenusFid tfid;
333 afs_GetDynrootFid(&tfid);
336 ObtainReadLock(&afs_xvcache);
337 tvc = afs_FindVCache(&tfid, &retry, 0);
338 ReleaseReadLock(&afs_xvcache);
341 tvc->states &= ~(CStatd | CUnique);
342 osi_dnlc_purgedp(tvc);
349 * Returns a pointer to the base of the dynroot directory in memory,
350 * length thereof, and a FetchStatus.
352 void afs_GetDynroot(char **dynrootDir, int *dynrootLen, struct AFSFetchStatus *status)
354 ObtainReadLock(&afs_dynrootDirLock);
355 if (!afs_dynrootDir) {
356 ReleaseReadLock(&afs_dynrootDirLock);
357 afs_RefreshDynroot();
358 ObtainReadLock(&afs_dynrootDirLock);
361 if (dynrootDir) *dynrootDir = afs_dynrootDir;
362 if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
365 memset(status, 0, sizeof(struct AFSFetchStatus));
366 status->FileType = Directory;
367 status->LinkCount = afs_dynrootDirLinkcnt;
368 status->Length = afs_dynrootDirLen;
369 status->DataVersion = afs_dynrootVersion;
370 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
371 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
372 status->UnixModeBits = 0755;
373 status->ParentVnode = 1;
374 status->ParentUnique = 1;
375 status->dataVersionHigh = afs_dynrootVersionHigh;
380 * Puts back the dynroot read lock.
382 void afs_PutDynroot(void)
384 ReleaseReadLock(&afs_dynrootDirLock);
388 * Inform dynroot that a new vnode is being created. Return value
389 * is non-zero if this vnode is handled by dynroot, in which case
390 * FetchStatus will be filled in.
392 int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
394 if (!afs_dynrootEnable) return 0;
396 if (afs_IsDynroot(avc)) {
397 afs_GetDynroot(0, 0, status);
403 * Check if this is an entry under /afs, e.g. /afs/cellname.
405 if (avc->fid.Cell == AFS_DYNROOT_CELL &&
406 avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
409 int namelen, linklen, cellidx, rw;
411 memset(status, 0, sizeof(struct AFSFetchStatus));
413 status->FileType = SymbolicLink;
414 status->LinkCount = 1;
415 status->DataVersion = 1;
416 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
417 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
418 status->ParentVnode = 1;
419 status->ParentUnique = 1;
421 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
422 struct afs_dynSymlink *ts;
423 int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
425 ObtainReadLock(&afs_dynSymlinkLock);
426 ts = afs_dynSymlinkBase;
428 if (ts->index == index) break;
433 linklen = strlen(ts->target);
434 avc->linkData = afs_osi_Alloc(linklen + 1);
435 strcpy(avc->linkData, ts->target);
437 status->Length = linklen;
438 status->UnixModeBits = 0755;
440 ReleaseReadLock(&afs_dynSymlinkLock);
445 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL) {
446 afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
447 VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
451 cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
452 rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
454 c = afs_GetCellByIndex(cellidx, READ_LOCK, 1 /* refresh */);
456 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
461 if (c->states & CAlias) {
463 * linkData needs to contain the name of the cell
464 * we're aliasing for.
466 char *realName = c->realName;
469 afs_warn("dynroot: alias %s missing real cell name\n",
472 avc->linkData = afs_osi_Alloc(linklen + 1);
473 strcpy(avc->linkData, "unknown");
475 int namelen = strlen(realName);
476 linklen = rw + namelen;
477 avc->linkData = afs_osi_Alloc(linklen + 1);
478 strcpy(avc->linkData, rw ? "." : "");
479 strcat(avc->linkData, realName);
482 status->UnixModeBits = 0755;
485 * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
487 namelen = strlen(c->cellName);
488 linklen = 1 + namelen + 10;
489 avc->linkData = afs_osi_Alloc(linklen + 1);
490 strcpy(avc->linkData, rw ? "%" : "#");
491 strcat(avc->linkData, c->cellName);
492 strcat(avc->linkData, ":root.cell");
494 status->UnixModeBits = 0644;
497 status->Length = linklen;
498 afs_PutCell(c, READ_LOCK);
506 * Enable or disable dynroot. Returns 0 if successful.
508 int afs_SetDynrootEnable(int enable)
510 afs_dynrootEnable = enable;
515 * Check if dynroot support is enabled.
517 int afs_GetDynrootEnable(void)
519 return afs_dynrootEnable;
523 * Remove a temporary symlink entry from /afs.
525 int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred, char *aname)
527 struct afs_dynSymlink **tpps;
528 struct afs_dynSymlink *tps;
535 ObtainWriteLock(&afs_dynSymlinkLock, 97);
536 tpps = &afs_dynSymlinkBase;
539 if (afs_strcasecmp(aname, tps->name) == 0) {
540 afs_osi_Free(tps->name, strlen(tps->name) + 1);
541 afs_osi_Free(tps->target, strlen(tps->target) + 1);
543 afs_osi_Free(tps, sizeof(*tps));
544 afs_dynSymlinkIndex++;
550 ReleaseWriteLock(&afs_dynSymlinkLock);
552 afs_RefreshDynroot();
556 /* Check if this is an actual cell? */
557 c = afs_FindCellByName(aname, READ_LOCK);
559 afs_PutCell(c, READ_LOCK);
567 * Create a temporary symlink entry in /afs.
569 int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred,
570 char *aname, char *atargetName)
572 struct afs_dynSymlink *tps;
578 /* Check if it's already a cell */
579 c = afs_FindCellByName(aname, READ_LOCK);
581 afs_PutCell(c, READ_LOCK);
585 /* Check if it's already a symlink */
586 ObtainWriteLock(&afs_dynSymlinkLock, 91);
587 tps = afs_dynSymlinkBase;
589 if (afs_strcasecmp(aname, tps->name) == 0) {
590 ReleaseWriteLock(&afs_dynSymlinkLock);
596 /* Doesn't already exist -- go ahead and create it */
597 tps = afs_osi_Alloc(sizeof(*tps));
598 tps->index = afs_dynSymlinkIndex++;
599 tps->next = afs_dynSymlinkBase;
600 tps->name = afs_osi_Alloc(strlen(aname) + 1);
601 strcpy(tps->name, aname);
602 tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
603 strcpy(tps->target, atargetName);
604 afs_dynSymlinkBase = tps;
605 ReleaseWriteLock(&afs_dynSymlinkLock);
607 afs_RefreshDynroot();