d6f34395dc7999166fabc11845c40bf8f8508e8b
[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, size_t *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->rw is unlocked
138
139    On exit:
140        op->scp->rw 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->rw is locked
236
237    On exit:
238        op->scp->rw 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->rw is unlocked
273
274    On exit:
275        op->scp->rw 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->rw */
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->rw
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->rw
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->rw 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
576 /* Look up a file name in directory.
577
578    On entry:
579        op->scp->rw is unlocked
580
581    On exit:
582        op->scp->rw is unlocked
583
584    None of the directory buffers for op->scp should be locked by the
585    calling thread.
586 */
587 int
588 cm_DirLookup(cm_dirOp_t * op, char *entry, cm_fid_t * cfid)
589 {
590     cm_dirEntry_t *firstitem = NULL;
591     cm_buf_t      *itembuf = NULL;
592     unsigned short *previtem = NULL;
593     cm_buf_t      *pibuf = NULL;
594     long code;
595     LARGE_INTEGER       start;
596     LARGE_INTEGER       end;
597
598     QueryPerformanceCounter(&start);
599
600     osi_Log2(afsd_logp, "cm_DirLookup for op 0x%p, entry[%s]",
601              op, osi_LogSaveString(afsd_logp, entry));
602
603     code = cm_DirFindItem(op, entry,
604                           &itembuf, &firstitem,
605                           &pibuf, &previtem);
606     if (code != 0) {
607         dir_lookup_misses++;
608         code = ENOENT;
609         goto done;
610     }
611
612     cm_DirReleasePage(op, &pibuf, FALSE);
613
614     cfid->cell = op->scp->fid.cell;
615     cfid->volume = op->scp->fid.volume;
616     cfid->vnode = ntohl(firstitem->fid.vnode);
617     cfid->unique = ntohl(firstitem->fid.unique);
618
619     cm_DirReleasePage(op, &itembuf, FALSE);
620
621     osi_Log2(afsd_logp, "cm_DirLookup returning fid[%d,%d]",
622              cfid->vnode, cfid->unique);
623
624     dir_lookup_hits++;
625     code = 0;
626
627   done:
628     QueryPerformanceCounter(&end);
629
630     dir_lookup_time += (end.QuadPart - start.QuadPart);
631
632     return code;
633 }
634
635 /* Look up a file name in directory.
636
637    On entry:
638        op->scp->rw is locked
639
640    On exit:
641        op->scp->rw is locked
642
643    None of the directory buffers for op->scp should be locked by the
644    calling thread.
645 */
646 int
647 cm_DirLookupOffset(cm_dirOp_t * op, char *entry, cm_fid_t *cfid, osi_hyper_t *offsetp)
648 {
649     cm_dirEntry_t *firstitem = NULL;
650     cm_buf_t      *itembuf = NULL;
651     unsigned short *previtem = NULL;
652     cm_buf_t      *pibuf = NULL;
653
654     long code;
655
656     osi_Log2(afsd_logp, "cm_DirLookupOffset for op 0x%p, entry[%s]",
657              op, osi_LogSaveString(afsd_logp, entry));
658
659     code = cm_DirFindItem(op, entry,
660                           &itembuf, &firstitem,
661                           &pibuf, &previtem);
662     if (code != 0)
663         return ENOENT;
664
665     cm_DirReleasePage(op, &pibuf, FALSE);
666
667     cfid->cell = op->scp->fid.cell;
668     cfid->volume = op->scp->fid.volume;
669     cfid->vnode = ntohl(firstitem->fid.vnode);
670     cfid->unique = ntohl(firstitem->fid.unique);
671     if (offsetp) {
672         osi_hyper_t thyper;
673
674         thyper = itembuf->offset;
675         thyper = LargeIntegerAdd(thyper,
676                                  ConvertLongToLargeInteger(((char *) firstitem) - itembuf->datap));
677
678         *offsetp = thyper;
679     }
680
681     cm_DirReleasePage(op, &itembuf, FALSE);
682
683     osi_Log2(afsd_logp, "cm_DirLookupOffset returning fid[%d,%d]",
684              cfid->vnode, cfid->unique);
685     if (offsetp) {
686         osi_Log2(afsd_logp, "               offset [%x:%x]",
687                  offsetp->HighPart, offsetp->LowPart);
688     }
689
690     return 0;
691 }
692
693 /* Apply a function to every directory entry in a directory.
694
695    On entry:
696        op->scp->rw is locked
697
698    On exit:
699        op->scp->rw is locked
700
701    None of the directory buffers for op->scp should be locked by the
702    calling thread.
703
704    The hook function cannot modify or lock any directory buffers.
705  */
706 int
707 cm_DirApply(cm_dirOp_t * op, int (*hookproc) (void *, char *, long, long), void *hook)
708 {
709     /* Enumerate the contents of a directory. */
710     int i;
711     int num;
712
713     cm_dirHeader_t *dhp = NULL;
714     cm_buf_t       *dhpbuf = NULL;
715
716     cm_dirEntry_t  *ep = NULL;
717     cm_buf_t       *epbuf = NULL;
718
719     long code = 0;
720
721     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
722     if (code != 0)
723         return EIO;
724
725     for (i = 0; i < CM_DIR_NHASHENT; i++) {
726         /* For each hash chain, enumerate everyone on the list. */
727         num = ntohs(dhp->hashTable[i]);
728         while (num != 0) {
729             /* Walk down the hash table list. */
730             code = cm_DirGetBlob(op, num, &epbuf, &ep);
731             if (code != 0) {
732                 cm_DirReleasePage(op, &dhpbuf, FALSE);
733                 return code;
734             }
735
736             num = ntohs(ep->next);
737             (*hookproc) (hook, ep->name, ntohl(ep->fid.vnode),
738                          ntohl(ep->fid.unique));
739
740             cm_DirReleasePage(op, &epbuf, FALSE);
741         }
742     }
743     cm_DirReleasePage(op, &dhpbuf, FALSE);
744
745     return 0;
746 }
747
748 /* Check if a directory is empty
749
750    On entry:
751        op->scp->rw is locked
752
753    On exit:
754        op->scp->rw is locked
755
756    None of the directory buffers for op->scp should be locked by the
757    calling thread.
758  */
759 int
760 cm_DirIsEmpty(cm_dirOp_t * op)
761 {
762     /* Enumerate the contents of a directory. */
763     int i;
764     int num;
765
766     cm_dirHeader_t *dhp = NULL;
767     cm_buf_t       *dhpbuf = NULL;
768
769     cm_dirEntry_t  *ep = NULL;
770     cm_buf_t       *epbuf = NULL;
771
772     long code = 0;
773
774     code = cm_DirGetPage(op, 0, &dhpbuf, &dhp);
775     if (code != 0)
776         return 0;
777
778     for (i = 0; i < CM_DIR_NHASHENT; i++) {
779         /* For each hash chain, enumerate everyone on the list. */
780         num = ntohs(dhp->hashTable[i]);
781
782         while (num != 0) {
783             /* Walk down the hash table list. */
784             code = cm_DirGetBlob(op, num, &epbuf, &ep);
785             if (code != 0)
786                 break;
787
788             if (strcmp(ep->name, "..") && strcmp(ep->name, ".")) {
789                 cm_DirReleasePage(op, &epbuf, FALSE);
790                 cm_DirReleasePage(op, &dhpbuf, FALSE);
791                 return 1;
792             }
793             num = ntohs(ep->next);
794             cm_DirReleasePage(op, &epbuf, FALSE);
795         }
796     }
797     cm_DirReleasePage(op, &dhpbuf, FALSE);
798     return 0;
799 }
800
801 /* Return a pointer to an entry, given its number.
802
803    On entry:
804      scp->rw locked
805      if *bufferpp != NULL, then *bufferpp->mx is locked
806
807    During:
808      scp->rw may be unlocked
809      *bufferpp may be released
810
811    On exit:
812      scp->rw locked
813      if *bufferpp != NULL, then *bufferpp->mx is locked
814
815      *bufferpp should be released via cm_DirReleasePage() or any other
816      *call that releases a directory buffer.
817 */
818 static long
819 cm_DirGetBlob(cm_dirOp_t * op,
820               unsigned int blobno, cm_buf_t ** bufferpp, cm_dirEntry_t ** blobpp)
821 {
822     unsigned char * ep;
823     long code = 0;
824
825     osi_Log2(afsd_logp, "cm_DirGetBlob for op 0x%p, blobno=%d",
826              op, blobno);
827
828     code = cm_DirGetPage(op, blobno >> CM_DIR_LEPP,
829                          bufferpp, (void **) &ep);
830     if (code != 0)
831         return code;
832
833     *blobpp = (cm_dirEntry_t *) (ep + 32 * (blobno & (CM_DIR_EPP - 1)));
834
835     return code;
836 }       
837
838 int
839 cm_DirHash(char *string)
840 {
841     /* Hash a string to a number between 0 and NHASHENT. */
842     register unsigned char tc;
843     register int hval;
844     register int tval;
845     hval = 0;
846     while ((tc = (*string++))) {
847         hval *= 173;
848         hval += tc;
849     }
850     tval = hval & (CM_DIR_NHASHENT - 1);
851     if (tval == 0)
852         return tval;
853     else if (hval < 0)
854         tval = CM_DIR_NHASHENT - tval;
855     return tval;
856 }
857
858 /* Find a directory entry, given its name.  This entry returns a
859  * pointer to a locked buffer, and a pointer to a locked buffer (in
860  * previtem) referencing the found item (to aid the delete code).  If
861  * no entry is found, however, no items are left locked, and a null
862  * pointer is returned instead.
863  *
864  * On entry:
865  *  scp->rw locked
866  *
867  * On exit:
868  *  scp->rw locked
869  */
870 static long
871 cm_DirFindItem(cm_dirOp_t * op,
872                char *ename,
873                cm_buf_t ** itembufpp, cm_dirEntry_t ** itempp,
874                cm_buf_t ** prevbufpp, unsigned short **previtempp)
875 {
876     int                  i;
877     cm_dirHeader_t      *dhp = NULL;
878     unsigned short      *lp = NULL;
879     cm_dirEntry_t       *tp = NULL;
880     cm_buf_t            *hashbufp = NULL;
881     cm_buf_t            *itembufp = NULL;
882     long code = 0;
883
884     osi_Log2(afsd_logp, "cm_DirFindItem for op 0x%p, entry[%s]",
885              op, osi_LogSaveString(afsd_logp, ename));
886
887     i = cm_DirHash(ename);
888
889     if (op->scp->fileType != CM_SCACHETYPE_DIRECTORY) {
890         osi_Log0(afsd_logp, "cm_DirFindItem: The scp is not a directory");
891         return CM_ERROR_INVAL;
892     }
893
894     code = cm_DirGetPage(op, 0, &hashbufp, (void **) &dhp);
895     if (code != 0) {
896         return code;
897     }
898
899     if (dhp->hashTable[i] == 0) {
900         /* no such entry */
901         osi_Log1(afsd_logp, "cm_DirFindItem: Hash bucket %d is empty", i);
902         cm_DirReleasePage(op, &hashbufp, FALSE);
903         return ENOENT;
904     }
905
906     code = cm_DirGetBlob(op,
907                          (u_short) ntohs(dhp->hashTable[i]),
908                          &itembufp, &tp);
909     if (code != 0) {
910         cm_DirReleasePage(op, &hashbufp, FALSE);
911         return code;
912     }
913
914     lp = &(dhp->hashTable[i]);
915
916     /* loop invariant:
917
918        lp       : pointer to blob number of entry we are looking at
919        hashbufp : buffer containing lp
920        tp       : pointer to entry we are looking at
921        itembufp : buffer containing tp
922      */
923     while (1) {
924         /* Look at each hash conflict entry. */
925         if (!strcmp(ename, tp->name)) {
926             osi_Log0(afsd_logp, "cm_DirFindItem: returning success");
927             /* Found our entry. */
928             *previtempp = lp;
929             *prevbufpp = hashbufp;
930             *itempp = tp;
931             *itembufpp = itembufp;
932             return 0;
933         }
934
935         lp = &(tp->next);
936         cm_DirReleasePage(op, &hashbufp, FALSE);
937         hashbufp = itembufp;
938
939         itembufp = NULL;
940         tp = NULL;
941
942         if (*lp == 0) {
943             /* The end of the line */
944             osi_Log0(afsd_logp, "cm_DirFindItem: returning ENOENT");
945             cm_DirReleasePage(op, &hashbufp, FALSE);
946             return ENOENT;
947         }
948
949         code = cm_DirGetBlob(op,
950                              (u_short) ntohs(*lp),
951                              &itembufp, &tp);
952
953         if (code != 0) {
954             cm_DirReleasePage(op, &hashbufp, FALSE);
955             return code;
956         }
957     }
958 }
959
960 /* Begin a sequence of directory operations.  
961  * Called with scp->rw unlocked.
962  */
963 long
964 cm_BeginDirOp(cm_scache_t * scp, cm_user_t * userp, cm_req_t * reqp,
965               afs_uint32 lockType, cm_dirOp_t * op)
966 {
967     long code;
968     int i, mxheld = 0, haveWrite = 0;
969
970     osi_Log3(afsd_logp, "Beginning dirOp[0x%p] for scp[0x%p], userp[0x%p]",
971              op, scp, userp);
972
973     memset(op, 0, sizeof(*op));
974
975     cm_HoldSCache(scp);
976     op->scp = scp;
977     cm_HoldUser(userp);
978     op->userp = userp;
979     op->req = *reqp;            /* copy the values from the input */
980
981     op->dirtyBufCount = 0;
982     op->nBuffers = 0;
983
984     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
985         op->buffers[i].flags = 0;
986     }
987
988     if (lockType == CM_DIRLOCK_WRITE) {
989         lock_ObtainWrite(&scp->dirlock);
990         haveWrite = 1;
991     } else { 
992         lock_ObtainRead(&scp->dirlock);
993         haveWrite = 0;
994     }
995     lock_ObtainWrite(&scp->rw);
996     mxheld = 1;
997     code = cm_DirCheckStatus(op, 1);
998     if (code == 0) {
999         op->length = scp->length;
1000         op->newLength = op->length;
1001         op->dataVersion = scp->dataVersion;
1002         op->newDataVersion = op->dataVersion;
1003
1004 #ifdef USE_BPLUS
1005         if (!cm_BPlusTrees ||
1006             (scp->dirBplus &&
1007              scp->dirDataVersion == scp->dataVersion)) 
1008         {
1009             /* we know that haveWrite matches lockType at this point */
1010             switch (lockType) {
1011             case CM_DIRLOCK_NONE:
1012                 if (haveWrite)
1013                     lock_ReleaseWrite(&scp->dirlock);
1014                 else
1015                     lock_ReleaseRead(&scp->dirlock);
1016                 break;
1017             case CM_DIRLOCK_READ:
1018                 osi_assert(!haveWrite);
1019                 break;
1020             case CM_DIRLOCK_WRITE:
1021             default:
1022                 osi_assert(haveWrite);
1023             }
1024         } else {
1025             if (!(scp->dirBplus && 
1026                   scp->dirDataVersion == scp->dataVersion)) 
1027             {
1028               repeat:
1029                 if (!haveWrite) {
1030                     if (mxheld) {
1031                         lock_ReleaseWrite(&scp->rw);
1032                         mxheld = 0;
1033                     }
1034                     lock_ConvertRToW(&scp->dirlock);
1035                     haveWrite = 1;
1036                 }
1037                 if (!mxheld) {
1038                     lock_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
1054                         mxheld = 0;
1055                     }
1056                     cm_BPlusDirBuildTree(scp, userp, reqp);
1057                     if (!mxheld) {
1058                         lock_ObtainWrite(&scp->rw);
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_ReleaseWrite(&scp->rw);
1110     } else {
1111         if (haveWrite)
1112             lock_ReleaseWrite(&scp->dirlock);
1113         else
1114             lock_ReleaseRead(&scp->dirlock);
1115         if (mxheld)
1116             lock_ReleaseWrite(&scp->rw);
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->rw 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_ObtainWrite(&op->scp->rw);
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_ReleaseWrite(&op->scp->rw); 
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=%d, new dv=%d",
1154                  code, op->dataVersion, op->scp->dataVersion);
1155     return rc;
1156 }
1157
1158 /* End a sequence of directory operations.  
1159  * Called with op->scp->rw 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_ObtainWrite(&op->scp->rw);
1196         code = buf_ForceDataVersion(op->scp, op->dataVersion, op->newDataVersion);
1197         lock_ReleaseWrite(&op->scp->rw);
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->rw 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_ObtainWrite(&op->scp->rw);
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) {
1274             if (bufferp->dataVersion == CM_BUF_VERSION_BAD) {
1275                 /* This is a new buffer */
1276                 bufferp->dataVersion = op->dataVersion;
1277             } else if (bufferp->dataVersion != op->dataVersion) {
1278                 osi_Log2(afsd_logp,
1279                          "cm_DirOpAddBuffer: buffer data version mismatch. buf dv = %d. needs %d", 
1280                          bufferp->dataVersion, op->dataVersion);
1281
1282                 cm_SyncOpDone(op->scp, bufferp,
1283                               CM_SCACHESYNC_NEEDCALLBACK |
1284                               (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ) |
1285                               CM_SCACHESYNC_BUFLOCKED);
1286
1287                 code = CM_ERROR_INVAL;
1288             }
1289         }
1290
1291         lock_ReleaseWrite(&op->scp->rw);
1292         lock_ReleaseMutex(&bufferp->mx);
1293
1294         if (code) {
1295             osi_Log1(afsd_logp, "cm_DirOpAddBuffer: failed to sync buffer.  code=0x%x",
1296                      code);
1297             return code;
1298         }
1299
1300         buf_Hold(bufferp);
1301         op->buffers[i].bufferp = bufferp;
1302         op->buffers[i].refcount = 1; /* start with one ref */
1303         op->buffers[i].flags = CM_DIROPBUFF_INUSE;
1304
1305         op->nBuffers++;
1306
1307         osi_Log0(afsd_logp, "cm_DirOpAddBuffer: returning success");
1308
1309         return 0;
1310     }
1311 }
1312
1313 /* Note: Called without op->scp->rw */
1314 static int
1315 cm_DirOpFindBuffer(cm_dirOp_t * op, osi_hyper_t offset, cm_buf_t ** bufferpp)
1316 {
1317     int i;
1318
1319     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1320         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1321             LargeIntegerEqualTo(op->buffers[i].bufferp->offset, offset))
1322             break;
1323     }
1324
1325     if (i < CM_DIROP_MAXBUFFERS) {
1326         /* found it */
1327         op->buffers[i].refcount++;
1328         buf_Hold(op->buffers[i].bufferp);
1329         *bufferpp = op->buffers[i].bufferp;
1330
1331         osi_Log2(afsd_logp, "cm_DirOpFindBuffer: found buffer for offset [%x:%x]",
1332                  offset.HighPart, offset.LowPart);
1333         return 1;
1334     }
1335
1336     osi_Log2(afsd_logp, "cm_DirOpFindBuffer: buffer not found for offset [%x:%x]",
1337              offset.HighPart, offset.LowPart);
1338     return 0;
1339 }
1340
1341
1342 /* NOTE: called with scp->rw held or not depending on the flags */
1343 static int
1344 cm_DirOpDelBuffer(cm_dirOp_t * op, cm_buf_t * bufferp, int flags)
1345 {
1346     int i;
1347
1348     osi_Log3(afsd_logp, "cm_DirOpDelBuffer for op 0x%p, buffer 0x%p, flags=%d",
1349              op, bufferp, flags);
1350
1351     for (i=0; i < CM_DIROP_MAXBUFFERS; i++) {
1352         if ((op->buffers[i].flags & CM_DIROPBUFF_INUSE) &&
1353             op->buffers[i].bufferp == bufferp)
1354             break;
1355     }
1356
1357     if (i < CM_DIROP_MAXBUFFERS) {
1358
1359         if (flags & DIROP_MODIFIED)
1360             op->dirtyBufCount++;
1361
1362         osi_assert(op->buffers[i].refcount > 0);
1363         op->buffers[i].refcount --;
1364
1365         if (op->buffers[i].refcount == 0) {
1366             /* this was the last reference we had */
1367
1368             osi_Log0(afsd_logp, "cm_DirOpDelBuffer: releasing buffer");
1369
1370             /* if this buffer was modified, then we update the data
1371                version of the buffer with the data version of the
1372                scp. */
1373             if (!(flags & DIROP_SCPLOCKED)) {
1374                 lock_ObtainWrite(&op->scp->rw);
1375             }
1376
1377             /* first make sure that the buffer is idle.  It should
1378                have been idle all along. */
1379             osi_assertx((bufferp->cmFlags & (CM_BUF_CMFETCHING |
1380                                             CM_BUF_CMSTORING)) == 0,
1381                         "Buffer is not idle while performing dirOp");
1382
1383             cm_SyncOpDone(op->scp, bufferp,
1384                           CM_SCACHESYNC_NEEDCALLBACK |
1385                          (op->lockType == CM_DIRLOCK_WRITE ? CM_SCACHESYNC_WRITE : CM_SCACHESYNC_READ));
1386
1387 #ifdef DEBUG
1388             osi_assert(bufferp->dataVersion == op->dataVersion);
1389 #endif
1390
1391             lock_ReleaseWrite(&op->scp->rw);
1392
1393             lock_ObtainMutex(&bufferp->mx);
1394
1395             if (flags & DIROP_SCPLOCKED) {
1396                 lock_ObtainWrite(&op->scp->rw);
1397             }
1398
1399             if (flags & DIROP_MODIFIED) {
1400                 /* We don't update the dataversion here.  Instead we
1401                    wait until the dirOp is completed and then flip the
1402                    dataversion on all the buffers in one go.
1403                    Otherwise we won't know if the dataversion is
1404                    current because it was fetched from the server or
1405                    because we touched it during the dirOp. */
1406
1407                 if (bufferp->userp != op->userp) {
1408                     if (bufferp->userp != NULL)
1409                         cm_ReleaseUser(bufferp->userp);
1410                     cm_HoldUser(op->userp);
1411                     bufferp->userp = op->userp;
1412                 }
1413             }
1414
1415             lock_ReleaseMutex(&bufferp->mx);
1416
1417             op->buffers[i].bufferp = NULL;
1418             buf_Release(bufferp);
1419             op->buffers[i].flags = 0;
1420
1421             op->nBuffers--;
1422
1423             return 1;
1424         } else {
1425             /* we have other references to this buffer. so we have to
1426                let it be */
1427             return 0;
1428         }
1429
1430     } else {
1431         osi_Log0(afsd_logp, "cm_DirOpDelBuffer: buffer not found");
1432         osi_assertx(FALSE, "Attempt to delete a non-existent buffer from a dirOp");
1433         return -1;
1434     }
1435 }
1436
1437 /* Check if we have current status and a callback for the given scp.
1438    This should be called before cm_DirGetPage() is called per scp.
1439
1440    On entry:
1441      scp->rw locked state indicated by parameter
1442
1443    On exit:
1444      scp->rw same state as upon entry
1445
1446    During:
1447      scp->rw may be released
1448  */
1449 static long
1450 cm_DirCheckStatus(cm_dirOp_t * op, afs_uint32 locked)
1451 {
1452     long code;
1453
1454     if (!locked)
1455         lock_ObtainWrite(&op->scp->rw);
1456     code = cm_SyncOp(op->scp, NULL, op->userp, &op->req, PRSFS_LOOKUP,
1457                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1458     if (!locked)
1459         lock_ReleaseWrite(&op->scp->rw);
1460
1461     osi_Log2(afsd_logp, "cm_DirCheckStatus for op 0x%p returning code 0x%x",
1462              op, code);
1463
1464     return code;
1465 }
1466
1467 /* Release a directory buffer that was obtained via a call to
1468    cm_DirGetPage() or any other function that returns a locked, held,
1469    directory page buffer.
1470
1471    Called with scp->rw unlocked
1472  */
1473 static long
1474 cm_DirReleasePage(cm_dirOp_t * op, cm_buf_t ** bufferpp, int modified)
1475 {
1476     long code = 0;
1477
1478     if (!*bufferpp)
1479         return EINVAL;
1480
1481     cm_DirOpDelBuffer(op, *bufferpp,
1482                       ((modified ? DIROP_MODIFIED : 0)));
1483     buf_Release(*bufferpp);
1484     *bufferpp = NULL;
1485
1486     return code;
1487 }
1488
1489 /*
1490    Returns the index'th directory page from scp.  The userp and reqp
1491    will be used to fetch the buffer from the fileserver if necessary.
1492    If the call is successful, a locked and held cm_buf_t is returned
1493    via buferpp and a pointer to the directory page is returned via
1494    datapp.
1495
1496    The returned buffer should be released via a call to
1497    cm_DirReleasePage() or by passing it into a subsequent call to
1498    cm_DirGetPage() for the *same* scp.
1499
1500    If a *locked* buffer for the *same* scp is passed in via bufferpp
1501    to the function, it will check if the requested directory page is
1502    located in the specified buffer.  If not, the buffer will be
1503    released and a new buffer returned that contains the requested
1504    page.
1505
1506    Note: If a buffer is specified on entry via bufferpp, it is assumed
1507    that the buffer is unmodified.  If the buffer is modified, it
1508    should be released via cm_DirReleasePage().
1509
1510    On entry:
1511      scp->rw unlocked.
1512      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1513
1514    On exit:
1515      scp->rw unlocked
1516      If *bufferpp is non-NULL, then *bufferpp->mx is locked.
1517
1518    During:
1519      scp->rw will be obtained and released
1520
1521  */
1522 static long
1523 cm_DirGetPage(cm_dirOp_t * op,
1524               long index, cm_buf_t ** bufferpp, void ** datapp)
1525 {
1526     osi_hyper_t pageOffset;     /* offset of the dir page from the
1527                                    start of the directory */
1528     osi_hyper_t bufferOffset;   /* offset of the buffer from the start
1529                                    of the directory */
1530     osi_hyper_t thyper;
1531
1532     cm_buf_t * bufferp = NULL;
1533
1534     void * datap = NULL;
1535
1536     long code = 0;
1537
1538     osi_Log2(afsd_logp, "cm_DirGetPage for op 0x%p, index %d", op, index);
1539
1540     pageOffset = ConvertLongToLargeInteger(index * CM_DIR_PAGESIZE);
1541     bufferOffset.HighPart = pageOffset.HighPart;
1542     bufferOffset.LowPart = pageOffset.LowPart & ~(cm_data.buf_blockSize - 1);
1543
1544     bufferp = *bufferpp;
1545     if (bufferp != NULL) {
1546         osi_assert(cm_FidCmp(&bufferp->fid, &op->scp->fid) == 0);
1547
1548         thyper = bufferp->offset;
1549     }
1550
1551     if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
1552         /* wrong buffer */
1553
1554         if (bufferp) {
1555             buf_Release(bufferp);
1556             cm_DirOpDelBuffer(op, bufferp, 0);
1557             bufferp = NULL;
1558         }
1559
1560         /* first check if we are already working with the buffer */
1561         if (cm_DirOpFindBuffer(op, bufferOffset, &bufferp)) {
1562             code = 0;
1563             goto _has_buffer;
1564         }
1565
1566         code = buf_Get(op->scp, &bufferOffset, &bufferp);
1567         if (code) {
1568             osi_Log1(afsd_logp, "    buf_Get returned code 0x%x", code);
1569             bufferp = NULL;
1570             goto _exit;
1571         }
1572
1573         osi_assert(bufferp != NULL);
1574
1575         /* DirOpAddBuffer will obtain bufferp->mx if necessary */
1576         code = cm_DirOpAddBuffer(op, bufferp);
1577
1578         if (code != 0) {
1579             /* for some reason, the buffer was rejected.  We can't use
1580                this buffer, and since this is the only buffer we can
1581                potentially use, there's no recourse.*/
1582             buf_Release(bufferp);
1583             bufferp = NULL;
1584             goto _exit;
1585         }
1586
1587 #if 0
1588         /* The code below is for making sure the buffer contains
1589            current data.  This is a bad idea, since the whole point of
1590            doing directory updates locally is to avoid fetching all
1591            the data from the server. */
1592         while (1) {
1593             lock_ObtainWrite(&op->scp->rw);
1594             code = cm_SyncOp(op->scp, bufferp, op->userp, &op->req, PRSFS_LOOKUP,
1595                              CM_SCACHESYNC_NEEDCALLBACK |
1596                              CM_SCACHESYNC_READ |
1597                              CM_SCACHESYNC_BUFLOCKED);
1598
1599             if (code) {
1600                 lock_ReleaseWrite(&op->scp->rw);
1601                 break;
1602             }
1603
1604             cm_SyncOpDone(op->scp, bufferp,
1605                           CM_SCACHESYNC_NEEDCALLBACK |
1606                           CM_SCACHESYNC_READ |
1607                           CM_SCACHESYNC_BUFLOCKED);
1608
1609             if (cm_HaveBuffer(op->scp, bufferp, 1)) {
1610                 lock_ReleaseWrite(&op->scp->rw);
1611                 break;
1612             }
1613
1614             lock_ReleaseMutex(&bufferp->mx);
1615             code = cm_GetBuffer(op->scp, bufferp, NULL, op->userp, &op->req);
1616             lock_ReleaseWrite(&op->scp->rw);
1617             lock_ObtainMutex(&bufferp->mx);
1618
1619             if (code)
1620                 break;
1621         }
1622
1623         if (code) {
1624             cm_DirOpDelBuffer(op, bufferp, 0);
1625             buf_Release(bufferp);
1626             bufferp = NULL;
1627             goto _exit;
1628         }
1629 #endif
1630     }
1631
1632  _has_buffer:
1633
1634     /* now to figure out where the data is */
1635     thyper = LargeIntegerSubtract(pageOffset, bufferOffset);
1636
1637     osi_assert(thyper.HighPart == 0);
1638     osi_assert(cm_data.buf_blockSize > thyper.LowPart &&
1639                cm_data.buf_blockSize - thyper.LowPart >= CM_DIR_PAGESIZE);
1640
1641     datap = (void *) (((char *)bufferp->datap) + thyper.LowPart);
1642
1643     if (datapp)
1644         *datapp = datap;
1645
1646     /* also, if we are writing past EOF, we should make a note of the
1647        new length */
1648     thyper = LargeIntegerAdd(pageOffset,
1649                              ConvertLongToLargeInteger(CM_DIR_PAGESIZE));
1650     if (LargeIntegerLessThan(op->newLength, thyper)) {
1651         op->newLength = thyper;
1652     }
1653
1654  _exit:
1655
1656     *bufferpp = bufferp;
1657
1658     osi_Log1(afsd_logp, "cm_DirGetPage returning code 0x%x", code);
1659
1660     return code;
1661 }
1662
1663
1664 void
1665 cm_DirEntryListAdd(char * namep, cm_dirEntryList_t ** list)
1666 {
1667     size_t len;
1668     cm_dirEntryList_t * entry;
1669
1670     len = strlen(namep);
1671     len += sizeof(cm_dirEntryList_t);
1672
1673     entry = malloc(len);
1674     if (entry) {
1675         entry->nextp = *list;
1676         strcpy(entry->name, namep);
1677         *list = entry;
1678     }
1679 }
1680
1681 void
1682 cm_DirEntryListFree(cm_dirEntryList_t ** list)
1683 {
1684     cm_dirEntryList_t * entry;
1685     cm_dirEntryList_t * next;
1686
1687     for (entry = *list; entry; entry = next) {
1688         next = entry->nextp;
1689         free(entry);
1690     }
1691
1692     *list = NULL;
1693 }
1694