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