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.
17 * afs_DynrootInvalidate
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_CELLNAME "dynroot"
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_ALIAS 0x02 /* Corresponds to a struct cell_alias */
52 #define VN_TYPE_SYMLINK 0x03 /* User-created symlink in /afs */
54 #define VNUM_TO_VNTYPE(vnum) ((vnum) >> 24)
55 #define VNUM_TO_VNID(vnum) ((vnum) & 0x00ffffff)
56 #define VNUM_FROM_TYPEID(type, 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))
67 static int afs_dynrootEnable = 0;
68 static int afs_dynrootCell = 0;
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 */
80 /* A dynamically-created symlink in a dynroot /afs */
81 struct afs_dynSymlink {
82 struct afs_dynSymlink *next;
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 */
95 * Set up a cell for dynroot if it's not there yet.
97 static int afs_dynrootCellInit()
99 if (afs_dynrootEnable && !afs_dynrootCell) {
100 afs_int32 cellHosts[MAXCELLHOSTS];
104 memset(cellHosts, 0, sizeof(cellHosts));
105 code = afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
109 tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
112 afs_dynrootCell = tc->cellNum;
113 afs_PutCell(tc, READ_LOCK);
120 * Returns non-zero iff fid corresponds to the top of the dynroot volume.
122 int afs_IsDynrootFid(struct VenusFid *fid)
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);
133 * Obtain the magic dynroot volume Fid.
135 void afs_GetDynrootFid(struct VenusFid *fid)
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;
144 * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
146 int afs_IsDynroot(struct vcache *avc)
148 return afs_IsDynrootFid(&avc->fid);
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.
156 static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
157 int *curPageP, int *curChunkP, char *name, int vnode)
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;
168 * Check if we need to flip pages.. If so, init the new page.
170 sizeOfEntry = afs_dir_NameBlobs(name);
171 if (curChunk + sizeOfEntry > EPP) {
177 pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
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;
186 dirHeader->alloMap[curPage] = EPP - 1;
189 dirEntry = (struct DirEntry *) (pageHeader + curChunk);
191 dirEntry->length = 0;
193 dirEntry->fid.vnode = htonl(vnode);
194 dirEntry->fid.vunique = htonl(1);
195 strcpy(dirEntry->name, name);
197 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
200 pageHeader->freebitmap[t1] |= (1 << t2);
204 * Add the new entry to the correct hash chain.
207 dirEntry->next = dirHeader->hashTable[i];
208 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
210 curChunk += sizeOfEntry;
211 dirHeader->alloMap[curPage] -= sizeOfEntry;
214 *curChunkP = curChunk;
218 * Invalidate the /afs vnode for dynroot; called when the underlying
219 * directory has changed and needs to be re-read.
221 void afs_DynrootInvalidate(void)
225 struct VenusFid tfid;
227 if (!afs_dynrootEnable)
230 ObtainWriteLock(&afs_dynrootDirLock, 687);
231 afs_dynrootVersion++;
232 afs_dynrootVersionHigh = osi_Time();
233 ReleaseWriteLock(&afs_dynrootDirLock);
235 afs_GetDynrootFid(&tfid);
238 ObtainReadLock(&afs_xvcache);
239 tvc = afs_FindVCache(&tfid, &retry, 0);
240 ReleaseReadLock(&afs_xvcache);
243 tvc->states &= ~(CStatd | CUnique);
244 osi_dnlc_purgedp(tvc);
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.
254 static void afs_RebuildDynroot(void)
256 int cellidx, maxcellidx, i;
257 int aliasidx, maxaliasidx;
259 struct cell_alias *ca;
260 int curChunk, curPage;
261 int dirSize, sizeOfCurEntry;
262 char *newDir, *dotCell;
263 struct DirHeader *dirHeader;
265 struct afs_dynSymlink *ts;
268 ObtainReadLock(&afs_dynrootDirLock);
269 newVersion = afs_dynrootVersion;
270 ReleaseReadLock(&afs_dynrootDirLock);
273 * Compute the amount of space we need for the fake dir
278 /* Reserve space for "." and ".." */
281 for (cellidx = 0; ; cellidx++) {
282 c = afs_GetCellByIndex(cellidx, READ_LOCK);
284 if (c->cellNum == afs_dynrootCell) continue;
286 sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
287 if (curChunk + sizeOfCurEntry > EPP) {
291 curChunk += sizeOfCurEntry;
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) {
301 curChunk += sizeOfCurEntry;
303 afs_PutCell(c, READ_LOCK);
305 maxcellidx = cellidx;
307 for (aliasidx = 0; ; aliasidx++) {
308 ca = afs_GetCellAlias(aliasidx);
311 sizeOfCurEntry = afs_dir_NameBlobs(ca->alias);
312 if (curChunk + sizeOfCurEntry > EPP) {
316 curChunk += sizeOfCurEntry;
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) {
326 curChunk += sizeOfCurEntry;
328 afs_PutCellAlias(ca);
330 maxaliasidx = aliasidx;
332 ObtainReadLock(&afs_dynSymlinkLock);
333 ts = afs_dynSymlinkBase;
335 sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
336 if (curChunk + sizeOfCurEntry > EPP) {
340 curChunk += sizeOfCurEntry;
344 dirSize = (curPage + 1) * AFS_PAGESIZE;
345 newDir = afs_osi_Alloc(dirSize);
348 * Now actually construct the directory.
352 dirHeader = (struct DirHeader *) newDir;
354 dirHeader->header.pgcount = 0;
355 dirHeader->header.tag = htons(1234);
356 dirHeader->header.freecount = 0;
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;
368 /* Install "." and ".." */
369 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
370 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
373 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
374 c = afs_GetCellByIndex(cellidx, READ_LOCK);
376 if (c->cellNum == afs_dynrootCell) continue;
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));
387 afs_PutCell(c, READ_LOCK);
390 for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
391 ca = afs_GetCellAlias(aliasidx);
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);
404 ts = afs_dynSymlinkBase;
406 int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
407 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
412 ReleaseReadLock(&afs_dynSymlinkLock);
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);
424 * Returns a pointer to the base of the dynroot directory in memory,
425 * length thereof, and a FetchStatus.
427 void afs_GetDynroot(char **dynrootDir, int *dynrootLen,
428 struct AFSFetchStatus *status)
430 ObtainReadLock(&afs_dynrootDirLock);
431 if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
432 ReleaseReadLock(&afs_dynrootDirLock);
433 afs_RebuildDynroot();
434 ObtainReadLock(&afs_dynrootDirLock);
437 if (dynrootDir) *dynrootDir = afs_dynrootDir;
438 if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
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;
456 * Puts back the dynroot read lock.
458 void afs_PutDynroot(void)
460 ReleaseReadLock(&afs_dynrootDirLock);
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.
468 int afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
470 if (!afs_dynrootEnable) return 0;
472 if (afs_IsDynroot(avc)) {
473 afs_GetDynroot(0, 0, status);
479 * Check if this is an entry under /afs, e.g. /afs/cellname.
481 if (avc->fid.Cell == afs_dynrootCell &&
482 avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
485 struct cell_alias *ca;
486 int namelen, linklen, cellidx, rw;
488 memset(status, 0, sizeof(struct AFSFetchStatus));
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;
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);
502 ObtainReadLock(&afs_dynSymlinkLock);
503 ts = afs_dynSymlinkBase;
505 if (ts->index == index) break;
510 linklen = strlen(ts->target);
511 avc->linkData = afs_osi_Alloc(linklen + 1);
512 strcpy(avc->linkData, ts->target);
514 status->Length = linklen;
515 status->UnixModeBits = 0755;
517 ReleaseReadLock(&afs_dynSymlinkLock);
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));
529 cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
530 rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
532 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_ALIAS) {
535 ca = afs_GetCellAlias(cellidx);
537 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
543 * linkData needs to contain the name of the cell
544 * we're aliasing for.
548 afs_warn("dynroot: alias %s missing real cell name\n",
550 avc->linkData = afs_strdup("unknown");
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);
560 status->UnixModeBits = 0755;
561 afs_PutCellAlias(ca);
563 c = afs_GetCellByIndex(cellidx, READ_LOCK);
565 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
571 * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
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");
580 status->UnixModeBits = 0644;
581 afs_PutCell(c, READ_LOCK);
584 status->Length = linklen;
592 * Enable or disable dynroot. Returns 0 if successful.
594 int afs_SetDynrootEnable(int enable)
596 afs_dynrootEnable = enable;
597 return afs_dynrootCellInit();
601 * Check if dynroot support is enabled.
603 int afs_GetDynrootEnable(void)
605 return afs_dynrootEnable;
609 * Remove a temporary symlink entry from /afs.
611 int afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred,
614 struct afs_dynSymlink **tpps;
615 struct afs_dynSymlink *tps;
621 ObtainWriteLock(&afs_dynSymlinkLock, 97);
622 tpps = &afs_dynSymlinkBase;
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);
629 afs_osi_Free(tps, sizeof(*tps));
630 afs_dynSymlinkIndex++;
636 ReleaseWriteLock(&afs_dynSymlinkLock);
638 afs_DynrootInvalidate();
642 if (afs_CellOrAliasExists(aname))
649 * Create a temporary symlink entry in /afs.
651 int afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred,
652 char *aname, char *atargetName)
654 struct afs_dynSymlink *tps;
658 if (afs_CellOrAliasExists(aname))
661 /* Check if it's already a symlink */
662 ObtainWriteLock(&afs_dynSymlinkLock, 91);
663 tps = afs_dynSymlinkBase;
665 if (afs_strcasecmp(aname, tps->name) == 0) {
666 ReleaseWriteLock(&afs_dynSymlinkLock);
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);
683 afs_DynrootInvalidate();