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.
100 if (afs_dynrootEnable && !afs_dynrootCell) {
101 afs_int32 cellHosts[MAXCELLHOSTS];
105 memset(cellHosts, 0, sizeof(cellHosts));
107 afs_NewCell(AFS_DYNROOT_CELLNAME, cellHosts, CNoSUID | CNoAFSDB,
111 tc = afs_GetCellByName(AFS_DYNROOT_CELLNAME, READ_LOCK);
114 afs_dynrootCell = tc->cellNum;
115 afs_PutCell(tc, READ_LOCK);
122 * Returns non-zero iff fid corresponds to the top of the dynroot volume.
125 afs_IsDynrootFid(struct VenusFid *fid)
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);
134 * Obtain the magic dynroot volume Fid.
137 afs_GetDynrootFid(struct VenusFid *fid)
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;
146 * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
149 afs_IsDynroot(struct vcache *avc)
151 return afs_IsDynrootFid(&avc->fid);
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.
160 afs_dynroot_computeDirEnt(char *name, int *curPageP, int *curChunkP)
164 esize = afs_dir_NameBlobs(name);
165 if (*curChunkP + esize > EPP) {
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.
178 afs_dynroot_addDirEnt(struct DirHeader *dirHeader, int *curPageP,
179 int *curChunkP, char *name, int vnode)
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;
190 * Check if we need to flip pages.. If so, init the new page.
192 sizeOfEntry = afs_dir_NameBlobs(name);
193 if (curChunk + sizeOfEntry > EPP) {
199 pageHeader = (struct PageHeader *)(dirBase + curPage * AFS_PAGESIZE);
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;
208 dirHeader->alloMap[curPage] = EPP - 1;
211 dirEntry = (struct DirEntry *)(pageHeader + curChunk);
213 dirEntry->length = 0;
215 dirEntry->fid.vnode = htonl(vnode);
216 dirEntry->fid.vunique = htonl(1);
217 strcpy(dirEntry->name, name);
219 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
222 pageHeader->freebitmap[t1] |= (1 << t2);
226 * Add the new entry to the correct hash chain.
229 dirEntry->next = dirHeader->hashTable[i];
230 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
232 curChunk += sizeOfEntry;
233 dirHeader->alloMap[curPage] -= sizeOfEntry;
236 *curChunkP = curChunk;
240 * Invalidate the /afs vnode for dynroot; called when the underlying
241 * directory has changed and needs to be re-read.
244 afs_DynrootInvalidate(void)
248 struct VenusFid tfid;
250 if (!afs_dynrootEnable)
253 ObtainWriteLock(&afs_dynrootDirLock, 687);
254 afs_dynrootVersion++;
255 afs_dynrootVersionHigh = osi_Time();
256 ReleaseWriteLock(&afs_dynrootDirLock);
258 afs_GetDynrootFid(&tfid);
261 ObtainReadLock(&afs_xvcache);
262 tvc = afs_FindVCache(&tfid, &retry, 0);
263 ReleaseReadLock(&afs_xvcache);
266 tvc->states &= ~(CStatd | CUnique);
267 osi_dnlc_purgedp(tvc);
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.
278 afs_RebuildDynroot(void)
280 int cellidx, maxcellidx, i;
281 int aliasidx, maxaliasidx;
283 struct cell_alias *ca;
284 int curChunk, curPage;
286 char *newDir, *dotCell;
287 struct DirHeader *dirHeader;
289 struct afs_dynSymlink *ts;
292 ObtainReadLock(&afs_dynrootDirLock);
293 newVersion = afs_dynrootVersion;
294 ReleaseReadLock(&afs_dynrootDirLock);
297 * Compute the amount of space we need for the fake dir
302 /* Reserve space for "." and ".." */
305 for (cellidx = 0;; cellidx++) {
306 c = afs_GetCellByIndex(cellidx, READ_LOCK);
309 if (c->cellNum == afs_dynrootCell)
312 dotLen = strlen(c->cellName) + 2;
313 dotCell = afs_osi_Alloc(dotLen);
314 strcpy(dotCell, ".");
315 afs_strcat(dotCell, c->cellName);
317 afs_dynroot_computeDirEnt(c->cellName, &curPage, &curChunk);
318 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
320 afs_osi_Free(dotCell, dotLen);
321 afs_PutCell(c, READ_LOCK);
323 maxcellidx = cellidx;
325 for (aliasidx = 0;; aliasidx++) {
326 ca = afs_GetCellAlias(aliasidx);
330 dotLen = strlen(ca->alias) + 2;
331 dotCell = afs_osi_Alloc(dotLen);
332 strcpy(dotCell, ".");
333 afs_strcat(dotCell, ca->alias);
335 afs_dynroot_computeDirEnt(ca->alias, &curPage, &curChunk);
336 afs_dynroot_computeDirEnt(dotCell, &curPage, &curChunk);
338 afs_osi_Free(dotCell, dotLen);
339 afs_PutCellAlias(ca);
341 maxaliasidx = aliasidx;
343 ObtainReadLock(&afs_dynSymlinkLock);
344 ts = afs_dynSymlinkBase;
346 afs_dynroot_computeDirEnt(ts->name, &curPage, &curChunk);
350 dirSize = (curPage + 1) * AFS_PAGESIZE;
351 newDir = afs_osi_Alloc(dirSize);
354 * Now actually construct the directory.
358 dirHeader = (struct DirHeader *)newDir;
360 dirHeader->header.pgcount = 0;
361 dirHeader->header.tag = htons(1234);
362 dirHeader->header.freecount = 0;
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;
374 /* Install "." and ".." */
375 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
376 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
379 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
380 c = afs_GetCellByIndex(cellidx, READ_LOCK);
383 if (c->cellNum == afs_dynrootCell)
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);
397 afs_PutCell(c, READ_LOCK);
400 for (aliasidx = 0; aliasidx < maxaliasidx; aliasidx++) {
401 ca = afs_GetCellAlias(aliasidx);
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);
417 ts = afs_dynSymlinkBase;
419 int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
420 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ts->name, vnum);
424 ReleaseReadLock(&afs_dynSymlinkLock);
426 ObtainWriteLock(&afs_dynrootDirLock, 549);
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);
437 * Returns a pointer to the base of the dynroot directory in memory,
438 * length thereof, and a FetchStatus.
441 afs_GetDynroot(char **dynrootDir, int *dynrootLen,
442 struct AFSFetchStatus *status)
444 ObtainReadLock(&afs_dynrootDirLock);
445 if (!afs_dynrootDir || afs_dynrootDirVersion != afs_dynrootVersion) {
446 ReleaseReadLock(&afs_dynrootDirLock);
447 afs_RebuildDynroot();
448 ObtainReadLock(&afs_dynrootDirLock);
452 *dynrootDir = afs_dynrootDir;
454 *dynrootLen = afs_dynrootDirLen;
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;
472 * Puts back the dynroot read lock.
477 ReleaseReadLock(&afs_dynrootDirLock);
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.
486 afs_DynrootNewVnode(struct vcache *avc, struct AFSFetchStatus *status)
488 if (!afs_dynrootEnable)
491 if (afs_IsDynroot(avc)) {
492 afs_GetDynroot(0, 0, status);
498 * Check if this is an entry under /afs, e.g. /afs/cellname.
500 if (avc->fid.Cell == afs_dynrootCell
501 && avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
504 struct cell_alias *ca;
505 int namelen, linklen, cellidx, rw;
507 memset(status, 0, sizeof(struct AFSFetchStatus));
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;
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);
521 ObtainReadLock(&afs_dynSymlinkLock);
522 ts = afs_dynSymlinkBase;
524 if (ts->index == index)
530 linklen = strlen(ts->target);
531 avc->linkData = afs_osi_Alloc(linklen + 1);
532 strcpy(avc->linkData, ts->target);
534 status->Length = linklen;
535 status->UnixModeBits = 0755;
537 ReleaseReadLock(&afs_dynSymlinkLock);
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));
549 cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
550 rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
552 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_ALIAS) {
555 ca = afs_GetCellAlias(cellidx);
557 afs_warn("dynroot vnode inconsistency, can't find alias %d\n",
563 * linkData needs to contain the name of the cell
564 * we're aliasing for.
568 afs_warn("dynroot: alias %s missing real cell name\n",
570 avc->linkData = afs_strdup("unknown");
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);
580 status->UnixModeBits = 0755;
581 afs_PutCellAlias(ca);
583 c = afs_GetCellByIndex(cellidx, READ_LOCK);
585 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
591 * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
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");
600 status->UnixModeBits = 0644;
601 afs_PutCell(c, READ_LOCK);
604 status->Length = linklen;
612 * Enable or disable dynroot. Returns 0 if successful.
615 afs_SetDynrootEnable(int enable)
617 afs_dynrootEnable = enable;
618 return afs_dynrootCellInit();
622 * Check if dynroot support is enabled.
625 afs_GetDynrootEnable(void)
627 return afs_dynrootEnable;
631 * Remove a temporary symlink entry from /afs.
634 afs_DynrootVOPRemove(struct vcache *avc, struct AFS_UCRED *acred, char *aname)
636 struct afs_dynSymlink **tpps;
637 struct afs_dynSymlink *tps;
643 ObtainWriteLock(&afs_dynSymlinkLock, 97);
644 tpps = &afs_dynSymlinkBase;
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);
651 afs_osi_Free(tps, sizeof(*tps));
652 afs_dynSymlinkIndex++;
658 ReleaseWriteLock(&afs_dynSymlinkLock);
660 afs_DynrootInvalidate();
664 if (afs_CellOrAliasExists(aname))
671 * Create a temporary symlink entry in /afs.
674 afs_DynrootVOPSymlink(struct vcache *avc, struct AFS_UCRED *acred,
675 char *aname, char *atargetName)
677 struct afs_dynSymlink *tps;
681 if (afs_CellOrAliasExists(aname))
684 /* Check if it's already a symlink */
685 ObtainWriteLock(&afs_dynSymlinkLock, 91);
686 tps = afs_dynSymlinkBase;
688 if (afs_strcasecmp(aname, tps->name) == 0) {
689 ReleaseWriteLock(&afs_dynSymlinkLock);
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);
706 afs_DynrootInvalidate();