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 = malloc(sizeof(struct afscp_dirstream));
212 afscp_errno = ENOMEM;
215 memset(ret, 0, sizeof(struct afscp_dirstream));
216 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
217 code = _DirUpdate(ret);
228 struct afscp_dirent *
229 afscp_ReadDir(struct afscp_dirstream *d)
231 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
232 struct DirEntry *info;
237 while (ent == 0 && d->hashent < NHASHENT - 1) {
239 ent = ntohs(h->hashTable[d->hashent]);
245 info = dir_get_entry(d, ent);
250 d->ret.vnode = ntohl(info->fid.vnode);
251 d->ret.unique = ntohl(info->fid.vunique);
252 strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */
253 d->entry = ntohs(info->next);
258 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
260 afscp_RewindDir(struct afscp_dirstream *d)
269 afscp_CloseDir(struct afscp_dirstream *d)
276 namehash(const char *name)
281 while (*name != '\0')
282 hval = (hval * 173) + *name++;
283 tval = hval & (NHASHENT - 1);
284 return tval ? (hval < 0 ? NHASHENT - tval : tval)
288 struct afscp_venusfid *
289 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
293 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
294 struct DirEntry *info;
296 code = _DirUpdate(d);
300 hval = namehash(name);
301 entry = ntohs(h->hashTable[hval]);
304 info = dir_get_entry(d, entry);
309 if (strcmp(info->name, name) == 0)
311 entry = ntohs(info->next);
314 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
315 ntohl(info->fid.vnode),
316 ntohl(info->fid.vunique));
318 afscp_errno = ENOENT;
323 struct afscp_venusfid *
324 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
326 struct afscp_venusfid *ret;
327 struct afscp_dirstream *d;
329 d = afscp_OpenDir(dir);
333 ret = afscp_DirLookup(d, name);
339 gettoproot(struct afscp_cell *cell, char *p, char **q,
340 struct afscp_venusfid **root)
342 struct afscp_volume *rootvol;
345 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
346 afscp_errno = EINVAL;
349 if (strncmp(p, "/afs", 4) == 0) {
350 afs_dprintf(("gettoproot: path is absolute\n"));
354 if (dirmode == DIRMODE_DYNROOT) {
367 if (*p == '.' || *p == 0) {
368 afscp_errno = EINVAL;
372 while (*r && *r != '/')
375 afscp_errno = ENODEV;
380 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
381 cell = afscp_CellByName(p, NULL);
383 afs_dprintf(("gettoproot: no such cell\n"));
384 afscp_errno = ENODEV;
387 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
388 if (!rootvol && voltype == ROVOL)
389 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
392 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
394 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
397 afs_dprintf(("gettoproot: volume not found\n"));
399 afs_dprintf(("gettoproot: path is relative\n"));
404 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
406 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
409 if (rootvol == NULL) {
410 afscp_errno = ENODEV;
413 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
418 getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname,
419 struct afscp_venusfid **root)
421 struct afscp_volume *vol;
422 vol = afscp_VolumeByName(cell, vname, voltype);
423 if (!vol && voltype == ROVOL)
424 vol = afscp_VolumeByName(cell, vname, RWVOL);
426 afscp_errno = ENODEV;
429 *root = afscp_MakeFid(cell, vol->id, 1, 1);
433 typedef struct fidstack_s {
436 struct afscp_venusfid **entries;
444 ret = malloc(sizeof(struct fidstack_s));
446 afscp_errno = ENOMEM;
451 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
452 if (ret->entries == NULL) {
454 afscp_errno = ENOMEM;
461 fidstack_push(fidstack s, struct afscp_venusfid *entry)
463 struct afscp_venusfid **new;
464 if (s->count >= s->alloc) {
465 new = realloc(s->entries, (s->alloc + 10) *
466 sizeof(struct afscp_venusfid *));
473 s->entries[s->count++] = entry;
477 static struct afscp_venusfid *
478 fidstack_pop(fidstack s)
481 return s->entries[--s->count];
486 fidstack_free(fidstack s)
490 for (i = 0; i < s->count; i++)
496 static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *,
497 fidstack, char *, int);
499 static struct afscp_venusfid *
500 afscp_HandleLink(struct afscp_venusfid *in,
501 const struct afscp_venusfid *parent, fidstack fids,
502 int follow, const struct AFSFetchStatus *s, int terminal)
504 char *linkbuf, *linkbufq;
505 struct afscp_cell *cell;
506 struct afscp_volume *v;
507 struct afscp_venusfid *root, *ret;
511 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
514 linkbuf = malloc(s->Length + 1);
515 code = afscp_PRead(in, linkbuf, s->Length, 0);
521 if (code != s->Length) {
527 linkbuf[s->Length] = 0;
528 if (s->UnixModeBits & 0111) { /* normal link */
529 afs_dprintf(("Recursing on symlink %s...\n", linkbuf));
530 if (linkbuf[0] == '/') {
531 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
537 ret = _ResolvePath(root, 0, linkbufq, 0);
541 ret = _ResolvePath(parent, fids, linkbuf, 0);
544 } else { /* mountpoint */
545 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
546 linkbufq = strchr(linkbuf, ':');
548 v = afscp_VolumeById(cell, in->fid.Volume);
552 afscp_errno = ENODEV;
555 voltype = v->voltype;
556 if (linkbuf[0] == '%')
558 if (linkbufq == NULL) {
559 linkbufq = linkbuf + 1;
562 cell = afscp_CellByName(linkbuf + 1, NULL);
563 if (linkbuf[0] != '%')
568 afscp_errno = ENODEV;
571 len = strnlen(linkbufq, s->Length + 1);
574 afscp_errno = ENODEV;
577 len = strnlen(linkbufq, s->Length + 1);
578 linkbufq[len - 1] = 0; /* eliminate trailer */
579 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
588 static struct afscp_venusfid *
589 _ResolvePath(const struct afscp_venusfid *start, fidstack infids,
590 char *path, int follow)
592 struct afscp_venusfid *ret, *cwd;
593 struct AFSFetchStatus s;
600 ret = cwd = afscp_DupFid(start);
603 fids = fidstack_alloc();
612 if (strcmp(p, ".") == 0) {
614 } else if (strcmp(p, "..") == 0) {
615 ret = fidstack_pop(fids);
621 ret = afscp_ResolveName(cwd, p);
623 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
624 cwd->fid.Volume, cwd->fid.Vnode,
631 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p,
632 cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique,
633 ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
637 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
638 code = afscp_GetStatus(ret, &s);
646 if (s.FileType == SymbolicLink) {
647 if (linkcount++ > 5) {
656 afscp_HandleLink(ret, cwd, fids, follow, &s,
664 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
665 ret->fid.Vnode, ret->fid.Unique));
669 afscp_errno = ENOTDIR;
678 fidstack_push(fids, cwd);
682 while ((q != NULL) && (*q == '/'))
692 * Resolve a path to a FID starting from the root volume
694 * \param[in] path full path
696 * \post Returns a venusfid representing the final element of path
698 * \note There are three cases:
699 * (1) begins with /afs: start in root.afs of cell or home cell
700 * (2) else begins with /: error
701 * (3) else start in root.cell of cell or home cell
703 struct afscp_venusfid *
704 afscp_ResolvePath(const char *path)
706 struct afscp_venusfid *root, *ret;
707 struct afscp_cell *cell;
710 p = strdup(path); /* so we can modify the string */
712 afscp_errno = ENOMEM;
715 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 */
751 afscp_errno = ENOMEM;
754 root = afscp_MakeFid(v->cell, v->id, 1, 1);
758 ret = _ResolvePath(root, 0, p, 1);