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