libafscp: a library for "clientless" operations
[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 <afs/param.h>
28 #include <afs/afsint.h>
29 #include <afs/vlserver.h>
30 #include <afs/vldbint.h>
31 #include <afs/dir.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <search.h>
36 #include "afscp.h"
37 #include "afscp_internal.h"
38
39 static int dirmode=DIRMODE_CELL;
40
41 int SetDirMode(int mode) {
42
43      if (mode != DIRMODE_CELL && mode != DIRMODE_DYNROOT) {
44           afs_errno=EINVAL;
45           return -1;
46      }
47      dirmode=mode;
48      return 0;
49 }
50
51 /* comparison function for tsearch */
52 static int dircompare(const void *a, const void *b)
53 {
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;
59      return 0;
60 }
61
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;
65      int code;
66      struct afs_volume *v;
67      struct afs_dircache key, *stored;
68      void **cached;
69
70
71      code=afs_GetStatus(&d->fid, &s);
72      if (code)
73           return code;
74
75      if (d->dirbuffer && d->dv == s.DataVersion)
76           return 0;
77      v=afs_volumebyid(d->fid.cell, d->fid.fid.Volume);
78      if (!v) {
79           afs_errno=ENOENT;
80           return -1;
81      }
82
83      memcpy(&key.me, &d->fid, sizeof(struct afs_venusfid));
84      cached=tfind(&key, &v->dircache, dircompare);
85      if (cached) {
86           stored=*(struct afs_dircache **)cached;
87           if (d->dv == s.DataVersion) {
88                d->dirbuffer = stored->dirbuffer;
89                d->buflen = stored->buflen;
90                d->dv = stored->dv;
91                return 0;
92           }
93           tdelete(&key, &v->dircache, dircompare);
94           if (d->dirbuffer != stored->dirbuffer)
95                free(stored->dirbuffer);
96           free(stored);
97      }
98      if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) {
99           afs_errno=EFBIG;
100           return -1;
101      }
102      if (d->buflen != s.Length) {
103           char *new;
104           if (d->dirbuffer) {
105                new=realloc(d->dirbuffer, s.Length);
106           } else {
107                new=malloc(s.Length);
108           }
109           if (new) {
110                d->dirbuffer=new;
111           } else {
112                afs_errno=ENOMEM;
113                return -1;
114           }
115           d->buflen=s.Length;
116      }
117
118      code=afs_pread(&d->fid, d->dirbuffer, s.Length, 0);
119      if (code < 0) {
120           return -1;
121      }
122      d->dv=s.DataVersion;
123      cached=tsearch(&key, &v->dircache, dircompare);
124      if (cached) {
125           stored=malloc(sizeof(struct afs_dircache));
126           if (stored) {
127                memcpy(&stored->me, &d->fid, sizeof(struct afs_venusfid));
128                stored->buflen=d->buflen;
129                stored->dirbuffer=d->dirbuffer;
130                stored->dv=d->dv;
131                *(struct afs_dircache **)cached=stored;
132           } else {
133                tdelete(&key, &v->dircache, dircompare);
134           }
135      }
136      return 0;
137 }
138
139
140 static struct DirEntry *dir_get_entry(struct afs_dirstream *d, int entry) {
141
142      struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
143      struct PageHeader *p;
144      struct DirEntry *ret;
145      int fr;
146      int pg, off;
147
148
149      pg=entry >> LEPP;
150      off=entry & (EPP-1);
151
152      if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */
153           return NULL;
154      }
155      if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */
156           return NULL;
157      }
158      if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */
159           return NULL;
160      }
161      p=(struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE];
162      fr=p->freebitmap[off >> 8];
163 #if 0
164      if ((fr & (1<<(off & 7))) == 0 ) { /* entry isn't allocated */
165           return NULL;
166      }
167 #endif
168
169      ret=(struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off];
170
171      return ret;
172 }
173
174 struct afs_dirstream *afs_opendir(const struct afs_venusfid *fid) {
175      struct afs_dirstream *ret;
176      struct AFSFetchStatus s;
177      int code;
178
179      code=afs_GetStatus(fid, &s);
180      if (code) {
181           return NULL;
182      }
183
184      if (s.FileType != Directory) {
185           afs_errno=ENOTDIR;
186           return NULL;
187      }
188      ret=malloc(sizeof(struct afs_dirstream));
189      if (!ret) {
190           afs_errno=ENOMEM;
191           return NULL;
192      }
193      memset(ret,0,sizeof(struct afs_dirstream));
194      memmove(&ret->fid, fid, sizeof(struct afs_venusfid));
195      code=_DirUpdate(ret);
196      if (code < 0) {
197           afs_closedir(ret);
198           return NULL;
199      }
200      ret->hashent=-1;
201      ret->entry=0;
202      return ret;
203 }
204
205 struct afs_dirent *afs_readdir(struct afs_dirstream *d) {
206      struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
207      struct DirEntry *info;
208      int ent;
209
210
211      ent=d->entry;
212      while (ent == 0 && d->hashent < NHASHENT-1) {
213           d->hashent++;
214           ent=ntohs(h->hashTable[d->hashent]);
215      }
216      if (ent == 0) {
217           afs_errno=0;
218           return NULL;
219      }
220      info=dir_get_entry(d, ent);
221      if (!info) {
222           afs_errno=0;
223           return NULL;
224      }
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);
229
230      return &d->ret;
231 }
232
233 /* as it calls _DirUpdate, this may corrupt any previously returned dirent's */
234 int afs_rewinddir(struct afs_dirstream *d) {
235      _DirUpdate(d);
236      d->hashent=-1;
237      d->entry=0;
238      return 0;
239 }
240 int afs_closedir(struct afs_dirstream *d) {
241      free(d);
242      return 0;
243 }
244
245 static int namehash(const char *name)
246 {
247   int hval, tval;
248
249   hval=0;
250   while (*name) hval = (hval * 173) + *name++;
251   tval = hval & (NHASHENT - 1);
252   return tval ?
253        (hval < 0 ? NHASHENT - tval : tval)
254        : 0;
255 }
256
257
258 struct afs_venusfid *DirLookup(struct afs_dirstream *d, const char *name) {
259      int fid[3];
260      int code;
261      int hval, entry;
262      struct DirHeader *h=(struct DirHeader *)d->dirbuffer;
263      struct DirEntry *info;
264
265      code=_DirUpdate(d);
266      if (code)
267           return NULL;
268      hval=namehash(name);
269      entry=ntohs(h->hashTable[hval]);
270
271      while (entry) {
272           info=dir_get_entry(d, entry);
273           if (!info) {
274                afs_errno=EIO;
275                return NULL;
276           }
277           if (!strcmp(info->name, name))
278                break;
279           entry=ntohs(info->next);
280      }
281      if (entry) {
282           return makefid(d->fid.cell, d->fid.fid.Volume,
283                          ntohl(info->fid.vnode),
284                          ntohl(info->fid.vunique));
285      } else {
286           afs_errno=ENOENT;
287           return NULL;
288      }
289 }
290
291 struct afs_venusfid *ResolveName(const struct afs_venusfid *dir, const char *name) {
292      struct afs_venusfid *ret;
293      struct afs_dirstream *d;
294
295      d=afs_opendir(dir);
296      if (!d)
297           return NULL;
298      ret=DirLookup(d, name);
299      afs_closedir(d);
300      return ret;
301 }
302
303 static int gettoproot(struct afs_cell *cell, char *p, char **q, struct afs_venusfid **root) {
304      struct afs_volume *rootvol;
305      char *r;
306
307      if (dirmode == DIRMODE_DYNROOT && !strcmp(p, "/afs")) {
308           afs_errno=EINVAL;
309           return 1;
310      }
311      if (!strncmp(p,"/afs",4)) {
312           afscp_dprintf(("gettoproot: path is absolute\n"));
313           p=&p[5];
314           while (*p == '/') p++;
315           if (dirmode == DIRMODE_DYNROOT) {
316                int voltype;
317           retry_dot:
318                voltype = VOLTYPE_RO;
319                if (*p == '.') {
320                      p++;
321                      voltype=VOLTYPE_RW;
322                }
323                if (*p == '/') {
324                     while (*p == '/') p++;
325                     goto retry_dot;
326                }
327                if (*p == '.' || *p == 0) {
328                      afs_errno=EINVAL;
329                      return 1;
330                }
331                r=p;
332                while (*r && *r != '/') r++;
333                if (!*r) {
334                     afs_errno=ENODEV;
335                     return 1;
336                }
337                *r++=0;
338                *q=r;
339                afscp_dprintf(("gettoproot: dynroot looking up cell %s\n", p));
340                cell=afs_cellbyname(p);
341                if (!cell) {
342                     afscp_dprintf(("gettoproot: no such cell\n"));
343                     afs_errno=ENODEV;
344                     return 1;
345                }
346                rootvol=afs_volumebyname(cell, "root.cell", voltype);
347                if (!rootvol && voltype == VOLTYPE_RO)
348                    rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW);
349           } else {
350                *q=p;
351                rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RO);
352                if (!rootvol)
353                    rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RW);
354           }
355           if (!rootvol)
356                afscp_dprintf(("gettoproot: volume not found\n"));
357      } else {
358           afscp_dprintf(("gettoproot: path is relative\n"));
359           if (p[0] == '/') {
360                afs_errno=EXDEV;
361                return 1;
362           }
363           rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RO);
364           if (!rootvol)
365               rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW);
366           *q=p;
367      }
368      if (!rootvol) { afs_errno=ENODEV; return 1;}
369      *root=makefid(cell, rootvol->id, 1, 1);
370      return 0;
371 }
372
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);
380      return 0;
381 }
382
383 typedef struct fidstack_s
384 {
385      int alloc;
386      int count;
387      struct afs_venusfid ** entries;
388 } *fidstack;
389
390 static fidstack fidstack_alloc() {
391      fidstack ret;
392
393      ret=malloc(sizeof(struct fidstack_s));
394      if (!ret) {
395           afs_errno=ENOMEM;
396           return NULL;
397      }
398      ret->alloc=10;
399      ret->count=0;
400      ret->entries=malloc(ret->alloc * sizeof(struct afs_venusfid *));
401      if (!ret->entries) {
402           free(ret);
403           afs_errno=ENOMEM;
404           return NULL;
405      }
406      return ret;
407 }
408
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 *));
414           if (!new)
415                return;
416           s->entries=new;
417           s->alloc += 10;
418      }
419      s->entries[s->count++]=entry;
420      return;
421 }
422
423 static struct afs_venusfid *fidstack_pop(fidstack s) {
424      if (s->count)
425           return s->entries[-- s->count];
426      return NULL;
427 }
428
429 static void fidstack_free(fidstack s) {
430      int i;
431
432      for (i=0;i<s->count;i++)
433           free(s->entries[i]);
434      free (s->entries);
435      free(s);
436 }
437
438 static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *, fidstack, char *, int);
439
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;
446      int voltype;
447      int code;
448      if ((s->UnixModeBits & 0111) &&
449          (follow == 0) &&
450          terminal) { /* normal link */
451           return in;
452      }
453      linkbuf=malloc(s->Length + 1);
454      code=afs_pread(in, linkbuf, s->Length, 0);
455      if (code < 0) {
456           free(linkbuf);
457           free(in);
458           return NULL;
459      }
460      if (code != s->Length) {
461           afs_errno=EIO;
462           free(linkbuf);
463           free(in);
464           return NULL;
465      }
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)) {
471                     free(linkbuf);
472                     free(in);
473                     return NULL;
474                }
475                free(in);
476                ret=_ResolvePath(root, 0, linkbufq, 0);
477                free(root);
478           } else {
479                free(in);
480                ret=_ResolvePath(parent, fids, linkbuf, 0);
481           }
482
483           free(linkbuf);
484
485      } else { /* mountpoint */
486           afscp_dprintf(("EvalMountPoint %s...\n", linkbuf));
487           linkbufq=strchr(linkbuf, ':');
488           cell=in->cell;
489           v=afs_volumebyid(cell, in->fid.Volume);
490           free(in);
491           if (!v) {
492                free(linkbuf);
493                afs_errno=ENODEV;
494                return NULL;
495           }
496           voltype=v->voltype;
497           if (linkbuf[0] == '%')
498                voltype=VOLTYPE_RW;
499           if (!linkbufq) {
500                linkbufq=linkbuf+1;
501           } else {
502                *linkbufq++ = 0;
503                cell=afs_cellbyname(linkbuf+1);
504                if (linkbuf[0] != '%')
505                     voltype=VOLTYPE_RO;
506           }
507           if (!cell) {
508                free(linkbuf);
509                afs_errno=ENODEV;
510                return NULL;
511           }
512           if (strlen(linkbufq) < 2) {
513                free(linkbuf);
514                afs_errno=ENODEV;
515                return NULL;
516           }
517           linkbufq[strlen(linkbufq)-1]=0; /* eliminate trailer */
518           if (getvolumeroot(cell, voltype, linkbufq, &ret)) {
519                free(linkbuf);
520                return NULL;
521           }
522           free(linkbuf);
523      }
524      return ret;
525 }
526
527 static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *start,
528                                          fidstack infids, char *path,
529                                          int follow) {
530      struct afs_venusfid *ret,*cwd,*parent;
531      struct AFSFetchStatus s;
532      char *p,*q;
533      int code;
534      int linkcount;
535      fidstack fids;
536
537      p=path;
538      ret=cwd=dupfid(start);
539      fids=infids;
540      if (!fids)
541           fids=fidstack_alloc();
542      if (!fids)
543           return NULL;
544
545      while (p && *p) {
546           q=strchr(p, '/');
547           if (q) *q++=0;
548           if (!strcmp(p,".")) {
549           } else if (!strcmp(p,"..")) {
550                ret=fidstack_pop(fids);
551                if (!ret)
552                     ret=cwd;
553                else
554                     free(cwd);
555           } else {
556                ret=ResolveName(cwd, p);
557                if (!ret) {
558                     afscp_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p, cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique));
559                     free(cwd);
560                     if (!infids)
561                          fidstack_free(fids);
562                     return NULL;
563
564                }
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));
566                linkcount=0;
567
568           retry:
569                if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */
570                     code=afs_GetStatus(ret, &s);
571                     if (code) {
572                          if (!infids)
573                               fidstack_free(fids);
574                          free(cwd);
575                          free(ret);
576                          return NULL;
577                     }
578                     if (s.FileType == SymbolicLink) {
579                          if (linkcount++ > 5) {
580                               afs_errno=ELOOP;
581                               if (!infids)
582                                    fidstack_free(fids);
583                               free(cwd);
584                               free(ret);
585                               return NULL;
586                          }
587                          ret=HandleLink(ret, cwd, fids, follow, &s, (q==NULL));
588                          if (!ret) {
589                               free(cwd);
590                               if (!infids)
591                                    fidstack_free(fids);
592                               return NULL;
593                          }
594                          afscp_dprintf(("   ....-> %lu.%lu.%lu\n", ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique));
595                          goto retry;
596                     } else {
597                          if (q) {
598                               afs_errno=ENOTDIR;
599                               free(cwd);
600                               free(ret);
601                               if (!infids)
602                                    fidstack_free(fids);
603                               return NULL;
604                          }
605                     }
606                }
607                fidstack_push(fids,cwd);
608           }
609           cwd=ret;
610
611           if (q)
612                while (*q == '/')
613                     q++;
614           p=q;
615      }
616      if (!infids)
617           fidstack_free(fids);
618      return ret;
619 }
620 /* 3 cases:
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
624 */
625 struct afs_venusfid *ResolvePath(const char *path) {
626      struct afs_venusfid *root,*ret;
627      struct afs_cell *cell;
628
629      char *p,*q;
630      /* so we can modify the string */
631      p=strdup(path);
632      if (!p) {
633           afs_errno=ENOMEM;
634           return NULL;
635      }
636      cell=afs_defaultcell();
637      if (!cell) { afs_errno=EINVAL; return NULL;}
638      if (gettoproot(cell, p, &q, &root)) {
639           free(p);
640           return NULL;
641      }
642      if (q && *q) {
643           ret=_ResolvePath(root, 0, q, 1);
644           free(root);
645      } else
646           ret=root;
647      free(p);
648      return ret;
649 }
650
651 struct afs_venusfid *ResolvePath2(const struct afs_volume *v, const char *path) {
652      struct afs_venusfid *root,*ret;
653
654      char *p,*q;
655      /* so we can modify the string */
656      p=strdup(path);
657      if (!p) {
658           afs_errno=ENOMEM;
659           return NULL;
660      }
661      root=makefid(v->cell, v->id, 1,1);
662      while (*p == '/') p++;
663      if (*p) {
664           ret=_ResolvePath(root, 0, p, 1);
665           free(root);
666      } else
667           ret=root;
668      free(p);
669      return ret;
670 }