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