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"
32 #include "afs/sysincludes.h" /* Standard vendor system headers */
33 #include "afsincludes.h"
34 #include "afs/afs_osi.h"
38 #include "afs/prs_fs.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 * Given the current page and chunk pointers in a directory, adjust them
153 * appropriately so that the given file name can be appended. Used for
154 * computing the size of a directory.
156 static void afs_dynroot_computeDirEnt(char *name, int *curPageP, int *curChunkP)
160 esize = afs_dir_NameBlobs(name);
161 if (*curChunkP + esize > EPP) {
169 * Add directory entry by given name to a directory. Assumes the
170 * caller has allocated the directory to be large enough to hold
171 * the necessary entry.
173 static void afs_dynroot_addDirEnt(struct DirHeader *dirHeader,
174 int *curPageP, int *curChunkP, char *name, int vnode)
176 char *dirBase = (char *) dirHeader;
177 struct PageHeader *pageHeader;
178 struct DirEntry *dirEntry;
179 int sizeOfEntry, i, t1, t2;
180 int curPage = *curPageP;
181 int curChunk = *curChunkP;
185 * Check if we need to flip pages.. If so, init the new page.
187 sizeOfEntry = afs_dir_NameBlobs(name);
188 if (curChunk + sizeOfEntry > EPP) {
194 pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
196 pageHeader->pgcount = 0;
197 pageHeader->tag = htons(1234);
198 pageHeader->freecount = 0;
199 pageHeader->freebitmap[0] = 0x01;
200 for (i = 1; i < EPP/8; i++)
201 pageHeader->freebitmap[i] = 0;
203 dirHeader->alloMap[curPage] = EPP - 1;
206 dirEntry = (struct DirEntry *) (pageHeader + curChunk);
208 dirEntry->length = 0;
210 dirEntry->fid.vnode = htonl(vnode);
211 dirEntry->fid.vunique = htonl(1);
212 strcpy(dirEntry->name, name);
214 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
217 pageHeader->freebitmap[t1] |= (1 << t2);
221 * Add the new entry to the correct hash chain.
224 dirEntry->next = dirHeader->hashTable[i];
225 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
227 curChunk += sizeOfEntry;
228 dirHeader->alloMap[curPage] -= sizeOfEntry;
231 *curChunkP = curChunk;
235 * Invalidate the /afs vnode for dynroot; called when the underlying
236 * directory has changed and needs to be re-read.
238 void afs_DynrootInvalidate(void)
242 struct VenusFid tfid;
244 if (!afs_dynrootEnable)
247 ObtainWriteLock(&afs_dynrootDirLock, 687);
248 afs_dynrootVersion++;
249 afs_dynrootVersionHigh = osi_Time();
250 ReleaseWriteLock(&afs_dynrootDirLock);
252 afs_GetDynrootFid(&tfid);
255 ObtainReadLock(&afs_xvcache);
256 tvc = afs_FindVCache(&tfid, &retry, 0);
257 ReleaseReadLock(&afs_xvcache);
260 tvc->states &= ~(CStatd | CUnique);
261 osi_dnlc_purgedp(tvc);
267 * Regenerates the dynroot contents from the current list of
268 * cells. Useful when the list of cells has changed due to
269 * an AFSDB lookup, for instance.
271 static void afs_RebuildDynroot(void)
273 int cellidx, maxcellidx, i;
274 int aliasidx, maxaliasidx;
276 struct cell_alias *ca;
277 int curChunk, curPage;
279 char *newDir, *dotCell;
280 struct DirHeader *dirHeader;
282 struct afs_dynSymlink *ts;
285 ObtainReadLock(&afs_dynrootDirLock);
286 newVersion = afs_dynrootVersion;
287 ReleaseReadLock(&afs_dynrootDirLock);
290 * Compute the amount of space we need for the fake dir
295 /* Reserve space for "." and ".." */
298 for (cellidx = 0; ; cellidx++) {
299 c = afs_GetCellByIndex(cellidx, READ_LOCK);
301 if (c->cellNum == afs_dynrootCell) continue;
303 dotLen = strlen(c->cellName) + 2;
304 dotCell = afs_osi_Alloc(dotLen);
305 strcpy(dotCell, ".");
306 strcat(dotCell, c->cellName);
308 afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
309 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
311 afs_osi_Free(dotCell, dotLen);
312 afs_PutCell(c, READ_LOCK);
314 maxcellidx = cellidx;
316 for (aliasidx = 0; ; aliasidx++) {
317 ca = afs_GetCellAlias(aliasidx);
320 dotLen = strlen(ca->alias) + 2;
321 dotCell = afs_osi_Alloc(dotLen);
322 strcpy(dotCell, ".");
323 strcat(dotCell, ca->alias);
325 afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
326 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
328 afs_osi_Free(dotCell, dotLen);
329 afs_PutCellAlias(ca);
331 maxaliasidx = aliasidx;
333 ObtainReadLock(&afs_dynSymlinkLock);
334 ts = afs_dynSymlinkBase;
336 afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
340 dirSize = (curPage + 1) * AFS_PAGESIZE;
341 newDir = afs_osi_Alloc(dirSize);
344 * Now actually construct the directory.
348 dirHeader = (struct DirHeader *) newDir;
350 dirHeader->header.pgcount = 0;
351 dirHeader->header.tag = htons(1234);
352 dirHeader->header.freecount = 0;
354 dirHeader->header.freebitmap[0] = 0xff;
355 dirHeader->header.freebitmap[1] = 0x1f;
356 for (i = 2; i < EPP/8; i++)
357 dirHeader->header.freebitmap[i] = 0;
358 dirHeader->alloMap[0] = EPP - DHE - 1;
359 for (i = 1; i < MAXPAGES; i++)
360 dirHeader->alloMap[i] = EPP;
361 for (i = 0; i < NHASHENT; i++)
362 dirHeader->hashTable[i] = 0;
364 /* Install "." and ".." */
365 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
366 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
369 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
370 c = afs_GetCellByIndex(cellidx, READ_LOCK);
372 if (c->cellNum == afs_dynrootCell) continue;
374 dotLen = strlen(c->cellName) + 2;
375 dotCell = afs_osi_Alloc(dotLen);
376 strcpy(dotCell, ".");
377 strcat(dotCell, c->cellName);
378 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
379 c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
380 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
381 dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
382 afs_osi_Free(dotCell, dotLen);
385 afs_PutCell(c, READ_LOCK);
388 for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
389 ca = afs_GetCellAlias(aliasidx);
392 dotLen = strlen(ca->alias) + 2;
393 dotCell = afs_osi_Alloc(dotLen);
394 strcpy(dotCell, ".");
395 strcat(dotCell, ca->alias);
396 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
397 ca->alias, VNUM_FROM_CAIDX_RW(aliasidx, 0));
398 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
399 dotCell, VNUM_FROM_CAIDX_RW(aliasidx, 1));
400 afs_osi_Free(dotCell, dotLen);
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();