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 <afs/param.h>
28 #include <afs/afsint.h>
29 #include <afs/vlserver.h>
30 #include <afs/vldbint.h>
37 #include "afscp_internal.h"
39 static int dirmode=DIRMODE_CELL;
41 int SetDirMode(int mode) {
43 if (mode != DIRMODE_CELL && mode != DIRMODE_DYNROOT) {
51 /* comparison function for tsearch */
52 static int dircompare(const void *a, const void *b)
54 const struct afs_dircache *sa=a,*sb=b;
55 if (sa->me.fid.Vnode < sb->me.fid.Vnode) return -1;
56 if (sa->me.fid.Vnode > sb->me.fid.Vnode) return 1;
57 if (sa->me.fid.Unique < sb->me.fid.Unique) return -1;
58 if (sa->me.fid.Unique > sb->me.fid.Unique) return 1;
62 /* make sure the dirstream contains the most up to date directory contents */
63 static int _DirUpdate (struct afs_dirstream *d) {
64 struct AFSFetchStatus s;
67 struct afs_dircache key, *stored;
71 code=afs_GetStatus(&d->fid, &s);
75 if (d->dirbuffer && d->dv == s.DataVersion)
77 v=afs_volumebyid(d->fid.cell, d->fid.fid.Volume);
83 memcpy(&key.me, &d->fid, sizeof(struct afs_venusfid));
84 cached=tfind(&key, &v->dircache, dircompare);
86 stored=*(struct afs_dircache **)cached;
87 if (d->dv == s.DataVersion) {
88 d->dirbuffer = stored->dirbuffer;
89 d->buflen = stored->buflen;
93 tdelete(&key, &v->dircache, dircompare);
94 if (d->dirbuffer != stored->dirbuffer)
95 free(stored->dirbuffer);
98 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
102 if (d->buflen != s.Length) {
105 new=realloc(d->dirbuffer, s.Length);
107 new=malloc(s.Length);
118 code=afs_pread(&d->fid, d->dirbuffer, s.Length, 0);
123 cached=tsearch(&key, &v->dircache, dircompare);
125 stored=malloc(sizeof(struct afs_dircache));
127 memcpy(&stored->me, &d->fid, sizeof(struct afs_venusfid));
128 stored->buflen=d->buflen;
129 stored->dirbuffer=d->dirbuffer;
131 *(struct afs_dircache **)cached=stored;
133 tdelete(&key, &v->dircache, dircompare);
140 static struct DirEntry *dir_get_entry(struct afs_dirstream *d, int entry) {
142 struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
143 struct PageHeader *p;
144 struct DirEntry *ret;
152 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
155 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
158 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
161 p=(struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE];
162 fr=p->freebitmap[off >> 8];
164 if ((fr & (1<<(off & 7))) == 0 ) { /* entry isn't allocated */
169 ret=(struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
174 struct afs_dirstream *afs_opendir(const struct afs_venusfid *fid) {
175 struct afs_dirstream *ret;
176 struct AFSFetchStatus s;
179 code=afs_GetStatus(fid, &s);
184 if (s.FileType != Directory) {
188 ret=malloc(sizeof(struct afs_dirstream));
193 memset(ret,0,sizeof(struct afs_dirstream));
194 memmove(&ret->fid, fid, sizeof(struct afs_venusfid));
195 code=_DirUpdate(ret);
205 struct afs_dirent *afs_readdir(struct afs_dirstream *d) {
206 struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
207 struct DirEntry *info;
212 while (ent == 0 && d->hashent < NHASHENT-1) {
214 ent=ntohs(h->hashTable[d->hashent]);
220 info=dir_get_entry(d, ent);
225 d->ret.vnode=ntohl(info->fid.vnode);
226 d->ret.unique=ntohl(info->fid.vunique);
227 strcpy(d->ret.name, info->name); /* guaranteed to be NULL terminated? */
228 d->entry=ntohs(info->next);
233 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
234 int afs_rewinddir(struct afs_dirstream *d) {
240 int afs_closedir(struct afs_dirstream *d) {
245 static int namehash(const char *name)
250 while (*name) hval = (hval * 173) + *name++;
251 tval = hval & (NHASHENT - 1);
253 (hval < 0 ? NHASHENT - tval : tval)
258 struct afs_venusfid *DirLookup(struct afs_dirstream *d, const char *name) {
262 struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
263 struct DirEntry *info;
269 entry=ntohs(h->hashTable[hval]);
272 info=dir_get_entry(d, entry);
277 if (!strcmp(info->name, name))
279 entry=ntohs(info->next);
282 return makefid(d->fid.cell, d->fid.fid.Volume,
283 ntohl(info->fid.vnode),
284 ntohl(info->fid.vunique));
291 struct afs_venusfid *ResolveName(const struct afs_venusfid *dir, const char *name) {
292 struct afs_venusfid *ret;
293 struct afs_dirstream *d;
298 ret=DirLookup(d, name);
303 static int gettoproot(struct afs_cell *cell, char *p, char **q, struct afs_venusfid **root) {
304 struct afs_volume *rootvol;
307 if (dirmode == DIRMODE_DYNROOT && !strcmp(p, "/afs")) {
311 if (!strncmp(p,"/afs",4)) {
312 afscp_dprintf(("gettoproot: path is absolute\n"));
314 while (*p == '/') p++;
315 if (dirmode == DIRMODE_DYNROOT) {
318 voltype = VOLTYPE_RO;
324 while (*p == '/') p++;
327 if (*p == '.' || *p == 0) {
332 while (*r && *r != '/') r++;
339 afscp_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
340 cell=afs_cellbyname(p);
342 afscp_dprintf(("gettoproot: no such cell\n"));
346 rootvol=afs_volumebyname(cell, "root.cell", voltype);
347 if (!rootvol && voltype == VOLTYPE_RO)
348 rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW);
351 rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RO);
353 rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RW);
356 afscp_dprintf(("gettoproot: volume not found\n"));
358 afscp_dprintf(("gettoproot: path is relative\n"));
363 rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RO);
365 rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW);
368 if (!rootvol) { afs_errno=ENODEV; return 1;}
369 *root=makefid(cell, rootvol->id, 1, 1);
373 static int getvolumeroot(struct afs_cell *cell, int voltype, const char *vname, struct afs_venusfid **root) {
374 struct afs_volume *vol;
375 vol=afs_volumebyname(cell, vname, voltype);
376 if (!vol && voltype == VOLTYPE_RO)
377 vol=afs_volumebyname(cell, vname, VOLTYPE_RW);
378 if (!vol) { afs_errno=ENODEV; return 1;}
379 *root=makefid(cell, vol->id, 1, 1);
383 typedef struct fidstack_s
387 struct afs_venusfid ** entries;
390 static fidstack fidstack_alloc() {
393 ret=malloc(sizeof(struct fidstack_s));
400 ret->entries=malloc(ret->alloc * sizeof(struct afs_venusfid *));
409 static void fidstack_push(fidstack s, struct afs_venusfid *entry) {
410 struct afs_venusfid **new;
411 if (s->count >= s->alloc) {
412 new=realloc(s->entries, (s->alloc + 10) *
413 sizeof(struct afs_venusfid *));
419 s->entries[s->count++]=entry;
423 static struct afs_venusfid *fidstack_pop(fidstack s) {
425 return s->entries[-- s->count];
429 static void fidstack_free(fidstack s) {
432 for (i=0;i<s->count;i++)
438 static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *, fidstack, char *, int);
440 static struct afs_venusfid *HandleLink(struct afs_venusfid *in, const struct afs_venusfid *parent, fidstack fids, int follow,
441 const struct AFSFetchStatus *s, int terminal) {
442 char *linkbuf, *linkbufq;
443 struct afs_cell *cell;
444 struct afs_volume *v;
445 struct afs_venusfid *root,*ret;
448 if ((s->UnixModeBits & 0111) &&
450 terminal) { /* normal link */
453 linkbuf=malloc(s->Length + 1);
454 code=afs_pread(in, linkbuf, s->Length, 0);
460 if (code != s->Length) {
466 linkbuf[s->Length]=0;
467 if (s->UnixModeBits & 0111) { /* normal link */
468 afscp_dprintf(("Recursing on symlink %s...\n", linkbuf));
469 if (linkbuf[0] == '/') {
470 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
476 ret=_ResolvePath(root, 0, linkbufq, 0);
480 ret=_ResolvePath(parent, fids, linkbuf, 0);
485 } else { /* mountpoint */
486 afscp_dprintf(("EvalMountPoint %s...\n", linkbuf));
487 linkbufq=strchr(linkbuf, ':');
489 v=afs_volumebyid(cell, in->fid.Volume);
497 if (linkbuf[0] == '%')
503 cell=afs_cellbyname(linkbuf+1);
504 if (linkbuf[0] != '%')
512 if (strlen(linkbufq) < 2) {
517 linkbufq[strlen(linkbufq)-1]=0; /* eliminate trailer */
518 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
527 static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *start,
528 fidstack infids, char *path,
530 struct afs_venusfid *ret,*cwd,*parent;
531 struct AFSFetchStatus s;
538 ret=cwd=dupfid(start);
541 fids=fidstack_alloc();
548 if (!strcmp(p,".")) {
549 } else if (!strcmp(p,"..")) {
550 ret=fidstack_pop(fids);
556 ret=ResolveName(cwd, p);
558 afscp_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p, cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique));
565 afscp_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p, cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique, ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
569 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
570 code=afs_GetStatus(ret, &s);
578 if (s.FileType == SymbolicLink) {
579 if (linkcount++ > 5) {
587 ret=HandleLink(ret, cwd, fids, follow, &s, (q==NULL));
594 afscp_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
607 fidstack_push(fids,cwd);
621 begins with /afs: start in root.afs of cell or home cell
622 else begins with /: error
623 else start in root.cell of cell or home cell
625 struct afs_venusfid *ResolvePath(const char *path) {
626 struct afs_venusfid *root,*ret;
627 struct afs_cell *cell;
630 /* so we can modify the string */
636 cell=afs_defaultcell();
637 if (!cell) { afs_errno=EINVAL; return NULL;}
638 if (gettoproot(cell, p, &q, &root)) {
643 ret=_ResolvePath(root, 0, q, 1);
651 struct afs_venusfid *ResolvePath2(const struct afs_volume *v, const char *path) {
652 struct afs_venusfid *root,*ret;
655 /* so we can modify the string */
661 root=makefid(v->cell, v->id, 1,1);
662 while (*p == '/') p++;
664 ret=_ResolvePath(root, 0, p, 1);