windows-bplus-tree-20070823
[openafs.git] / src / WINNT / afsd / cm_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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <string.h>
15 #include <malloc.h>
16 #include <osi.h>
17 #include "afsd.h"
18 #ifdef USE_BPLUS
19 #include "cm_btree.h"
20 #endif
21 #include <rx/rx.h>
22
23
24 afs_int32 DErrno;
25
26 afs_uint32 dir_lookup_hits = 0;
27 afs_uint32 dir_lookup_misses = 0;
28 afs_uint32 dir_create_entry = 0;
29 afs_uint32 dir_remove_entry = 0;
30
31 afs_uint64 dir_lookup_time = 0;
32 afs_uint64 dir_create_time = 0;
33 afs_uint64 dir_remove_time = 0;
34
35 afs_int32  cm_BPlusTrees = 1;
36
37 void cm_DirDumpStats(void)
38 {
39     afsi_log("Dir Lookup   Hits: %-8d", dir_lookup_hits);
40     afsi_log("           Misses: %-8d", dir_lookup_misses);
41     afsi_log("           Create: %-8d", dir_create_entry);
42     afsi_log("           Remove: %-8d", dir_remove_entry);
43
44     afsi_log("Dir Times  Lookup: %-16I64d", dir_lookup_time);
45     afsi_log("           Create: %-16I64d", dir_create_time);
46     afsi_log("           Remove: %-16I64d", dir_remove_time);
47 }
48
49
50 /* Local static prototypes */
51 static long
52 cm_DirGetBlob(cm_dirOp_t * op,
53               unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp);
54
55 static long
56 cm_DirFindItem(cm_dirOp_t * op,
57                char *ename,
58                cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
59                cm_buf_t ** prevbufpp, unsigned short **previtempp);
60
61 static long
62 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * buffer);
63
64 /* flags for cm_DirOpDelBuffer */
65 #define DIROP_MODIFIED  1
66 #define DIROP_SCPLOCKED 2
67
68 static int
69 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * buffer, int flags);
70
71 static long
72 cm_DirCheckStatus(cm_dirOp_t * op);
73
74 static long
75 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified);
76
77 static long
78 cm_DirGetPage(cm_dirOp_t * op,
79               long index, cm_buf_t ** bufferpp, void ** datapp);
80
81 static long
82 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs);
83
84 static long
85 cm_DirAddPage(cm_dirOp_t * op, int pageno);
86
87 static long
88 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs);
89
90
91 /* compute how many 32 byte entries an AFS 3 dir requires for storing
92  * the specified name.
93  */
94 long 
95 cm_NameEntries(char *namep, long *lenp)
96 {
97     long i;
98         
99     i = (long)strlen(namep) + 1;
100     if (lenp) *lenp = i;
101     return 1 + ((i+15) >> 5);
102 }
103
104 /* Create an entry in a file.  Dir is a file representation, while
105    entry is a string name.
106
107    On entry:
108        op->scp->mx is unlocked
109
110    On exit:
111        op->scp->mx is unlocked
112
113    None of the directory buffers for op->scp should be locked by the
114    calling thread.
115 */
116 long
117 cm_DirCreateEntry(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
118 {
119     int blobs, firstelt;
120     int i;
121     LARGE_INTEGER start, end;
122
123     cm_dirEntry_t *ep = NULL;
124     cm_buf_t *entrybuf = NULL;
125
126     unsigned short *pp = NULL;
127     cm_buf_t *prevptrbuf = NULL;
128
129     cm_dirHeader_t *dhp = NULL;
130     cm_buf_t *dhpbuf = NULL;
131
132     long code = 0;
133
134     /* check name quality */
135     if (*entry == 0)
136         return EINVAL;
137
138     QueryPerformanceCounter(&start);
139
140     dir_create_entry++;
141
142     osi_Log4(afsd_logp, "cm_DirCreateEntry for op 0x%p, name [%s] and fid[%d,%d]",
143              op, osi_LogSaveString(afsd_logp, entry), cfid->vnode, cfid->unique);
144
145     /* First check if file already exists. */
146     code = cm_DirFindItem(op,
147                           entry,
148                           &entrybuf, &ep,
149                           &prevptrbuf, &pp);
150     if (code == 0) {
151         cm_DirReleasePage(op, &entrybuf, FALSE);
152         cm_DirReleasePage(op, &prevptrbuf, FALSE);
153         code = EEXIST;
154         goto done;
155     }
156
157     blobs = cm_NameEntries(entry, NULL);        /* number of entries required */
158     firstelt = cm_DirFindBlobs(op, blobs);
159     if (firstelt < 0) {
160         osi_Log0(afsd_logp, "cm_DirCreateEntry returning EFBIG");
161         code = EFBIG;           /* directory is full */
162         goto done;
163     }
164
165     /* First, we fill in the directory entry. */
166     code = cm_DirGetBlob(op, firstelt, &entrybuf, &ep);
167     if (code != 0) {
168         code = EIO;
169         goto done;
170     }
171
172     ep->flag = CM_DIR_FFIRST;
173     ep->fid.vnode = htonl(cfid->vnode);
174     ep->fid.unique = htonl(cfid->unique);
175     strcpy(ep->name, entry);
176
177     /* Now we just have to thread it on the hash table list. */
178     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
179     if (code != 0) {
180         cm_DirReleasePage(op, &entrybuf, TRUE);
181         code = EIO;
182         goto done;
183     }
184
185     i = cm_DirHash(entry);
186
187     ep->next = dhp->hashTable[i];
188     dhp->hashTable[i] = htons(firstelt);
189
190     cm_DirReleasePage(op, &dhpbuf, TRUE);
191     cm_DirReleasePage(op, &entrybuf, TRUE);
192
193     osi_Log0(afsd_logp, "cm_DirCreateEntry returning success");
194
195     code = 0;
196   done:
197     QueryPerformanceCounter(&end);
198
199     dir_create_time += (end.QuadPart - start.QuadPart);
200     return code;
201 }
202
203 /* Return the length of a directory in pages
204
205    On entry:
206        op->scp->mx is locked
207
208    On exit:
209        op->scp->mx is locked
210
211    The first directory page for op->scp should not be locked by the
212    calling thread.
213 */
214 int
215 cm_DirLength(cm_dirOp_t * op)
216 {
217     int i, ctr;
218     cm_dirHeader_t *dhp = NULL;
219     cm_buf_t       *dhpbuf = NULL;
220
221     long code;
222
223     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
224     if (code != 0)
225         return 0;
226
227     if (dhp->header.pgcount != 0)
228         ctr = ntohs(dhp->header.pgcount);
229     else {
230         /* old style, count the pages */
231         ctr = 0;
232         for (i = 0; i < CM_DIR_MAXPAGES; i++)
233             if (dhp->alloMap[i] != CM_DIR_EPP)
234                 ctr++;
235     }
236     cm_DirReleasePage(op, &dhpbuf, FALSE);
237     return ctr * CM_DIR_PAGESIZE;
238 }
239
240 /* Delete a directory entry.
241
242    On entry:
243        op->scp->mx is unlocked
244
245    On exit:
246        op->scp->mx is unlocked
247
248    None of the directory buffers for op->scp should be locked by the
249    calling thread.
250  */
251 int
252 cm_DirDeleteEntry(cm_dirOp_t * op, char *entry)
253 {
254     /* Delete an entry from a directory, including update of all free
255        entry descriptors. */
256
257     int nitems, index;
258     cm_dirEntry_t *firstitem = NULL;
259     cm_buf_t      *itembuf = NULL;
260     unsigned short *previtem = NULL;
261     cm_buf_t      *pibuf = NULL;
262     osi_hyper_t    thyper;
263     unsigned long  junk;
264     long code;
265     LARGE_INTEGER start, end;
266
267     QueryPerformanceCounter(&start);
268
269     osi_Log2(afsd_logp, "cm_DirDeleteEntry for op 0x%p, entry [%s]",
270              op, osi_LogSaveString(afsd_logp, entry));
271
272     code = cm_DirFindItem(op, entry,
273                           &itembuf, &firstitem,
274                           &pibuf, &previtem);
275     if (code != 0) {
276         osi_Log0(afsd_logp, "cm_DirDeleteEntry returning ENOENT");
277         code = ENOENT;
278         goto done;
279     }
280
281     dir_remove_entry++;
282
283     *previtem = firstitem->next;
284     cm_DirReleasePage(op, &pibuf, TRUE);
285
286     thyper = itembuf->offset;
287     thyper = LargeIntegerAdd(thyper,
288                              ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
289     thyper = ExtendedLargeIntegerDivide(thyper, 32, &junk);
290
291     index = thyper.LowPart;
292     osi_assert(thyper.HighPart == 0);
293
294     nitems = cm_NameEntries(firstitem->name, NULL);
295     cm_DirReleasePage(op, &itembuf, FALSE);
296
297     cm_DirFreeBlobs(op, index, nitems);
298
299     osi_Log0(afsd_logp, "cm_DirDeleteEntry returning success");
300     code = 0;
301
302   done:
303     QueryPerformanceCounter(&end);
304
305     dir_remove_time += (end.QuadPart - start.QuadPart);
306
307     return code;
308 }
309
310 /* Find a bunch of contiguous entries; at least nblobs in a row.
311
312    Called with op->scp->mx */
313 static long
314 cm_DirFindBlobs(cm_dirOp_t * op, int nblobs)
315 {
316     int i, j, k;
317     int failed = 0;
318
319     cm_dirHeader_t *dhp = NULL;
320     cm_buf_t *dhpbuf = NULL;
321     int dhpModified = FALSE;
322
323     cm_pageHeader_t *pp = NULL;
324     cm_buf_t *pagebuf = NULL;
325     int pageModified = FALSE;
326
327     int pgcount;
328
329     long code;
330
331     osi_Log2(afsd_logp, "cm_DirFindBlobs for op 0x%p, nblobs = %d",
332              op, nblobs);
333
334     code = cm_DirGetPage(op, 0, &dhpbuf, (void **) &dhp);
335     if (code)
336         return -1;
337
338     for (i = 0; i < CM_DIR_BIGMAXPAGES; i++) {
339         if (i >= CM_DIR_MAXPAGES || dhp->alloMap[i] >= nblobs) {
340             /* if page could contain enough entries */
341             /* If there are CM_DIR_EPP free entries, then the page is
342                not even allocated. */
343             if (i >= CM_DIR_MAXPAGES) {
344
345                 /* this pages exists past the end of the old-style dir */
346                 pgcount = ntohs(dhp->header.pgcount);
347                 if (pgcount == 0) {
348                     pgcount = CM_DIR_MAXPAGES;
349                     dhp->header.pgcount = htons(pgcount);
350                     dhpModified = TRUE;
351                 }
352
353                 if (i > pgcount - 1) {
354                     /* this page is bigger than last allocated page */
355                     cm_DirAddPage(op, i);
356                     dhp->header.pgcount = htons(i + 1);
357                     dhpModified = TRUE;
358                 }
359             } else if (dhp->alloMap[i] == CM_DIR_EPP) {
360                 /* Add the page to the directory. */
361                 cm_DirAddPage(op, i);
362                 dhp->alloMap[i] = CM_DIR_EPP - 1;
363                 dhp->header.pgcount = htons(i + 1);
364                 dhpModified = TRUE;
365             }
366
367             code = cm_DirGetPage(op, i, &pagebuf, &pp);
368             if (code) {
369                 cm_DirReleasePage(op, &dhpbuf, dhpModified);
370                 break;
371             }
372
373             for (j = 0; j <= CM_DIR_EPP - nblobs; j++) {
374                 failed = 0;
375                 for (k = 0; k < nblobs; k++)
376                     if ((pp->freeBitmap[(j + k) >> 3] >> ((j + k) & 7)) & 1) {
377                         failed = 1;
378                         break;
379                     }
380                 if (!failed)
381                     break;
382                 failed = 1;
383             }
384
385             if (!failed) {
386                 /* Here we have the first index in j.  We update the allocation maps
387                  * and free up any resources we've got allocated. */
388                 if (i < CM_DIR_MAXPAGES) {
389                     dhp->alloMap[i] -= nblobs;
390                     dhpModified = TRUE;
391                 }
392
393                 cm_DirReleasePage(op, &dhpbuf, dhpModified);
394
395                 for (k = 0; k < nblobs; k++)
396                     pp->freeBitmap[(j + k) >> 3] |= 1 << ((j + k) & 7);
397
398                 cm_DirReleasePage(op, &pagebuf, TRUE);
399
400                 osi_Log0(afsd_logp, "cm_DirFindBlobs returning success");
401
402                 return j + i * CM_DIR_EPP;
403             }
404             cm_DirReleasePage(op, &pagebuf, pageModified);
405         }
406     }
407
408     /* If we make it here, the directory is full. */
409     osi_Log0(afsd_logp, "cm_DirFindBlobs directory is full");
410     cm_DirReleasePage(op, &dhpbuf, dhpModified);
411     return -1;
412 }
413
414 /* Add a page to a directory. 
415
416    Called with op->scp->mx
417 */
418 static long
419 cm_DirAddPage(cm_dirOp_t * op, int pageno)
420 {
421     int i;
422     cm_pageHeader_t *pp = NULL;
423     cm_buf_t *pagebuf = NULL;
424     long code = 0;
425
426     osi_Log2(afsd_logp, "cm_DirAddPage for op 0x%p, pageno=%d", op, pageno);
427
428     code = cm_DirGetPage(op, pageno, &pagebuf, (void **) &pp);
429     if (code != 0)
430         return code;
431
432     pp->tag = htons(1234);
433     if (pageno > 0)
434         pp->pgcount = 0;
435     pp->freeCount = CM_DIR_EPP - 1; /* The first dude is already allocated */
436     pp->freeBitmap[0] = 0x01;
437     for (i = 1; i < CM_DIR_EPP / 8; i++) /* It's a constant */
438         pp->freeBitmap[i] = 0;
439
440     cm_DirReleasePage(op, &pagebuf, TRUE);
441
442     osi_Log0(afsd_logp, "cm_DirAddPage returning success");
443
444     return code;
445 }
446
447 /* Free a whole bunch of directory entries.
448
449    Called with op->scp->mx
450 */
451 static long
452 cm_DirFreeBlobs(cm_dirOp_t * op, int firstblob, int nblobs)
453 {
454     int i;
455     int page;
456
457     cm_dirHeader_t *dhp = NULL;
458     cm_buf_t       *dhpbuf = NULL;
459     int             dhpmodified = FALSE;
460
461     cm_pageHeader_t *pp = NULL;
462     cm_buf_t        *pagebuf = NULL;
463     long code = 0;
464
465     osi_Log3(afsd_logp, "cm_DirFreeBlobs for op 0x%p, firstblob=%d, nblobs=%d",
466              op, firstblob, nblobs);
467
468     page = firstblob / CM_DIR_EPP;
469     firstblob -= CM_DIR_EPP * page;     /* convert to page-relative entry */
470
471     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
472     if (code)
473         return code;
474
475     if (page < CM_DIR_MAXPAGES) {
476         dhp->alloMap[page] += nblobs;
477         dhpmodified = TRUE;
478     }
479
480     cm_DirReleasePage(op, &dhpbuf, dhpmodified);
481
482     code = cm_DirGetPage(op, page, &pagebuf, &pp);
483     if (code == 0) {
484         for (i = 0; i < nblobs; i++)
485             pp->freeBitmap[(firstblob + i) >> 3] &=
486                 ~(1 << ((firstblob + i) & 7));
487         cm_DirReleasePage(op, &pagebuf, TRUE);
488     }
489
490     osi_Log1(afsd_logp, "cm_DirFreeBlobs returning code 0x%x", code);
491
492     return code;
493 }
494
495 /*
496  * Format an empty directory properly.  Note that the first 13 entries in a
497  * directory header page are allocated, 1 to the page header, 4 to the
498  * allocation map and 8 to the hash table.
499  *
500  * Called with op->scp->mx unlocked
501  */
502 int
503 cm_DirMakeDir(cm_dirOp_t * op, cm_fid_t * me, cm_fid_t * parent)
504 {
505     int i;
506     cm_dirHeader_t *dhp = NULL;
507     cm_buf_t *dhpbuf = NULL;
508     int rc = 0;
509     long code;
510
511     osi_Log3(afsd_logp, "cm_DirMakeDir for op 0x%p, directory fid[%d, %d]",
512              op, me->vnode, me->unique);
513     osi_Log2(afsd_logp, "              parent[%d, %d]",
514              parent->vnode, parent->unique);
515
516     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
517     if (code) {
518         rc = 1;
519         goto done;
520     }
521
522     dhp->header.pgcount = htons(1);
523     dhp->header.tag = htons(1234);
524     dhp->header.freeCount = (CM_DIR_EPP - CM_DIR_DHE - 1);
525     dhp->header.freeBitmap[0] = 0xff;
526     dhp->header.freeBitmap[1] = 0x1f;
527     for (i = 2; i < CM_DIR_EPP / 8; i++)
528         dhp->header.freeBitmap[i] = 0;
529     dhp->alloMap[0] = (CM_DIR_EPP - CM_DIR_DHE - 1);
530     for (i = 1; i < CM_DIR_MAXPAGES; i++)
531         dhp->alloMap[i] = CM_DIR_EPP;
532     for (i = 0; i < CM_DIR_NHASHENT; i++)
533         dhp->hashTable[i] = 0;
534
535     cm_DirReleasePage(op, &dhpbuf, TRUE);
536
537     cm_DirCreateEntry(op, ".", me);
538     cm_DirCreateEntry(op, "..", parent);        /* Virtue is its own .. */
539
540     osi_Log0(afsd_logp, "cm_DirMakeDir returning success");
541
542   done:
543     return rc;
544 }
545
546 /* Look up a file name in directory.
547
548    On entry:
549        op->scp->mx is unlocked
550
551    On exit:
552        op->scp->mx is unlocked
553
554    None of the directory buffers for op->scp should be locked by the
555    calling thread.
556 */
557 int
558 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
559 {
560     cm_dirEntry_t *firstitem = NULL;
561     cm_buf_t      *itembuf = NULL;
562     unsigned short *previtem = NULL;
563     cm_buf_t      *pibuf = NULL;
564     long code;
565     LARGE_INTEGER       start;
566     LARGE_INTEGER       end;
567
568     QueryPerformanceCounter(&start);
569
570     osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
571              op, osi_LogSaveString(afsd_logp, entry));
572
573     code = cm_DirFindItem(op, entry,
574                           &itembuf, &firstitem,
575                           &pibuf, &previtem);
576     if (code != 0) {
577         dir_lookup_misses++;
578         code = ENOENT;
579         goto done;
580     }
581
582     cm_DirReleasePage(op, &pibuf, FALSE);
583
584     cfid->cell = op->scp->fid.cell;
585     cfid->volume = op->scp->fid.volume;
586     cfid->vnode = ntohl(firstitem->fid.vnode);
587     cfid->unique = ntohl(firstitem->fid.unique);
588
589     cm_DirReleasePage(op, &itembuf, FALSE);
590
591     osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
592              cfid->vnode, cfid->unique);
593
594     dir_lookup_hits++;
595     code = 0;
596
597   done:
598     QueryPerformanceCounter(&end);
599
600     dir_lookup_time += (end.QuadPart - start.QuadPart);
601
602     return code;
603 }
604
605 /* Look up a file name in directory.
606
607    On entry:
608        op->scp->mx is locked
609
610    On exit:
611        op->scp->mx is locked
612
613    None of the directory buffers for op->scp should be locked by the
614    calling thread.
615 */
616 int
617 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
618 {
619     cm_dirEntry_t *firstitem = NULL;
620     cm_buf_t      *itembuf = NULL;
621     unsigned short *previtem = NULL;
622     cm_buf_t      *pibuf = NULL;
623
624     long code;
625
626     osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
627              op, osi_LogSaveString(afsd_logp, entry));
628
629     code = cm_DirFindItem(op, entry,
630                           &itembuf, &firstitem,
631                           &pibuf, &previtem);
632     if (code != 0)
633         return ENOENT;
634
635     cm_DirReleasePage(op, &pibuf, FALSE);
636
637     cfid->cell = op->scp->fid.cell;
638     cfid->volume = op->scp->fid.volume;
639     cfid->vnode = ntohl(firstitem->fid.vnode);
640     cfid->unique = ntohl(firstitem->fid.unique);
641     if (offsetp) {
642         osi_hyper_t thyper;
643
644         thyper = itembuf->offset;
645         thyper = LargeIntegerAdd(thyper,
646                                  ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
647
648         *offsetp = thyper;
649     }
650
651     cm_DirReleasePage(op, &itembuf, FALSE);
652
653     osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
654              cfid->vnode, cfid->unique);
655     if (offsetp) {
656         osi_Log2(afsd_logp, "               offset [%x:%x]",
657                  offsetp->HighPart, offsetp->LowPart);
658     }
659
660     return 0;
661 }
662
663 /* Apply a function to every directory entry in a directory.
664
665    On entry:
666        op->scp->mx is locked
667
668    On exit:
669        op->scp->mx is locked
670
671    None of the directory buffers for op->scp should be locked by the
672    calling thread.
673
674    The hook function cannot modify or lock any directory buffers.
675  */
676 int
677 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
678 {
679     /* Enumerate the contents of a directory. */
680     int i;
681     int num;
682
683     cm_dirHeader_t *dhp = NULL;
684     cm_buf_t       *dhpbuf = NULL;
685
686     cm_dirEntry_t  *ep = NULL;
687     cm_buf_t       *epbuf = NULL;
688
689     long code = 0;
690
691     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
692     if (code != 0)
693         return EIO;
694
695     for (i = 0; i < CM_DIR_NHASHENT; i++) {
696         /* For each hash chain, enumerate everyone on the list. */
697         num = ntohs(dhp->hashTable[i]);
698         while (num != 0) {
699             /* Walk down the hash table list. */
700             code = cm_DirGetBlob(op, num, &epbuf, &ep);
701             if (code != 0) {
702                 cm_DirReleasePage(op, &dhpbuf, FALSE);
703                 return code;
704             }
705
706             num = ntohs(ep->next);
707             (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
708                          ntohl(ep->fid.unique));
709
710             cm_DirReleasePage(op, &epbuf, FALSE);
711         }
712     }
713     cm_DirReleasePage(op, &dhpbuf, FALSE);
714
715     return 0;
716 }
717
718 /* Check if a directory is empty
719
720    On entry:
721        op->scp->mx is locked
722
723    On exit:
724        op->scp->mx is locked
725
726    None of the directory buffers for op->scp should be locked by the
727    calling thread.
728  */
729 int
730 cm_DirIsEmpty(cm_dirOp_t * op)
731 {
732     /* Enumerate the contents of a directory. */
733     int i;
734     int num;
735
736     cm_dirHeader_t *dhp = NULL;
737     cm_buf_t       *dhpbuf = NULL;
738
739     cm_dirEntry_t  *ep = NULL;
740     cm_buf_t       *epbuf = NULL;
741
742     long code = 0;
743
744     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
745     if (code != 0)
746         return 0;
747
748     for (i = 0; i < CM_DIR_NHASHENT; i++) {
749         /* For each hash chain, enumerate everyone on the list. */
750         num = ntohs(dhp->hashTable[i]);
751
752         while (num != 0) {
753             /* Walk down the hash table list. */
754             code = cm_DirGetBlob(op, num, &epbuf, &ep);
755             if (code != 0)
756                 break;
757
758             if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
759                 cm_DirReleasePage(op, &epbuf, FALSE);
760                 cm_DirReleasePage(op, &dhpbuf, FALSE);
761                 return 1;
762             }
763             num = ntohs(ep->next);
764             cm_DirReleasePage(op, &epbuf, FALSE);
765         }
766     }
767     cm_DirReleasePage(op, &dhpbuf, FALSE);
768     return 0;
769 }
770
771 /* Return a pointer to an entry, given its number.
772
773    On entry:
774      scp->mx locked
775      if *bufferpp != NULL, then *bufferpp->mx is locked
776
777    During:
778      scp->mx may be unlocked
779      *bufferpp may be released
780
781    On exit:
782      scp->mx locked
783      if *bufferpp != NULL, then *bufferpp->mx is locked
784
785      *bufferpp should be released via cm_DirReleasePage() or any other
786      *call that releases a directory buffer.
787 */
788 static long
789 cm_DirGetBlob(cm_dirOp_t * op,
790               unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
791 {
792     unsigned char * ep;
793     long code = 0;
794
795     osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
796              op, blobno);
797
798     code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
799                          bufferpp, (void **) &ep);
800     if (code != 0)
801         return code;
802
803     *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
804
805     return code;
806 }       
807
808 int
809 cm_DirHash(char *string)
810 {
811     /* Hash a string to a number between 0 and NHASHENT. */
812     register unsigned char tc;
813     register int hval;
814     register int tval;
815     hval = 0;
816     while ((tc = (*string++))) {
817         hval *= 173;
818         hval += tc;
819     }
820     tval = hval & (CM_DIR_NHASHENT - 1);
821     if (tval == 0)
822         return tval;
823     else if (hval < 0)
824         tval = CM_DIR_NHASHENT - tval;
825     return tval;
826 }
827
828 /* Find a directory entry, given its name.  This entry returns a
829  * pointer to a locked buffer, and a pointer to a locked buffer (in
830  * previtem) referencing the found item (to aid the delete code).  If
831  * no entry is found, however, no items are left locked, and a null
832  * pointer is returned instead.
833  *
834  * On entry:
835  *  scp->mx locked
836  *
837  * On exit:
838  *  scp->mx locked
839  */
840 static long
841 cm_DirFindItem(cm_dirOp_t * op,
842                char *ename,
843                cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
844                cm_buf_t ** prevbufpp, unsigned short **previtempp)
845 {
846     int                  i;
847     cm_dirHeader_t      *dhp = NULL;
848     unsigned short      *lp = NULL;
849     cm_dirEntry_t       *tp = NULL;
850     cm_buf_t            *hashbufp = NULL;
851     cm_buf_t            *itembufp = NULL;
852     long code = 0;
853
854     osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
855              op, osi_LogSaveString(afsd_logp, ename));
856
857     i = cm_DirHash(ename);
858
859     if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
860         osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
861         return CM_ERROR_INVAL;
862     }
863
864     code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
865     if (code != 0) {
866         return code;
867     }
868
869     if (dhp->hashTable[i] == 0) {
870         /* no such entry */
871         osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
872         cm_DirReleasePage(op, &hashbufp, FALSE);
873         return ENOENT;
874     }
875
876     code = cm_DirGetBlob(op,
877                          (u_short) ntohs(dhp->hashTable[i]),
878                          &itembufp, &tp);
879     if (code != 0) {
880         cm_DirReleasePage(op, &hashbufp, FALSE);
881         return code;
882     }
883
884     lp = &(dhp->hashTable[i]);
885
886     /* loop invariant:
887
888        lp       : pointer to blob number of entry we are looking at
889        hashbufp : buffer containing lp
890        tp       : pointer to entry we are looking at
891        itembufp : buffer containing tp
892      */
893     while (1) {
894         /* Look at each hash conflict entry. */
895         if (!strcmp(ename, tp->name)) {
896             osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
897             /* Found our entry. */
898             *previtempp = lp;
899             *prevbufpp = hashbufp;
900             *itempp = tp;
901             *itembufpp = itembufp;
902             return 0;
903         }
904
905         lp = &(tp->next);
906         cm_DirReleasePage(op, &hashbufp, FALSE);
907         hashbufp = itembufp;
908
909         itembufp = NULL;
910         tp = NULL;
911
912         if (*lp == 0) {
913             /* The end of the line */
914             osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
915             cm_DirReleasePage(op, &hashbufp, FALSE);
916             return ENOENT;
917         }
918
919         code = cm_DirGetBlob(op,
920                              (u_short) ntohs(*lp),
921                              &itembufp, &tp);
922
923         if (code != 0) {
924             cm_DirReleasePage(op, &hashbufp, FALSE);
925             return code;
926         }
927     }
928 }
929
930 /* Begin a sequence of directory operations.  scp->mx should be
931    locked.
932 */
933 long
934 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
935               afs_uint32 lockType, cm_dirOp_t * op)
936 {
937     long code;
938     int i, mxheld = 0;
939
940     osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
941              op, scp, userp);
942
943     memset(op, 0, sizeof(*op));
944
945     cm_HoldSCache(scp);
946     op->scp = scp;
947     cm_HoldUser(userp);
948     op->userp = userp;
949     cm_InitReq(&op->req);
950
951     op->dirtyBufCount = 0;
952     op->nBuffers = 0;
953
954     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
955         op->buffers[i].flags = 0;
956     }
957
958     code = cm_DirCheckStatus(op);
959
960     if (code == 0) {
961         op->length = scp->length;
962         op->newLength = op->length;
963         op->dataVersion = scp->dataVersion;
964         op->newDataVersion = op->dataVersion;
965
966 #ifdef USE_BPLUS
967         lock_ObtainRead(&scp->dirlock);
968         if (!cm_BPlusTrees ||
969             (scp->dirBplus &&
970              scp->dirDataVersion == scp->dataVersion)) 
971         {
972             int mxheld = 0;
973
974             switch (lockType) {
975             case CM_DIRLOCK_NONE:
976                 lock_ReleaseRead(&scp->dirlock);
977                 break;
978             case CM_DIRLOCK_READ:
979                 /* got it already */
980                 break;
981             case CM_DIRLOCK_WRITE:
982             default:
983                 lock_ReleaseRead(&scp->dirlock);
984                 lock_ObtainWrite(&scp->dirlock);
985             }
986         } else {
987             lock_ReleaseRead(&scp->dirlock);
988             lock_ObtainWrite(&scp->dirlock);
989             if (scp->dirBplus && 
990                 scp->dirDataVersion != scp->dataVersion)
991             {
992                 bplus_dv_error++;
993                 bplus_free_tree++;
994                 freeBtree(scp->dirBplus);
995                 scp->dirBplus = NULL;
996                 scp->dirDataVersion = -1;
997             }
998
999             if (!scp->dirBplus) {
1000                 cm_BPlusDirBuildTree(scp, userp, reqp);
1001                 if (scp->dirBplus)
1002                     scp->dirDataVersion = scp->dataVersion;
1003             }
1004
1005             switch (lockType) {
1006             case CM_DIRLOCK_NONE:
1007                 lock_ReleaseWrite(&scp->dirlock);
1008                 break;
1009             case CM_DIRLOCK_READ:
1010                 lock_ConvertWToR(&scp->dirlock);
1011                 break;
1012             case CM_DIRLOCK_WRITE:
1013             default:
1014                 /* got it already */;
1015             }
1016         }
1017 #else
1018         switch (lockType) {
1019         case CM_DIRLOCK_NONE:
1020             break;
1021         case CM_DIRLOCK_READ:
1022             lock_ObtainRead(&scp->dirlock);
1023             break;
1024         case CM_DIRLOCK_WRITE:
1025         default:
1026             lock_ObtainWrite(&scp->dirlock);
1027         }
1028 #endif
1029         op->lockType = lockType;
1030     } else {
1031     
1032         cm_EndDirOp(op);
1033     }
1034
1035     return code;
1036 }
1037
1038 /* Check if it is safe for us to perform local directory updates.
1039    Called with scp->mx unlocked. */
1040 int
1041 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1042 {
1043     long code;
1044
1045     if (op->scp == NULL)
1046         return 0;
1047
1048     code = cm_DirCheckStatus(op);
1049
1050     if (code == 0 &&
1051         op->dataVersion == op->scp->dataVersion - 1) {
1052         /* only one set of changes happened between cm_BeginDirOp()
1053            and this function.  It is safe for us to perform local
1054            changes. */
1055         op->newDataVersion = op->scp->dataVersion;
1056         op->newLength = op->scp->serverLength;
1057
1058         osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1059
1060         return 1;
1061     }
1062
1063     osi_Log3(afsd_logp,
1064              "cm_CheckDirOpForSingleChange failed.  code=0x%x, old dv=%d, new dv=%d",
1065              code, op->dataVersion, op->scp->dataVersion);
1066     return 0;
1067 }
1068
1069 /* End a sequence of directory operations.  Called with op->scp->mx
1070    unlocked.*/
1071 long
1072 cm_EndDirOp(cm_dirOp_t * op)
1073 {
1074     long code = 0;
1075
1076     if (op->scp == NULL)
1077         return 0;
1078
1079     osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1080              op, op->dirtyBufCount);
1081
1082     if (op->dirtyBufCount > 0) {
1083         /* we made changes.  We should go through the list of buffers
1084            and update the dataVersion for each. */
1085         code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1086
1087 #ifdef USE_BPLUS
1088         /* and update the data version on the B+ tree */
1089         if (op->scp->dirBplus && 
1090             op->scp->dirDataVersion == op->dataVersion) {
1091
1092             switch (op->lockType) {
1093             case CM_DIRLOCK_READ:
1094                 lock_ReleaseRead(&op->scp->dirlock);
1095                 /* fall through ... */
1096             case CM_DIRLOCK_NONE:
1097                 lock_ObtainWrite(&op->scp->dirlock);
1098                 op->lockType = CM_DIRLOCK_WRITE;
1099                 break;
1100             case CM_DIRLOCK_WRITE:
1101             default:
1102                 /* already got it */;
1103             }
1104             op->scp->dirDataVersion = op->newDataVersion;
1105         }
1106 #endif
1107     }
1108
1109     switch (op->lockType) {
1110     case CM_DIRLOCK_NONE:
1111         break;
1112     case CM_DIRLOCK_READ:
1113         lock_ReleaseRead(&op->scp->dirlock);
1114         break;
1115     case CM_DIRLOCK_WRITE:
1116     default:
1117         lock_ReleaseWrite(&op->scp->dirlock);
1118     }
1119
1120     if (op->scp)
1121         cm_ReleaseSCache(op->scp);
1122     op->scp = NULL;
1123
1124     if (op->userp)
1125         cm_ReleaseUser(op->userp);
1126     op->userp = 0;
1127
1128     osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1129
1130     return code;
1131 }
1132
1133 /* NOTE: Called without scp->mx and without bufferp->mx */
1134 static long
1135 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1136 {
1137     int i;
1138     long code = 0;
1139
1140     osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1141
1142     if (bufferp == NULL)
1143         return -1;
1144
1145     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1146         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1147             op->buffers[i].bufferp == bufferp) {
1148             break;
1149         }
1150     }
1151
1152     if (i < CM_DIROP_MAXBUFFERS) {
1153         /* we already have this buffer on our list */
1154
1155         op->buffers[i].refcount++;
1156         osi_Log0(afsd_logp,
1157                  "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1158         return 0;
1159     } else {
1160         /* we have to add a new buffer */
1161         osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1162                     "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1163
1164         for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1165             if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1166                 break;
1167         }
1168
1169         osi_assert(i < CM_DIROP_MAXBUFFERS);
1170
1171         lock_ObtainMutex(&bufferp->mx);
1172         lock_ObtainMutex(&op->scp->mx);
1173
1174         /* Make sure we are synchronized. */
1175         code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1176                          CM_SCACHESYNC_NEEDCALLBACK |
1177                          CM_SCACHESYNC_WRITE |
1178                          CM_SCACHESYNC_BUFLOCKED);
1179
1180         if (code == 0 &&
1181             bufferp->dataVersion != op->dataVersion) {
1182
1183             osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer version mismatch. buf ver = %d. want %d", bufferp->dataVersion, op->dataVersion);
1184
1185             cm_SyncOpDone(op->scp, bufferp,
1186                           CM_SCACHESYNC_NEEDCALLBACK |
1187                           CM_SCACHESYNC_WRITE |
1188                           CM_SCACHESYNC_BUFLOCKED);
1189
1190             code = CM_ERROR_INVAL;
1191         }
1192
1193         lock_ReleaseMutex(&op->scp->mx);
1194         lock_ReleaseMutex(&bufferp->mx);
1195
1196         if (code) {
1197             osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer.  code=0x%x",
1198                      code);
1199             return code;
1200         }
1201
1202         buf_Hold(bufferp);
1203         op->buffers[i].bufferp = bufferp;
1204         op->buffers[i].refcount = 1; /* start with one ref */
1205         op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1206
1207         op->nBuffers++;
1208
1209         osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1210
1211         return 0;
1212     }
1213 }
1214
1215 /* Note: Called without op->scp->mx */
1216 static int
1217 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1218 {
1219     int i;
1220
1221     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1222         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1223             LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1224             break;
1225     }
1226
1227     if (i < CM_DIROP_MAXBUFFERS) {
1228         /* found it */
1229         op->buffers[i].refcount++;
1230         buf_Hold(op->buffers[i].bufferp);
1231         *bufferpp = op->buffers[i].bufferp;
1232
1233         osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1234                  offset.HighPart, offset.LowPart);
1235         return 1;
1236     }
1237
1238     osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1239              offset.HighPart, offset.LowPart);
1240     return 0;
1241 }
1242
1243
1244 /* NOTE: called with scp->mx held or not depending on the flags */
1245 static int
1246 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1247 {
1248     int i;
1249
1250     osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1251              op, bufferp, flags);
1252
1253     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1254         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1255             op->buffers[i].bufferp == bufferp)
1256             break;
1257     }
1258
1259     if (i < CM_DIROP_MAXBUFFERS) {
1260
1261         if (flags & DIROP_MODIFIED)
1262             op->dirtyBufCount++;
1263
1264         osi_assert(op->buffers[i].refcount > 0);
1265         op->buffers[i].refcount --;
1266
1267         if (op->buffers[i].refcount == 0) {
1268             /* this was the last reference we had */
1269
1270             osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1271
1272             /* if this buffer was modified, then we update the data
1273                version of the buffer with the data version of the
1274                scp. */
1275             if (!(flags & DIROP_SCPLOCKED)) {
1276                 lock_ObtainMutex(&op->scp->mx);
1277             }
1278
1279             /* first make sure that the buffer is idle.  It should
1280                have been idle all along. */
1281             osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1282                                             CM_BUF_CMSTORING)) == 0,
1283                         "Buffer is not idle while performing dirOp");
1284
1285             cm_SyncOpDone(op->scp, bufferp,
1286                           CM_SCACHESYNC_NEEDCALLBACK |
1287                           CM_SCACHESYNC_WRITE);
1288
1289 #ifdef DEBUG
1290             osi_assert(bufferp->dataVersion == op->dataVersion);
1291 #endif
1292
1293             lock_ReleaseMutex(&op->scp->mx);
1294
1295             lock_ObtainMutex(&bufferp->mx);
1296
1297             if (flags & DIROP_SCPLOCKED) {
1298                 lock_ObtainMutex(&op->scp->mx);
1299             }
1300
1301             if (flags & DIROP_MODIFIED) {
1302                 /* We don't update the dataversion here.  Instead we
1303                    wait until the dirOp is completed and then flip the
1304                    dataversion on all the buffers in one go.
1305                    Otherwise we won't know if the dataversion is
1306                    current because it was fetched from the server or
1307                    because we touched it during the dirOp. */
1308
1309                 if (bufferp->userp != op->userp) {
1310                     if (bufferp->userp != NULL)
1311                         cm_ReleaseUser(bufferp->userp);
1312                     cm_HoldUser(op->userp);
1313                     bufferp->userp = op->userp;
1314                 }
1315             }
1316
1317             lock_ReleaseMutex(&bufferp->mx);
1318
1319             op->buffers[i].bufferp = NULL;
1320             buf_Release(bufferp);
1321             op->buffers[i].flags = 0;
1322
1323             op->nBuffers--;
1324
1325             return 1;
1326         } else {
1327             /* we have other references to this buffer. so we have to
1328                let it be */
1329             return 0;
1330         }
1331
1332     } else {
1333         osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1334         osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1335         return -1;
1336     }
1337 }
1338
1339 /* Check if we have current status and a callback for the given scp.
1340    This should be called before cm_DirGetPage() is called per scp.
1341
1342    On entry:
1343      scp->mx unlocked
1344
1345    On exit:
1346      scp->mx unlocked
1347
1348    During:
1349      scp->mx may be released
1350  */
1351 static long
1352 cm_DirCheckStatus(cm_dirOp_t * op)
1353 {
1354     long code;
1355
1356     lock_ObtainMutex(&op->scp->mx);
1357     code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1358                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1359     lock_ReleaseMutex(&op->scp->mx);
1360
1361     osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1362              op, code);
1363
1364     return code;
1365 }
1366
1367 /* Release a directory buffer that was obtained via a call to
1368    cm_DirGetPage() or any other function that returns a locked, held,
1369    directory page buffer.
1370
1371    Called with scp->mx unlocked
1372  */
1373 static long
1374 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1375 {
1376     long code = 0;
1377
1378     if (!*bufferpp)
1379         return EINVAL;
1380
1381     cm_DirOpDelBuffer(op, *bufferpp,
1382                       ((modified ? DIROP_MODIFIED : 0)));
1383     buf_Release(*bufferpp);
1384     *bufferpp = NULL;
1385
1386     return code;
1387 }
1388
1389 /*
1390    Returns the index'th directory page from scp.  The userp and reqp
1391    will be used to fetch the buffer from the fileserver if necessary.
1392    If the call is successful, a locked and held cm_buf_t is returned
1393    via buferpp and a pointer to the directory page is returned via
1394    datapp.
1395
1396    The returned buffer should be released via a call to
1397    cm_DirReleasePage() or by passing it into a subsequent call to
1398    cm_DirGetPage() for the *same* scp.
1399
1400    If a *locked* buffer for the *same* scp is passed in via bufferpp
1401    to the function, it will check if the requested directory page is
1402    located in the specified buffer.  If not, the buffer will be
1403    released and a new buffer returned that contains the requested
1404    page.
1405
1406    Note: If a buffer is specified on entry via bufferpp, it is assumed
1407    that the buffer is unmodified.  If the buffer is modified, it
1408    should be released via cm_DirReleasePage().
1409
1410    On entry:
1411      scp->mx unlocked.
1412      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1413
1414    On exit:
1415      scp->mx unlocked
1416      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1417
1418    During:
1419      scp->mx will be obtained and released
1420
1421  */
1422 static long
1423 cm_DirGetPage(cm_dirOp_t * op,
1424               long index, cm_buf_t ** bufferpp, void ** datapp)
1425 {
1426     osi_hyper_t pageOffset;     /* offset of the dir page from the
1427                                    start of the directory */
1428     osi_hyper_t bufferOffset;   /* offset of the buffer from the start
1429                                    of the directory */
1430     osi_hyper_t thyper;
1431
1432     cm_buf_t * bufferp = NULL;
1433
1434     void * datap = NULL;
1435
1436     long code = 0;
1437
1438     osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1439
1440     pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1441     bufferOffset.HighPart = pageOffset.HighPart;
1442     bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1443
1444     bufferp = *bufferpp;
1445     if (bufferp != NULL) {
1446         osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1447
1448         thyper = bufferp->offset;
1449     }
1450
1451     if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1452         /* wrong buffer */
1453
1454         if (bufferp) {
1455             buf_Release(bufferp);
1456             cm_DirOpDelBuffer(op, bufferp, 0);
1457             bufferp = NULL;
1458         }
1459
1460         /* first check if we are already working with the buffer */
1461         if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1462             code = 0;
1463             goto _has_buffer;
1464         }
1465
1466         lock_ObtainRead(&op->scp->bufCreateLock);
1467         code = buf_Get(op->scp, &bufferOffset, &bufferp);
1468         lock_ReleaseRead(&op->scp->bufCreateLock);
1469
1470         if (code) {
1471             osi_Log1(afsd_logp, "    buf_Get returned code 0x%x", code);
1472             bufferp = NULL;
1473             goto _exit;
1474         }
1475
1476         osi_assert(bufferp != NULL);
1477
1478         /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1479         code = cm_DirOpAddBuffer(op, bufferp);
1480
1481         if (code != 0) {
1482             /* for some reason, the buffer was rejected.  We can't use
1483                this buffer, and since this is the only buffer we can
1484                potentially use, there's no recourse.*/
1485             buf_Release(bufferp);
1486             bufferp = NULL;
1487             goto _exit;
1488         }
1489
1490 #if 0
1491         /* The code below is for making sure the buffer contains
1492            current data.  This is a bad idea, since the whole point of
1493            doing directory updates locally is to avoid fetching all
1494            the data from the server. */
1495         while (1) {
1496             lock_ObtainMutex(&op->scp->mx);
1497             code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1498                              CM_SCACHESYNC_NEEDCALLBACK |
1499                              CM_SCACHESYNC_READ |
1500                              CM_SCACHESYNC_BUFLOCKED);
1501
1502             if (code) {
1503                 lock_ReleaseMutex(&op->scp->mx);
1504                 break;
1505             }
1506
1507             cm_SyncOpDone(op->scp, bufferp,
1508                           CM_SCACHESYNC_NEEDCALLBACK |
1509                           CM_SCACHESYNC_READ |
1510                           CM_SCACHESYNC_BUFLOCKED);
1511
1512             if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1513                 lock_ReleaseMutex(&op->scp->mx);
1514                 break;
1515             }
1516
1517             lock_ReleaseMutex(&bufferp->mx);
1518             code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1519             lock_ReleaseMutex(&op->scp->mx);
1520             lock_ObtainMutex(&bufferp->mx);
1521
1522             if (code)
1523                 break;
1524         }
1525
1526         if (code) {
1527             cm_DirOpDelBuffer(op, bufferp, 0);
1528             buf_Release(bufferp);
1529             bufferp = NULL;
1530             goto _exit;
1531         }
1532 #endif
1533     }
1534
1535  _has_buffer:
1536
1537     /* now to figure out where the data is */
1538     thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1539
1540     osi_assert(thyper.HighPart == 0);
1541     osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1542                cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1543
1544     datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1545
1546     if (datapp)
1547         *datapp = datap;
1548
1549     /* also, if we are writing past EOF, we should make a note of the
1550        new length */
1551     thyper = LargeIntegerAdd(pageOffset,
1552                              ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1553     if (LargeIntegerLessThan(op->newLength, thyper)) {
1554         op->newLength = thyper;
1555     }
1556
1557  _exit:
1558
1559     *bufferpp = bufferp;
1560
1561     osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1562
1563     return code;
1564 }
1565
1566
1567 void
1568 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1569 {
1570     size_t len;
1571     cm_dirEntryList_t * entry;
1572
1573     len = strlen(namep);
1574     len += sizeof(cm_dirEntryList_t);
1575
1576     entry = malloc(len);
1577     if (entry) {
1578         entry->nextp = *list;
1579         strcpy(entry->name, namep);
1580         *list = entry;
1581     }
1582 }
1583
1584 void
1585 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1586 {
1587     cm_dirEntryList_t * entry;
1588     cm_dirEntryList_t * next;
1589
1590     for (entry = *list; entry; entry = next) {
1591         next = entry->nextp;
1592         free(entry);
1593     }
1594
1595     *list = NULL;
1596 }
1597