bcc2ed6613b00b2abff94f9ea59fea7d8a74ec42
[openafs.git] / src / dir / dir.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #ifdef KERNEL
14 # if !defined(UKERNEL)
15 #  include "h/types.h"
16 #  include "h/param.h"
17 #  ifdef        AFS_AUX_ENV
18 #   include "h/mmu.h"
19 #   include "h/seg.h"
20 #   include "h/sysmacros.h"
21 #   include "h/signal.h"
22 #   include "h/errno.h"
23 #  endif
24 #  include "h/time.h"
25 #  if defined(AFS_AIX_ENV) || defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_LINUX20_ENV)
26 #   include "h/errno.h"
27 #  else
28 #   if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV)
29 #    include "h/kernel.h"
30 #   endif
31 #  endif
32 #  if   defined(AFS_SUN56_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_DARWIN80_ENV)
33 #   include "afs/sysincludes.h"
34 #  endif
35 #  if !defined(AFS_SGI64_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_OBSD48_ENV) && !defined(AFS_NBSD_ENV)
36 #   include "h/user.h"
37 #  endif /* AFS_SGI64_ENV */
38 #  include "h/uio.h"
39 #  ifdef        AFS_OSF_ENV
40 #   include <sys/mount.h>
41 #   include <sys/vnode.h>
42 #   include <ufs/inode.h>
43 #  endif
44 #  if !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_HPUX110_ENV)
45 #   include "h/mbuf.h"
46 #  endif
47 #  ifndef AFS_LINUX20_ENV
48 #   include "netinet/in.h"
49 #  endif
50 # else /* !defined(UKERNEL) */
51 #  include "afs/stds.h"
52 #  include "afs/sysincludes.h"
53 # endif /* !defined(UKERNEL) */
54
55 /* afs_buffer.c */
56 /* These are needed because afs_prototypes.h is not included here */
57 struct dcache;
58 struct DirBuffer;
59 extern int DRead(struct dcache *adc, int page, struct DirBuffer *);
60 extern int DNew(struct dcache *adc, int page, struct DirBuffer *);
61
62 # include "afs/afs_osi.h"
63
64 # include "afs/dir.h"
65
66 # ifdef AFS_LINUX20_ENV
67 #  include "h/string.h"
68 # endif
69
70 /* generic renaming */
71 # define        NameBlobs       afs_dir_NameBlobs
72 # define        GetBlob         afs_dir_GetBlob
73 # define        Create          afs_dir_Create
74 # define        Length          afs_dir_Length
75 # define        Delete          afs_dir_Delete
76 # define        MakeDir         afs_dir_MakeDir
77 # define        Lookup          afs_dir_Lookup
78 # define        LookupOffset    afs_dir_LookupOffset
79 # define        EnumerateDir    afs_dir_EnumerateDir
80 # define        IsEmpty         afs_dir_IsEmpty
81 # define InverseLookup   afs_dir_InverseLookup
82 # define ChangeFid      afs_dir_ChangeFid
83
84 #else /* KERNEL */
85
86 # include <roken.h>
87 # include "dir.h"
88 #endif /* KERNEL */
89
90 afs_int32 DErrno;
91
92 /* Local static prototypes */
93 static int FindItem(void *dir, char *ename, struct DirBuffer *,
94                     struct DirBuffer *);
95
96 /* Find out how many entries are required to store a name. */
97 int
98 NameBlobs(char *name)
99 {
100     int i;
101     i = strlen(name) + 1;
102     return 1 + ((i + 15) >> 5);
103 }
104
105 /* Create an entry in a file.  Dir is a file representation, while entry is
106  * a string name. */
107 int
108 Create(void *dir, char *entry, void *voidfid)
109 {
110     afs_int32 *vfid = (afs_int32 *) voidfid;
111     int blobs, firstelt;
112     int i;
113     struct DirBuffer entrybuf, prevbuf, headerbuf;
114     struct DirEntry *ep;
115     struct DirHeader *dhp;
116
117     /* check name quality */
118     if (*entry == 0)
119         return EINVAL;
120
121     /* First check if file already exists. */
122     if (FindItem(dir, entry, &prevbuf, &entrybuf) == 0) {
123         DRelease(&entrybuf, 0);
124         DRelease(&prevbuf, 0);
125         printf("Exists ...\n");
126         return EEXIST;
127     }
128
129     blobs = NameBlobs(entry);   /* number of entries required */
130     firstelt = FindBlobs(dir, blobs);
131     if (firstelt < 0)
132         return EFBIG;           /* directory is full */
133
134     /* First, we fill in the directory entry. */
135     if (GetBlob(dir, firstelt, &entrybuf) != 0)
136         return EIO;
137     ep = (struct DirEntry *)entrybuf.data;
138
139     ep->flag = FFIRST;
140     ep->fid.vnode = htonl(vfid[1]);
141     ep->fid.vunique = htonl(vfid[2]);
142     strcpy(ep->name, entry);
143
144     /* Now we just have to thread it on the hash table list. */
145     if (DRead(dir, 0, &headerbuf) != 0) {
146         DRelease(&entrybuf, 1);
147         return EIO;
148     }
149     dhp = (struct DirHeader *)headerbuf.data;
150
151     i = DirHash(entry);
152     ep->next = dhp->hashTable[i];
153     dhp->hashTable[i] = htons(firstelt);
154     DRelease(&headerbuf, 1);
155     DRelease(&entrybuf, 1);
156     return 0;
157 }
158
159 int
160 Length(void *dir)
161 {
162     int i, ctr;
163     struct DirBuffer headerbuf;
164     struct DirHeader *dhp;
165
166     if (DRead(dir, 0, &headerbuf) != 0)
167         return 0;
168     dhp = (struct DirHeader *)headerbuf.data;
169
170     if (dhp->header.pgcount != 0)
171         ctr = ntohs(dhp->header.pgcount);
172     else {
173         /* old style, count the pages */
174         ctr = 0;
175         for (i = 0; i < MAXPAGES; i++)
176             if (dhp->alloMap[i] != EPP)
177                 ctr++;
178     }
179     DRelease(&headerbuf, 0);
180     return ctr * AFS_PAGESIZE;
181 }
182
183 int
184 Delete(void *dir, char *entry)
185 {
186     /* Delete an entry from a directory, including update of all free entry
187      * descriptors. */
188     int nitems, index;
189     struct DirBuffer entrybuf, prevbuf;
190     struct DirEntry *firstitem;
191     unsigned short *previtem;
192
193     if (FindItem(dir, entry, &prevbuf, &entrybuf) != 0)
194         return ENOENT;
195
196     firstitem = (struct DirEntry *)entrybuf.data;
197     previtem = (unsigned short *)prevbuf.data;
198
199     *previtem = firstitem->next;
200     DRelease(&prevbuf, 1);
201     index = DVOffset(&entrybuf) / 32;
202     nitems = NameBlobs(firstitem->name);
203     DRelease(&entrybuf, 0);
204     FreeBlobs(dir, index, nitems);
205     return 0;
206 }
207
208 int
209 FindBlobs(void *dir, int nblobs)
210 {
211     /* Find a bunch of contiguous entries; at least nblobs in a row. */
212     int i, j, k;
213     int failed = 0;
214     struct DirBuffer headerbuf, pagebuf;
215     struct DirHeader *dhp;
216     struct PageHeader *pp;
217     int pgcount;
218
219     /* read the dir header in first. */
220     if (DRead(dir, 0, &headerbuf) != 0)
221         return -1;
222     dhp = (struct DirHeader *)headerbuf.data;
223
224     for (i = 0; i < BIGMAXPAGES; i++) {
225         if (i >= MAXPAGES || dhp->alloMap[i] >= nblobs) {
226             /* if page could contain enough entries */
227             /* If there are EPP free entries, then the page is not even allocated. */
228             if (i >= MAXPAGES) {
229                 /* this pages exists past the end of the old-style dir */
230                 pgcount = ntohs(dhp->header.pgcount);
231                 if (pgcount == 0) {
232                     pgcount = MAXPAGES;
233                     dhp->header.pgcount = htons(pgcount);
234                 }
235                 if (i > pgcount - 1) {
236                     /* this page is bigger than last allocated page */
237                     AddPage(dir, i);
238                     dhp->header.pgcount = htons(i + 1);
239                 }
240             } else if (dhp->alloMap[i] == EPP) {
241                 /* Add the page to the directory. */
242                 AddPage(dir, i);
243                 dhp->alloMap[i] = EPP - 1;
244                 dhp->header.pgcount = htons(i + 1);
245             }
246
247             /* read the page in. */
248             if (DRead(dir, i, &pagebuf) != 0) {
249                 DRelease(&headerbuf, 1);
250                 break;
251             }
252             pp = (struct PageHeader *)pagebuf.data;
253             for (j = 0; j <= EPP - nblobs; j++) {
254                 failed = 0;
255                 for (k = 0; k < nblobs; k++)
256                     if ((pp->freebitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
257                         failed = 1;
258                         break;
259                     }
260                 if (!failed)
261                     break;
262                 failed = 1;
263             }
264             if (!failed) {
265                 /* Here we have the first index in j.  We update the allocation maps
266                  * and free up any resources we've got allocated. */
267                 if (i < MAXPAGES)
268                     dhp->alloMap[i] -= nblobs;
269                 DRelease(&headerbuf, 1);
270                 for (k = 0; k < nblobs; k++)
271                     pp->freebitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
272                 DRelease(&pagebuf, 1);
273                 return j + i * EPP;
274             }
275             DRelease(&pagebuf, 0);      /* This dir page is unchanged. */
276         }
277     }
278     /* If we make it here, the directory is full. */
279     DRelease(&headerbuf, 1);
280     return -1;
281 }
282
283 void
284 AddPage(void *dir, int pageno)
285 {                               /* Add a page to a directory. */
286     int i;
287     struct PageHeader *pp;
288     struct DirBuffer pagebuf;
289
290     /* Get a new buffer labelled dir,pageno */
291     DNew(dir, pageno, &pagebuf);
292     pp = (struct PageHeader *)pagebuf.data;
293
294     pp->tag = htons(1234);
295     if (pageno > 0)
296         pp->pgcount = 0;
297     pp->freecount = EPP - 1;    /* The first dude is already allocated */
298     pp->freebitmap[0] = 0x01;
299     for (i = 1; i < EPP / 8; i++)       /* It's a constant */
300         pp->freebitmap[i] = 0;
301     DRelease(&pagebuf, 1);
302 }
303
304 /* Free a whole bunch of directory entries. */
305
306 void
307 FreeBlobs(void *dir, int firstblob, int nblobs)
308 {
309     int i;
310     int page;
311     struct DirBuffer headerbuf, pagehdbuf;
312     struct DirHeader *dhp;
313     struct PageHeader *pp;
314     page = firstblob / EPP;
315     firstblob -= EPP * page;    /* convert to page-relative entry */
316
317     if (DRead(dir, 0, &headerbuf) != 0)
318         return;
319     dhp = (struct DirHeader *)headerbuf.data;
320
321     if (page < MAXPAGES)
322         dhp->alloMap[page] += nblobs;
323
324     DRelease(&headerbuf, 1);
325
326     if (DRead(dir, page, &pagehdbuf) != 0)
327         return;
328     pp = (struct PageHeader *)pagehdbuf.data;
329
330     for (i = 0; i < nblobs; i++)
331         pp->freebitmap[(firstblob + i) >> 3] &= ~(1 << ((firstblob + i) & 7));
332
333     DRelease(&pagehdbuf, 1);
334 }
335
336 /*
337  * Format an empty directory properly.  Note that the first 13 entries in a
338  * directory header page are allocated, 1 to the page header, 4 to the
339  * allocation map and 8 to the hash table.
340  */
341 int
342 MakeDir(void *dir, afs_int32 * me, afs_int32 * parent)
343 {
344     int i;
345     struct DirBuffer buffer;
346     struct DirHeader *dhp;
347
348     DNew(dir, 0, &buffer);
349     dhp = (struct DirHeader *)buffer.data;
350
351     dhp->header.pgcount = htons(1);
352     dhp->header.tag = htons(1234);
353     dhp->header.freecount = (EPP - DHE - 1);
354     dhp->header.freebitmap[0] = 0xff;
355     dhp->header.freebitmap[1] = 0x1f;
356     for (i = 2; i < EPP / 8; i++)
357         dhp->header.freebitmap[i] = 0;
358     dhp->alloMap[0] = (EPP - DHE - 1);
359     for (i = 1; i < MAXPAGES; i++)
360         dhp->alloMap[i] = EPP;
361     for (i = 0; i < NHASHENT; i++)
362         dhp->hashTable[i] = 0;
363     DRelease(&buffer, 1);
364     Create(dir, ".", me);
365     Create(dir, "..", parent);  /* Virtue is its own .. */
366     return 0;
367 }
368
369 /* Look up a file name in directory. */
370
371 int
372 Lookup(void *dir, char *entry, void *voidfid)
373 {
374     afs_int32 *fid = (afs_int32 *) voidfid;
375     struct DirBuffer firstbuf, prevbuf;
376     struct DirEntry *firstitem;
377
378     if (FindItem(dir, entry, &prevbuf, &firstbuf) != 0)
379         return ENOENT;
380     DRelease(&prevbuf, 0);
381     firstitem = (struct DirEntry *)firstbuf.data;
382
383     fid[1] = ntohl(firstitem->fid.vnode);
384     fid[2] = ntohl(firstitem->fid.vunique);
385     DRelease(&firstbuf, 0);
386     return 0;
387 }
388
389 /* Look up a file name in directory. */
390
391 int
392 LookupOffset(void *dir, char *entry, void *voidfid, long *offsetp)
393 {
394     afs_int32 *fid = (afs_int32 *) voidfid;
395     struct DirBuffer firstbuf, prevbuf;
396     struct DirEntry *firstitem;
397
398     if (FindItem(dir, entry, &prevbuf, &firstbuf) != 0)
399         return ENOENT;
400     DRelease(&prevbuf, 0);
401     firstitem = (struct DirEntry *)firstbuf.data;
402
403     fid[1] = ntohl(firstitem->fid.vnode);
404     fid[2] = ntohl(firstitem->fid.vunique);
405     if (offsetp)
406         *offsetp = DVOffset(&firstbuf);
407     DRelease(&firstbuf, 0);
408     return 0;
409 }
410
411 /*
412  * Enumerate the contents of a directory. Break when hook function
413  * returns non 0.
414  */
415
416 int
417 EnumerateDir(void *dir, int (*hookproc) (void *dir, char *name,
418                                          afs_int32 vnode, afs_int32 unique),
419              void *hook)
420 {
421     int i;
422     int num;
423     struct DirBuffer headerbuf, entrybuf;
424     struct DirHeader *dhp;
425     struct DirEntry *ep;
426     int code = 0;
427
428     if (DRead(dir, 0, &headerbuf) != 0)
429         return EIO;
430     dhp = (struct DirHeader *)headerbuf.data;
431
432     for (i = 0; i < NHASHENT; i++) {
433         /* For each hash chain, enumerate everyone on the list. */
434         num = ntohs(dhp->hashTable[i]);
435         while (num != 0) {
436             /* Walk down the hash table list. */
437             DErrno = 0;
438             if (GetBlob(dir, num, &entrybuf) != 0) {
439                 if (DErrno) {
440                     /* we failed, return why */
441                     DRelease(&headerbuf, 0);
442                     return DErrno;
443                 }
444                 break;
445             }
446             ep = (struct DirEntry *)entrybuf.data;
447
448             num = ntohs(ep->next);
449             code = (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
450                          ntohl(ep->fid.vunique));
451             DRelease(&entrybuf, 0);
452             if (code)
453                 break;
454         }
455     }
456     DRelease(&headerbuf, 0);
457     return 0;
458 }
459
460 int
461 IsEmpty(void *dir)
462 {
463     /* Enumerate the contents of a directory. */
464     int i;
465     int num;
466     struct DirBuffer headerbuf, entrybuf;
467     struct DirHeader *dhp;
468     struct DirEntry *ep;
469
470     if (DRead(dir, 0, &headerbuf) != 0)
471         return 0;
472     dhp = (struct DirHeader *)headerbuf.data;
473
474     for (i = 0; i < NHASHENT; i++) {
475         /* For each hash chain, enumerate everyone on the list. */
476         num = ntohs(dhp->hashTable[i]);
477         while (num != 0) {
478             /* Walk down the hash table list. */
479             if (GetBlob(dir, num, &entrybuf) != 0);
480                 break;
481             ep = (struct DirEntry *)entrybuf.data;
482             if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
483                 DRelease(&entrybuf, 0);
484                 DRelease(&headerbuf, 0);
485                 return 1;
486             }
487             num = ntohs(ep->next);
488             DRelease(&entrybuf, 0);
489         }
490     }
491     DRelease(&headerbuf, 0);
492     return 0;
493 }
494
495 int
496 GetBlob(void *dir, afs_int32 blobno, struct DirBuffer *buffer)
497 {
498     int code;
499
500     memset(buffer, 0, sizeof(struct DirBuffer));
501
502     code = DRead(dir, blobno >> LEPP, buffer);
503     if (code)
504         return code;
505
506     buffer->data = (void *)(((long)buffer->data) + 32 * (blobno & (EPP - 1)));
507
508     return 0;
509 }
510
511 int
512 DirHash(char *string)
513 {
514     /* Hash a string to a number between 0 and NHASHENT. */
515     unsigned char tc;
516     unsigned int hval;
517     int tval;
518     hval = 0;
519     while ((tc = (*string++))) {
520         hval *= 173;
521         hval += tc;
522     }
523     tval = hval & (NHASHENT - 1);
524     if (tval == 0)
525         return tval;
526     else if (hval >= 1<<31)
527         tval = NHASHENT - tval;
528     return tval;
529 }
530
531
532 /* Find a directory entry, given its name.  This entry returns a pointer
533  * to a locked buffer, and a pointer to a locked buffer (in previtem)
534  * referencing the found item (to aid the delete code).  If no entry is
535  * found, however, no items are left locked, and a null pointer is
536  * returned instead. */
537
538 static int
539 FindItem(void *dir, char *ename, struct DirBuffer *prevbuf,
540          struct DirBuffer *itembuf )
541 {
542     int i, code;
543     struct DirBuffer curr, prev;
544     struct DirHeader *dhp;
545     struct DirEntry *tp;
546
547     memset(prevbuf, 0, sizeof(struct DirBuffer));
548     memset(itembuf, 0, sizeof(struct DirBuffer));
549
550     code = DRead(dir, 0, &prev);
551     if (code)
552         return code;
553     dhp = (struct DirHeader *)prev.data;
554
555     i = DirHash(ename);
556     if (dhp->hashTable[i] == 0) {
557         /* no such entry */
558         DRelease(&prev, 0);
559         return ENOENT;
560     }
561
562     code = GetBlob(dir, (u_short) ntohs(dhp->hashTable[i]), &curr);
563     if (code) {
564         DRelease(&prev, 0);
565         return code;
566     }
567
568     prev.data = &(dhp->hashTable[i]);
569
570     while (1) {
571         /* Look at each entry on the hash chain */
572         tp = (struct DirEntry *)curr.data;
573         if (!strcmp(ename, tp->name)) {
574             /* Found it! */
575             *prevbuf = prev;
576             *itembuf = curr;
577             return 0;
578         }
579         DRelease(&prev, 0);
580         if (tp->next == 0) {
581             /* The end of the line */
582             DRelease(&curr, 0);
583             return ENOENT;
584         }
585
586         prev = curr;
587         prev.data = &(tp->next);
588
589         code = GetBlob(dir, (u_short) ntohs(tp->next), &curr);
590         if (code) {
591             DRelease(&prev, 0);
592             return code;
593         }
594     }
595     /* Never reached */
596 }
597
598 static int
599 FindFid (void *dir, afs_uint32 vnode, afs_uint32 unique,
600          struct DirBuffer *itembuf)
601 {
602     /* Find a directory entry, given the vnode and uniquifier of a object.
603      * This entry returns a pointer to a locked buffer.  If no entry is found,
604      * however, no items are left locked, and a null pointer is returned
605      * instead.
606      */
607     int i, code;
608     unsigned short next;
609     struct DirBuffer curr, header;
610     struct DirHeader *dhp;
611     struct DirEntry *tp;
612
613     memset(itembuf, 0, sizeof(struct DirBuffer));
614
615     code = DRead(dir, 0, &header);
616     if (code)
617         return code;
618     dhp = (struct DirHeader *)header.data;
619
620     for (i=0; i<NHASHENT; i++) {
621         if (dhp->hashTable[i] != 0) {
622             code = GetBlob(dir, (u_short)ntohs(dhp->hashTable[i]),
623                            &curr);
624             if (code) {
625                 DRelease(&header, 0);
626                 return code;
627             }
628
629             while (curr.data != NULL) {
630                 tp = (struct DirEntry *)curr.data;
631
632                 if (vnode == ntohl(tp->fid.vnode)
633                     && unique == ntohl(tp->fid.vunique)) {
634                     DRelease(&header, 0);
635                     *itembuf = curr;
636                     return 0;
637                 }
638
639                 next = tp->next;
640                 DRelease(&curr, 0);
641
642                 if (next == 0)
643                     break;
644
645                 code = GetBlob(dir, (u_short)ntohs(next), &curr);
646                 if (code) {
647                     DRelease(&header, 0);
648                     return code;
649                 }
650             }
651         }
652     }
653     DRelease(&header, 0);
654     return ENOENT;
655 }
656
657 int
658 InverseLookup (void *dir, afs_uint32 vnode, afs_uint32 unique, char *name,
659                afs_uint32 length)
660 {
661     /* Look for the name pointing to given vnode and unique in a directory */
662     struct DirBuffer entrybuf;
663     struct DirEntry *entry;
664     int code = 0;
665
666     if (FindFid(dir, vnode, unique, &entrybuf) != 0)
667         return ENOENT;
668     entry = (struct DirEntry *)entrybuf.data;
669
670     if (strlen(entry->name) >= length)
671         code = E2BIG;
672     else
673         strcpy(name, entry->name);
674     DRelease(&entrybuf, 0);
675     return code;
676 }
677
678 /*!
679  * Change an entry fid.
680  *
681  * \param dir
682  * \param entry The entry name.
683  * \param old_fid The old find in MKFid format (host order).
684  * It can be omitted if you don't need a safety check...
685  * \param new_fid The new find in MKFid format (host order).
686  */
687 int ChangeFid(void *dir,
688                 char *entry,
689                 afs_uint32 *old_fid,
690                 afs_uint32 *new_fid)
691 {
692     struct DirBuffer prevbuf, entrybuf;
693     struct DirEntry *firstitem;
694     struct MKFid *fid_old = (struct MKFid *) old_fid;
695     struct MKFid *fid_new = (struct MKFid *) new_fid;
696
697     /* Find entry. */
698     if (FindItem(dir, entry, &prevbuf, &entrybuf) != 0)
699         return ENOENT;
700     firstitem = (struct DirEntry *)entrybuf.data;
701     DRelease(&prevbuf, 1);
702
703     /* Replace fid. */
704     if (!old_fid ||
705         ((htonl(fid_old->vnode) == firstitem->fid.vnode) &&
706         (htonl(fid_old->vunique) == firstitem->fid.vunique))) {
707
708         firstitem->fid.vnode = htonl(fid_new->vnode);
709         firstitem->fid.vunique = htonl(fid_new->vunique);
710     }
711
712     DRelease(&entrybuf, 1);
713
714     return 0;
715 }