2 Copyright (C) 2003 - 2010 Chaskiel Grundman
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <afsconfig.h>
28 #include <afs/param.h>
34 #include <afs/vlserver.h>
35 #include <afs/vldbint.h>
38 #include "afscp_internal.h"
40 static int dirmode = DIRMODE_CELL;
43 afscp_SetDirMode(int mode)
45 if ((mode != DIRMODE_CELL) && (mode != DIRMODE_DYNROOT)) {
53 /* comparison function for tsearch */
55 dircompare(const void *a, const void *b)
57 const struct afscp_dircache *sa = a, *sb = b;
58 if (sa->me.fid.Vnode < sb->me.fid.Vnode)
60 if (sa->me.fid.Vnode > sb->me.fid.Vnode)
62 if (sa->me.fid.Unique < sb->me.fid.Unique)
64 if (sa->me.fid.Unique > sb->me.fid.Unique)
69 /* make sure the dirstream contains the most up to date directory contents */
71 _DirUpdate(struct afscp_dirstream *d)
73 struct AFSFetchStatus s;
75 struct afscp_volume *v;
76 struct afscp_dircache key, *stored;
80 code = afscp_GetStatus(&d->fid, &s);
85 if (d->dirbuffer && d->dv == s.DataVersion) {
88 v = afscp_VolumeById(d->fid.cell, d->fid.fid.Volume);
94 memcpy(&key.me, &d->fid, sizeof(struct afscp_venusfid));
95 cached = tfind(&key, &v->dircache, dircompare);
97 stored = *(struct afscp_dircache **)cached;
98 if (d->dv == s.DataVersion) {
99 d->dirbuffer = stored->dirbuffer;
100 d->buflen = stored->buflen;
104 tdelete(&key, &v->dircache, dircompare);
105 if (d->dirbuffer != stored->dirbuffer)
106 free(stored->dirbuffer);
109 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
113 if (d->buflen != s.Length) {
116 new = realloc(d->dirbuffer, s.Length);
118 new = malloc(s.Length);
123 afscp_errno = ENOMEM;
126 d->buflen = s.Length;
129 code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0);
133 d->dv = s.DataVersion;
134 cached = tsearch(&key, &v->dircache, dircompare);
135 if (cached != NULL) {
136 stored = malloc(sizeof(struct afscp_dircache));
137 if (stored != NULL) {
138 memcpy(&stored->me, &d->fid, sizeof(struct afscp_venusfid));
139 stored->buflen = d->buflen;
140 stored->dirbuffer = d->dirbuffer;
142 *(struct afscp_dircache **)cached = stored;
144 tdelete(&key, &v->dircache, dircompare);
150 static struct DirEntry *
151 dir_get_entry(struct afscp_dirstream *d, int entry)
153 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
154 /* struct PageHeader *p; */
155 struct DirEntry *ret;
160 off = entry & (EPP - 1);
162 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
165 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
168 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
171 /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */
172 /* p is set but not referenced later */
173 /* fr = p->freebitmap[off >> 8]; */
174 /* fr is set but not referenced later */
175 ret = (struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
179 struct afscp_dirstream *
180 afscp_OpenDir(const struct afscp_venusfid *fid)
182 struct afscp_dirstream *ret;
183 struct AFSFetchStatus s;
186 code = afscp_GetStatus(fid, &s);
191 if (s.FileType != Directory) {
192 afscp_errno = ENOTDIR;
195 ret = malloc(sizeof(struct afscp_dirstream));
197 afscp_errno = ENOMEM;
200 memset(ret, 0, sizeof(struct afscp_dirstream));
201 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
202 code = _DirUpdate(ret);
213 struct afscp_dirent *
214 afscp_ReadDir(struct afscp_dirstream *d)
216 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
217 struct DirEntry *info;
222 while (ent == 0 && d->hashent < NHASHENT - 1) {
224 ent = ntohs(h->hashTable[d->hashent]);
230 info = dir_get_entry(d, ent);
235 d->ret.vnode = ntohl(info->fid.vnode);
236 d->ret.unique = ntohl(info->fid.vunique);
237 strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */
238 d->entry = ntohs(info->next);
243 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
245 afscp_RewindDir(struct afscp_dirstream *d)
254 afscp_CloseDir(struct afscp_dirstream *d)
261 namehash(const char *name)
266 while (*name != '\0')
267 hval = (hval * 173) + *name++;
268 tval = hval & (NHASHENT - 1);
269 return tval ? (hval < 0 ? NHASHENT - tval : tval)
273 struct afscp_venusfid *
274 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
278 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
279 struct DirEntry *info;
281 code = _DirUpdate(d);
285 hval = namehash(name);
286 entry = ntohs(h->hashTable[hval]);
289 info = dir_get_entry(d, entry);
294 if (strcmp(info->name, name) == 0)
296 entry = ntohs(info->next);
299 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
300 ntohl(info->fid.vnode),
301 ntohl(info->fid.vunique));
303 afscp_errno = ENOENT;
308 struct afscp_venusfid *
309 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
311 struct afscp_venusfid *ret;
312 struct afscp_dirstream *d;
314 d = afscp_OpenDir(dir);
318 ret = afscp_DirLookup(d, name);
324 gettoproot(struct afscp_cell *cell, char *p, char **q,
325 struct afscp_venusfid **root)
327 struct afscp_volume *rootvol;
330 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
331 afscp_errno = EINVAL;
334 if (strncmp(p, "/afs", 4) == 0) {
335 afs_dprintf(("gettoproot: path is absolute\n"));
339 if (dirmode == DIRMODE_DYNROOT) {
352 if (*p == '.' || *p == 0) {
353 afscp_errno = EINVAL;
357 while (*r && *r != '/')
360 afscp_errno = ENODEV;
365 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
366 cell = afscp_CellByName(p, NULL);
368 afs_dprintf(("gettoproot: no such cell\n"));
369 afscp_errno = ENODEV;
372 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
373 if (!rootvol && voltype == ROVOL)
374 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
377 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
379 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
382 afs_dprintf(("gettoproot: volume not found\n"));
384 afs_dprintf(("gettoproot: path is relative\n"));
389 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
391 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
394 if (rootvol == NULL) {
395 afscp_errno = ENODEV;
398 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
403 getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname,
404 struct afscp_venusfid **root)
406 struct afscp_volume *vol;
407 vol = afscp_VolumeByName(cell, vname, voltype);
408 if (!vol && voltype == ROVOL)
409 vol = afscp_VolumeByName(cell, vname, RWVOL);
411 afscp_errno = ENODEV;
414 *root = afscp_MakeFid(cell, vol->id, 1, 1);
418 typedef struct fidstack_s {
421 struct afscp_venusfid **entries;
429 ret = malloc(sizeof(struct fidstack_s));
431 afscp_errno = ENOMEM;
436 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
437 if (ret->entries == NULL) {
439 afscp_errno = ENOMEM;
446 fidstack_push(fidstack s, struct afscp_venusfid *entry)
448 struct afscp_venusfid **new;
449 if (s->count >= s->alloc) {
450 new = realloc(s->entries, (s->alloc + 10) *
451 sizeof(struct afscp_venusfid *));
458 s->entries[s->count++] = entry;
462 static struct afscp_venusfid *
463 fidstack_pop(fidstack s)
466 return s->entries[--s->count];
471 fidstack_free(fidstack s)
475 for (i = 0; i < s->count; i++)
481 static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *,
482 fidstack, char *, int);
484 static struct afscp_venusfid *
485 afscp_HandleLink(struct afscp_venusfid *in,
486 const struct afscp_venusfid *parent, fidstack fids,
487 int follow, const struct AFSFetchStatus *s, int terminal)
489 char *linkbuf, *linkbufq;
490 struct afscp_cell *cell;
491 struct afscp_volume *v;
492 struct afscp_venusfid *root, *ret;
496 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
499 linkbuf = malloc(s->Length + 1);
500 code = afscp_PRead(in, linkbuf, s->Length, 0);
506 if (code != s->Length) {
512 linkbuf[s->Length] = 0;
513 if (s->UnixModeBits & 0111) { /* normal link */
514 afs_dprintf(("Recursing on symlink %s...\n", linkbuf));
515 if (linkbuf[0] == '/') {
516 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
522 ret = _ResolvePath(root, 0, linkbufq, 0);
526 ret = _ResolvePath(parent, fids, linkbuf, 0);
529 } else { /* mountpoint */
530 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
531 linkbufq = strchr(linkbuf, ':');
533 v = afscp_VolumeById(cell, in->fid.Volume);
537 afscp_errno = ENODEV;
540 voltype = v->voltype;
541 if (linkbuf[0] == '%')
543 if (linkbufq == NULL) {
544 linkbufq = linkbuf + 1;
547 cell = afscp_CellByName(linkbuf + 1, NULL);
548 if (linkbuf[0] != '%')
553 afscp_errno = ENODEV;
556 len = strnlen(linkbufq, s->Length + 1);
559 afscp_errno = ENODEV;
562 len = strnlen(linkbufq, s->Length + 1);
563 linkbufq[len - 1] = 0; /* eliminate trailer */
564 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
573 static struct afscp_venusfid *
574 _ResolvePath(const struct afscp_venusfid *start, fidstack infids,
575 char *path, int follow)
577 struct afscp_venusfid *ret, *cwd;
578 struct AFSFetchStatus s;
585 ret = cwd = afscp_DupFid(start);
588 fids = fidstack_alloc();
597 if (strcmp(p, ".") == 0) {
599 } else if (strcmp(p, "..") == 0) {
600 ret = fidstack_pop(fids);
606 ret = afscp_ResolveName(cwd, p);
608 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
609 cwd->fid.Volume, cwd->fid.Vnode,
616 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p,
617 cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique,
618 ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
622 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
623 code = afscp_GetStatus(ret, &s);
631 if (s.FileType == SymbolicLink) {
632 if (linkcount++ > 5) {
641 afscp_HandleLink(ret, cwd, fids, follow, &s,
649 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
650 ret->fid.Vnode, ret->fid.Unique));
654 afscp_errno = ENOTDIR;
663 fidstack_push(fids, cwd);
667 while ((q != NULL) && (*q == '/'))
677 * Resolve a path to a FID starting from the root volume
679 * \param[in] path full path
681 * \post Returns a venusfid representing the final element of path
683 * \note There are three cases:
684 * (1) begins with /afs: start in root.afs of cell or home cell
685 * (2) else begins with /: error
686 * (3) else start in root.cell of cell or home cell
688 struct afscp_venusfid *
689 afscp_ResolvePath(const char *path)
691 struct afscp_venusfid *root, *ret;
692 struct afscp_cell *cell;
695 p = strdup(path); /* so we can modify the string */
697 afscp_errno = ENOMEM;
700 cell = afscp_DefaultCell();
702 afscp_errno = EINVAL;
705 code = gettoproot(cell, p, &q, &root);
711 ret = _ResolvePath(root, 0, q, 1);
720 * Resolve a path to a FID starting from the given volume
722 * \param[in] v volume structure containing id and cell info
723 * \param[in] path path relative to volume v
725 * \post Returns a venusfid representing the final element of path
727 struct afscp_venusfid *
728 afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path)
730 struct afscp_venusfid *root, *ret;
733 /* so we can modify the string */
736 afscp_errno = ENOMEM;
739 root = afscp_MakeFid(v->cell, v->id, 1, 1);
743 ret = _ResolvePath(root, 0, p, 1);