windows-osi-convertRToW-and-logging-optimizations-20080227
[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);
129     if (lenp) *lenp = i;
130     return 1 + ((i+16) >> 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_ConvertRToW(&scp->dirlock);
1034                     haveWrite = 1;
1035                 }
1036                 if (!mxheld) {
1037                     lock_ObtainMutex(&scp->mx);
1038                     mxheld = 1;
1039                 }
1040                 if (scp->dirBplus && 
1041                      scp->dirDataVersion != scp->dataVersion)
1042                 {
1043                     bplus_dv_error++;
1044                     bplus_free_tree++;
1045                     freeBtree(scp->dirBplus);
1046                     scp->dirBplus = NULL;
1047                     scp->dirDataVersion = -1;
1048                 }
1049
1050                 if (!scp->dirBplus) {
1051                     if (mxheld) {
1052                         lock_ReleaseMutex(&scp->mx);
1053                         mxheld = 0;
1054                     }
1055                     cm_BPlusDirBuildTree(scp, userp, reqp);
1056                     if (!mxheld) {
1057                         lock_ObtainMutex(&scp->mx);
1058                         mxheld = 1;
1059                     }
1060                     if (op->dataVersion != scp->dataVersion) {
1061                         /* We lost the race, therefore we must update the
1062                          * dirop state and retry to build the tree.
1063                          */
1064                         op->length = scp->length;
1065                         op->newLength = op->length;
1066                         op->dataVersion = scp->dataVersion;
1067                         op->newDataVersion = op->dataVersion;
1068                         goto repeat;
1069                     }
1070
1071                     if (scp->dirBplus)
1072                         scp->dirDataVersion = scp->dataVersion;
1073                 }
1074             }
1075
1076             switch (lockType) {
1077             case CM_DIRLOCK_NONE:
1078                 lock_ReleaseWrite(&scp->dirlock);
1079                 break;
1080             case CM_DIRLOCK_READ:
1081                 lock_ConvertWToR(&scp->dirlock);
1082                 break;
1083             case CM_DIRLOCK_WRITE:
1084             default:
1085                 /* got it already */;
1086             }
1087             haveWrite = 0;
1088         }
1089 #else
1090         /* we know that haveWrite matches lockType at this point */
1091         switch (lockType) {
1092         case CM_DIRLOCK_NONE:
1093             if (haveWrite)
1094                 lock_ReleaseWrite(&scp->dirlock);
1095             else
1096                 lock_ReleaseRead(&scp->dirlock);
1097             break;
1098         case CM_DIRLOCK_READ:
1099             osi_assert(!haveWrite);
1100             break;
1101         case CM_DIRLOCK_WRITE:
1102         default:
1103             osi_assert(haveWrite);
1104         }
1105 #endif
1106         op->lockType = lockType;
1107         if (mxheld)
1108             lock_ReleaseMutex(&scp->mx);
1109     } else {
1110         if (haveWrite)
1111             lock_ReleaseWrite(&scp->dirlock);
1112         else
1113             lock_ReleaseRead(&scp->dirlock);
1114         if (mxheld)
1115             lock_ReleaseMutex(&scp->mx);
1116         cm_EndDirOp(op);
1117     }
1118
1119     return code;
1120 }
1121
1122 /* Check if it is safe for us to perform local directory updates.
1123    Called with scp->mx unlocked. */
1124 int
1125 cm_CheckDirOpForSingleChange(cm_dirOp_t * op)
1126 {
1127     long code;
1128     int  rc = 0;
1129
1130     if (op->scp == NULL)
1131         return 0;
1132
1133     lock_ObtainMutex(&op->scp->mx);
1134     code = cm_DirCheckStatus(op, 1);
1135
1136     if (code == 0 &&
1137         op->dataVersion == op->scp->dataVersion - 1) {
1138         /* only one set of changes happened between cm_BeginDirOp()
1139            and this function.  It is safe for us to perform local
1140            changes. */
1141         op->newDataVersion = op->scp->dataVersion;
1142         op->newLength = op->scp->serverLength;
1143
1144         rc = 1;
1145     }
1146     lock_ReleaseMutex(&op->scp->mx); 
1147     
1148     if (rc)
1149         osi_Log0(afsd_logp, "cm_CheckDirOpForSingleChange succeeded");
1150     else
1151         osi_Log3(afsd_logp,
1152                  "cm_CheckDirOpForSingleChange failed.  code=0x%x, old dv=%I64d, new dv=%I64d",
1153                  code, op->dataVersion, op->scp->dataVersion);
1154     return rc;
1155 }
1156
1157 /* End a sequence of directory operations.  
1158  * Called with op->scp->mx unlocked.*/
1159 long
1160 cm_EndDirOp(cm_dirOp_t * op)
1161 {
1162     long code = 0;
1163
1164     if (op->scp == NULL)
1165         return 0;
1166
1167     osi_Log2(afsd_logp, "Ending dirOp 0x%p with %d dirty buffer releases",
1168              op, op->dirtyBufCount);
1169
1170     if (op->dirtyBufCount > 0) {
1171 #ifdef USE_BPLUS
1172         /* update the data version on the B+ tree */
1173         if (op->scp->dirBplus && 
1174              op->scp->dirDataVersion == op->dataVersion) {
1175
1176             switch (op->lockType) {
1177             case CM_DIRLOCK_READ:
1178                 lock_ReleaseRead(&op->scp->dirlock);
1179                 /* fall through ... */
1180             case CM_DIRLOCK_NONE:
1181                 lock_ObtainWrite(&op->scp->dirlock);
1182                 op->lockType = CM_DIRLOCK_WRITE;
1183                 break;
1184             case CM_DIRLOCK_WRITE:
1185             default:
1186                 /* already got it */;
1187             }
1188             op->scp->dirDataVersion = op->newDataVersion;
1189         }
1190 #endif
1191
1192         /* we made changes.  We should go through the list of buffers
1193          * and update the dataVersion for each. */
1194         lock_ObtainMutex(&op->scp->mx);
1195         code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1196         lock_ReleaseMutex(&op->scp->mx);
1197     }
1198
1199     switch (op->lockType) {
1200     case CM_DIRLOCK_NONE:
1201         break;
1202     case CM_DIRLOCK_READ:
1203         lock_ReleaseRead(&op->scp->dirlock);
1204         break;
1205     case CM_DIRLOCK_WRITE:
1206     default:
1207         lock_ReleaseWrite(&op->scp->dirlock);
1208     }
1209
1210     if (op->scp)
1211         cm_ReleaseSCache(op->scp);
1212     op->scp = NULL;
1213
1214     if (op->userp)
1215         cm_ReleaseUser(op->userp);
1216     op->userp = 0;
1217
1218     osi_assertx(op->nBuffers == 0, "Buffer leak after dirOp termination");
1219
1220     return code;
1221 }
1222
1223 /* NOTE: Called without scp->mx and without bufferp->mx */
1224 static long
1225 cm_DirOpAddBuffer(cm_dirOp_t * op, cm_buf_t * bufferp)
1226 {
1227     int i;
1228     long code = 0;
1229
1230     osi_Log2(afsd_logp, "cm_DirOpAddBuffer for op 0x%p, buffer %p", op, bufferp);
1231
1232     if (bufferp == NULL)
1233         return -1;
1234
1235     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1236         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1237             op->buffers[i].bufferp == bufferp) {
1238             break;
1239         }
1240     }
1241
1242     if (i < CM_DIROP_MAXBUFFERS) {
1243         /* we already have this buffer on our list */
1244
1245         op->buffers[i].refcount++;
1246         osi_Log0(afsd_logp,
1247                  "cm_DirOpAddBuffer: the buffer is already listed for the dirOp");
1248         return 0;
1249     } else {
1250         /* we have to add a new buffer */
1251         osi_assertx(op->nBuffers < CM_DIROP_MAXBUFFERS - 1,
1252                     "DirOp has exceeded CM_DIROP_MAXBUFFERS buffers");
1253
1254         for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1255             if (!(op->buffers[i].flags & CM_DIROPBUFF_INUSE))
1256                 break;
1257         }
1258
1259         osi_assert(i < CM_DIROP_MAXBUFFERS);
1260
1261         lock_ObtainMutex(&bufferp->mx);
1262         lock_ObtainMutex(&op->scp->mx);
1263
1264         /* Make sure we are synchronized. */
1265         osi_assert(op->lockType != CM_DIRLOCK_NONE);
1266
1267         code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1268                          CM_SCACHESYNC_NEEDCALLBACK |
1269                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1270                          CM_SCACHESYNC_BUFLOCKED);
1271
1272         if (code == 0 && bufferp->dataVersion != op->dataVersion) {
1273             osi_Log2(afsd_logp, "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %I64d. needs %I64d", 
1274                      bufferp->dataVersion, op->dataVersion);
1275
1276             cm_SyncOpDone(op->scp, bufferp,
1277                           CM_SCACHESYNC_NEEDCALLBACK |
1278                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1279                           CM_SCACHESYNC_BUFLOCKED);
1280
1281             code = CM_ERROR_INVAL;
1282         }
1283
1284         lock_ReleaseMutex(&op->scp->mx);
1285         lock_ReleaseMutex(&bufferp->mx);
1286
1287         if (code) {
1288             osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer.  code=0x%x",
1289                      code);
1290             return code;
1291         }
1292
1293         buf_Hold(bufferp);
1294         op->buffers[i].bufferp = bufferp;
1295         op->buffers[i].refcount = 1; /* start with one ref */
1296         op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1297
1298         op->nBuffers++;
1299
1300         osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1301
1302         return 0;
1303     }
1304 }
1305
1306 /* Note: Called without op->scp->mx */
1307 static int
1308 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1309 {
1310     int i;
1311
1312     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1313         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1314             LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1315             break;
1316     }
1317
1318     if (i < CM_DIROP_MAXBUFFERS) {
1319         /* found it */
1320         op->buffers[i].refcount++;
1321         buf_Hold(op->buffers[i].bufferp);
1322         *bufferpp = op->buffers[i].bufferp;
1323
1324         osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1325                  offset.HighPart, offset.LowPart);
1326         return 1;
1327     }
1328
1329     osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1330              offset.HighPart, offset.LowPart);
1331     return 0;
1332 }
1333
1334
1335 /* NOTE: called with scp->mx held or not depending on the flags */
1336 static int
1337 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1338 {
1339     int i;
1340
1341     osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1342              op, bufferp, flags);
1343
1344     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1345         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1346             op->buffers[i].bufferp == bufferp)
1347             break;
1348     }
1349
1350     if (i < CM_DIROP_MAXBUFFERS) {
1351
1352         if (flags & DIROP_MODIFIED)
1353             op->dirtyBufCount++;
1354
1355         osi_assert(op->buffers[i].refcount > 0);
1356         op->buffers[i].refcount --;
1357
1358         if (op->buffers[i].refcount == 0) {
1359             /* this was the last reference we had */
1360
1361             osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1362
1363             /* if this buffer was modified, then we update the data
1364                version of the buffer with the data version of the
1365                scp. */
1366             if (!(flags & DIROP_SCPLOCKED)) {
1367                 lock_ObtainMutex(&op->scp->mx);
1368             }
1369
1370             /* first make sure that the buffer is idle.  It should
1371                have been idle all along. */
1372             osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1373                                             CM_BUF_CMSTORING)) == 0,
1374                         "Buffer is not idle while performing dirOp");
1375
1376             cm_SyncOpDone(op->scp, bufferp,
1377                           CM_SCACHESYNC_NEEDCALLBACK |
1378                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1379
1380 #ifdef DEBUG
1381             osi_assert(bufferp->dataVersion == op->dataVersion);
1382 #endif
1383
1384             lock_ReleaseMutex(&op->scp->mx);
1385
1386             lock_ObtainMutex(&bufferp->mx);
1387
1388             if (flags & DIROP_SCPLOCKED) {
1389                 lock_ObtainMutex(&op->scp->mx);
1390             }
1391
1392             if (flags & DIROP_MODIFIED) {
1393                 /* We don't update the dataversion here.  Instead we
1394                    wait until the dirOp is completed and then flip the
1395                    dataversion on all the buffers in one go.
1396                    Otherwise we won't know if the dataversion is
1397                    current because it was fetched from the server or
1398                    because we touched it during the dirOp. */
1399
1400                 if (bufferp->userp != op->userp) {
1401                     if (bufferp->userp != NULL)
1402                         cm_ReleaseUser(bufferp->userp);
1403                     cm_HoldUser(op->userp);
1404                     bufferp->userp = op->userp;
1405                 }
1406             }
1407
1408             lock_ReleaseMutex(&bufferp->mx);
1409
1410             op->buffers[i].bufferp = NULL;
1411             buf_Release(bufferp);
1412             op->buffers[i].flags = 0;
1413
1414             op->nBuffers--;
1415
1416             return 1;
1417         } else {
1418             /* we have other references to this buffer. so we have to
1419                let it be */
1420             return 0;
1421         }
1422
1423     } else {
1424         osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1425         osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1426         return -1;
1427     }
1428 }
1429
1430 /* Check if we have current status and a callback for the given scp.
1431    This should be called before cm_DirGetPage() is called per scp.
1432
1433    On entry:
1434      scp->mx locked state indicated by parameter
1435
1436    On exit:
1437      scp->mx same state as upon entry
1438
1439    During:
1440      scp->mx may be released
1441  */
1442 static long
1443 cm_DirCheckStatus(cm_dirOp_t * op, afs_uint32 locked)
1444 {
1445     long code;
1446
1447     if (!locked)
1448         lock_ObtainMutex(&op->scp->mx);
1449     code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1450                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1451     if (!locked)
1452         lock_ReleaseMutex(&op->scp->mx);
1453
1454     osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1455              op, code);
1456
1457     return code;
1458 }
1459
1460 /* Release a directory buffer that was obtained via a call to
1461    cm_DirGetPage() or any other function that returns a locked, held,
1462    directory page buffer.
1463
1464    Called with scp->mx unlocked
1465  */
1466 static long
1467 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1468 {
1469     long code = 0;
1470
1471     if (!*bufferpp)
1472         return EINVAL;
1473
1474     cm_DirOpDelBuffer(op, *bufferpp,
1475                       ((modified ? DIROP_MODIFIED : 0)));
1476     buf_Release(*bufferpp);
1477     *bufferpp = NULL;
1478
1479     return code;
1480 }
1481
1482 /*
1483    Returns the index'th directory page from scp.  The userp and reqp
1484    will be used to fetch the buffer from the fileserver if necessary.
1485    If the call is successful, a locked and held cm_buf_t is returned
1486    via buferpp and a pointer to the directory page is returned via
1487    datapp.
1488
1489    The returned buffer should be released via a call to
1490    cm_DirReleasePage() or by passing it into a subsequent call to
1491    cm_DirGetPage() for the *same* scp.
1492
1493    If a *locked* buffer for the *same* scp is passed in via bufferpp
1494    to the function, it will check if the requested directory page is
1495    located in the specified buffer.  If not, the buffer will be
1496    released and a new buffer returned that contains the requested
1497    page.
1498
1499    Note: If a buffer is specified on entry via bufferpp, it is assumed
1500    that the buffer is unmodified.  If the buffer is modified, it
1501    should be released via cm_DirReleasePage().
1502
1503    On entry:
1504      scp->mx unlocked.
1505      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1506
1507    On exit:
1508      scp->mx unlocked
1509      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1510
1511    During:
1512      scp->mx will be obtained and released
1513
1514  */
1515 static long
1516 cm_DirGetPage(cm_dirOp_t * op,
1517               long index, cm_buf_t ** bufferpp, void ** datapp)
1518 {
1519     osi_hyper_t pageOffset;     /* offset of the dir page from the
1520                                    start of the directory */
1521     osi_hyper_t bufferOffset;   /* offset of the buffer from the start
1522                                    of the directory */
1523     osi_hyper_t thyper;
1524
1525     cm_buf_t * bufferp = NULL;
1526
1527     void * datap = NULL;
1528
1529     long code = 0;
1530
1531     osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1532
1533     pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1534     bufferOffset.HighPart = pageOffset.HighPart;
1535     bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1536
1537     bufferp = *bufferpp;
1538     if (bufferp != NULL) {
1539         osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1540
1541         thyper = bufferp->offset;
1542     }
1543
1544     if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1545         /* wrong buffer */
1546
1547         if (bufferp) {
1548             buf_Release(bufferp);
1549             cm_DirOpDelBuffer(op, bufferp, 0);
1550             bufferp = NULL;
1551         }
1552
1553         /* first check if we are already working with the buffer */
1554         if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1555             code = 0;
1556             goto _has_buffer;
1557         }
1558
1559         code = buf_Get(op->scp, &bufferOffset, &bufferp);
1560         if (code) {
1561             osi_Log1(afsd_logp, "    buf_Get returned code 0x%x", code);
1562             bufferp = NULL;
1563             goto _exit;
1564         }
1565
1566         osi_assert(bufferp != NULL);
1567
1568         /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1569         code = cm_DirOpAddBuffer(op, bufferp);
1570
1571         if (code != 0) {
1572             /* for some reason, the buffer was rejected.  We can't use
1573                this buffer, and since this is the only buffer we can
1574                potentially use, there's no recourse.*/
1575             buf_Release(bufferp);
1576             bufferp = NULL;
1577             goto _exit;
1578         }
1579
1580 #if 0
1581         /* The code below is for making sure the buffer contains
1582            current data.  This is a bad idea, since the whole point of
1583            doing directory updates locally is to avoid fetching all
1584            the data from the server. */
1585         while (1) {
1586             lock_ObtainMutex(&op->scp->mx);
1587             code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1588                              CM_SCACHESYNC_NEEDCALLBACK |
1589                              CM_SCACHESYNC_READ |
1590                              CM_SCACHESYNC_BUFLOCKED);
1591
1592             if (code) {
1593                 lock_ReleaseMutex(&op->scp->mx);
1594                 break;
1595             }
1596
1597             cm_SyncOpDone(op->scp, bufferp,
1598                           CM_SCACHESYNC_NEEDCALLBACK |
1599                           CM_SCACHESYNC_READ |
1600                           CM_SCACHESYNC_BUFLOCKED);
1601
1602             if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1603                 lock_ReleaseMutex(&op->scp->mx);
1604                 break;
1605             }
1606
1607             lock_ReleaseMutex(&bufferp->mx);
1608             code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1609             lock_ReleaseMutex(&op->scp->mx);
1610             lock_ObtainMutex(&bufferp->mx);
1611
1612             if (code)
1613                 break;
1614         }
1615
1616         if (code) {
1617             cm_DirOpDelBuffer(op, bufferp, 0);
1618             buf_Release(bufferp);
1619             bufferp = NULL;
1620             goto _exit;
1621         }
1622 #endif
1623     }
1624
1625  _has_buffer:
1626
1627     /* now to figure out where the data is */
1628     thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1629
1630     osi_assert(thyper.HighPart == 0);
1631     osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1632                cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1633
1634     datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1635
1636     if (datapp)
1637         *datapp = datap;
1638
1639     /* also, if we are writing past EOF, we should make a note of the
1640        new length */
1641     thyper = LargeIntegerAdd(pageOffset,
1642                              ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1643     if (LargeIntegerLessThan(op->newLength, thyper)) {
1644         op->newLength = thyper;
1645     }
1646
1647  _exit:
1648
1649     *bufferpp = bufferp;
1650
1651     osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1652
1653     return code;
1654 }
1655
1656
1657 void
1658 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1659 {
1660     size_t len;
1661     cm_dirEntryList_t * entry;
1662
1663     len = strlen(namep);
1664     len += sizeof(cm_dirEntryList_t);
1665
1666     entry = malloc(len);
1667     if (entry) {
1668         entry->nextp = *list;
1669         strcpy(entry->name, namep);
1670         *list = entry;
1671     }
1672 }
1673
1674 void
1675 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1676 {
1677     cm_dirEntryList_t * entry;
1678     cm_dirEntryList_t * next;
1679
1680     for (entry = *list; entry; entry = next) {
1681         next = entry->nextp;
1682         free(entry);
1683     }
1684
1685     *list = NULL;
1686 }
1687