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