Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / libafscp / afscp_dir.c
blob513a78388d6e018ce195771e07e3d872e80ef938
1 /* AUTORIGHTS
2 Copyright (C) 2003 - 2010 Chaskiel Grundman
3 All rights reserved
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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>
30 #ifdef HAVE_SEARCH_H
31 #include <search.h>
32 #else
33 #include "afscp_search.h"
34 #endif
36 #include <afs/vlserver.h>
37 #include <afs/vldbint.h>
38 #include <afs/dir.h>
39 #ifdef AFS_NT40_ENV
40 #include <afs/errmap_nt.h>
41 #endif
42 #include "afscp.h"
43 #include "afscp_internal.h"
45 static int dirmode = DIRMODE_CELL;
47 int
48 afscp_SetDirMode(int mode)
50 if ((mode != DIRMODE_CELL) && (mode != DIRMODE_DYNROOT)) {
51 afscp_errno = EINVAL;
52 return -1;
54 dirmode = mode;
55 return 0;
58 /* comparison function for tsearch */
59 static int
60 dircompare(const void *a, const void *b)
62 const struct afscp_dircache *sa = a, *sb = b;
63 if (sa->me.fid.Vnode < sb->me.fid.Vnode)
64 return -1;
65 if (sa->me.fid.Vnode > sb->me.fid.Vnode)
66 return 1;
67 if (sa->me.fid.Unique < sb->me.fid.Unique)
68 return -1;
69 if (sa->me.fid.Unique > sb->me.fid.Unique)
70 return 1;
71 return 0;
74 /* make sure the dirstream contains the most up to date directory contents */
75 static int
76 _DirUpdate(struct afscp_dirstream *d)
78 struct AFSFetchStatus s;
79 int code;
80 struct afscp_volume *v;
81 struct afscp_dircache key, *stored;
82 void **cached;
85 code = afscp_GetStatus(&d->fid, &s);
86 if (code != 0) {
87 return code;
90 if (d->dirbuffer && d->dv == s.DataVersion) {
91 return 0;
93 v = afscp_VolumeById(d->fid.cell, d->fid.fid.Volume);
94 if (v == NULL) {
95 afscp_errno = ENOENT;
96 return -1;
99 memcpy(&key.me, &d->fid, sizeof(struct afscp_venusfid));
100 cached = tfind(&key, &v->dircache, dircompare);
101 if (cached != NULL) {
102 stored = *(struct afscp_dircache **)cached;
103 if (d->dv == s.DataVersion) {
104 d->dirbuffer = stored->dirbuffer;
105 d->buflen = stored->buflen;
106 d->dv = stored->dv;
107 return 0;
109 pthread_mutex_lock(&(stored->mtx));
110 tdelete(&key, &v->dircache, dircompare);
111 stored->nwaiters++;
112 while (stored->nwaiters > 1) {
113 pthread_cond_wait(&(stored->cv), &(stored->mtx));
115 stored->nwaiters--;
116 pthread_cond_destroy(&(stored->cv));
117 pthread_mutex_unlock(&(stored->mtx));
118 pthread_mutex_destroy(&(stored->mtx));
119 if (d->dirbuffer != stored->dirbuffer)
120 free(stored->dirbuffer);
121 free(stored);
123 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
124 afscp_errno = EFBIG;
125 return -1;
127 if (d->buflen != s.Length) {
128 char *new;
129 if (d->dirbuffer) {
130 new = realloc(d->dirbuffer, s.Length);
131 } else {
132 new = malloc(s.Length);
134 if (new != NULL) {
135 d->dirbuffer = new;
136 } else {
137 afscp_errno = ENOMEM;
138 return -1;
140 d->buflen = s.Length;
143 code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0);
144 if (code < 0) {
145 return -1;
147 d->dv = s.DataVersion;
148 cached = tsearch(&key, &v->dircache, dircompare);
149 if (cached != NULL) {
150 stored = malloc(sizeof(struct afscp_dircache));
151 if (stored != NULL) {
152 memcpy(&stored->me, &d->fid, sizeof(struct afscp_venusfid));
153 stored->buflen = d->buflen;
154 stored->dirbuffer = d->dirbuffer;
155 stored->dv = d->dv;
156 stored->nwaiters = 0;
157 pthread_mutex_init(&(stored->mtx), NULL);
158 pthread_cond_init(&(stored->cv), NULL);
159 *(struct afscp_dircache **)cached = stored;
160 } else {
161 tdelete(&key, &v->dircache, dircompare);
164 return 0;
167 static struct DirEntry *
168 dir_get_entry(struct afscp_dirstream *d, int entry)
170 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
171 /* struct PageHeader *p; */
172 struct DirEntry *ret;
173 /* int fr; */
174 int pg, off;
176 pg = entry >> LEPP;
177 off = entry & (EPP - 1);
179 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
180 return NULL;
182 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
183 return NULL;
185 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
186 return NULL;
188 /* p = (struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; */
189 /* p is set but not referenced later */
190 /* fr = p->freebitmap[off >> 8]; */
191 /* fr is set but not referenced later */
192 ret = (struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
193 return ret;
196 struct afscp_dirstream *
197 afscp_OpenDir(const struct afscp_venusfid *fid)
199 struct afscp_dirstream *ret;
200 struct AFSFetchStatus s;
201 int code;
203 code = afscp_GetStatus(fid, &s);
204 if (code != 0) {
205 return NULL;
208 if (s.FileType != Directory) {
209 afscp_errno = ENOTDIR;
210 return NULL;
212 ret = malloc(sizeof(struct afscp_dirstream));
213 if (ret == NULL) {
214 afscp_errno = ENOMEM;
215 return NULL;
217 memset(ret, 0, sizeof(struct afscp_dirstream));
218 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
219 code = _DirUpdate(ret);
220 if (code < 0) {
221 afscp_CloseDir(ret);
222 return NULL;
224 ret->hashent = -1;
225 ret->entry = 0;
227 return ret;
230 struct afscp_dirent *
231 afscp_ReadDir(struct afscp_dirstream *d)
233 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
234 struct DirEntry *info;
235 int ent;
238 ent = d->entry;
239 while (ent == 0 && d->hashent < NHASHENT - 1) {
240 d->hashent++;
241 ent = ntohs(h->hashTable[d->hashent]);
243 if (ent == 0) {
244 afscp_errno = 0;
245 return NULL;
247 info = dir_get_entry(d, ent);
248 if (info == NULL) {
249 afscp_errno = 0;
250 return NULL;
252 d->ret.vnode = ntohl(info->fid.vnode);
253 d->ret.unique = ntohl(info->fid.vunique);
254 strlcpy(d->ret.name, info->name, sizeof(d->ret.name)); /* guaranteed to be NULL terminated? */
255 d->entry = ntohs(info->next);
257 return &d->ret;
260 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
262 afscp_RewindDir(struct afscp_dirstream *d)
264 _DirUpdate(d);
265 d->hashent = -1;
266 d->entry = 0;
267 return 0;
271 afscp_CloseDir(struct afscp_dirstream *d)
273 free(d);
274 return 0;
277 static int
278 namehash(const char *name)
280 int hval, tval;
282 hval = 0;
283 while (*name != '\0')
284 hval = (hval * 173) + *name++;
285 tval = hval & (NHASHENT - 1);
286 return tval ? (hval < 0 ? NHASHENT - tval : tval)
287 : 0;
290 struct afscp_venusfid *
291 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
293 int code;
294 int hval, entry;
295 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
296 struct DirEntry *info;
298 code = _DirUpdate(d);
299 if (code != 0) {
300 return NULL;
302 hval = namehash(name);
303 entry = ntohs(h->hashTable[hval]);
305 while (entry != 0) {
306 info = dir_get_entry(d, entry);
307 if (info == NULL) {
308 afscp_errno = EIO;
309 return NULL;
311 if (strcmp(info->name, name) == 0)
312 break;
313 entry = ntohs(info->next);
315 if (entry != 0) {
316 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
317 ntohl(info->fid.vnode),
318 ntohl(info->fid.vunique));
319 } else {
320 afscp_errno = ENOENT;
321 return NULL;
325 struct afscp_venusfid *
326 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
328 struct afscp_venusfid *ret;
329 struct afscp_dirstream *d;
331 d = afscp_OpenDir(dir);
332 if (d == NULL) {
333 return NULL;
335 ret = afscp_DirLookup(d, name);
336 afscp_CloseDir(d);
337 return ret;
340 static int
341 gettoproot(struct afscp_cell *cell, char *p, char **q,
342 struct afscp_venusfid **root)
344 struct afscp_volume *rootvol;
345 char *r;
347 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
348 afscp_errno = EINVAL;
349 return 1;
351 if (strncmp(p, "/afs", 4) == 0) {
352 afs_dprintf(("gettoproot: path is absolute\n"));
353 p = &p[5];
354 while (*p == '/')
355 p++;
356 if (dirmode == DIRMODE_DYNROOT) {
357 int voltype;
358 retry_dot:
359 voltype = ROVOL;
360 if (*p == '.') {
361 p++;
362 voltype = RWVOL;
364 if (*p == '/') {
365 while (*p == '/')
366 p++;
367 goto retry_dot;
369 if (*p == '.' || *p == 0) {
370 afscp_errno = EINVAL;
371 return 1;
373 r = p;
374 while (*r && *r != '/')
375 r++;
376 if (*r)
377 *r++ = 0;
378 *q = r;
379 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
380 cell = afscp_CellByName(p, NULL);
381 if (cell == NULL) {
382 afs_dprintf(("gettoproot: no such cell\n"));
383 afscp_errno = ENODEV;
384 return 1;
386 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
387 if (!rootvol && voltype == ROVOL)
388 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
389 } else {
390 *q = p;
391 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
392 if (!rootvol)
393 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
395 if (!rootvol)
396 afs_dprintf(("gettoproot: volume not found\n"));
397 } else {
398 afs_dprintf(("gettoproot: path is relative\n"));
399 if (p[0] == '/') {
400 afscp_errno = EXDEV;
401 return 1;
403 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
404 if (!rootvol)
405 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
406 *q = p;
408 if (rootvol == NULL) {
409 afscp_errno = ENODEV;
410 return 1;
412 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
413 return 0;
416 static int
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);
424 if (vol == NULL) {
425 afscp_errno = ENODEV;
426 return 1;
428 *root = afscp_MakeFid(cell, vol->id, 1, 1);
429 return 0;
432 typedef struct fidstack_s {
433 int alloc;
434 int count;
435 struct afscp_venusfid **entries;
436 } *fidstack;
438 static fidstack
439 fidstack_alloc(void)
441 fidstack ret;
443 ret = malloc(sizeof(struct fidstack_s));
444 if (ret == NULL) {
445 afscp_errno = ENOMEM;
446 return NULL;
448 ret->alloc = 10;
449 ret->count = 0;
450 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
451 if (ret->entries == NULL) {
452 free(ret);
453 afscp_errno = ENOMEM;
454 return NULL;
456 return ret;
459 static void
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 *));
466 if (new == NULL) {
467 return;
469 s->entries = new;
470 s->alloc += 10;
472 s->entries[s->count++] = entry;
473 return;
476 static struct afscp_venusfid *
477 fidstack_pop(fidstack s)
479 if (s->count)
480 return s->entries[--s->count];
481 return NULL;
484 static void
485 fidstack_free(fidstack s)
487 int i;
489 for (i = 0; i < s->count; i++)
490 free(s->entries[i]);
491 free(s->entries);
492 free(s);
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;
507 int voltype;
508 int code;
509 ssize_t len;
510 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
511 return in;
513 linkbuf = malloc(s->Length + 1);
514 code = afscp_PRead(in, linkbuf, s->Length, 0);
515 if (code < 0) {
516 free(linkbuf);
517 free(in);
518 return NULL;
520 if (code != s->Length) {
521 afscp_errno = EIO;
522 free(linkbuf);
523 free(in);
524 return NULL;
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)) {
531 free(linkbuf);
532 free(in);
533 return NULL;
535 free(in);
536 ret = _ResolvePath(root, 0, linkbufq, 0);
537 free(root);
538 } else {
539 free(in);
540 ret = _ResolvePath(parent, fids, linkbuf, 0);
542 free(linkbuf);
543 } else { /* mountpoint */
544 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
545 linkbufq = strchr(linkbuf, ':');
546 cell = in->cell;
547 v = afscp_VolumeById(cell, in->fid.Volume);
548 free(in);
549 if (v == NULL) {
550 free(linkbuf);
551 afscp_errno = ENODEV;
552 return NULL;
554 voltype = v->voltype;
555 if (linkbuf[0] == '%')
556 voltype = RWVOL;
557 if (linkbufq == NULL) {
558 linkbufq = linkbuf + 1;
559 } else {
560 *linkbufq++ = 0;
561 cell = afscp_CellByName(linkbuf + 1, NULL);
562 if (linkbuf[0] != '%')
563 voltype = ROVOL;
565 if (cell == NULL) {
566 free(linkbuf);
567 afscp_errno = ENODEV;
568 return NULL;
570 len = strnlen(linkbufq, s->Length + 1);
571 if (len < 2) {
572 free(linkbuf);
573 afscp_errno = ENODEV;
574 return NULL;
576 len = strnlen(linkbufq, s->Length + 1);
577 linkbufq[len - 1] = 0; /* eliminate trailer */
578 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
579 free(linkbuf);
580 return NULL;
582 free(linkbuf);
584 return 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;
593 char *p, *q;
594 int code;
595 int linkcount;
596 fidstack fids;
598 p = path;
599 ret = cwd = afscp_DupFid(start);
600 fids = infids;
601 if (fids == NULL)
602 fids = fidstack_alloc();
603 if (fids == NULL) {
604 return NULL;
607 while (p && *p) {
608 q = strchr(p, '/');
609 if (q)
610 *q++ = 0;
611 if (strcmp(p, ".") == 0) {
612 /* do nothing */
613 } else if (strcmp(p, "..") == 0) {
614 ret = fidstack_pop(fids);
615 if (ret == NULL)
616 ret = cwd;
617 else
618 free(cwd);
619 } else {
620 ret = afscp_ResolveName(cwd, p);
621 if (ret == NULL) {
622 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
623 cwd->fid.Volume, cwd->fid.Vnode,
624 cwd->fid.Unique));
625 free(cwd);
626 if (infids == NULL)
627 fidstack_free(fids);
628 return NULL;
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));
633 linkcount = 0;
635 retry:
636 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
637 code = afscp_GetStatus(ret, &s);
638 if (code != 0) {
639 if (infids == NULL)
640 fidstack_free(fids);
641 free(cwd);
642 free(ret);
643 return NULL;
645 if (s.FileType == SymbolicLink) {
646 if (linkcount++ > 5) {
647 afscp_errno = ELOOP;
648 if (infids == NULL)
649 fidstack_free(fids);
650 free(cwd);
651 free(ret);
652 return NULL;
654 ret =
655 afscp_HandleLink(ret, cwd, fids, follow, &s,
656 (q == NULL));
657 if (ret == NULL) {
658 free(cwd);
659 if (infids == NULL)
660 fidstack_free(fids);
661 return NULL;
663 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
664 ret->fid.Vnode, ret->fid.Unique));
665 goto retry;
666 } else {
667 if (q != NULL) {
668 afscp_errno = ENOTDIR;
669 free(cwd);
670 free(ret);
671 if (infids == NULL)
672 fidstack_free(fids);
673 return NULL;
677 fidstack_push(fids, cwd);
679 cwd = ret;
681 while ((q != NULL) && (*q == '/'))
682 q++;
683 p = q;
685 if (infids == NULL)
686 fidstack_free(fids);
687 return ret;
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;
707 int code;
708 char *p, *q;
709 p = strdup(path); /* so we can modify the string */
710 if (p == NULL) {
711 afscp_errno = ENOMEM;
712 return NULL;
714 cell = afscp_DefaultCell();
715 if (cell == NULL) {
716 afscp_errno = EINVAL;
717 return NULL;
719 code = gettoproot(cell, p, &q, &root);
720 if (code != 0) {
721 free(p);
722 return NULL;
724 if (q && *q) {
725 ret = _ResolvePath(root, 0, q, 1);
726 free(root);
727 } else
728 ret = root;
729 free(p);
730 return ret;
734 * Resolve a path to a FID starting from the given volume
736 * \param[in] v volume structure containing id and cell info
737 * \param[in] path path relative to volume v
739 * \post Returns a venusfid representing the final element of path
741 struct afscp_venusfid *
742 afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path)
744 struct afscp_venusfid *root, *ret;
745 char *p;
747 /* so we can modify the string */
748 p = strdup(path);
749 if (p == NULL) {
750 afscp_errno = ENOMEM;
751 return NULL;
753 root = afscp_MakeFid(v->cell, v->id, 1, 1);
754 while (*p == '/')
755 p++;
756 if (*p != '\0') {
757 ret = _ResolvePath(root, 0, p, 1);
758 free(root);
759 } else
760 ret = root;
761 free(p);
762 return ret;