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 <afs/errmap_nt.h>
41 #include "afscp_internal.h"
43 static int dirmode = DIRMODE_CELL;
46 afscp_SetDirMode(int mode)
48 if ((mode != DIRMODE_CELL) && (mode != DIRMODE_DYNROOT)) {
56 /* comparison function for tsearch */
58 dircompare(const void *a, const void *b)
60 const struct afscp_dircache *sa = a, *sb = b;
61 if (sa->me.fid.Vnode < sb->me.fid.Vnode)
63 if (sa->me.fid.Vnode > sb->me.fid.Vnode)
65 if (sa->me.fid.Unique < sb->me.fid.Unique)
67 if (sa->me.fid.Unique > sb->me.fid.Unique)
72 /* make sure the dirstream contains the most up to date directory contents */
74 _DirUpdate(struct afscp_dirstream *d)
76 struct AFSFetchStatus s;
78 struct afscp_volume *v;
79 struct afscp_dircache key, *stored;
83 code = afscp_GetStatus(&d->fid, &s);
88 if (d->dirbuffer && d->dv == s.DataVersion) {
91 v = afscp_VolumeById(d->fid.cell, d->fid.fid.Volume);
97 memcpy(&key.me, &d->fid, sizeof(struct afscp_venusfid));
98 cached = tfind(&key, &v->dircache, dircompare);
100 stored = *(struct afscp_dircache **)cached;
101 if (d->dv == s.DataVersion) {
102 d->dirbuffer = stored->dirbuffer;
103 d->buflen = stored->buflen;
107 tdelete(&key, &v->dircache, dircompare);
108 if (d->dirbuffer != stored->dirbuffer)
109 free(stored->dirbuffer);
112 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
116 if (d->buflen != s.Length) {
119 new = realloc(d->dirbuffer, s.Length);
121 new = malloc(s.Length);
126 afscp_errno = ENOMEM;
129 d->buflen = s.Length;
132 code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0);
136 d->dv = s.DataVersion;
137 cached = tsearch(&key, &v->dircache, dircompare);
138 if (cached != NULL) {
139 stored = malloc(sizeof(struct afscp_dircache));
140 if (stored != NULL) {
141 memcpy(&stored->me, &d->fid, sizeof(struct afscp_venusfid));
142 stored->buflen = d->buflen;
143 stored->dirbuffer = d->dirbuffer;
145 *(struct afscp_dircache **)cached = stored;
147 tdelete(&key, &v->dircache, dircompare);
153 static struct DirEntry *
154 dir_get_entry(struct afscp_dirstream *d, int entry)
156 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
157 /* struct PageHeader *p; */
158 struct DirEntry *ret;
163 off = entry & (EPP - 1);
165 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
168 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
171 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
174 /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */
175 /* p is set but not referenced later */
176 /* fr = p->freebitmap[off >> 8]; */
177 /* fr is set but not referenced later */
178 ret = (struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
182 struct afscp_dirstream *
183 afscp_OpenDir(const struct afscp_venusfid *fid)
185 struct afscp_dirstream *ret;
186 struct AFSFetchStatus s;
189 code = afscp_GetStatus(fid, &s);
194 if (s.FileType != Directory) {
195 afscp_errno = ENOTDIR;
198 ret = malloc(sizeof(struct afscp_dirstream));
200 afscp_errno = ENOMEM;
203 memset(ret, 0, sizeof(struct afscp_dirstream));
204 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
205 code = _DirUpdate(ret);
216 struct afscp_dirent *
217 afscp_ReadDir(struct afscp_dirstream *d)
219 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
220 struct DirEntry *info;
225 while (ent == 0 && d->hashent < NHASHENT - 1) {
227 ent = ntohs(h->hashTable[d->hashent]);
233 info = dir_get_entry(d, ent);
238 d->ret.vnode = ntohl(info->fid.vnode);
239 d->ret.unique = ntohl(info->fid.vunique);
240 strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */
241 d->entry = ntohs(info->next);
246 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
248 afscp_RewindDir(struct afscp_dirstream *d)
257 afscp_CloseDir(struct afscp_dirstream *d)
264 namehash(const char *name)
269 while (*name != '\0')
270 hval = (hval * 173) + *name++;
271 tval = hval & (NHASHENT - 1);
272 return tval ? (hval < 0 ? NHASHENT - tval : tval)
276 struct afscp_venusfid *
277 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
281 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
282 struct DirEntry *info;
284 code = _DirUpdate(d);
288 hval = namehash(name);
289 entry = ntohs(h->hashTable[hval]);
292 info = dir_get_entry(d, entry);
297 if (strcmp(info->name, name) == 0)
299 entry = ntohs(info->next);
302 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
303 ntohl(info->fid.vnode),
304 ntohl(info->fid.vunique));
306 afscp_errno = ENOENT;
311 struct afscp_venusfid *
312 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
314 struct afscp_venusfid *ret;
315 struct afscp_dirstream *d;
317 d = afscp_OpenDir(dir);
321 ret = afscp_DirLookup(d, name);
327 gettoproot(struct afscp_cell *cell, char *p, char **q,
328 struct afscp_venusfid **root)
330 struct afscp_volume *rootvol;
333 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
334 afscp_errno = EINVAL;
337 if (strncmp(p, "/afs", 4) == 0) {
338 afs_dprintf(("gettoproot: path is absolute\n"));
342 if (dirmode == DIRMODE_DYNROOT) {
355 if (*p == '.' || *p == 0) {
356 afscp_errno = EINVAL;
360 while (*r && *r != '/')
363 afscp_errno = ENODEV;
368 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
369 cell = afscp_CellByName(p, NULL);
371 afs_dprintf(("gettoproot: no such cell\n"));
372 afscp_errno = ENODEV;
375 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
376 if (!rootvol && voltype == ROVOL)
377 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
380 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
382 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
385 afs_dprintf(("gettoproot: volume not found\n"));
387 afs_dprintf(("gettoproot: path is relative\n"));
392 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
394 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
397 if (rootvol == NULL) {
398 afscp_errno = ENODEV;
401 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
406 getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname,
407 struct afscp_venusfid **root)
409 struct afscp_volume *vol;
410 vol = afscp_VolumeByName(cell, vname, voltype);
411 if (!vol && voltype == ROVOL)
412 vol = afscp_VolumeByName(cell, vname, RWVOL);
414 afscp_errno = ENODEV;
417 *root = afscp_MakeFid(cell, vol->id, 1, 1);
421 typedef struct fidstack_s {
424 struct afscp_venusfid **entries;
432 ret = malloc(sizeof(struct fidstack_s));
434 afscp_errno = ENOMEM;
439 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
440 if (ret->entries == NULL) {
442 afscp_errno = ENOMEM;
449 fidstack_push(fidstack s, struct afscp_venusfid *entry)
451 struct afscp_venusfid **new;
452 if (s->count >= s->alloc) {
453 new = realloc(s->entries, (s->alloc + 10) *
454 sizeof(struct afscp_venusfid *));
461 s->entries[s->count++] = entry;
465 static struct afscp_venusfid *
466 fidstack_pop(fidstack s)
469 return s->entries[--s->count];
474 fidstack_free(fidstack s)
478 for (i = 0; i < s->count; i++)
484 static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *,
485 fidstack, char *, int);
487 static struct afscp_venusfid *
488 afscp_HandleLink(struct afscp_venusfid *in,
489 const struct afscp_venusfid *parent, fidstack fids,
490 int follow, const struct AFSFetchStatus *s, int terminal)
492 char *linkbuf, *linkbufq;
493 struct afscp_cell *cell;
494 struct afscp_volume *v;
495 struct afscp_venusfid *root, *ret;
499 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
502 linkbuf = malloc(s->Length + 1);
503 code = afscp_PRead(in, linkbuf, s->Length, 0);
509 if (code != s->Length) {
515 linkbuf[s->Length] = 0;
516 if (s->UnixModeBits & 0111) { /* normal link */
517 afs_dprintf(("Recursing on symlink %s...\n", linkbuf));
518 if (linkbuf[0] == '/') {
519 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
525 ret = _ResolvePath(root, 0, linkbufq, 0);
529 ret = _ResolvePath(parent, fids, linkbuf, 0);
532 } else { /* mountpoint */
533 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
534 linkbufq = strchr(linkbuf, ':');
536 v = afscp_VolumeById(cell, in->fid.Volume);
540 afscp_errno = ENODEV;
543 voltype = v->voltype;
544 if (linkbuf[0] == '%')
546 if (linkbufq == NULL) {
547 linkbufq = linkbuf + 1;
550 cell = afscp_CellByName(linkbuf + 1, NULL);
551 if (linkbuf[0] != '%')
556 afscp_errno = ENODEV;
559 len = strnlen(linkbufq, s->Length + 1);
562 afscp_errno = ENODEV;
565 len = strnlen(linkbufq, s->Length + 1);
566 linkbufq[len - 1] = 0; /* eliminate trailer */
567 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
576 static struct afscp_venusfid *
577 _ResolvePath(const struct afscp_venusfid *start, fidstack infids,
578 char *path, int follow)
580 struct afscp_venusfid *ret, *cwd;
581 struct AFSFetchStatus s;
588 ret = cwd = afscp_DupFid(start);
591 fids = fidstack_alloc();
600 if (strcmp(p, ".") == 0) {
602 } else if (strcmp(p, "..") == 0) {
603 ret = fidstack_pop(fids);
609 ret = afscp_ResolveName(cwd, p);
611 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
612 cwd->fid.Volume, cwd->fid.Vnode,
619 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p,
620 cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique,
621 ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
625 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
626 code = afscp_GetStatus(ret, &s);
634 if (s.FileType == SymbolicLink) {
635 if (linkcount++ > 5) {
644 afscp_HandleLink(ret, cwd, fids, follow, &s,
652 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
653 ret->fid.Vnode, ret->fid.Unique));
657 afscp_errno = ENOTDIR;
666 fidstack_push(fids, cwd);
670 while ((q != NULL) && (*q == '/'))
680 * Resolve a path to a FID starting from the root volume
682 * \param[in] path full path
684 * \post Returns a venusfid representing the final element of path
686 * \note There are three cases:
687 * (1) begins with /afs: start in root.afs of cell or home cell
688 * (2) else begins with /: error
689 * (3) else start in root.cell of cell or home cell
691 struct afscp_venusfid *
692 afscp_ResolvePath(const char *path)
694 struct afscp_venusfid *root, *ret;
695 struct afscp_cell *cell;
698 p = strdup(path); /* so we can modify the string */
700 afscp_errno = ENOMEM;
703 cell = afscp_DefaultCell();
705 afscp_errno = EINVAL;
708 code = gettoproot(cell, p, &q, &root);
714 ret = _ResolvePath(root, 0, q, 1);
723 * Resolve a path to a FID starting from the given volume
725 * \param[in] v volume structure containing id and cell info
726 * \param[in] path path relative to volume v
728 * \post Returns a venusfid representing the final element of path
730 struct afscp_venusfid *
731 afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path)
733 struct afscp_venusfid *root, *ret;
736 /* so we can modify the string */
739 afscp_errno = ENOMEM;
742 root = afscp_MakeFid(v->cell, v->id, 1, 1);
746 ret = _ResolvePath(root, 0, p, 1);