20b0a8b17930ed3d6ec2965f809455c9215ea7a5
[openafs.git] / src / WINNT / afsd / cm_diskcache95.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 /* This code is experimental persistent disk cache support for the
11    Windows 95/DJGPP AFS client.  It uses synchronous I/O and assumes
12    non-preemptible threads (which is the case in DJGPP), so it has
13    no locking. */
14
15    
16 #ifdef DISKCACHE95
17
18 #include <afs/param.h>
19 #include <afs/stds.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <sys/errno.h>
23 #include <sys/stat.h>
24
25 #include "afsd.h"
26
27 cm_diskcache_t **diskHashTable;    /* pointers to hash chains */
28 Queue diskLRUList;
29 Queue openFileList;
30 cm_diskcache_t *diskCBBuf;
31
32 extern int afs_diskCacheChunks;
33 /*extern int cm_diskCacheChunkSize;*/
34 extern long buf_blockSize;
35 long cm_diskCacheChunkSize;
36 extern char cm_cachePath[];
37 extern int cm_cachePathLen;
38 extern int cm_diskCacheEnabled;
39
40 int cacheInfo_fd;
41 int accessOrd = 0;            /* monotonically increasing access ordinal */
42 int updates = 0;
43 int afs_dhashsize = 2048;
44 int openCacheFiles = 0;
45
46 char cacheInfoPath[128];
47 char cacheFileName[128];
48
49 extern int errno;
50
51 #define MAX_OPEN_FILES 22
52
53 /* internal functions */
54 void diskcache_WriteCacheInfo(cm_diskcache_t *dcp);
55 int diskcache_New();
56 cm_diskcache_t *diskcache_Find(cm_fid_t *fid, int chunk);
57 cm_diskcache_t *diskcache_Alloc(cm_fid_t *fid, int chunk, int dataVersion);
58 int diskcache_Read(cm_diskcache_t *dcp, char *buf, int size);
59 int diskcache_Write(cm_diskcache_t *dcp, char *buf, int size);
60
61 #define complain printf
62
63 #define OFFSET_TO_CHUNK(a) (LargeIntegerDivideByLong((a), cm_diskCacheChunkSize))
64 #define GEN_CACHE_DIR_NAME(name, path, i) \
65            sprintf(name, "%s\\D%07d", cm_cachePath, (i) / CACHE_FILES_PER_DIR)
66 #define GEN_CACHE_FILE_NAME(name, path, i) \
67            sprintf(name, "%s\\D%07d\\C%07d", path, (i) / CACHE_FILES_PER_DIR, \
68                    (i) % CACHE_FILES_PER_DIR)
69
70 /* Initialize the disk cache */
71 int diskcache_Init()
72 {
73   int i;
74   int rc;
75   int fd;
76   int invalid;
77   int index;
78   char *chunkBuf;
79   char tmpBuf[512];
80   struct stat cacheInfoStat, chunkStat;
81   cm_cacheInfoHdr_t hdr;
82   cm_diskcache_t *dcp;
83   int validCount = 0;
84
85   if (!cm_diskCacheEnabled)
86     return 0;
87   
88   cm_diskCacheChunkSize = buf_blockSize;
89   if (cm_diskCacheChunkSize % buf_blockSize != 0)
90   {
91     complain("Error: disk cache chunk size %d not a multiple of buffer size %d\n",
92              cm_diskCacheChunkSize, buf_blockSize);
93     return CM_ERROR_INVAL;
94   }
95   
96   /* alloc mem for chunk file control blocks */
97   diskCBBuf = (cm_diskcache_t *) malloc(afs_diskCacheChunks * sizeof(cm_diskcache_t));
98   if (diskCBBuf == NULL)
99     return CM_ERROR_SPACE;
100   memset(diskCBBuf, 0, afs_diskCacheChunks * sizeof(cm_diskcache_t));
101
102   /* alloc mem for hash table pointers */
103   diskHashTable = (cm_diskcache_t **) malloc(afs_dhashsize * sizeof(cm_diskcache_t *));
104   if (diskHashTable == NULL)
105     return CM_ERROR_SPACE;
106   memset(diskHashTable, 0, afs_dhashsize*sizeof(cm_diskcache_t *));
107
108   QInit(&diskLRUList);
109   QInit(&openFileList);
110   
111   /*sprintf(cacheInfoPath, "%s\\%s", cm_cachePath, CACHE_INFO_FILE);*/
112   memset(cacheInfoPath, 0, 128);
113   DPRINTF("cm_cachePath=%s\n", cm_cachePath);
114   strncpy(cacheInfoPath, cm_cachePath, 50);
115   strcat(cacheInfoPath, "\\");
116   strcat(cacheInfoPath, CACHE_INFO_FILE);
117   DPRINTF("cacheInfoPath=%s\n", cacheInfoPath);
118   
119   cacheInfo_fd = open(cacheInfoPath, O_RDWR | O_BINARY);
120   
121   if (cacheInfo_fd < 0)
122   {
123     /* file not present */
124     return diskcache_New();   /* initialize new empty disk cache */
125   }
126
127   /* get stat of cache info file */
128   rc = fstat(cacheInfo_fd, &cacheInfoStat);
129
130   /* Check for valid header in cache info file */
131   rc = read(cacheInfo_fd, &hdr, sizeof(cm_cacheInfoHdr_t));
132   if (rc < sizeof(cm_cacheInfoHdr_t) ||
133       hdr.magic != CACHE_INFO_MAGIC)
134   /*hdrp = (cm_cacheInfoHdr_t *) tmpBuf;*/
135   {
136     close(cacheInfo_fd);
137     return diskcache_New();
138   }
139         
140   if (hdr.chunks != afs_diskCacheChunks ||
141       hdr.chunkSize != cm_diskCacheChunkSize)
142   {
143     /* print error message saying params don't match */
144     return CM_ERROR_INVAL;
145   }
146   
147   chunkBuf = (char *) malloc(cm_diskCacheChunkSize);
148   if (chunkBuf == NULL)
149     return CM_ERROR_SPACE;
150
151   /* read metadata from cache info file into control blocks */
152   /* reconstruct hash chains based on fid, chunk */
153   for (i = 0; i < afs_diskCacheChunks; i++)
154   {  /* for all cache chunks */
155     if (i % 500 == 0)
156     {
157       printf("%d...", i);
158       fflush(stdout);
159     }
160     dcp = &diskCBBuf[i];
161     dcp->refCount = 0;
162     rc = read(cacheInfo_fd, &dcp->f, sizeof(struct fcache));
163     if (rc < sizeof(struct fcache))
164     {
165       /* print error message about reading cache info file */
166       /* this isn't the right error code for a read error */
167       return CM_ERROR_INVAL;
168     }
169
170     if (dcp->f.index != i)
171       return CM_ERROR_INVAL;   /* index should match position in cache info file */
172
173     /* Try to open cache file.  This chunk will be invalidated if we can't
174        find the file, the file is newer than the cache info file, the file
175        size doesn't match the cache info file, or the file's header is
176        invalid. */
177     GEN_CACHE_FILE_NAME(cacheFileName, cm_cachePath, i);
178 #if 1
179     /*fd = open(cacheFileName, O_RDWR | O_BINARY);
180     if (fd < 0) invalid = 1;
181     else
182     {
183     rc = fstat(fd, &chunkStat);*/
184     rc = stat(cacheFileName, &chunkStat);
185
186       if (rc < 0) invalid = 1;
187       /*else if (cacheInfoStat.st_mtime < chunkStat.st_mtime + 120) invalid = 1;*/
188       else if (cacheInfoStat.st_mtime < chunkStat.st_mtime) invalid = 1;
189       /*else if (cacheInfoStat.st_mtime < dcp->f.modTime + 120) invalid = 1;*/
190       else if (cacheInfoStat.st_mtime < dcp->f.modTime) invalid = 1;
191       else if (cm_diskCacheChunkSize != chunkStat.st_size ||
192                dcp->f.chunkBytes != chunkStat.st_size) invalid = 1;
193       /*else
194         {*/
195         /*rc = read(fd, chunkBuf, cm_diskCacheChunkSize);
196           if (rc < 0) invalid = 1;*/
197
198         /*else
199         {
200           cacheFileHdrP = (cm_cacheFileHdr_t *) chunkBuf;
201           if (cacheFileHdrP->magic != CACHE_FILE_MAGIC ||
202               cacheFileHdrP->index != i)
203           {
204             invalid = 1;
205           }
206         }*/
207       /*}*/
208       /*}*/
209 #else
210     invalid = 0;
211 #endif
212       
213     if (invalid == 0)
214     {
215       /* Cache file seems to be valid */
216     
217       validCount++;
218       DPRINTF("Found fid/chunk=%08x-%08x-%08x-%08x/%04d in slot=%d dcp=%x\n",
219              dcp->f.fid.cell, dcp->f.fid.volume, dcp->f.fid.vnode,
220              dcp->f.fid.unique, dcp->f.chunk, i, dcp);
221       /* Put control block in hash table */
222       index = DCHash(&dcp->f.fid, dcp->f.chunk);
223       /*osi_QAdd(&diskHashTable[index], &dcp->f.hashq);*/
224
225       /* add to head of hash list.  (we should probably look at
226          ord here instead.  use queues?) */
227       dcp->hash_next = diskHashTable[index];
228       dcp->hash_prev = NULL;
229       if (diskHashTable[index]) diskHashTable[index]->hash_prev = dcp;
230       diskHashTable[index] = dcp;
231
232       /* Add to LRU queue in access time order (lowest at tail) */
233       QAddOrd(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);
234
235       close(fd);
236     }
237     else
238     {
239       /* Cache file is invalid */
240       
241       /* Create the cache file with correct size */
242       memset(chunkBuf, 0, cm_diskCacheChunkSize);
243       /*cacheFileHdrP->magic = CACHE_FILE_MAGIC;
244         cacheFileHdrP->index = i;*/
245       
246       if (fd != 0) close(fd);
247       /* Note that if the directory this file is supposed to be in doesn't
248          exist, the creat call will fail and we will return an error. */
249       /*fd = creat(cacheFileName, S_IRUSR|S_IWUSR);*/
250       fd = open(cacheFileName, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
251                 S_IRUSR | S_IWUSR);
252       if (fd < 0) return CM_ERROR_INVAL;   /* couldn't create file */
253       rc = write(fd, chunkBuf, cm_diskCacheChunkSize);
254       if (rc < 0)   /* ran out of space? */
255         return CM_ERROR_INVAL;
256       close(fd);
257       
258       /* We consider an invalid chunk as empty, so we put it at tail of LRU */
259       memset(dcp, 0, sizeof(cm_diskcache_t));
260       dcp->f.accessOrd = 0;
261       dcp->f.states = DISK_CACHE_EMPTY;
262       dcp->f.index = i;
263       dcp->f.chunkBytes = cm_diskCacheChunkSize;
264       /*osi_QAdd(diskLRUList, &dcp->lruq);*/
265       QAddOrd(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);     /* head is LRU */
266
267       /* write out cache info modifications */
268       lseek(cacheInfo_fd, -sizeof(struct fcache), SEEK_CUR);
269       write(cacheInfo_fd, &dcp->f, sizeof(struct fcache));
270     }
271   }  /* for all cache chunks */
272
273   free(chunkBuf);
274   /*close(cacheInfo_fd);*/
275   fprintf(stderr, "\nFound %d of %d valid %d-byte blocks\n", validCount,
276           afs_diskCacheChunks, cm_diskCacheChunkSize);
277
278   return 0;
279 }
280
281 /* create empty disk cache files */
282 /* assumes tables have already been malloc'd by diskcache_Init */
283 int diskcache_New()
284 {
285   int i;
286   int rc;
287   int fd;
288   int invalid;
289   int index;
290   /*char cacheInfoPath[256];
291     char cacheFileName[256];*/
292   char dirName[256];
293   char *chunkBuf;
294   struct stat cacheInfoStat, chunkStat;
295   cm_cacheInfoHdr_t hdr;
296   cm_diskcache_t *dcp;
297   
298   sprintf(cacheInfoPath, "%s\\%s", cm_cachePath, CACHE_INFO_FILE);
299   /*cacheInfo_fd = creat(cacheInfoPath, S_IRUSR | S_IWUSR);*/
300   cacheInfo_fd = open(cacheInfoPath, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
301                       S_IRUSR | S_IWUSR);
302   if (cacheInfo_fd < 0)
303   {
304     complain("diskcache_New: Error creating cache info file in cache directory %s\n",
305              cm_cachePath);
306     return CM_ERROR_INVAL;
307   }
308
309   /* write valid header */
310   hdr.magic = CACHE_INFO_MAGIC;
311   hdr.chunks = afs_diskCacheChunks;
312   hdr.chunkSize = cm_diskCacheChunkSize;
313   rc = write(cacheInfo_fd, (char *) &hdr, sizeof(cm_cacheInfoHdr_t));
314   if (rc < 0)
315     return CM_ERROR_INVAL;
316   
317   chunkBuf = (char *) malloc(cm_diskCacheChunkSize);
318   if (chunkBuf == NULL)
319     return CM_ERROR_SPACE;
320   memset(chunkBuf, 0, cm_diskCacheChunkSize);
321
322   for (i = 0; i < afs_diskCacheChunks; i++)
323   {  /* for all cache chunks */
324     if (i % 500 == 0)
325     {
326       printf("%d...", i);
327       fflush(stdout);
328     }
329
330     dcp = &diskCBBuf[i];
331
332     dcp->refCount = 0;
333     /* $$$: init mutex mx */
334     memset(dcp, 0, sizeof(cm_diskcache_t));
335     dcp->f.accessOrd = 0;
336     dcp->f.index = i;
337     dcp->f.states = DISK_CACHE_EMPTY;
338     dcp->f.chunkBytes = cm_diskCacheChunkSize;
339     QAddT(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);     /* head is LRU */
340     rc = write(cacheInfo_fd, &dcp->f, sizeof(struct fcache));
341
342     if (i % CACHE_FILES_PER_DIR == 0)
343     {
344       GEN_CACHE_DIR_NAME(dirName, cm_cachePath, i);
345       rc = mkdir(dirName, S_IRUSR | S_IWUSR);
346       if (rc < 0 && errno != EEXIST)
347       {
348         complain("diskcache_New: Couldn't create cache directory %s\n", dirName);
349         return CM_ERROR_INVAL;
350       }
351     }
352     
353     GEN_CACHE_FILE_NAME(cacheFileName, cm_cachePath, i);
354     /*fd = creat(cacheFileName, S_IRUSR | S_IWUSR);*/
355     fd = open(cacheFileName, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
356               S_IRUSR | S_IWUSR);
357     if (fd < 0)
358     {
359       if (errno == ENOSPC)
360         complain("diskcache_New: Not enough space in cache path to create file %s\n",
361                  cacheFileName);
362       else
363         complain("diskcache_New: Couldn't create cache file %s\n", cacheFileName);
364       return CM_ERROR_INVAL;
365     }
366
367     /*fileHdr.magic = CACHE_FILE_MAGIC;
368     fileHdr.index = i;
369     rc = write(fd, &fileHdr, sizeof(cm_cacheFileHdr_t));  /* maybe we should write
370                                                              a full block? */
371     /*if (rc == 0)
372       {*/
373       rc = write(fd, chunkBuf, cm_diskCacheChunkSize);
374       /*}*/
375
376     if (rc < 0)
377     {
378       if (errno == ENOSPC)
379         complain("diskcache_New: Not enough space in cache path to write to file %s\n",
380                  cacheFileName);
381       else
382         complain("diskcache_New: Couldn't write to cache file %s\n",
383                  cacheFileName);
384       return CM_ERROR_INVAL;
385     }
386
387     close(fd);
388   }  /* for all cache chunks */
389
390   free(chunkBuf);
391     
392   /*close(cacheInfo_fd);*/
393
394   return 0;
395 }
396
397 /* Get chunk from the cache or allocate a new chunk */
398 int diskcache_Get(cm_fid_t *fid, osi_hyper_t *offset, char *buf, int size, int *dataVersion, int *dataCount, cm_diskcache_t **dcpRet)
399 {
400   cm_diskcache_t *dcp;
401   int rc;
402   int chunk;
403
404   
405   if (!cm_diskCacheEnabled)
406   {
407     *dcpRet = NULL;
408     return 0;
409   }
410
411   chunk = OFFSET_TO_CHUNK(*offset);  /* chunk number */
412
413   DPRINTF("diskcache_Get: fid/chunk=%08x-%08x-%08x-%08x/%04d\n",
414            fid->cell, fid->volume, fid->vnode, fid->unique, chunk);
415   
416   dcp = diskcache_Find(fid, chunk);
417   if (dcp != NULL)
418   {
419     rc = diskcache_Read(dcp, buf, size);
420     *dataVersion = dcp->f.dataVersion;   /* update caller's data version */
421     if (rc < 0)
422       return -1;
423     else
424       *dataCount = rc;
425   }
426   else
427   {
428     dcp = diskcache_Alloc(fid, chunk, *dataVersion);
429     if (dcp == NULL)
430       return -1;
431   }
432
433   if (++updates >= CACHE_INFO_UPDATES_PER_WRITE)
434   {
435     updates = 0;
436     diskcache_WriteCacheInfo(dcp);  /* update cache info for this slot */
437   }
438
439   *dcpRet = dcp;
440   /*printf("diskcache_Get: returning dcp=%x\n", dcp);*/
441   return 0;
442 }
443
444
445 /* Look for a file chunk in the cache */
446 cm_diskcache_t *diskcache_Find(cm_fid_t *fid, int chunk)
447 {
448   int index;
449   cm_diskcache_t *dcp;
450   cm_diskcache_t *prev;
451
452   index = DCHash(fid, chunk);
453   dcp = diskHashTable[index];
454   prev = NULL;
455
456   while (dcp != NULL)
457   {
458     if (cm_FidCmp(&dcp->f.fid, fid) == 0 && chunk == dcp->f.chunk)
459     {
460       dcp->f.accessOrd = accessOrd++;
461       /* Move it to the beginning of the list */
462       if (diskHashTable[index] != dcp)
463       {
464         assert(dcp->hash_prev->hash_next == dcp);
465         dcp->hash_prev->hash_next = dcp->hash_next;
466         if (dcp->hash_next)
467         {
468           assert(dcp->hash_next->hash_prev == dcp);
469           dcp->hash_next->hash_prev = dcp->hash_prev;
470         }
471         dcp->hash_next = diskHashTable[index];
472         dcp->hash_prev = NULL;
473         if (diskHashTable[index]) diskHashTable[index]->hash_prev = dcp;
474         diskHashTable[index] = dcp;
475       }
476       break;
477     }
478     prev = dcp;
479     dcp = dcp->hash_next;
480   }
481       
482   if (dcp)
483     DPRINTF("diskcache_Find: fid/chunk=%08x-%08x-%08x-%08x/%04d slot=%d hash=%d dcp=%x\n",
484            fid->cell, fid->volume, fid->vnode, fid->unique, chunk, dcp->f.index, index, dcp);
485   else
486     DPRINTF("diskcache_Find: fid/chunk=%08x/%04d not found\n",
487            fid->unique, chunk);
488     
489   return dcp;
490 }
491     
492 int diskcache_Read(cm_diskcache_t *dcp, char *buf, int size)
493 {
494   char cacheFileName[256];
495   int fd;
496   int rc;
497   int opened = 0;
498
499   GEN_CACHE_FILE_NAME(cacheFileName, cm_cachePath, dcp->f.index);
500
501   DPRINTF("diskcache_Read: filename=%s dcp=%x\n", cacheFileName,
502          dcp);
503   
504   /* For reads, we will use the fd if already open, but we won't leave
505      the file open.  Note that if we use async I/O, we will need to
506      do locking to prevent someone from closing the file while I/O
507      is going on.  But for now, all I/O is synchronous, and threads
508      are non-preemptible. */
509   
510   if (dcp->openfd == 0)
511   {
512     fd = open(cacheFileName, O_RDWR | O_BINARY);
513     if (fd < 0)
514     {
515       complain("diskcache_Read: Couldn't open cache file %s\n", cacheFileName);
516       return -1;
517     }
518     opened = 1;
519   }
520   else
521     fd = dcp->openfd;
522
523   if (fd < 0)
524   {
525     complain("diskcache_Read: Couldn't open cache file %s\n", cacheFileName);
526     return -1;
527   }
528
529   rc = read(fd, buf, size);
530   if (rc < 0)
531   {
532     complain("diskcache_Read: Couldn't read cache file %s\n", cacheFileName);
533     close(fd); return -1;
534   }
535     
536   if (opened)
537     close(fd);   /* close it if we opened it */
538   return rc;  /* bytes read */
539 }
540
541 /* Write out buffer to disk */
542 int diskcache_Update(cm_diskcache_t *dcp, char *buf, int size, int dataVersion)
543 {
544   if (!cm_diskCacheEnabled)
545     return 0;
546
547   DPRINTF("diskcache_Update dcp=%x, dataVersion=%d\n", dcp, dataVersion);
548   diskcache_Write(dcp, buf, size);
549   /*diskcache_SetMRU(dcp);*/
550   dcp->f.dataVersion = dataVersion;
551   /*dcp->f.accessOrd = accessOrd++;*/
552   /*QMoveToTail(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);*/
553
554   if (++updates >= CACHE_INFO_UPDATES_PER_WRITE)
555   {
556     updates = 0;
557     diskcache_WriteCacheInfo(dcp);  /* update cache info */
558   }
559   return 0;
560 }
561
562 /* Allocate a new chunk file control block for this fid/chunk */
563 cm_diskcache_t *diskcache_Alloc(cm_fid_t *fid, int chunk, int dataVersion)
564 {
565   cm_diskcache_t *dcp;
566   QLink* q;
567   int index;
568   int stole=0, stolen_chunk, stolen_fid_unique;
569   
570   /* Remove LRU elt. (head) from free list */
571   q = QServe(&diskLRUList);
572   if (q == NULL)
573     dcp = NULL;
574   else
575     dcp = (cm_diskcache_t *) MEM_TO_OBJ(cm_diskcache_t, lruq, q);
576   if (dcp == NULL)
577   {
578     DPRINTF("diskcache_Alloc: fid/chunk=%08x/%04d allocation failed\n",
579            fid->unique, chunk);
580     return NULL;
581   }
582
583   /* Use this element for this fid/chunk */
584   if (dcp->f.states == DISK_CACHE_USED)
585   {
586     /* Remove from old hash chain */
587     if (dcp->hash_prev)
588     {
589       assert(dcp->hash_prev->hash_next == dcp);
590       dcp->hash_prev->hash_next = dcp->hash_next;
591     }
592     else
593     {
594       index = DCHash(&dcp->f.fid, dcp->f.chunk);
595       diskHashTable[index] = dcp->hash_next;
596     }
597     if (dcp->hash_next)
598     {
599       assert(dcp->hash_next->hash_prev == dcp);
600       dcp->hash_next->hash_prev = dcp->hash_prev;
601     }
602     
603     stole = 1;
604     stolen_chunk = dcp->f.chunk;
605     stolen_fid_unique = dcp->f.fid.unique;
606   }
607   
608   memcpy(&dcp->f.fid, fid, sizeof(cm_fid_t));
609   dcp->f.chunk = chunk;
610   dcp->f.dataVersion = dataVersion;
611   dcp->f.accessOrd = accessOrd++;
612   dcp->f.states = DISK_CACHE_USED;
613   
614   /* allocate at head of new hash chain */
615   index = DCHash(fid, chunk);
616   /*osi_QAddH(&diskHashTable[index], &dcp->hashq);*/
617   dcp->hash_next = diskHashTable[index];
618   dcp->hash_prev = NULL;
619   if (diskHashTable[index]) diskHashTable[index]->hash_prev = dcp;
620   diskHashTable[index] = dcp;
621
622   /* put at tail of queue */
623   QAddT(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);
624   
625   if (stole)
626     DPRINTF("diskcache_Alloc: fid/chunk=%08x/%04d (recyc fid/chunk=%08x/%04d) "
627            "slot=%d hash=%d dcp=%x\n",
628            fid->unique, chunk, stolen_fid_unique, stolen_chunk,
629            dcp->f.index, index, dcp);
630   else
631     DPRINTF("diskcache_Alloc: fid/chunk=%08x/%04d slot=%d hash=%d dcp=%x\n",
632            fid->unique, chunk, dcp->f.index, index, dcp);
633   return dcp;
634 }
635
636 /* Write this chunk to its disk file */
637 int diskcache_Write(cm_diskcache_t *dcp, /*int bufferNum,*/ char *buf, int size)
638 {
639    char cacheFileName[256];
640    int fd;
641    int rc;
642    int opened = 0;
643    QLink *q;
644    
645    /*return 0;*/
646    
647    DPRINTF("diskcache_Write\n");
648    
649    /* write bytes of buf into chunk file */
650    GEN_CACHE_FILE_NAME(cacheFileName, cm_cachePath, dcp->f.index);
651    if (dcp->openfd == 0)
652    {
653      dcp->openfd = open(cacheFileName, O_RDWR | O_BINARY);
654      if (dcp->openfd < 0)
655      {
656        dcp->openfd = 0;
657        complain("diskcache_Write: Couldn't open cache file %s\n", cacheFileName);
658        return -1;
659      }
660      opened = 1;
661    }
662
663    /*lseek(dcp->openfd, bufferNum * buf_blockSize, SEEK_SET);*/
664    /* only write size bytes */
665    rc = write(dcp->openfd, buf, size);
666    if (rc < 0)
667    {
668       complain("diskcache_Write: Couldn't write cache file %s\n", cacheFileName);
669       close(dcp->openfd); dcp->openfd = 0; return rc;
670    }
671
672    if (opened)
673    {
674      /* add to open file list */
675      QAddT(&openFileList, &dcp->openq, 0);
676      openCacheFiles++;
677    }
678    else
679      QMoveToTail(&openFileList, &dcp->openq, 0);
680
681    if (openCacheFiles >= MAX_OPEN_FILES)
682    {
683      /* close longest-open file */
684      q = QServe(&openFileList);
685      dcp = (cm_diskcache_t *) MEM_TO_OBJ(cm_diskcache_t, openq, q);
686      assert(dcp != NULL);
687      if (dcp->openfd > 0)
688        close(dcp->openfd);
689      dcp->openfd = 0;
690      openCacheFiles--;
691    }
692      
693    return 0;
694 }
695
696 /* we accessed this chunk (hit on buffer read), so move to MRU */
697 void diskcache_Touch(cm_diskcache_t *dcp)
698 {
699   if (!cm_diskCacheEnabled || !dcp) return;
700   dcp->f.accessOrd = accessOrd++;
701   QMoveToTail(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);  /* tail is MRU */
702 }
703
704 /* invalidate this disk cache entry */
705 int diskcache_Invalidate(cm_diskcache_t *dcp)
706 {
707   /* We consider an invalid chunk as empty, so we put it at tail of LRU */
708   QRemove(&diskLRUList, &dcp->lruq);
709
710   dcp->f.accessOrd = 0;
711   dcp->f.states = DISK_CACHE_EMPTY;
712   dcp->f.chunk = 0;
713   memset(&dcp->f.fid, sizeof(cm_fid_t));
714   /*osi_QAdd(diskLRUList, &dcp->lruq);*/
715   QAddH(&diskLRUList, &dcp->lruq, dcp->f.accessOrd);     /* head is LRU */
716 }
717
718 void diskcache_WriteCacheInfo(cm_diskcache_t *dcp)
719 {
720   /*char cacheInfoPath[256];
721     int cacheInfo_fd;*/
722   int rc;
723   
724   /*return;   /* skip this for perf. testing */
725   /*sprintf(cacheInfoPath, "%s\\%s", cm_cachePath, CACHE_INFO_FILE);
726     cacheInfo_fd = open(cacheInfoPath, O_RDWR);*/
727
728   DPRINTF("diskcache_WriteCacheInfo\n");
729
730   lseek(cacheInfo_fd, dcp->f.index * sizeof(struct fcache) +
731         sizeof(cm_cacheInfoHdr_t), SEEK_SET);
732
733   rc = write(cacheInfo_fd, &dcp->f, sizeof(struct fcache));
734   if (rc < 0)
735     complain("diskcache_WriteCacheInfo: Couldn't write cache info file, error=%d\n", errno);
736   /*fsync(cacheInfo_fd);*/
737
738   /*close(cacheInfo_fd);*/
739 }
740
741 void diskcache_Shutdown()
742 {
743   cm_diskcache_t *dcp;
744   QLink *q;
745
746   /* close cache info file */
747   close (cacheInfo_fd);
748   
749   /* close all open cache files */
750   q = QServe(&openFileList);
751   while (q)
752   {
753     dcp = (cm_diskcache_t *) MEM_TO_OBJ(cm_diskcache_t, openq, q);
754     if (dcp->openfd)
755       close(dcp->openfd);
756     q = QServe(&openFileList);
757   }
758 }
759
760 #endif /* DISKCACHE95 */