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