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