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