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