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 */
89 extern afs_int32 afs_cellindex;
90 extern afs_rwlock_t afs_xvcache;
93 * Returns non-zero iff fid corresponds to the top of the dynroot volume.
96 afs_IsDynrootFid(struct VenusFid *fid)
100 fid->Cell == AFS_DYNROOT_CELL &&
101 fid->Fid.Volume == AFS_DYNROOT_VOLUME &&
102 fid->Fid.Vnode == AFS_DYNROOT_VNODE &&
103 fid->Fid.Unique == AFS_DYNROOT_UNIQUE);
107 * Obtain the magic dynroot volume Fid.
110 afs_GetDynrootFid(struct VenusFid *fid)
112 fid->Cell = AFS_DYNROOT_CELL;
113 fid->Fid.Volume = AFS_DYNROOT_VOLUME;
114 fid->Fid.Vnode = AFS_DYNROOT_VNODE;
115 fid->Fid.Unique = AFS_DYNROOT_UNIQUE;
119 * Returns non-zero iff avc is a pointer to the dynroot /afs vnode.
125 return afs_IsDynrootFid(&avc->fid);
129 * Add directory entry by given name to a directory. Assumes the
130 * caller has allocated the directory to be large enough to hold
131 * the necessary entry.
134 afs_dynroot_addDirEnt(dirHeader, curPageP, curChunkP, name, vnode)
135 struct DirHeader *dirHeader;
141 char *dirBase = (char *) dirHeader;
142 struct PageHeader *pageHeader;
143 struct DirEntry *dirEntry;
144 int sizeOfEntry, i, t1, t2;
145 int curPage = *curPageP;
146 int curChunk = *curChunkP;
150 * Check if we need to flip pages.. If so, init the new page.
152 sizeOfEntry = afs_dir_NameBlobs(name);
153 if (curChunk + sizeOfEntry > EPP) {
159 pageHeader = (struct PageHeader *) (dirBase + curPage * AFS_PAGESIZE);
161 pageHeader->pgcount = 0;
162 pageHeader->tag = htons(1234);
163 pageHeader->freecount = 0;
164 pageHeader->freebitmap[0] = 0x01;
165 for (i = 1; i < EPP/8; i++)
166 pageHeader->freebitmap[i] = 0;
168 dirHeader->alloMap[curPage] = EPP - 1;
171 dirEntry = (struct DirEntry *) (pageHeader + curChunk);
173 dirEntry->length = 0;
175 dirEntry->fid.vnode = htonl(vnode);
176 dirEntry->fid.vunique = htonl(1);
177 strcpy(dirEntry->name, name);
179 for (i = curChunk; i < curChunk + sizeOfEntry; i++) {
182 pageHeader->freebitmap[t1] |= (1 << t2);
186 * Add the new entry to the correct hash chain.
189 dirEntry->next = dirHeader->hashTable[i];
190 dirHeader->hashTable[i] = htons(curPage * EPP + curChunk);
192 curChunk += sizeOfEntry;
193 dirHeader->alloMap[curPage] -= sizeOfEntry;
196 *curChunkP = curChunk;
200 * Regenerates the dynroot contents from the current list of
201 * cells. Useful when the list of cells has changed due to
202 * an AFSDB lookup, for instance.
207 int cellidx, maxcellidx, i;
209 int curChunk, curPage;
210 int dirSize, sizeOfCurEntry;
211 char *newDir, *dotCell;
212 struct DirHeader *dirHeader;
213 struct PageHeader *pageHeader;
214 struct DirEntry *dirEntry;
217 struct afs_dynSymlink *ts;
221 * Save afs_cellindex here, in case it changes between the
224 maxcellidx = afs_cellindex;
227 * Compute the amount of space we need for the fake dir
232 /* Reserve space for "." and ".." */
235 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
236 c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
239 sizeOfCurEntry = afs_dir_NameBlobs(c->cellName);
240 if (curChunk + sizeOfCurEntry > EPP) {
244 curChunk += sizeOfCurEntry;
246 dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
247 strcpy(dotCell, ".");
248 strcat(dotCell, c->cellName);
249 sizeOfCurEntry = afs_dir_NameBlobs(dotCell);
250 if (curChunk + sizeOfCurEntry > EPP) {
254 curChunk += sizeOfCurEntry;
256 afs_PutCell(c, READ_LOCK);
259 ObtainReadLock(&afs_dynSymlinkLock);
260 ts = afs_dynSymlinkBase;
262 sizeOfCurEntry = afs_dir_NameBlobs(ts->name);
263 if (curChunk + sizeOfCurEntry > EPP) {
267 curChunk += sizeOfCurEntry;
271 dirSize = (curPage + 1) * AFS_PAGESIZE;
272 newDir = afs_osi_Alloc(dirSize);
275 * Now actually construct the directory.
279 dirHeader = (struct DirHeader *) newDir;
281 dirHeader->header.pgcount = 0;
282 dirHeader->header.tag = htons(1234);
283 dirHeader->header.freecount = 0;
285 dirHeader->header.freebitmap[0] = 0xff;
286 dirHeader->header.freebitmap[1] = 0x1f;
287 for (i = 2; i < EPP/8; i++)
288 dirHeader->header.freebitmap[i] = 0;
289 dirHeader->alloMap[0] = EPP - DHE - 1;
290 for (i = 1; i < MAXPAGES; i++)
291 dirHeader->alloMap[i] = EPP;
292 for (i = 0; i < NHASHENT; i++)
293 dirHeader->hashTable[i] = 0;
295 /* Install "." and ".." */
296 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, ".", 1);
297 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk, "..", 1);
300 for (cellidx = 0; cellidx < maxcellidx; cellidx++) {
301 c = afs_GetCellByIndex(cellidx, READ_LOCK, 0 /* don't refresh */);
302 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
303 c->cellName, VNUM_FROM_CIDX_RW(cellidx, 0));
305 dotCell = afs_osi_Alloc(strlen(c->cellName) + 2);
306 strcpy(dotCell, ".");
307 strcat(dotCell, c->cellName);
308 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
309 dotCell, VNUM_FROM_CIDX_RW(cellidx, 1));
311 if (!(c->states & CAlias)) linkCount += 2;
312 afs_PutCell(c, READ_LOCK);
315 ts = afs_dynSymlinkBase;
317 int vnum = VNUM_FROM_TYPEID(VN_TYPE_SYMLINK, ts->index);
318 afs_dynroot_addDirEnt(dirHeader, &curPage, &curChunk,
323 newCellCount = maxcellidx + afs_dynSymlinkIndex;
324 ReleaseReadLock(&afs_dynSymlinkLock);
326 ObtainWriteLock(&afs_dynrootDirLock, 549);
327 if (afs_dynrootDir) afs_osi_Free(afs_dynrootDir, afs_dynrootDirLen);
328 afs_dynrootDir = newDir;
329 afs_dynrootDirLen = dirSize;
330 afs_dynrootDirLinkcnt = linkCount;
331 if (afs_dynrootCellCount != newCellCount) {
333 * New cells/symlinks added -- bump data version, invalidate vcache.
335 afs_dynrootCellCount = newCellCount;
336 afs_dynrootVersion++;
337 afs_dynrootVersionHigh = osi_Time();
340 ReleaseWriteLock(&afs_dynrootDirLock);
345 struct VenusFid tfid;
347 afs_GetDynrootFid(&tfid);
350 ObtainReadLock(&afs_xvcache);
351 tvc = afs_FindVCache(&tfid, 0, 0, &retry, 0);
352 ReleaseReadLock(&afs_xvcache);
355 tvc->states &= ~(CStatd | CUnique);
356 osi_dnlc_purgedp(tvc);
363 * Returns a pointer to the base of the dynroot directory in memory,
364 * length thereof, and a FetchStatus.
367 afs_GetDynroot(dynrootDir, dynrootLen, status)
370 struct AFSFetchStatus *status;
372 ObtainReadLock(&afs_dynrootDirLock);
373 if (!afs_dynrootDir) {
374 ReleaseReadLock(&afs_dynrootDirLock);
375 afs_RefreshDynroot();
376 ObtainReadLock(&afs_dynrootDirLock);
379 if (dynrootDir) *dynrootDir = afs_dynrootDir;
380 if (dynrootLen) *dynrootLen = afs_dynrootDirLen;
383 memset(status, 0, sizeof(struct AFSFetchStatus));
384 status->FileType = Directory;
385 status->LinkCount = afs_dynrootDirLinkcnt;
386 status->Length = afs_dynrootDirLen;
387 status->DataVersion = afs_dynrootVersion;
388 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
389 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
390 status->UnixModeBits = 0755;
391 status->ParentVnode = 1;
392 status->ParentUnique = 1;
393 status->dataVersionHigh = afs_dynrootVersionHigh;
398 * Puts back the dynroot read lock.
403 ReleaseReadLock(&afs_dynrootDirLock);
407 * Inform dynroot that a new vnode is being created. Return value
408 * is non-zero if this vnode is handled by dynroot, in which case
409 * FetchStatus will be filled in.
412 afs_DynrootNewVnode(avc, status)
414 struct AFSFetchStatus *status;
416 if (!afs_dynrootEnable) return 0;
418 if (afs_IsDynroot(avc)) {
419 afs_GetDynroot(0, 0, status);
425 * Check if this is an entry under /afs, e.g. /afs/cellname.
427 if (avc->fid.Cell == AFS_DYNROOT_CELL &&
428 avc->fid.Fid.Volume == AFS_DYNROOT_VOLUME) {
431 int namelen, linklen, cellidx, rw;
433 memset(status, 0, sizeof(struct AFSFetchStatus));
435 status->FileType = SymbolicLink;
436 status->LinkCount = 1;
437 status->DataVersion = 1;
438 status->CallerAccess = PRSFS_LOOKUP | PRSFS_READ;
439 status->AnonymousAccess = PRSFS_LOOKUP | PRSFS_READ;
440 status->ParentVnode = 1;
441 status->ParentUnique = 1;
443 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) == VN_TYPE_SYMLINK) {
444 struct afs_dynSymlink *ts;
445 int index = VNUM_TO_VNID(avc->fid.Fid.Vnode);
447 ObtainReadLock(&afs_dynSymlinkLock);
448 ts = afs_dynSymlinkBase;
450 if (ts->index == index) break;
455 linklen = strlen(ts->target);
456 avc->linkData = afs_osi_Alloc(linklen + 1);
457 strcpy(avc->linkData, ts->target);
459 status->Length = linklen;
460 status->UnixModeBits = 0755;
462 ReleaseReadLock(&afs_dynSymlinkLock);
467 if (VNUM_TO_VNTYPE(avc->fid.Fid.Vnode) != VN_TYPE_CELL) {
468 afs_warn("dynroot vnode inconsistency, unknown VNTYPE %d\n",
469 VNUM_TO_VNTYPE(avc->fid.Fid.Vnode));
473 cellidx = VNUM_TO_CIDX(avc->fid.Fid.Vnode);
474 rw = VNUM_TO_RW(avc->fid.Fid.Vnode);
476 c = afs_GetCellByIndex(cellidx, READ_LOCK, 1 /* refresh */);
478 afs_warn("dynroot vnode inconsistency, can't find cell %d\n",
483 if (c->states & CAlias) {
485 * linkData needs to contain the name of the cell
486 * we're aliasing for.
488 char *realName = c->realName;
491 afs_warn("dynroot: alias %s missing real cell name\n",
494 avc->linkData = afs_osi_Alloc(linklen + 1);
495 strcpy(avc->linkData, "unknown");
497 int namelen = strlen(realName);
498 linklen = rw + namelen;
499 avc->linkData = afs_osi_Alloc(linklen + 1);
500 strcpy(avc->linkData, rw ? "." : "");
501 strcat(avc->linkData, realName);
504 status->UnixModeBits = 0755;
507 * linkData needs to contain "#cell:root.cell" or "%cell:root.cell"
509 namelen = strlen(c->cellName);
510 linklen = 1 + namelen + 10;
511 avc->linkData = afs_osi_Alloc(linklen + 1);
512 strcpy(avc->linkData, rw ? "%" : "#");
513 strcat(avc->linkData, c->cellName);
514 strcat(avc->linkData, ":root.cell");
516 status->UnixModeBits = 0644;
519 status->Length = linklen;
520 afs_PutCell(c, READ_LOCK);
528 * Enable or disable dynroot. Returns 0 if successful.
531 afs_SetDynrootEnable(enable)
534 afs_dynrootEnable = enable;
539 * Check if dynroot support is enabled.
542 afs_GetDynrootEnable()
544 return afs_dynrootEnable;
548 * Remove a temporary symlink entry from /afs.
551 afs_DynrootVOPRemove(avc, acred, aname)
553 struct AFS_UCRED *acred;
556 struct afs_dynSymlink **tpps;
557 struct afs_dynSymlink *tps;
564 ObtainWriteLock(&afs_dynSymlinkLock, 97);
565 tpps = &afs_dynSymlinkBase;
568 if (afs_strcasecmp(aname, tps->name) == 0) {
569 afs_osi_Free(tps->name, strlen(tps->name) + 1);
570 afs_osi_Free(tps->target, strlen(tps->target) + 1);
572 afs_osi_Free(tps, sizeof(*tps));
573 afs_dynSymlinkIndex++;
579 ReleaseWriteLock(&afs_dynSymlinkLock);
581 afs_RefreshDynroot();
585 /* Check if this is an actual cell? */
586 c = afs_GetCellByName2(aname, READ_LOCK, 0 /* no AFSDB */);
588 afs_PutCell(c, READ_LOCK);
596 * Create a temporary symlink entry in /afs.
599 afs_DynrootVOPSymlink(avc, acred, aname, atargetName)
601 struct AFS_UCRED *acred;
605 struct afs_dynSymlink *tps;
611 /* Check if it's already a cell */
612 c = afs_GetCellByName2(aname, READ_LOCK, 0 /* no AFSDB */);
614 afs_PutCell(c, READ_LOCK);
618 /* Check if it's already a symlink */
619 ObtainWriteLock(&afs_dynSymlinkLock, 91);
620 tps = afs_dynSymlinkBase;
622 if (afs_strcasecmp(aname, tps->name) == 0) {
623 ReleaseWriteLock(&afs_dynSymlinkLock);
629 /* Doesn't already exist -- go ahead and create it */
630 tps = afs_osi_Alloc(sizeof(*tps));
631 tps->index = afs_dynSymlinkIndex++;
632 tps->next = afs_dynSymlinkBase;
633 tps->name = afs_osi_Alloc(strlen(aname) + 1);
634 strcpy(tps->name, aname);
635 tps->target = afs_osi_Alloc(strlen(atargetName) + 1);
636 strcpy(tps->target, atargetName);
637 afs_dynSymlinkBase = tps;
638 ReleaseWriteLock(&afs_dynSymlinkLock);
640 afs_RefreshDynroot();