DEVEL15-DELTA15-windows-handle-invalid-utf16-names-20081019
[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     QueryPerformanceCounter(&start);
604
605     osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
606              op, osi_LogSaveString(afsd_logp, entry));
607
608     code = cm_DirFindItem(op, entry,
609                           &itembuf, &firstitem,
610                           &pibuf, &previtem);
611
612     if (code == CM_ERROR_NOTINCACHE) {
613         code = cm_DirPrefetchBuffers(op);
614         if (code == 0)
615             code = cm_DirFindItem(op, entry, &itembuf, &firstitem,
616                                   &pibuf, &previtem);
617     }
618
619     if (code != 0) {
620         dir_lookup_misses++;
621         code = ENOENT;
622         goto done;
623     }
624
625     cm_DirReleasePage(op, &pibuf, FALSE);
626
627     cfid->cell = op->scp->fid.cell;
628     cfid->volume = op->scp->fid.volume;
629     cfid->vnode = ntohl(firstitem->fid.vnode);
630     cfid->unique = ntohl(firstitem->fid.unique);
631
632     cm_DirReleasePage(op, &itembuf, FALSE);
633
634     osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
635              cfid->vnode, cfid->unique);
636
637     dir_lookup_hits++;
638     code = 0;
639
640   done:
641     QueryPerformanceCounter(&end);
642
643     dir_lookup_time += (end.QuadPart - start.QuadPart);
644
645     return code;
646 }
647
648 /* Look up a file name in directory.
649
650    On entry:
651        op->scp->rw is locked
652
653    On exit:
654        op->scp->rw is locked
655
656    None of the directory buffers for op->scp should be locked by the
657    calling thread.
658 */
659 int
660 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
661 {
662     cm_dirEntry_t *firstitem = NULL;
663     cm_buf_t      *itembuf = NULL;
664     unsigned short *previtem = NULL;
665     cm_buf_t      *pibuf = NULL;
666
667     long code;
668
669     osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
670              op, osi_LogSaveString(afsd_logp, entry));
671
672     code = cm_DirFindItem(op, entry,
673                           &itembuf, &firstitem,
674                           &pibuf, &previtem);
675     if (code != 0)
676         return ENOENT;
677
678     cm_DirReleasePage(op, &pibuf, FALSE);
679
680     cfid->cell = op->scp->fid.cell;
681     cfid->volume = op->scp->fid.volume;
682     cfid->vnode = ntohl(firstitem->fid.vnode);
683     cfid->unique = ntohl(firstitem->fid.unique);
684     if (offsetp) {
685         osi_hyper_t thyper;
686
687         thyper = itembuf->offset;
688         thyper = LargeIntegerAdd(thyper,
689                                  ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
690
691         *offsetp = thyper;
692     }
693
694     cm_DirReleasePage(op, &itembuf, FALSE);
695
696     osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
697              cfid->vnode, cfid->unique);
698     if (offsetp) {
699         osi_Log2(afsd_logp, "               offset [%x:%x]",
700                  offsetp->HighPart, offsetp->LowPart);
701     }
702
703     return 0;
704 }
705
706 /* Apply a function to every directory entry in a directory.
707
708    On entry:
709        op->scp->rw is locked
710
711    On exit:
712        op->scp->rw is locked
713
714    None of the directory buffers for op->scp should be locked by the
715    calling thread.
716
717    The hook function cannot modify or lock any directory buffers.
718  */
719 int
720 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
721 {
722     /* Enumerate the contents of a directory. */
723     int i;
724     int num;
725
726     cm_dirHeader_t *dhp = NULL;
727     cm_buf_t       *dhpbuf = NULL;
728
729     cm_dirEntry_t  *ep = NULL;
730     cm_buf_t       *epbuf = NULL;
731
732     long code = 0;
733
734     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
735     if (code != 0)
736         return EIO;
737
738     for (i = 0; i < CM_DIR_NHASHENT; i++) {
739         /* For each hash chain, enumerate everyone on the list. */
740         num = ntohs(dhp->hashTable[i]);
741         while (num != 0) {
742             /* Walk down the hash table list. */
743             code = cm_DirGetBlob(op, num, &epbuf, &ep);
744             if (code != 0) {
745                 cm_DirReleasePage(op, &dhpbuf, FALSE);
746                 return code;
747             }
748
749             num = ntohs(ep->next);
750             (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
751                          ntohl(ep->fid.unique));
752
753             cm_DirReleasePage(op, &epbuf, FALSE);
754         }
755     }
756     cm_DirReleasePage(op, &dhpbuf, FALSE);
757
758     return 0;
759 }
760
761 /* Check if a directory is empty
762
763    On entry:
764        op->scp->rw is locked
765
766    On exit:
767        op->scp->rw is locked
768
769    None of the directory buffers for op->scp should be locked by the
770    calling thread.
771  */
772 int
773 cm_DirIsEmpty(cm_dirOp_t * op)
774 {
775     /* Enumerate the contents of a directory. */
776     int i;
777     int num;
778
779     cm_dirHeader_t *dhp = NULL;
780     cm_buf_t       *dhpbuf = NULL;
781
782     cm_dirEntry_t  *ep = NULL;
783     cm_buf_t       *epbuf = NULL;
784
785     long code = 0;
786
787     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
788     if (code != 0)
789         return 0;
790
791     for (i = 0; i < CM_DIR_NHASHENT; i++) {
792         /* For each hash chain, enumerate everyone on the list. */
793         num = ntohs(dhp->hashTable[i]);
794
795         while (num != 0) {
796             /* Walk down the hash table list. */
797             code = cm_DirGetBlob(op, num, &epbuf, &ep);
798             if (code != 0)
799                 break;
800
801             if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
802                 cm_DirReleasePage(op, &epbuf, FALSE);
803                 cm_DirReleasePage(op, &dhpbuf, FALSE);
804                 return 1;
805             }
806             num = ntohs(ep->next);
807             cm_DirReleasePage(op, &epbuf, FALSE);
808         }
809     }
810     cm_DirReleasePage(op, &dhpbuf, FALSE);
811     return 0;
812 }
813
814 /* Return a pointer to an entry, given its number.
815
816    On entry:
817      scp->rw locked
818      if *bufferpp != NULL, then *bufferpp->mx is locked
819
820    During:
821      scp->rw may be unlocked
822      *bufferpp may be released
823
824    On exit:
825      scp->rw locked
826      if *bufferpp != NULL, then *bufferpp->mx is locked
827
828      *bufferpp should be released via cm_DirReleasePage() or any other
829      *call that releases a directory buffer.
830 */
831 static long
832 cm_DirGetBlob(cm_dirOp_t * op,
833               unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
834 {
835     unsigned char * ep;
836     long code = 0;
837
838     osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
839              op, blobno);
840
841     code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
842                          bufferpp, (void **) &ep);
843     if (code != 0)
844         return code;
845
846     *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
847
848     return code;
849 }       
850
851 int
852 cm_DirHash(char *string)
853 {
854     /* Hash a string to a number between 0 and NHASHENT. */
855     register unsigned char tc;
856     register int hval;
857     register int tval;
858     hval = 0;
859     while ((tc = (*string++))) {
860         hval *= 173;
861         hval += tc;
862     }
863     tval = hval & (CM_DIR_NHASHENT - 1);
864     if (tval == 0)
865         return tval;
866     else if (hval < 0)
867         tval = CM_DIR_NHASHENT - tval;
868     return tval;
869 }
870
871 /* Find a directory entry, given its name.  This entry returns a
872  * pointer to a locked buffer, and a pointer to a locked buffer (in
873  * previtem) referencing the found item (to aid the delete code).  If
874  * no entry is found, however, no items are left locked, and a null
875  * pointer is returned instead.
876  *
877  * On entry:
878  *  scp->rw locked
879  *
880  * On exit:
881  *  scp->rw locked
882  */
883 static long
884 cm_DirFindItem(cm_dirOp_t * op,
885                char *ename,
886                cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
887                cm_buf_t ** prevbufpp, unsigned short **previtempp)
888 {
889     int                  i;
890     cm_dirHeader_t      *dhp = NULL;
891     unsigned short      *lp = NULL;
892     cm_dirEntry_t       *tp = NULL;
893     cm_buf_t            *hashbufp = NULL;
894     cm_buf_t            *itembufp = NULL;
895     long code = 0;
896
897     osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
898              op, osi_LogSaveString(afsd_logp, ename));
899
900     i = cm_DirHash(ename);
901
902     if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
903         osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
904         return CM_ERROR_INVAL;
905     }
906
907     code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
908     if (code != 0) {
909         return code;
910     }
911
912     if (dhp->hashTable[i] == 0) {
913         /* no such entry */
914         osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
915         cm_DirReleasePage(op, &hashbufp, FALSE);
916         return ENOENT;
917     }
918
919     code = cm_DirGetBlob(op,
920                          (u_short) ntohs(dhp->hashTable[i]),
921                          &itembufp, &tp);
922     if (code != 0) {
923         cm_DirReleasePage(op, &hashbufp, FALSE);
924         return code;
925     }
926
927     lp = &(dhp->hashTable[i]);
928
929     /* loop invariant:
930
931        lp       : pointer to blob number of entry we are looking at
932        hashbufp : buffer containing lp
933        tp       : pointer to entry we are looking at
934        itembufp : buffer containing tp
935      */
936     while (1) {
937         /* Look at each hash conflict entry. */
938         if (!strcmp(ename, tp->name)) {
939             osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
940             /* Found our entry. */
941             *previtempp = lp;
942             *prevbufpp = hashbufp;
943             *itempp = tp;
944             *itembufpp = itembufp;
945             return 0;
946         }
947
948         lp = &(tp->next);
949         cm_DirReleasePage(op, &hashbufp, FALSE);
950         hashbufp = itembufp;
951
952         itembufp = NULL;
953         tp = NULL;
954
955         if (*lp == 0) {
956             /* The end of the line */
957             osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
958             cm_DirReleasePage(op, &hashbufp, FALSE);
959             return ENOENT;
960         }
961
962         code = cm_DirGetBlob(op,
963                              (u_short) ntohs(*lp),
964                              &itembufp, &tp);
965
966         if (code != 0) {
967             cm_DirReleasePage(op, &hashbufp, FALSE);
968             return code;
969         }
970     }
971 }
972
973 /* Begin a sequence of directory operations.  
974  * Called with scp->rw unlocked.
975  */
976 long
977 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
978               afs_uint32 lockType, cm_dirOp_t * op)
979 {
980     long code;
981     int i, mxheld = 0, haveWrite = 0;
982
983     osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
984              op, scp, userp);
985
986     memset(op, 0, sizeof(*op));
987
988     cm_HoldSCache(scp);
989     op->scp = scp;
990     cm_HoldUser(userp);
991     op->userp = userp;
992     op->req = *reqp;            /* copy the values from the input */
993
994     op->dirtyBufCount = 0;
995     op->nBuffers = 0;
996
997     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
998         op->buffers[i].flags = 0;
999     }
1000
1001     if (lockType == CM_DIRLOCK_WRITE) {
1002         lock_ObtainWrite(&scp->dirlock);
1003         haveWrite = 1;
1004     } else { 
1005         lock_ObtainRead(&scp->dirlock);
1006         haveWrite = 0;
1007     }
1008     lock_ObtainWrite(&scp->rw);
1009     mxheld = 1;
1010     code = cm_DirCheckStatus(op, 1);
1011     if (code == 0) {
1012         op->length = scp->length;
1013         op->newLength = op->length;
1014         op->dataVersion = scp->dataVersion;
1015         op->newDataVersion = op->dataVersion;
1016
1017 #ifdef USE_BPLUS
1018         if (!cm_BPlusTrees ||
1019             (scp->dirBplus &&
1020              scp->dirDataVersion == scp->dataVersion)) 
1021         {
1022             /* we know that haveWrite matches lockType at this point */
1023             switch (lockType) {
1024             case CM_DIRLOCK_NONE:
1025                 if (haveWrite)
1026                     lock_ReleaseWrite(&scp->dirlock);
1027                 else
1028                     lock_ReleaseRead(&scp->dirlock);
1029                 break;
1030             case CM_DIRLOCK_READ:
1031                 osi_assert(!haveWrite);
1032                 break;
1033             case CM_DIRLOCK_WRITE:
1034             default:
1035                 osi_assert(haveWrite);
1036             }
1037         } else {
1038             if (!(scp->dirBplus && 
1039                   scp->dirDataVersion == scp->dataVersion)) 
1040             {
1041               repeat:
1042                 if (!haveWrite) {
1043                     if (mxheld) {
1044                         lock_ReleaseWrite(&scp->rw);
1045                         mxheld = 0;
1046                     }
1047                     lock_ConvertRToW(&scp->dirlock);
1048                     haveWrite = 1;
1049                 }
1050                 if (!mxheld) {
1051                     lock_ObtainWrite(&scp->rw);
1052                     mxheld = 1;
1053                 }
1054                 if (scp->dirBplus && 
1055                      scp->dirDataVersion != scp->dataVersion)
1056                 {
1057                     bplus_dv_error++;
1058                     bplus_free_tree++;
1059                     freeBtree(scp->dirBplus);
1060                     scp->dirBplus = NULL;
1061                     scp->dirDataVersion = -1;
1062                 }
1063
1064                 if (!scp->dirBplus) {
1065                     if (mxheld) {
1066                         lock_ReleaseWrite(&scp->rw);
1067                         mxheld = 0;
1068                     }
1069                     cm_BPlusDirBuildTree(scp, userp, reqp);
1070                     if (!mxheld) {
1071                         lock_ObtainWrite(&scp->rw);
1072                         mxheld = 1;
1073                     }
1074                     if (op->dataVersion != scp->dataVersion) {
1075                         /* We lost the race, therefore we must update the
1076                          * dirop state and retry to build the tree.
1077                          */
1078                         op->length = scp->length;
1079                         op->newLength = op->length;
1080                         op->dataVersion = scp->dataVersion;
1081                         op->newDataVersion = op->dataVersion;
1082                         goto repeat;
1083                     }
1084
1085                     if (scp->dirBplus)
1086                         scp->dirDataVersion = scp->dataVersion;
1087                 }
1088             }
1089
1090             switch (lockType) {
1091             case CM_DIRLOCK_NONE:
1092                 lock_ReleaseWrite(&scp->dirlock);
1093                 break;
1094             case CM_DIRLOCK_READ:
1095                 lock_ConvertWToR(&scp->dirlock);
1096                 break;
1097             case CM_DIRLOCK_WRITE:
1098             default:
1099                 /* got it already */;
1100             }
1101             haveWrite = 0;
1102         }
1103 #else
1104         /* we know that haveWrite matches lockType at this point */
1105         switch (lockType) {
1106         case CM_DIRLOCK_NONE:
1107             if (haveWrite)
1108                 lock_ReleaseWrite(&scp->dirlock);
1109             else
1110                 lock_ReleaseRead(&scp->dirlock);
1111             break;
1112         case CM_DIRLOCK_READ:
1113             osi_assert(!haveWrite);
1114             break;
1115         case CM_DIRLOCK_WRITE:
1116         default:
1117             osi_assert(haveWrite);
1118         }
1119 #endif
1120         op->lockType = lockType;
1121         if (mxheld)
1122             lock_ReleaseWrite(&scp->rw);
1123     } else {
1124         if (haveWrite)
1125             lock_ReleaseWrite(&scp->dirlock);
1126         else
1127             lock_ReleaseRead(&scp->dirlock);
1128         if (mxheld)
1129             lock_ReleaseWrite(&scp->rw);
1130         cm_EndDirOp(op);
1131     }
1132
1133     return code;
1134 }
1135
1136 /* Check if it is safe for us to perform local directory updates.
1137    Called with op->scp->rw unlocked. */
1138 int
1139 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1140 {
1141     long code;
1142     int  rc = 0;
1143
1144     if (op->scp == NULL)
1145         return 0;
1146
1147     lock_ObtainWrite(&op->scp->rw);
1148     code = cm_DirCheckStatus(op, 1);
1149
1150     if (code == 0 &&
1151         op->dataVersion == op->scp->dataVersion - 1) {
1152         /* only one set of changes happened between cm_BeginDirOp()
1153            and this function.  It is safe for us to perform local
1154            changes. */
1155         op->newDataVersion = op->scp->dataVersion;
1156         op->newLength = op->scp->serverLength;
1157
1158         rc = 1;
1159     }
1160     lock_ReleaseWrite(&op->scp->rw); 
1161     
1162     if (rc)
1163         osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1164     else
1165         osi_Log3(afsd_logp,
1166                  "cm_CheckDirOpForSingleChange failed.  code=0x%x, old dv=%d, new dv=%d",
1167                  code, op->dataVersion, op->scp->dataVersion);
1168     return rc;
1169 }
1170
1171 /* End a sequence of directory operations.  
1172  * Called with op->scp->rw unlocked.*/
1173 long
1174 cm_EndDirOp(cm_dirOp_t * op)
1175 {
1176     long code = 0;
1177
1178     if (op->scp == NULL)
1179         return 0;
1180
1181     osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1182              op, op->dirtyBufCount);
1183
1184     if (op->dirtyBufCount > 0) {
1185 #ifdef USE_BPLUS
1186         /* update the data version on the B+ tree */
1187         if (op->scp->dirBplus && 
1188              op->scp->dirDataVersion == op->dataVersion) {
1189
1190             switch (op->lockType) {
1191             case CM_DIRLOCK_READ:
1192                 lock_ReleaseRead(&op->scp->dirlock);
1193                 /* fall through ... */
1194             case CM_DIRLOCK_NONE:
1195                 lock_ObtainWrite(&op->scp->dirlock);
1196                 op->lockType = CM_DIRLOCK_WRITE;
1197                 break;
1198             case CM_DIRLOCK_WRITE:
1199             default:
1200                 /* already got it */;
1201             }
1202             op->scp->dirDataVersion = op->newDataVersion;
1203         }
1204 #endif
1205
1206         /* we made changes.  We should go through the list of buffers
1207          * and update the dataVersion for each. */
1208         lock_ObtainWrite(&op->scp->rw);
1209         code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1210         op->scp->flags |= CM_SCACHEFLAG_LOCAL;
1211         lock_ReleaseWrite(&op->scp->rw);
1212     }
1213
1214     switch (op->lockType) {
1215     case CM_DIRLOCK_NONE:
1216         break;
1217     case CM_DIRLOCK_READ:
1218         lock_ReleaseRead(&op->scp->dirlock);
1219         break;
1220     case CM_DIRLOCK_WRITE:
1221     default:
1222         lock_ReleaseWrite(&op->scp->dirlock);
1223     }
1224
1225     if (op->scp)
1226         cm_ReleaseSCache(op->scp);
1227     op->scp = NULL;
1228
1229     if (op->userp)
1230         cm_ReleaseUser(op->userp);
1231     op->userp = 0;
1232
1233     osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1234
1235     return code;
1236 }
1237
1238 /* NOTE: Called without scp->rw and without bufferp->mx */
1239 static long
1240 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1241 {
1242     int i;
1243     long code = 0;
1244
1245     osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1246
1247     if (bufferp == NULL)
1248         return -1;
1249
1250     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1251         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1252             op->buffers[i].bufferp == bufferp) {
1253             break;
1254         }
1255     }
1256
1257     if (i < CM_DIROP_MAXBUFFERS) {
1258         /* we already have this buffer on our list */
1259
1260         op->buffers[i].refcount++;
1261         osi_Log0(afsd_logp,
1262                  "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1263         return 0;
1264     } else {
1265         /* we have to add a new buffer */
1266         osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1267                     "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1268
1269         for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1270             if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1271                 break;
1272         }
1273
1274         osi_assert(i < CM_DIROP_MAXBUFFERS);
1275
1276         lock_ObtainMutex(&bufferp->mx);
1277         lock_ObtainWrite(&op->scp->rw);
1278
1279         /* Make sure we are synchronized. */
1280         osi_assert(op->lockType != CM_DIRLOCK_NONE);
1281
1282         code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1283                          CM_SCACHESYNC_NEEDCALLBACK |
1284                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1285                          CM_SCACHESYNC_BUFLOCKED);
1286
1287         if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1288                 osi_Log2(afsd_logp,
1289                          "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d", 
1290                          bufferp->dataVersion, op->dataVersion);
1291
1292                 cm_SyncOpDone(op->scp, bufferp,
1293                               CM_SCACHESYNC_NEEDCALLBACK |
1294                               (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1295                               CM_SCACHESYNC_BUFLOCKED);
1296             code = CM_ERROR_NOTINCACHE;
1297         }
1298
1299         lock_ReleaseWrite(&op->scp->rw);
1300         lock_ReleaseMutex(&bufferp->mx);
1301
1302         if (code) {
1303             osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer.  code=0x%x",
1304                      code);
1305             return code;
1306         }
1307
1308         buf_Hold(bufferp);
1309         op->buffers[i].bufferp = bufferp;
1310         op->buffers[i].refcount = 1; /* start with one ref */
1311         op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1312
1313         op->nBuffers++;
1314
1315         osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1316
1317         return 0;
1318     }
1319 }
1320
1321 /* Note: Called without op->scp->rw */
1322 static int
1323 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1324 {
1325     int i;
1326
1327     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1328         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1329             LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1330             break;
1331     }
1332
1333     if (i < CM_DIROP_MAXBUFFERS) {
1334         /* found it */
1335         op->buffers[i].refcount++;
1336         buf_Hold(op->buffers[i].bufferp);
1337         *bufferpp = op->buffers[i].bufferp;
1338
1339         osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1340                  offset.HighPart, offset.LowPart);
1341         return 1;
1342     }
1343
1344     osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1345              offset.HighPart, offset.LowPart);
1346     return 0;
1347 }
1348
1349
1350 /* NOTE: called with scp->rw held or not depending on the flags */
1351 static int
1352 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1353 {
1354     int i;
1355
1356     osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1357              op, bufferp, flags);
1358
1359     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1360         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1361             op->buffers[i].bufferp == bufferp)
1362             break;
1363     }
1364
1365     if (i < CM_DIROP_MAXBUFFERS) {
1366
1367         if (flags & DIROP_MODIFIED)
1368             op->dirtyBufCount++;
1369
1370         osi_assert(op->buffers[i].refcount > 0);
1371         op->buffers[i].refcount --;
1372
1373         if (op->buffers[i].refcount == 0) {
1374             /* this was the last reference we had */
1375
1376             osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1377
1378             /* if this buffer was modified, then we update the data
1379                version of the buffer with the data version of the
1380                scp. */
1381             if (!(flags & DIROP_SCPLOCKED)) {
1382                 lock_ObtainWrite(&op->scp->rw);
1383             }
1384
1385             /* first make sure that the buffer is idle.  It should
1386                have been idle all along. */
1387             osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1388                                             CM_BUF_CMSTORING)) == 0,
1389                         "Buffer is not idle while performing dirOp");
1390
1391             cm_SyncOpDone(op->scp, bufferp,
1392                           CM_SCACHESYNC_NEEDCALLBACK |
1393                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1394
1395 #ifdef DEBUG
1396             osi_assert(bufferp->dataVersion == op->dataVersion);
1397 #endif
1398
1399             lock_ReleaseWrite(&op->scp->rw);
1400
1401             lock_ObtainMutex(&bufferp->mx);
1402
1403             if (flags & DIROP_SCPLOCKED) {
1404                 lock_ObtainWrite(&op->scp->rw);
1405             }
1406
1407             if (flags & DIROP_MODIFIED) {
1408                 /* We don't update the dataversion here.  Instead we
1409                    wait until the dirOp is completed and then flip the
1410                    dataversion on all the buffers in one go.
1411                    Otherwise we won't know if the dataversion is
1412                    current because it was fetched from the server or
1413                    because we touched it during the dirOp. */
1414
1415                 if (bufferp->userp != op->userp) {
1416                     if (bufferp->userp != NULL)
1417                         cm_ReleaseUser(bufferp->userp);
1418                     cm_HoldUser(op->userp);
1419                     bufferp->userp = op->userp;
1420                 }
1421             }
1422
1423             lock_ReleaseMutex(&bufferp->mx);
1424
1425             op->buffers[i].bufferp = NULL;
1426             buf_Release(bufferp);
1427             op->buffers[i].flags = 0;
1428
1429             op->nBuffers--;
1430
1431             return 1;
1432         } else {
1433             /* we have other references to this buffer. so we have to
1434                let it be */
1435             return 0;
1436         }
1437
1438     } else {
1439         osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1440         osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1441         return -1;
1442     }
1443 }
1444
1445 /* Check if we have current status and a callback for the given scp.
1446    This should be called before cm_DirGetPage() is called per scp.
1447
1448    On entry:
1449      scp->rw locked state indicated by parameter
1450
1451    On exit:
1452      scp->rw same state as upon entry
1453
1454    During:
1455      scp->rw may be released
1456  */
1457 static long
1458 cm_DirCheckStatus(cm_dirOp_t * op, int scp_locked)
1459 {
1460     long code;
1461
1462     if (!scp_locked)
1463         lock_ObtainWrite(&op->scp->rw);
1464     code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1465                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1466     if (!scp_locked)
1467         lock_ReleaseWrite(&op->scp->rw);
1468
1469     osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1470              op, code);
1471
1472     return code;
1473 }
1474
1475 /* Attempt to prefetch all the buffers for this operation.
1476
1477    Called with scp->rw unlocked
1478  */
1479 static long
1480 cm_DirPrefetchBuffers(cm_dirOp_t * op)
1481 {
1482     long code = 0;
1483     osi_hyper_t offset;
1484     cm_buf_t *bufferp = NULL;
1485
1486     osi_Log1(afsd_logp, "cm_DirPrefetchBuffers for op 0x%p", op);
1487
1488     /* prefetching is only done on read operations where we don't
1489        expect the data version to change. */
1490     if (op->dataVersion != op->newDataVersion) {
1491         osi_Log0(afsd_logp, "Skipping prefetch for write operation.");
1492         return CM_ERROR_INVAL;
1493     }
1494
1495     lock_ObtainWrite(&op->scp->rw);
1496
1497     /* When we are prefetching a file, we first flush out any of its
1498        contents just to make sure that we don't end up with buffers
1499        that was locally modified. */
1500
1501     if (op->scp->flags & CM_SCACHEFLAG_LOCAL) {
1502         lock_ReleaseWrite(&op->scp->rw);
1503         code = cm_FlushFile(op->scp, op->userp, &op->req);
1504         if (code != 0)
1505             return code;
1506         lock_ObtainWrite(&op->scp->rw);
1507     }
1508
1509     offset = ConvertLongToLargeInteger(0);
1510     while (LargeIntegerLessThan(offset, op->scp->length)) {
1511         osi_Log2(afsd_logp, "Trying prefetch for offset %08x:%08x",
1512                  offset.HighPart, offset.LowPart);
1513         lock_ReleaseWrite(&op->scp->rw);
1514
1515         code = buf_Get(op->scp, &offset, &bufferp);
1516
1517         lock_ObtainWrite(&op->scp->rw);
1518
1519         if (code)
1520             break;
1521
1522         while (1) {
1523
1524             code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1525                              CM_SCACHESYNC_NEEDCALLBACK |
1526                              (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1527
1528             if (code)
1529                 break;
1530
1531             cm_SyncOpDone(op->scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK |
1532                           (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1533
1534             if (cm_HaveBuffer(op->scp, bufferp, 0))
1535                 break;
1536
1537             code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1538             if (code)
1539                 break;
1540         }
1541
1542         if (code)
1543             break;
1544
1545         if (bufferp) {
1546             buf_Release(bufferp);
1547             bufferp = NULL;
1548         }
1549
1550         offset = LargeIntegerAdd(offset, ConvertLongToLargeInteger(cm_data.buf_blockSize));
1551     }
1552
1553  done:
1554     lock_ReleaseWrite(&op->scp->rw);
1555
1556     osi_Log1(afsd_logp, "cm_DirPrefetchBuffers returning code 0x%x", code);
1557
1558     return code;
1559 }
1560
1561 /* Release a directory buffer that was obtained via a call to
1562    cm_DirGetPage() or any other function that returns a locked, held,
1563    directory page buffer.
1564
1565    Called with scp->rw unlocked
1566  */
1567 static long
1568 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1569 {
1570     long code = 0;
1571
1572     if (!*bufferpp)
1573         return EINVAL;
1574
1575     cm_DirOpDelBuffer(op, *bufferpp,
1576                       ((modified ? DIROP_MODIFIED : 0)));
1577     buf_Release(*bufferpp);
1578     *bufferpp = NULL;
1579
1580     return code;
1581 }
1582
1583 /*
1584    Returns the index'th directory page from scp.  The userp and reqp
1585    will be used to fetch the buffer from the fileserver if necessary.
1586    If the call is successful, a locked and held cm_buf_t is returned
1587    via buferpp and a pointer to the directory page is returned via
1588    datapp.
1589
1590    The returned buffer should be released via a call to
1591    cm_DirReleasePage() or by passing it into a subsequent call to
1592    cm_DirGetPage() for the *same* scp.
1593
1594    If a *locked* buffer for the *same* scp is passed in via bufferpp
1595    to the function, it will check if the requested directory page is
1596    located in the specified buffer.  If not, the buffer will be
1597    released and a new buffer returned that contains the requested
1598    page.
1599
1600    If the specified page exists beyond the EOF for the scp, a new
1601    buffer will be allocated only if create is set to TRUE.
1602
1603    Note: If a buffer is specified on entry via bufferpp, it is assumed
1604    that the buffer is unmodified.  If the buffer is modified, it
1605    should be released via cm_DirReleasePage().
1606
1607    On entry:
1608      scp->rw unlocked.
1609      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1610
1611    On exit:
1612      scp->rw unlocked
1613      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1614
1615    During:
1616      scp->rw will be obtained and released
1617
1618  */
1619 static long
1620 cm_DirGetPage(cm_dirOp_t * op,
1621               long index, cm_buf_t ** bufferpp, void ** datapp)
1622 {
1623     osi_hyper_t pageOffset;     /* offset of the dir page from the
1624                                    start of the directory */
1625     osi_hyper_t bufferOffset;   /* offset of the buffer from the start
1626                                    of the directory */
1627     osi_hyper_t thyper;
1628
1629     cm_buf_t * bufferp = NULL;
1630
1631     void * datap = NULL;
1632
1633     long code = 0;
1634
1635     osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1636
1637     pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1638     bufferOffset.HighPart = pageOffset.HighPart;
1639     bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1640
1641     bufferp = *bufferpp;
1642     if (bufferp != NULL) {
1643         osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1644
1645         thyper = bufferp->offset;
1646     }
1647
1648     if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1649         /* wrong buffer */
1650
1651         if (bufferp) {
1652             buf_Release(bufferp);
1653             cm_DirOpDelBuffer(op, bufferp, 0);
1654             bufferp = NULL;
1655         }
1656
1657         /* first check if we are already working with the buffer */
1658         if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1659             code = 0;
1660             goto _has_buffer;
1661         }
1662
1663         code = buf_Get(op->scp, &bufferOffset, &bufferp);
1664         if (code) {
1665             osi_Log1(afsd_logp, "    buf_Get returned code 0x%x", code);
1666             bufferp = NULL;
1667             goto _exit;
1668         }
1669
1670         osi_assert(bufferp != NULL);
1671
1672         /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1673         code = cm_DirOpAddBuffer(op, bufferp);
1674
1675         if (code != 0) {
1676             /* for some reason, the buffer was rejected.  We can't use
1677                this buffer, and since this is the only buffer we can
1678                potentially use, there's no recourse.*/
1679             buf_Release(bufferp);
1680             bufferp = NULL;
1681             goto _exit;
1682         }
1683     }
1684
1685  _has_buffer:
1686
1687     /* now to figure out where the data is */
1688     thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1689
1690     osi_assert(thyper.HighPart == 0);
1691     osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1692                cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1693
1694     datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1695
1696     if (datapp)
1697         *datapp = datap;
1698
1699     /* also, if we are writing past EOF, we should make a note of the
1700        new length */
1701     thyper = LargeIntegerAdd(pageOffset,
1702                              ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1703     if (LargeIntegerLessThan(op->newLength, thyper)) {
1704         op->newLength = thyper;
1705     }
1706
1707  _exit:
1708
1709     *bufferpp = bufferp;
1710
1711     osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1712
1713     return code;
1714 }
1715
1716
1717 void
1718 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1719 {
1720     size_t len;
1721     cm_dirEntryList_t * entry;
1722
1723     len = strlen(namep);
1724     len += sizeof(cm_dirEntryList_t);
1725
1726     entry = malloc(len);
1727     if (entry) {
1728         entry->nextp = *list;
1729         strcpy(entry->name, namep);
1730         *list = entry;
1731     }
1732 }
1733
1734 void
1735 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1736 {
1737     cm_dirEntryList_t * entry;
1738     cm_dirEntryList_t * next;
1739
1740     for (entry = *list; entry; entry = next) {
1741         next = entry->nextp;
1742         free(entry);
1743     }
1744
1745     *list = NULL;
1746 }
1747