Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / libafscp / afscp_dir.c
1 /* AUTORIGHTS
2 Copyright (C) 2003 - 2010 Chaskiel Grundman
3 All rights reserved
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
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.
15
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.
26 */
27 #include <afsconfig.h>
28 #include <afs/param.h>
29
30 #include <roken.h>
31
32 #include <search.h>
33
34 #include <afs/vlserver.h>
35 #include <afs/vldbint.h>
36 #include <afs/dir.h>
37 #ifdef AFS_NT40_ENV
38 #include <afs/errmap_nt.h>
39 #endif
40 #include "afscp.h"
41 #include "afscp_internal.h"
42
43 static int dirmode = DIRMODE_CELL;
44
45 int
46 afscp_SetDirMode(int mode)
47 {
48 if ((mode != DIRMODE_CELL) && (mode != DIRMODE_DYNROOT)) {
49 afscp_errno = EINVAL;
50 return -1;
51 }
52 dirmode = mode;
53 return 0;
54 }
55
56 /* comparison function for tsearch */
57 static int
58 dircompare(const void *a, const void *b)
59 {
60 const struct afscp_dircache *sa = a, *sb = b;
61 if (sa->me.fid.Vnode < sb->me.fid.Vnode)
62 return -1;
63 if (sa->me.fid.Vnode > sb->me.fid.Vnode)
64 return 1;
65 if (sa->me.fid.Unique < sb->me.fid.Unique)
66 return -1;
67 if (sa->me.fid.Unique > sb->me.fid.Unique)
68 return 1;
69 return 0;
70 }
71
72 /* make sure the dirstream contains the most up to date directory contents */
73 static int
74 _DirUpdate(struct afscp_dirstream *d)
75 {
76 struct AFSFetchStatus s;
77 int code;
78 struct afscp_volume *v;
79 struct afscp_dircache key, *stored;
80 void **cached;
81
82
83 code = afscp_GetStatus(&d->fid, &s);
84 if (code != 0) {
85 return code;
86 }
87
88 if (d->dirbuffer && d->dv == s.DataVersion) {
89 return 0;
90 }
91 v = afscp_VolumeById(d->fid.cell, d->fid.fid.Volume);
92 if (v == NULL) {
93 afscp_errno = ENOENT;
94 return -1;
95 }
96
97 memcpy(&key.me, &d->fid, sizeof(struct afscp_venusfid));
98 cached = tfind(&key, &v->dircache, dircompare);
99 if (cached != NULL) {
100 stored = *(struct afscp_dircache **)cached;
101 if (d->dv == s.DataVersion) {
102 d->dirbuffer = stored->dirbuffer;
103 d->buflen = stored->buflen;
104 d->dv = stored->dv;
105 return 0;
106 }
107 pthread_mutex_lock(&(stored->mtx));
108 tdelete(&key, &v->dircache, dircompare);
109 stored->nwaiters++;
110 while (stored->nwaiters > 1) {
111 pthread_cond_wait(&(stored->cv), &(stored->mtx));
112 }
113 stored->nwaiters--;
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);
119 free(stored);
120 }
121 if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
122 afscp_errno = EFBIG;
123 return -1;
124 }
125 if (d->buflen != s.Length) {
126 char *new;
127 if (d->dirbuffer) {
128 new = realloc(d->dirbuffer, s.Length);
129 } else {
130 new = malloc(s.Length);
131 }
132 if (new != NULL) {
133 d->dirbuffer = new;
134 } else {
135 afscp_errno = ENOMEM;
136 return -1;
137 }
138 d->buflen = s.Length;
139 }
140
141 code = afscp_PRead(&d->fid, d->dirbuffer, s.Length, 0);
142 if (code < 0) {
143 return -1;
144 }
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;
153 stored->dv = d->dv;
154 stored->nwaiters = 0;
155 pthread_mutex_init(&(stored->mtx), NULL);
156 pthread_cond_init(&(stored->cv), NULL);
157 *(struct afscp_dircache **)cached = stored;
158 } else {
159 tdelete(&key, &v->dircache, dircompare);
160 }
161 }
162 return 0;
163 }
164
165 static struct DirEntry *
166 dir_get_entry(struct afscp_dirstream *d, int entry)
167 {
168 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
169 /* struct PageHeader *p; */
170 struct DirEntry *ret;
171 /* int fr; */
172 int pg, off;
173
174 pg = entry >> LEPP;
175 off = entry & (EPP - 1);
176
177 if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
178 return NULL;
179 }
180 if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
181 return NULL;
182 }
183 if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
184 return NULL;
185 }
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];
191 return ret;
192 }
193
194 struct afscp_dirstream *
195 afscp_OpenDir(const struct afscp_venusfid *fid)
196 {
197 struct afscp_dirstream *ret;
198 struct AFSFetchStatus s;
199 int code;
200
201 code = afscp_GetStatus(fid, &s);
202 if (code != 0) {
203 return NULL;
204 }
205
206 if (s.FileType != Directory) {
207 afscp_errno = ENOTDIR;
208 return NULL;
209 }
210 ret = calloc(1, sizeof(struct afscp_dirstream));
211 if (ret == NULL) {
212 afscp_errno = ENOMEM;
213 return NULL;
214 }
215 memmove(&ret->fid, fid, sizeof(struct afscp_venusfid));
216 code = _DirUpdate(ret);
217 if (code < 0) {
218 afscp_CloseDir(ret);
219 return NULL;
220 }
221 ret->hashent = -1;
222 ret->entry = 0;
223
224 return ret;
225 }
226
227 struct afscp_dirent *
228 afscp_ReadDir(struct afscp_dirstream *d)
229 {
230 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
231 struct DirEntry *info;
232 int ent;
233
234
235 ent = d->entry;
236 while (ent == 0 && d->hashent < NHASHENT - 1) {
237 d->hashent++;
238 ent = ntohs(h->hashTable[d->hashent]);
239 }
240 if (ent == 0) {
241 afscp_errno = 0;
242 return NULL;
243 }
244 info = dir_get_entry(d, ent);
245 if (info == NULL) {
246 afscp_errno = 0;
247 return NULL;
248 }
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);
253
254 return &d->ret;
255 }
256
257 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
258 int
259 afscp_RewindDir(struct afscp_dirstream *d)
260 {
261 _DirUpdate(d);
262 d->hashent = -1;
263 d->entry = 0;
264 return 0;
265 }
266
267 int
268 afscp_CloseDir(struct afscp_dirstream *d)
269 {
270 free(d);
271 return 0;
272 }
273
274 static int
275 namehash(const char *name)
276 {
277 int hval, tval;
278
279 hval = 0;
280 while (*name != '\0')
281 hval = (hval * 173) + *name++;
282 tval = hval & (NHASHENT - 1);
283 return tval ? (hval < 0 ? NHASHENT - tval : tval)
284 : 0;
285 }
286
287 struct afscp_venusfid *
288 afscp_DirLookup(struct afscp_dirstream *d, const char *name)
289 {
290 int code;
291 int hval, entry;
292 struct DirHeader *h = (struct DirHeader *)d->dirbuffer;
293 struct DirEntry *info;
294
295 code = _DirUpdate(d);
296 if (code != 0) {
297 return NULL;
298 }
299 hval = namehash(name);
300 entry = ntohs(h->hashTable[hval]);
301
302 while (entry != 0) {
303 info = dir_get_entry(d, entry);
304 if (info == NULL) {
305 afscp_errno = EIO;
306 return NULL;
307 }
308 if (strcmp(info->name, name) == 0)
309 break;
310 entry = ntohs(info->next);
311 }
312 if (entry != 0) {
313 return afscp_MakeFid(d->fid.cell, d->fid.fid.Volume,
314 ntohl(info->fid.vnode),
315 ntohl(info->fid.vunique));
316 } else {
317 afscp_errno = ENOENT;
318 return NULL;
319 }
320 }
321
322 struct afscp_venusfid *
323 afscp_ResolveName(const struct afscp_venusfid *dir, const char *name)
324 {
325 struct afscp_venusfid *ret;
326 struct afscp_dirstream *d;
327
328 d = afscp_OpenDir(dir);
329 if (d == NULL) {
330 return NULL;
331 }
332 ret = afscp_DirLookup(d, name);
333 afscp_CloseDir(d);
334 return ret;
335 }
336
337 static int
338 gettoproot(struct afscp_cell *cell, char *p, char **q,
339 struct afscp_venusfid **root)
340 {
341 struct afscp_volume *rootvol;
342 char *r;
343
344 if (dirmode == DIRMODE_DYNROOT && (strcmp(p, "/afs") == 0)) {
345 afscp_errno = EINVAL;
346 return 1;
347 }
348 if (strncmp(p, "/afs", 4) == 0) {
349 afs_dprintf(("gettoproot: path is absolute\n"));
350 p = &p[5];
351 while (*p == '/')
352 p++;
353 if (dirmode == DIRMODE_DYNROOT) {
354 int voltype;
355 retry_dot:
356 voltype = ROVOL;
357 if (*p == '.') {
358 p++;
359 voltype = RWVOL;
360 }
361 if (*p == '/') {
362 while (*p == '/')
363 p++;
364 goto retry_dot;
365 }
366 if (*p == '.' || *p == 0) {
367 afscp_errno = EINVAL;
368 return 1;
369 }
370 r = p;
371 while (*r && *r != '/')
372 r++;
373 if (*r)
374 *r++ = 0;
375 *q = r;
376 afs_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
377 cell = afscp_CellByName(p, NULL);
378 if (cell == NULL) {
379 afs_dprintf(("gettoproot: no such cell\n"));
380 afscp_errno = ENODEV;
381 return 1;
382 }
383 rootvol = afscp_VolumeByName(cell, "root.cell", voltype);
384 if (!rootvol && voltype == ROVOL)
385 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
386 } else {
387 *q = p;
388 rootvol = afscp_VolumeByName(cell, "root.afs", ROVOL);
389 if (!rootvol)
390 rootvol = afscp_VolumeByName(cell, "root.afs", RWVOL);
391 }
392 if (!rootvol)
393 afs_dprintf(("gettoproot: volume not found\n"));
394 } else {
395 afs_dprintf(("gettoproot: path is relative\n"));
396 if (p[0] == '/') {
397 afscp_errno = EXDEV;
398 return 1;
399 }
400 rootvol = afscp_VolumeByName(cell, "root.cell", ROVOL);
401 if (!rootvol)
402 rootvol = afscp_VolumeByName(cell, "root.cell", RWVOL);
403 *q = p;
404 }
405 if (rootvol == NULL) {
406 afscp_errno = ENODEV;
407 return 1;
408 }
409 *root = afscp_MakeFid(cell, rootvol->id, 1, 1);
410 return 0;
411 }
412
413 static int
414 getvolumeroot(struct afscp_cell *cell, int voltype, const char *vname,
415 struct afscp_venusfid **root)
416 {
417 struct afscp_volume *vol;
418 vol = afscp_VolumeByName(cell, vname, voltype);
419 if (!vol && voltype == ROVOL)
420 vol = afscp_VolumeByName(cell, vname, RWVOL);
421 if (vol == NULL) {
422 afscp_errno = ENODEV;
423 return 1;
424 }
425 *root = afscp_MakeFid(cell, vol->id, 1, 1);
426 return 0;
427 }
428
429 typedef struct fidstack_s {
430 int alloc;
431 int count;
432 struct afscp_venusfid **entries;
433 } *fidstack;
434
435 static fidstack
436 fidstack_alloc(void)
437 {
438 fidstack ret;
439
440 ret = malloc(sizeof(struct fidstack_s));
441 if (ret == NULL) {
442 afscp_errno = ENOMEM;
443 return NULL;
444 }
445 ret->alloc = 10;
446 ret->count = 0;
447 ret->entries = malloc(ret->alloc * sizeof(struct afscp_venusfid *));
448 if (ret->entries == NULL) {
449 free(ret);
450 afscp_errno = ENOMEM;
451 return NULL;
452 }
453 return ret;
454 }
455
456 static void
457 fidstack_push(fidstack s, struct afscp_venusfid *entry)
458 {
459 struct afscp_venusfid **new;
460 if (s->count >= s->alloc) {
461 new = realloc(s->entries, (s->alloc + 10) *
462 sizeof(struct afscp_venusfid *));
463 if (new == NULL) {
464 return;
465 }
466 s->entries = new;
467 s->alloc += 10;
468 }
469 s->entries[s->count++] = entry;
470 return;
471 }
472
473 static struct afscp_venusfid *
474 fidstack_pop(fidstack s)
475 {
476 if (s->count)
477 return s->entries[--s->count];
478 return NULL;
479 }
480
481 static void
482 fidstack_free(fidstack s)
483 {
484 int i;
485
486 for (i = 0; i < s->count; i++)
487 free(s->entries[i]);
488 free(s->entries);
489 free(s);
490 }
491
492 static struct afscp_venusfid *_ResolvePath(const struct afscp_venusfid *,
493 fidstack, char *, int);
494
495 static struct afscp_venusfid *
496 afscp_HandleLink(struct afscp_venusfid *in,
497 const struct afscp_venusfid *parent, fidstack fids,
498 int follow, const struct AFSFetchStatus *s, int terminal)
499 {
500 char *linkbuf, *linkbufq;
501 struct afscp_cell *cell;
502 struct afscp_volume *v;
503 struct afscp_venusfid *root, *ret;
504 int voltype;
505 int code;
506 ssize_t len;
507 if ((s->UnixModeBits & 0111) && (follow == 0) && terminal) { /* normal link */
508 return in;
509 }
510 linkbuf = malloc(s->Length + 1);
511 code = afscp_PRead(in, linkbuf, s->Length, 0);
512 if (code < 0) {
513 free(linkbuf);
514 free(in);
515 return NULL;
516 }
517 if (code != s->Length) {
518 afscp_errno = EIO;
519 free(linkbuf);
520 free(in);
521 return NULL;
522 }
523 linkbuf[s->Length] = 0;
524 if (s->UnixModeBits & 0111) { /* normal link */
525 afs_dprintf(("Recursing on symlink %s...\n", linkbuf));
526 if (linkbuf[0] == '/') {
527 if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) {
528 free(linkbuf);
529 free(in);
530 return NULL;
531 }
532 free(in);
533 ret = _ResolvePath(root, 0, linkbufq, 0);
534 free(root);
535 } else {
536 free(in);
537 ret = _ResolvePath(parent, fids, linkbuf, 0);
538 }
539 free(linkbuf);
540 } else { /* mountpoint */
541 afs_dprintf(("EvalMountPoint %s...\n", linkbuf));
542 linkbufq = strchr(linkbuf, ':');
543 cell = in->cell;
544 v = afscp_VolumeById(cell, in->fid.Volume);
545 free(in);
546 if (v == NULL) {
547 free(linkbuf);
548 afscp_errno = ENODEV;
549 return NULL;
550 }
551 voltype = v->voltype;
552 if (linkbuf[0] == '%')
553 voltype = RWVOL;
554 if (linkbufq == NULL) {
555 linkbufq = linkbuf + 1;
556 } else {
557 *linkbufq++ = 0;
558 cell = afscp_CellByName(linkbuf + 1, NULL);
559 if (linkbuf[0] != '%')
560 voltype = ROVOL;
561 }
562 if (cell == NULL) {
563 free(linkbuf);
564 afscp_errno = ENODEV;
565 return NULL;
566 }
567 len = strnlen(linkbufq, s->Length + 1);
568 if (len < 2) {
569 free(linkbuf);
570 afscp_errno = ENODEV;
571 return NULL;
572 }
573 len = strnlen(linkbufq, s->Length + 1);
574 linkbufq[len - 1] = 0; /* eliminate trailer */
575 if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
576 free(linkbuf);
577 return NULL;
578 }
579 free(linkbuf);
580 }
581 return ret;
582 }
583
584 static struct afscp_venusfid *
585 _ResolvePath(const struct afscp_venusfid *start, fidstack infids,
586 char *path, int follow)
587 {
588 struct afscp_venusfid *ret, *cwd;
589 struct AFSFetchStatus s;
590 char *p, *q;
591 int code;
592 int linkcount;
593 fidstack fids;
594
595 p = path;
596 ret = cwd = afscp_DupFid(start);
597 fids = infids;
598 if (fids == NULL)
599 fids = fidstack_alloc();
600 if (fids == NULL) {
601 return NULL;
602 }
603
604 while (p && *p) {
605 q = strchr(p, '/');
606 if (q)
607 *q++ = 0;
608 if (strcmp(p, ".") == 0) {
609 /* do nothing */
610 } else if (strcmp(p, "..") == 0) {
611 ret = fidstack_pop(fids);
612 if (ret == NULL)
613 ret = cwd;
614 else
615 free(cwd);
616 } else {
617 ret = afscp_ResolveName(cwd, p);
618 if (ret == NULL) {
619 afs_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p,
620 cwd->fid.Volume, cwd->fid.Vnode,
621 cwd->fid.Unique));
622 free(cwd);
623 if (infids == NULL)
624 fidstack_free(fids);
625 return NULL;
626 }
627 afs_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p,
628 cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique,
629 ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
630 linkcount = 0;
631
632 retry:
633 if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
634 code = afscp_GetStatus(ret, &s);
635 if (code != 0) {
636 if (infids == NULL)
637 fidstack_free(fids);
638 free(cwd);
639 free(ret);
640 return NULL;
641 }
642 if (s.FileType == SymbolicLink) {
643 if (linkcount++ > 5) {
644 afscp_errno = ELOOP;
645 if (infids == NULL)
646 fidstack_free(fids);
647 free(cwd);
648 free(ret);
649 return NULL;
650 }
651 ret =
652 afscp_HandleLink(ret, cwd, fids, follow, &s,
653 (q == NULL));
654 if (ret == NULL) {
655 free(cwd);
656 if (infids == NULL)
657 fidstack_free(fids);
658 return NULL;
659 }
660 afs_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume,
661 ret->fid.Vnode, ret->fid.Unique));
662 goto retry;
663 } else {
664 if (q != NULL) {
665 afscp_errno = ENOTDIR;
666 free(cwd);
667 free(ret);
668 if (infids == NULL)
669 fidstack_free(fids);
670 return NULL;
671 }
672 }
673 }
674 fidstack_push(fids, cwd);
675 }
676 cwd = ret;
677
678 while ((q != NULL) && (*q == '/'))
679 q++;
680 p = q;
681 }
682 if (infids == NULL)
683 fidstack_free(fids);
684 return ret;
685 }
686
687 /*!
688 * Resolve a path to a FID starting from the root volume
689 *
690 * \param[in] path full path
691 *
692 * \post Returns a venusfid representing the final element of path
693 *
694 * \note There are three cases:
695 * (1) begins with /afs: start in root.afs of cell or home cell
696 * (2) else begins with /: error
697 * (3) else start in root.cell of cell or home cell
698 */
699 struct afscp_venusfid *
700 afscp_ResolvePath(const char *path)
701 {
702 struct afscp_venusfid *root, *ret;
703 struct afscp_cell *cell;
704 int code;
705 char *p, *q;
706 p = strdup(path); /* so we can modify the string */
707 if (p == NULL) {
708 afscp_errno = ENOMEM;
709 return NULL;
710 }
711 cell = afscp_DefaultCell();
712 if (cell == NULL) {
713 free(p);
714 afscp_errno = EINVAL;
715 return NULL;
716 }
717 code = gettoproot(cell, p, &q, &root);
718 if (code != 0) {
719 free(p);
720 return NULL;
721 }
722 if (q && *q) {
723 ret = _ResolvePath(root, 0, q, 1);
724 free(root);
725 } else
726 ret = root;
727 free(p);
728 return ret;
729 }
730
731 /*!
732 * Resolve a path to a FID starting from the given volume
733 *
734 * \param[in] v volume structure containing id and cell info
735 * \param[in] path path relative to volume v
736 *
737 * \post Returns a venusfid representing the final element of path
738 */
739 struct afscp_venusfid *
740 afscp_ResolvePathFromVol(const struct afscp_volume *v, const char *path)
741 {
742 struct afscp_venusfid *root, *ret;
743 char *origp, *p;
744
745 /* so we can modify the string */
746 origp = p = strdup(path);
747 if (p == NULL) {
748 afscp_errno = ENOMEM;
749 return NULL;
750 }
751 root = afscp_MakeFid(v->cell, v->id, 1, 1);
752 while (*p == '/')
753 p++;
754 if (*p != '\0') {
755 ret = _ResolvePath(root, 0, p, 1);
756 free(root);
757 } else
758 ret = root;
759 free(origp);
760 return ret;
761 }