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 pthread_mutex_lock(&(stored->mtx));
108 tdelete(&key, &v->dircache, dircompare);
110 while (stored->nwaiters > 1) {
111 pthread_cond_wait(&(stored->cv), &(stored->mtx));
114 pthread_cond_destroy(&(stored->cv));
115 pthread_mutex_unlock(&(stored->mtx));
116 pthread_mutex_destroy(&(stored->mtx));
117 if (d->dirbuffer != stored->dirbuffer)
118 free(stored->dirbuffer);
121 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
125 if (d->buflen != s.Length) {
128 new = realloc(d->dirbuffer, s.Length);
130 new = malloc(s.Length);
135 afscp_errno = ENOMEM;
138 d->buflen = s.Length;
141 code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0);
145 d->dv = s.DataVersion;
146 cached = tsearch(&key, &v->dircache, dircompare);
147 if (cached != NULL) {
148 stored = malloc(sizeof(struct afscp_dircache));
149 if (stored != NULL) {
150 memcpy(&stored->me, &d->fid, sizeof(struct afscp_venusfid));
151 stored->buflen = d->buflen;
152 stored->dirbuffer = d->dirbuffer;
154 stored->nwaiters = 0;
155 pthread_mutex_init(&(stored->mtx), NULL);
156 pthread_cond_init(&(stored->cv), NULL);
157 *(struct afscp_dircache **)cached = stored;
159 tdelete(&key, &v->dircache, dircompare);
165 static struct DirEntry *
166 dir_get_entry(struct afscp_dirstream *d, int entry)
168 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
169 /* struct PageHeader *p; */
170 struct DirEntry *ret;
175 off = entry & (EPP - 1);
177 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
180 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
183 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
186 /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */
187 /* p is set but not referenced later */
188 /* fr = p->freebitmap[off >> 8]; */
189 /* fr is set but not referenced later */
190 ret = (struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
194 struct afscp_dirstream *
195 afscp_OpenDir(const struct afscp_venusfid *fid)
197 struct afscp_dirstream *ret;
198 struct AFSFetchStatus s;
201 code = afscp_GetStatus(fid, &s);
206 if (s.FileType != Directory) {
207 afscp_errno = ENOTDIR;
210 ret = calloc(1, sizeof(struct afscp_dirstream));
212 afscp_errno = ENOMEM;
215 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
216 code = _DirUpdate(ret);
227 struct afscp_dirent *
228 afscp_ReadDir(struct afscp_dirstream *d)
230 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
231 struct DirEntry *info;
236 while (ent == 0 && d->hashent < NHASHENT - 1) {
238 ent = ntohs(h->hashTable[d->hashent]);
244 info = dir_get_entry(d, ent);
249 d->ret.vnode = ntohl(info->fid.vnode);
250 d->ret.unique = ntohl(info->fid.vunique);
251 strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */
252 d->entry = ntohs(info->next);
257 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
259 afscp_RewindDir(struct afscp_dirstream *d)
268 afscp_CloseDir(struct afscp_dirstream *d)
275 namehash(const char *name)
280 while (*name != '\0')
281 hval = (hval * 173) + *name++;
282 tval = hval & (NHASHENT - 1);
283 return tval ? (hval < 0 ? NHASHENT - tval : tval)
287 struct afscp_venusfid *
288 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
292 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
293 struct DirEntry *info;
295 code = _DirUpdate(d);
299 hval = namehash(name);
300 entry = ntohs(h->hashTable[hval]);
303 info = dir_get_entry(d, entry);
308 if (strcmp(info->name, name) == 0)
310 entry = ntohs(info->next);
313 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
314 ntohl(info->fid.vnode),
315 ntohl(info->fid.vunique));
317 afscp_errno = ENOENT;
322 struct afscp_venusfid *
323 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
325 struct afscp_venusfid *ret;
326 struct afscp_dirstream *d;
328 d = afscp_OpenDir(dir);
332 ret = afscp_DirLookup(d, name);
338 gettoproot(struct afscp_cell *cell, char *p, char **q,
339 struct afscp_venusfid **root)
341 struct afscp_volume *rootvol;
344 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
345 afscp_errno = EINVAL;
348 if (strncmp(p, "/afs", 4) == 0) {
349 afs_dprintf(("gettoproot: path is absolute\n"));
353 if (dirmode == DIRMODE_DYNROOT) {
366 if (*p == '.' || *p == 0) {
367 afscp_errno = EINVAL;
371 while (*r && *r != '/')
374 afscp_errno = ENODEV;
379 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
380 cell = afscp_CellByName(p, NULL);
382 afs_dprintf(("gettoproot: no such cell\n"));
383 afscp_errno = ENODEV;
386 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
387 if (!rootvol && voltype == ROVOL)
388 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
391 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
393 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
396 afs_dprintf(("gettoproot: volume not found\n"));
398 afs_dprintf(("gettoproot: path is relative\n"));
403 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
405 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
408 if (rootvol == NULL) {
409 afscp_errno = ENODEV;
412 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
417 getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname,
418 struct afscp_venusfid **root)
420 struct afscp_volume *vol;
421 vol = afscp_VolumeByName(cell, vname, voltype);
422 if (!vol && voltype == ROVOL)
423 vol = afscp_VolumeByName(cell, vname, RWVOL);
425 afscp_errno = ENODEV;
428 *root = afscp_MakeFid(cell, vol->id, 1, 1);
432 typedef struct fidstack_s {
435 struct afscp_venusfid **entries;
443 ret = malloc(sizeof(struct fidstack_s));
445 afscp_errno = ENOMEM;
450 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
451 if (ret->entries == NULL) {
453 afscp_errno = ENOMEM;
460 fidstack_push(fidstack s, struct afscp_venusfid *entry)
462 struct afscp_venusfid **new;
463 if (s->count >= s->alloc) {
464 new = realloc(s->entries, (s->alloc + 10) *
465 sizeof(struct afscp_venusfid *));
472 s->entries[s->count++] = entry;
476 static struct afscp_venusfid *
477 fidstack_pop(fidstack s)
480 return s->entries[--s->count];
485 fidstack_free(fidstack s)
489 for (i = 0; i < s->count; i++)
495 static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *,
496 fidstack, char *, int);
498 static struct afscp_venusfid *
499 afscp_HandleLink(struct afscp_venusfid *in,
500 const struct afscp_venusfid *parent, fidstack fids,
501 int follow, const struct AFSFetchStatus *s, int terminal)
503 char *linkbuf, *linkbufq;
504 struct afscp_cell *cell;
505 struct afscp_volume *v;
506 struct afscp_venusfid *root, *ret;
510 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
513 linkbuf = malloc(s->Length + 1);
514 code = afscp_PRead(in, linkbuf, s->Length, 0);
520 if (code != s->Length) {
526 linkbuf[s->Length] = 0;
527 if (s->UnixModeBits & 0111) { /* normal link */
528 afs_dprintf(("Recursing on symlink %s...\n", linkbuf));
529 if (linkbuf[0] == '/') {
530 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
536 ret = _ResolvePath(root, 0, linkbufq, 0);
540 ret = _ResolvePath(parent, fids, linkbuf, 0);
543 } else { /* mountpoint */
544 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
545 linkbufq = strchr(linkbuf, ':');
547 v = afscp_VolumeById(cell, in->fid.Volume);
551 afscp_errno = ENODEV;
554 voltype = v->voltype;
555 if (linkbuf[0] == '%')
557 if (linkbufq == NULL) {
558 linkbufq = linkbuf + 1;
561 cell = afscp_CellByName(linkbuf + 1, NULL);
562 if (linkbuf[0] != '%')
567 afscp_errno = ENODEV;
570 len = strnlen(linkbufq, s->Length + 1);
573 afscp_errno = ENODEV;
576 len = strnlen(linkbufq, s->Length + 1);
577 linkbufq[len - 1] = 0; /* eliminate trailer */
578 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
587 static struct afscp_venusfid *
588 _ResolvePath(const struct afscp_venusfid *start, fidstack infids,
589 char *path, int follow)
591 struct afscp_venusfid *ret, *cwd;
592 struct AFSFetchStatus s;
599 ret = cwd = afscp_DupFid(start);
602 fids = fidstack_alloc();
611 if (strcmp(p, ".") == 0) {
613 } else if (strcmp(p, "..") == 0) {
614 ret = fidstack_pop(fids);
620 ret = afscp_ResolveName(cwd, p);
622 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
623 cwd->fid.Volume, cwd->fid.Vnode,
630 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p,
631 cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique,
632 ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
636 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
637 code = afscp_GetStatus(ret, &s);
645 if (s.FileType == SymbolicLink) {
646 if (linkcount++ > 5) {
655 afscp_HandleLink(ret, cwd, fids, follow, &s,
663 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
664 ret->fid.Vnode, ret->fid.Unique));
668 afscp_errno = ENOTDIR;
677 fidstack_push(fids, cwd);
681 while ((q != NULL) && (*q == '/'))
691 * Resolve a path to a FID starting from the root volume
693 * \param[in] path full path
695 * \post Returns a venusfid representing the final element of path
697 * \note There are three cases:
698 * (1) begins with /afs: start in root.afs of cell or home cell
699 * (2) else begins with /: error
700 * (3) else start in root.cell of cell or home cell
702 struct afscp_venusfid *
703 afscp_ResolvePath(const char *path)
705 struct afscp_venusfid *root, *ret;
706 struct afscp_cell *cell;
709 p = strdup(path); /* so we can modify the string */
711 afscp_errno = ENOMEM;
714 cell = afscp_DefaultCell();
717 afscp_errno = EINVAL;
720 code = gettoproot(cell, p, &q, &root);
726 ret = _ResolvePath(root, 0, q, 1);
735 * Resolve a path to a FID starting from the given volume
737 * \param[in] v volume structure containing id and cell info
738 * \param[in] path path relative to volume v
740 * \post Returns a venusfid representing the final element of path
742 struct afscp_venusfid *
743 afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path)
745 struct afscp_venusfid *root, *ret;
748 /* so we can modify the string */
749 origp = p = strdup(path);
751 afscp_errno = ENOMEM;
754 root = afscp_MakeFid(v->cell, v->id, 1, 1);
758 ret = _ResolvePath(root, 0, p, 1);