2 * reactos Cache Manager (Cc*) SharedCacheMap structure of libcaptive
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "sharedcachemap.h" /* self */
23 #include "sharedcachemap-priv.h" /* self */
25 #include "marshallers.h"
26 #include <glib-object.h>
27 #include "privatebcbpin.h"
28 #include "captive/macros.h"
30 #include "reactos/ddk/obfuncs.h"
35 /* Maximum allocatable (without filling in data) on test: 0xA8000000 */
36 #define TOTAL_MAPPED_MAX 0x30000000 /* Maximum mapped area; FIXME: Fix Cache Manager. */
38 static size_t total_mapped=0;
41 gboolean captive_shared_cache_map_restart_needed(void)
43 return total_mapped>=TOTAL_MAPPED_MAX;
47 static GHashTable *CaptiveSharedCacheMapObject_hash;
49 static void CaptiveSharedCacheMapObject_hash_init(void)
51 if (CaptiveSharedCacheMapObject_hash)
53 CaptiveSharedCacheMapObject_hash=g_hash_table_new(
54 g_direct_hash, /* hash_func */
55 g_direct_equal); /* key_equal_func */
59 static gpointer captive_shared_cache_map_object_parent_class=NULL;
62 static void captive_shared_cache_map_object_finalize(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
64 static const CC_FILE_SIZES FileSizes_zero;
67 CaptiveSharedCacheMapObject_page *page;
69 g_return_if_fail(captive_shared_cache_map_object!=NULL);
71 for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
72 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
73 if (!page->data_valid)
75 g_assert(!page->dirty); /* FIXME: Is it allowed by W32? */
78 CaptiveSharedCacheMapObject_hash_init();
79 errbool=g_hash_table_remove(CaptiveSharedCacheMapObject_hash,captive_shared_cache_map_object);
80 g_assert(errbool==TRUE);
82 captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,&FileSizes_zero);
83 g_assert(captive_shared_cache_map_object->buffer==NULL);
84 g_assert(captive_shared_cache_map_object->pages==NULL);
86 if (captive_shared_cache_map_object->pin_hash) {
87 captive_private_bcb_pin_object_hash_destroy(captive_shared_cache_map_object->pin_hash);
88 captive_shared_cache_map_object->pin_hash=NULL;
90 g_assert(captive_shared_cache_map_object->map==NULL);
91 if (captive_shared_cache_map_object->SectionObjectPointer) {
92 g_assert(captive_shared_cache_map_object==captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap);
93 captive_shared_cache_map_object->SectionObjectPointer->SharedCacheMap=NULL;
96 if (captive_shared_cache_map_object->FileObject) {
97 /* W32 dereferences twice. */
98 ObDereferenceObject(captive_shared_cache_map_object->FileObject);
99 captive_shared_cache_map_object->FileObject=NULL;
102 G_OBJECT_CLASS(captive_shared_cache_map_object_parent_class)->finalize((GObject *)captive_shared_cache_map_object);
105 static guint FileSizes_changed_signal;
106 static guint purge_signal;
108 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
109 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength);
110 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object);
112 static void captive_shared_cache_map_object_class_init(CaptiveSharedCacheMapObjectClass *class)
114 GObjectClass *gobject_class=G_OBJECT_CLASS(class);
116 captive_shared_cache_map_object_parent_class=g_type_class_ref(g_type_parent(G_TYPE_FROM_CLASS(class)));
117 gobject_class->finalize=(void (*)(GObject *object))captive_shared_cache_map_object_finalize;
119 class->FileSizes_changed=captive_shared_cache_map_object_FileSizes_changed;
120 class->purge=captive_shared_cache_map_object_purge;
122 FileSizes_changed_signal=g_signal_new("FileSizes_changed",
123 G_TYPE_FROM_CLASS(gobject_class),
125 G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,FileSizes_changed),
127 captive_cc_VOID__UINT64_UINT64_UINT64,
128 G_TYPE_NONE,3,G_TYPE_UINT64,G_TYPE_UINT64,G_TYPE_UINT64);
129 purge_signal=g_signal_new("purge",
130 G_TYPE_FROM_CLASS(gobject_class),
132 G_STRUCT_OFFSET(CaptiveSharedCacheMapObjectClass,purge),
134 captive_cc_VOID__VOID,
139 static void captive_shared_cache_map_object_init(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
141 captive_shared_cache_map_object->pin_hash=captive_private_bcb_pin_object_hash_new();
143 captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
144 g_object_unref(captive_shared_cache_map_object); /* ==sink */
146 CaptiveSharedCacheMapObject_hash_init();
147 g_hash_table_insert(CaptiveSharedCacheMapObject_hash,
148 captive_shared_cache_map_object,captive_shared_cache_map_object);
152 GType captive_shared_cache_map_object_get_type(void)
154 static GType captive_shared_cache_map_object_type=0;
156 if (!captive_shared_cache_map_object_type) {
157 static const GTypeInfo captive_shared_cache_map_object_info={
158 sizeof(CaptiveSharedCacheMapObjectClass),
159 NULL, /* base_init */
160 NULL, /* base_finalize */
161 (GClassInitFunc)captive_shared_cache_map_object_class_init,
162 NULL, /* class_finalize */
163 NULL, /* class_data */
164 sizeof(CaptiveSharedCacheMapObject),
166 (GInstanceInitFunc)captive_shared_cache_map_object_init,
169 captive_shared_cache_map_object_type=g_type_register_static(G_TYPE_OBJECT,
170 "CaptiveSharedCacheMapObject",&captive_shared_cache_map_object_info,0);
173 return captive_shared_cache_map_object_type;
176 static void captive_shared_cache_map_object_FileSizes_changed(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
177 guint64 AllocationSize,guint64 FileSize,guint64 ValidDataLength)
179 size_t size_old,size_new;
180 guint64 size64_old,size64_new;
182 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
184 g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
185 g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
187 size64_old=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);
189 g_assert(size_old==size64_old);
190 size64_new=CAPTIVE_ROUND_UP64(AllocationSize,PAGE_SIZE);
192 if (size_new!=size64_new) {
194 g_error("Mapped size %" G_GUINT64_FORMAT " is too big; FIXME: non-mmap(2)-able areas not yet supported",size64_new);
195 g_assert_not_reached();
198 if (size_old!=size_new) {
199 /* ntfs.sys of NT-5.1sp1 may extend StreamFileObject while dirty pins exist.
200 * How to extend SharedCacheMap size without changing the memory location?
201 * I hope ntfs.sys does not expect long-term absolute position of its
204 if (!(captive_shared_cache_map_object->FileObject->Flags&FO_STREAM_FILE)) {
205 /* These two assertions should be already catched by pin/map signal handlers. */
206 g_assert(!captive_shared_cache_map_object->map);
207 g_assert(!g_hash_table_size(captive_shared_cache_map_object->pin_hash));
211 if (!size_new || size_new > captive_shared_cache_map_object->alloc_length) {
216 alloc64_new=CAPTIVE_ROUND_UP64((!size64_new ? 0 : MAX(size64_new*2,0x10000)),PAGE_SIZE);
217 alloc_new=alloc64_new;
218 if (alloc_new!=alloc64_new)
229 PAGE_SIZE+alloc_new+PAGE_SIZE, /* length; leading and trailing boundary check pages */
230 PROT_READ|PROT_WRITE, /* prot; read/write must be possible although write is not guaranteed to be flushed yet */
231 MAP_PRIVATE|MAP_ANONYMOUS /* flags */
232 |MAP_NORESERVE, /* At least ext2fsd maps the whole disk. */
233 -1, /* fd; ignored due to MAP_ANONYMOUS */
234 0); /* offset; ignored due to MAP_ANONYMOUS */
235 if (base==MAP_FAILED)
237 g_assert(base!=NULL);
238 total_mapped+=alloc_new;
241 errint=munmap(base-PAGE_SIZE,PAGE_SIZE); /* unmap leading boundary check page */
243 errint=munmap(base+alloc_new,PAGE_SIZE); /* unmap trailing boundary check page */
249 memcpy(buffer_new,captive_shared_cache_map_object->buffer,
250 MIN(AllocationSize,captive_shared_cache_map_object->AllocationSize));
252 if (captive_shared_cache_map_object->alloc_length) {
255 errint=munmap(captive_shared_cache_map_object->buffer,captive_shared_cache_map_object->alloc_length);
257 total_mapped-=captive_shared_cache_map_object->alloc_length;
260 captive_shared_cache_map_object->buffer=buffer_new;
262 #if 0 /* It appears it is valid to squeeze out 'dirty' blocks. FIXME: Flush them? */
263 /* FIXME: The code may be no longer valid with the 'alloc_new' introduction! */
264 if (size_old>size_new) {
267 for (now=size_new;now<size_old;now+=PAGE_SIZE) {
268 if (!captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
270 g_assert(!captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty);
275 captive_shared_cache_map_object->pages=g_realloc(captive_shared_cache_map_object->pages,
276 alloc_new/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
278 captive_shared_cache_map_object->alloc_length=alloc_new;
281 if (size_new>size_old) /* prevent 'size_new-size_old' as it is unsigned! */
282 memset(captive_shared_cache_map_object->pages+(size_old/PAGE_SIZE),0,
283 (size_new-size_old)/PAGE_SIZE*sizeof(*captive_shared_cache_map_object->pages));
285 captive_shared_cache_map_object->AllocationSize=AllocationSize;
286 captive_shared_cache_map_object->FileSize=FileSize;
287 captive_shared_cache_map_object->ValidDataLength=ValidDataLength;
289 g_assert((!captive_shared_cache_map_object->buffer)==(!captive_shared_cache_map_object->AllocationSize));
290 g_assert((!captive_shared_cache_map_object->pages)==(!captive_shared_cache_map_object->AllocationSize));
293 static void captive_shared_cache_map_object_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
295 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
297 /* NOP; just to provide slot for checking by Bcbs */
300 CaptiveSharedCacheMapObject *captive_shared_cache_map_get_ref(FILE_OBJECT *FileObject,
301 const CC_FILE_SIZES *FileSizes,BOOLEAN PinAccess,const CACHE_MANAGER_CALLBACKS *CallBacks,VOID *LazyWriterContext)
303 CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
305 g_return_val_if_fail(FileObject!=NULL,NULL);
306 g_return_val_if_fail(FileObject->SectionObjectPointer!=NULL,NULL);
307 g_return_val_if_fail(FileSizes!=NULL,NULL);
308 g_return_val_if_fail(CallBacks!=NULL,NULL);
310 if ((captive_shared_cache_map_object=FileObject->SectionObjectPointer->SharedCacheMap)) {
311 captive_shared_cache_map_w32_ref(captive_shared_cache_map_object);
314 captive_shared_cache_map_object=g_object_new(
315 CAPTIVE_SHARED_CACHE_MAP_TYPE_OBJECT, /* object_type */
316 NULL); /* first_property_name; FIXME: support properties */
318 /* FIXME: When to drop SharedCacheMap?
319 * Currently we never close it.
320 * Fix also CcZeroData() workaround.
322 g_object_ref(captive_shared_cache_map_object);
324 /* W32 references twice. */
325 ObReferenceObject(FileObject);
326 captive_shared_cache_map_object->FileObject=FileObject;
327 captive_shared_cache_map_object->SectionObjectPointer=FileObject->SectionObjectPointer;
328 captive_shared_cache_map_object->AllocationSize=0;
329 captive_shared_cache_map_object->FileSize=0;
330 captive_shared_cache_map_object->ValidDataLength=0;
331 captive_shared_cache_map_object->PinAccess=PinAccess;
332 captive_shared_cache_map_object->CallBacks=*CallBacks;
333 captive_shared_cache_map_object->LazyWriterContext=LazyWriterContext;
335 FileObject->SectionObjectPointer->SharedCacheMap=captive_shared_cache_map_object;
338 g_assert(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
339 /* FileObject may differ - we can have a different reference to the same FCB. */
340 g_assert(FileObject->SectionObjectPointer==captive_shared_cache_map_object->SectionObjectPointer);
341 g_assert(PinAccess==captive_shared_cache_map_object->PinAccess);
342 g_assert(CallBacks->AcquireForLazyWrite==captive_shared_cache_map_object->CallBacks.AcquireForLazyWrite);
343 g_assert(CallBacks->ReleaseFromLazyWrite==captive_shared_cache_map_object->CallBacks.ReleaseFromLazyWrite);
344 g_assert(CallBacks->AcquireForReadAhead==captive_shared_cache_map_object->CallBacks.AcquireForReadAhead);
345 g_assert(CallBacks->ReleaseFromReadAhead==captive_shared_cache_map_object->CallBacks.ReleaseFromReadAhead);
346 g_assert(LazyWriterContext==captive_shared_cache_map_object->LazyWriterContext);
348 captive_shared_cache_map_FileSizes_set(captive_shared_cache_map_object,FileSizes);
350 return captive_shared_cache_map_object;
353 void captive_shared_cache_map_FileSizes_set(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
354 const CC_FILE_SIZES *FileSizes)
356 guint64 AllocationSize,FileSize,ValidDataLength;
358 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
359 g_return_if_fail(FileSizes!=NULL);
361 AllocationSize=FileSizes->AllocationSize.QuadPart;
362 FileSize=FileSizes->FileSize.QuadPart;
363 ValidDataLength=FileSizes->ValidDataLength.QuadPart;
365 /* Do not: if (ValidDataLength==G_MAXINT64)
366 * ValidDataLength=FileSize;
367 * In some cases (during NTFS mount) there may be also invalid 'ValidDataLength' at all:
368 * CcSetFileSizes(AllocationSize=0x1000000,FileSize=0xf80208,ValidDataLength=0x23b801a0)
370 ValidDataLength=FileSize;
372 g_assert(AllocationSize>=0);
373 g_assert(FileSize>=0);
374 g_assert(ValidDataLength>=0);
376 g_assert(ValidDataLength<=FileSize);
377 /* Do not: g_assert(0==(AllocationSize%0x200));
378 * as it is true for ntfs.sys of NT-5.1sp1 but it fails for fastfat.sys of NT-5.1sp1.
380 /* AllocationSize can be much higher: */
381 g_assert(FileSize<=AllocationSize);
383 /* Prevent signalling if not needed. */
385 || captive_shared_cache_map_object->AllocationSize!=AllocationSize
386 || captive_shared_cache_map_object->FileSize!=FileSize
387 || captive_shared_cache_map_object->ValidDataLength!=ValidDataLength) {
388 /* Signalling is forbidden in captive_shared_cache_map_object_finalize(). */
389 if (G_OBJECT(captive_shared_cache_map_object)->ref_count==0) {
390 (*CAPTIVE_SHARED_CACHE_MAP_OBJECT_GET_CLASS(captive_shared_cache_map_object)->FileSizes_changed)
391 (captive_shared_cache_map_object,AllocationSize,FileSize,ValidDataLength);
394 g_signal_emit(captive_shared_cache_map_object,FileSizes_changed_signal,0,
395 AllocationSize,FileSize,ValidDataLength);
399 g_assert(captive_shared_cache_map_object->AllocationSize==AllocationSize);
400 g_assert(captive_shared_cache_map_object->FileSize==FileSize);
401 g_assert(captive_shared_cache_map_object->ValidDataLength==ValidDataLength);
404 CaptiveSharedCacheMapObject *captive_SectionObjectPointers_to_SharedCacheMap(SECTION_OBJECT_POINTERS *SectionObjectPointers)
406 g_return_val_if_fail(SectionObjectPointers!=NULL,NULL);
407 g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(SectionObjectPointers->SharedCacheMap),NULL);
409 return SectionObjectPointers->SharedCacheMap;
412 CaptiveSharedCacheMapObject *captive_FileObject_to_SharedCacheMap(FILE_OBJECT *FileObject)
414 g_return_val_if_fail(FileObject!=NULL,NULL);
416 return captive_SectionObjectPointers_to_SharedCacheMap(FileObject->SectionObjectPointer);
419 void captive_shared_cache_map_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
421 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
423 g_object_ref(captive_shared_cache_map_object);
424 captive_shared_cache_map_object->w32_ref_count++;
427 void captive_shared_cache_map_w32_unref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
429 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
430 g_return_if_fail(G_OBJECT(captive_shared_cache_map_object)->ref_count>0);
432 captive_shared_cache_map_object->w32_ref_count--;
433 g_object_unref(captive_shared_cache_map_object);
436 gint captive_shared_cache_map_query_w32_ref(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
438 g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
440 return captive_shared_cache_map_object->w32_ref_count;
443 void captive_shared_cache_map_data_validate_read(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
444 FILE_OBJECT *FileObject,guint64 start,guint64 end)
447 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
449 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
450 g_return_if_fail(captive_shared_cache_map_object==captive_FileObject_to_SharedCacheMap(FileObject));
451 g_return_if_fail(start<=end);
452 g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
454 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
455 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
457 for (now=start;now<end;now+=PAGE_SIZE) {
458 LARGE_INTEGER now_LargeInteger;
461 if (captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid)
463 now_LargeInteger.QuadPart=now;
464 got=captive_Cc_IoPageRead(FileObject,captive_shared_cache_map_object->buffer+now,PAGE_SIZE,&now_LargeInteger);
468 g_assert(got<=PAGE_SIZE);
469 after_eof=(got<PAGE_SIZE);
470 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
471 captive_shared_cache_map_object->pages[now/PAGE_SIZE].dirty=FALSE;
475 void captive_shared_cache_map_data_validate_noread(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
476 guint64 start,guint64 end)
480 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
481 g_return_if_fail(start<=end);
482 g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
484 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
485 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
487 for (now=start;now<end;now+=PAGE_SIZE) {
488 g_assert(captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid);
492 void captive_shared_cache_map_set_data_valid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
493 guint64 start,guint64 end)
497 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
498 g_return_if_fail(start<=end);
499 g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
501 start=CAPTIVE_ROUND_UP64(start,PAGE_SIZE);
502 if (end<captive_shared_cache_map_object->FileSize)
503 end=CAPTIVE_ROUND_DOWN64(end,PAGE_SIZE);
505 /* We can validate the last page of the file
506 * even if it does not end on PAGE_SIZE boundary.
508 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
510 /* We may get end<start here - it is valid to not to validate anything. */
512 for (now=start;now<end;now+=PAGE_SIZE) {
513 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=TRUE;
514 /* .dirty is undefined */
518 void captive_shared_cache_map_set_data_invalid(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
519 guint64 start,guint64 end)
523 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
524 g_return_if_fail(start<=end);
525 g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
527 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
528 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
530 for (now=start;now<end;now+=PAGE_SIZE) {
531 captive_shared_cache_map_object->pages[now/PAGE_SIZE].data_valid=FALSE;
535 void captive_shared_cache_map_set_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
536 guint64 start,guint64 end)
539 CaptiveSharedCacheMapObject_page *page;
541 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
542 g_return_if_fail(end<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
544 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
545 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
547 for (now=start;now<end;now+=PAGE_SIZE) {
548 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
549 g_assert(page->data_valid);
558 typedef struct _captive_shared_cache_map_memory_range_set_dirty_param
559 captive_shared_cache_map_memory_range_set_dirty_param;
560 struct _captive_shared_cache_map_memory_range_set_dirty_param {
561 gpointer address_start;
562 gpointer address_end;
566 static void captive_shared_cache_map_memory_range_set_dirty_foreach(
567 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
568 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
569 captive_shared_cache_map_memory_range_set_dirty_param *param) /* user_data */
571 gpointer address_start_local,address_end_local;
573 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
574 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
575 g_return_if_fail(param!=NULL);
577 address_start_local=MAX(param->address_start,captive_shared_cache_map_object->buffer);
578 address_end_local=MIN(param->address_end,captive_shared_cache_map_object->buffer+captive_shared_cache_map_object->AllocationSize);
580 if (address_start_local>address_end_local)
583 captive_shared_cache_map_set_dirty(captive_shared_cache_map_object,
584 address_start_local-captive_shared_cache_map_object->buffer, /* start */
585 address_end_local -captive_shared_cache_map_object->buffer); /* end */
587 param->bytes_set+=(address_end_local-address_start_local);
590 guint64 captive_shared_cache_map_memory_range_set_dirty(gpointer address_start,gpointer address_end)
592 captive_shared_cache_map_memory_range_set_dirty_param set_dirty_param;
594 g_return_val_if_fail(address_start!=NULL,0);
595 g_return_val_if_fail(address_end!=NULL,0);
596 g_return_val_if_fail(address_start<=address_end,0);
598 address_start=CAPTIVE_ROUND_DOWN(address_start,PAGE_SIZE);
599 address_end=CAPTIVE_ROUND_UP(address_end,PAGE_SIZE);
601 CaptiveSharedCacheMapObject_hash_init();
602 set_dirty_param.address_start=address_start;
603 set_dirty_param.address_end=address_end;
604 set_dirty_param.bytes_set=0;
605 g_hash_table_foreach(
606 CaptiveSharedCacheMapObject_hash, /* hash_table */
607 (GHFunc)captive_shared_cache_map_memory_range_set_dirty_foreach, /* func */
608 &set_dirty_param); /* user_data */
610 return set_dirty_param.bytes_set;
613 gboolean captive_shared_cache_map_is_page_dirty(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
616 CaptiveSharedCacheMapObject_page *page;
618 g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),FALSE);
619 g_return_val_if_fail(offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE),FALSE);
620 g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE),FALSE);
621 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
622 g_return_val_if_fail(page->data_valid,FALSE);
627 void captive_shared_cache_map_page_set_lsn(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
628 guint64 offset,gint64 lsn)
630 CaptiveSharedCacheMapObject_page *page;
632 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
633 g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
634 g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
635 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
636 g_return_if_fail(page->data_valid);
637 g_return_if_fail(page->dirty);
638 g_return_if_fail(page->lsn_oldest<=page->lsn_newest);
639 g_return_if_fail(!page->lsn_newest || lsn>=page->lsn_newest);
640 g_return_if_fail(captive_shared_cache_map_object->LogHandle_set);
641 g_return_if_fail(captive_shared_cache_map_object->FlushToLsnRoutine_set);
643 if (!page->lsn_oldest)
644 page->lsn_oldest=lsn;
645 page->lsn_newest=lsn;
648 void captive_shared_cache_map_purge(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
651 CaptiveSharedCacheMapObject_page *page;
653 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
655 g_signal_emit(captive_shared_cache_map_object,purge_signal,0);
657 /* Needed by fastfat.sys of NT-5.1sp1 during FAT32 unmount
658 * otherwise it fails on: g_assert(!page->dirty);
659 * It corrupts the disk if the buffer is dropped instead.
661 captive_shared_cache_map_flush(captive_shared_cache_map_object,
662 0,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
664 for (offset=0;offset<CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);offset+=PAGE_SIZE) {
665 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
666 if (!page->data_valid)
668 g_assert(!page->dirty);
669 page->data_valid=FALSE;
673 static VOID *captive_LogHandle;
674 static PFLUSH_TO_LSN captive_FlushToLsnRoutine;
676 void captive_shared_cache_map_set_LogHandle(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,VOID *LogHandle)
678 g_return_if_fail(!captive_shared_cache_map_object || CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
679 /* FIXME: 'captive_shared_cache_map_object->LogHandle_set' may be set.
680 * Does it mean 'LogHandle_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
683 /* Do not: if (!LogHandle)
685 * See CcGetDirtyPages()/"W32 undocumented: What does mean LogHandle==NULL?"
687 g_assert(!captive_LogHandle || !LogHandle || captive_LogHandle==LogHandle);
688 captive_LogHandle=LogHandle;
689 if (captive_shared_cache_map_object)
690 captive_shared_cache_map_object->LogHandle_set=!!LogHandle;
693 void captive_shared_cache_map_set_FlushToLsnRoutine
694 (CaptiveSharedCacheMapObject *captive_shared_cache_map_object,PFLUSH_TO_LSN FlushToLsnRoutine)
696 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
697 /* FIXME: 'captive_shared_cache_map_object->FlushToLsnRoutine_set' may be set.
698 * Does it mean 'FlushToLsnRoutine_set' is 'FileObject'-based instead of 'SharedCacheMap'-based?
701 if (!FlushToLsnRoutine)
703 g_assert(!captive_FlushToLsnRoutine || captive_FlushToLsnRoutine==FlushToLsnRoutine);
704 captive_FlushToLsnRoutine=FlushToLsnRoutine;
705 if (FlushToLsnRoutine)
706 captive_shared_cache_map_object->FlushToLsnRoutine_set=TRUE;
709 static void captive_shared_cache_map_page_write(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
712 LARGE_INTEGER offset_LargeInteger;
713 static gint64 lsn_last;
714 CaptiveSharedCacheMapObject_page *page;
716 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
717 g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
718 g_return_if_fail(offset<=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
719 g_return_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(offset,PAGE_SIZE));
720 page=captive_shared_cache_map_object->pages+offset/PAGE_SIZE;
721 g_return_if_fail(page->data_valid);
722 g_return_if_fail(page->dirty);
724 if (page->lsn_newest) {
726 g_assert(!lsn_last || lsn_last<=page->lsn_newest);
727 lsn_last=page->lsn_newest;
729 captive_stdcall_call_12((CaptiveStdCallFunc12)captive_FlushToLsnRoutine,
731 (gpointer)(guint32)(page->lsn_newest>> 0U), /* 'LARGE_INTEGER' argument */
732 (gpointer)(guint32)(page->lsn_newest>>32U));
735 offset_LargeInteger.QuadPart=offset;
736 captive_Cc_IoPageWrite(captive_shared_cache_map_object->FileObject,
737 captive_shared_cache_map_object->buffer+offset,PAGE_SIZE,&offset_LargeInteger);
744 typedef struct _captive_shared_cache_map_flush_lsn_sort captive_shared_cache_map_flush_lsn_sort;
745 struct _captive_shared_cache_map_flush_lsn_sort {
747 CaptiveSharedCacheMapObject *captive_shared_cache_map_object;
751 typedef struct _captive_shared_cache_map_flush_lsn_pages_foreach_param
752 captive_shared_cache_map_flush_lsn_pages_foreach_param;
753 struct _captive_shared_cache_map_flush_lsn_pages_foreach_param {
755 guint lsn_pages_count;
756 captive_shared_cache_map_flush_lsn_sort *lsn_pages_pointer; /* Not filled in if NULL */
759 static void captive_shared_cache_map_flush_lsn_pages_foreach(
760 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
761 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
762 captive_shared_cache_map_flush_lsn_pages_foreach_param *param) /* user_data */
765 CaptiveSharedCacheMapObject_page *page;
767 #if 0 /* acceleration */
768 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
769 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
770 g_return_if_fail(param!=NULL);
773 for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
774 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
775 if (!page->data_valid)
779 if (!page->lsn_newest)
781 if (page->lsn_newest>param->lsn_target)
783 param->lsn_pages_count++;
784 if (!param->lsn_pages_pointer)
786 param->lsn_pages_pointer->lsn=page->lsn_newest;
787 param->lsn_pages_pointer->captive_shared_cache_map_object=captive_shared_cache_map_object;
788 param->lsn_pages_pointer->offset=now;
789 param->lsn_pages_pointer++;
793 static int captive_shared_cache_map_flush_lsn_pages_compar
794 (const captive_shared_cache_map_flush_lsn_sort *a,const captive_shared_cache_map_flush_lsn_sort *b)
796 #if 0 /* acceleration */
797 g_return_val_if_fail(a!=NULL,0);
798 g_return_val_if_fail(b!=NULL,0);
801 return (a->lsn>b->lsn)-(b->lsn>a->lsn);
804 guint64 captive_shared_cache_map_flush(CaptiveSharedCacheMapObject *captive_shared_cache_map_object,
805 guint64 start,guint64 end)
810 captive_shared_cache_map_flush_lsn_pages_foreach_param lsn_pages_foreach_param;
811 captive_shared_cache_map_flush_lsn_sort *lsn_pages_pointer;
812 const captive_shared_cache_map_flush_lsn_sort *lsn_page;
813 guint lsn_pages_count;
815 g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),0);
816 g_return_val_if_fail(start<=end,0);
818 end=MIN(end,CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE));
820 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
821 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
824 for (now=start;now<end;now+=PAGE_SIZE) {
825 CaptiveSharedCacheMapObject_page *page;
827 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
828 if (!page->data_valid)
832 if (!page->lsn_newest)
834 if (!lsn_target || lsn_target<page->lsn_newest)
835 lsn_target=page->lsn_newest;
838 CaptiveSharedCacheMapObject_hash_init();
840 lsn_pages_foreach_param.lsn_target=lsn_target;
841 lsn_pages_foreach_param.lsn_pages_count=0;
842 lsn_pages_foreach_param.lsn_pages_pointer=NULL; /* Not yet filling */
843 g_hash_table_foreach(
844 CaptiveSharedCacheMapObject_hash, /* hash_table */
845 (GHFunc)captive_shared_cache_map_flush_lsn_pages_foreach, /* func */
846 &lsn_pages_foreach_param); /* user_data */
848 lsn_pages_count=lsn_pages_foreach_param.lsn_pages_count;
849 captive_newn(lsn_pages_pointer,lsn_pages_count);
850 g_assert(lsn_pages_foreach_param.lsn_target==lsn_target);
851 lsn_pages_foreach_param.lsn_pages_count=0;
852 lsn_pages_foreach_param.lsn_pages_pointer=lsn_pages_pointer;
853 g_hash_table_foreach(
854 CaptiveSharedCacheMapObject_hash, /* hash_table */
855 (GHFunc)captive_shared_cache_map_flush_lsn_pages_foreach, /* func */
856 &lsn_pages_foreach_param); /* user_data */
858 g_assert(lsn_pages_foreach_param.lsn_target==lsn_target);
859 g_assert(lsn_pages_foreach_param.lsn_pages_count==lsn_pages_count);
860 g_assert(lsn_pages_foreach_param.lsn_pages_pointer==lsn_pages_pointer+lsn_pages_count);
862 qsort(lsn_pages_pointer,lsn_pages_count,sizeof(*lsn_pages_pointer),
863 (int (*)(const void *,const void *))captive_shared_cache_map_flush_lsn_pages_compar);
867 for (lsn_page=lsn_pages_pointer;lsn_page<lsn_pages_pointer+lsn_pages_count;lsn_page++) {
868 captive_shared_cache_map_page_write(lsn_page->captive_shared_cache_map_object,lsn_page->offset);
869 if (lsn_page->captive_shared_cache_map_object==captive_shared_cache_map_object
870 && lsn_page->offset>=start && lsn_page->offset<end)
874 g_free(lsn_pages_pointer);
876 for (now=start;now<end;now+=PAGE_SIZE) {
877 CaptiveSharedCacheMapObject_page *page;
879 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
880 if (!page->data_valid)
884 captive_shared_cache_map_page_write(captive_shared_cache_map_object,now);
888 /* We were calling W32 code - recheck our task completion. */
889 for (now=start;now<end;now+=PAGE_SIZE) {
890 CaptiveSharedCacheMapObject_page *page;
892 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
893 if (!page->data_valid)
895 g_assert(!page->dirty);
901 static void captive_shared_cache_map_flush_all_foreach_flush(
902 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
903 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
904 gboolean user_data) /* unused */
906 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
907 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
909 captive_shared_cache_map_flush(captive_shared_cache_map_object,0,G_MAXUINT64-1); /* '-1' for overflow safety */
912 static void captive_shared_cache_map_flush_all_foreach_check(
913 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
914 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
915 gboolean user_data) /* unused */
920 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
921 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
924 end=CAPTIVE_ROUND_UP64(captive_shared_cache_map_object->AllocationSize,PAGE_SIZE);
926 start=CAPTIVE_ROUND_DOWN64(start,PAGE_SIZE);
927 end=CAPTIVE_ROUND_UP64(end,PAGE_SIZE);
929 /* We were calling W32 code - recheck our task completion. */
930 for (now=start;now<end;now+=PAGE_SIZE) {
931 CaptiveSharedCacheMapObject_page *page;
933 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
934 if (!page->data_valid)
936 g_assert(!page->dirty);
940 void captive_shared_cache_map_flush_all(void)
942 CaptiveSharedCacheMapObject_hash_init();
943 g_hash_table_foreach(
944 CaptiveSharedCacheMapObject_hash, /* hash_table */
945 (GHFunc)captive_shared_cache_map_flush_all_foreach_flush, /* func */
946 NULL); /* user_data; unused */
947 g_hash_table_foreach(
948 CaptiveSharedCacheMapObject_hash, /* hash_table */
949 (GHFunc)captive_shared_cache_map_flush_all_foreach_check, /* func */
950 NULL); /* user_data; unused */
953 static void captive_shared_cache_map_is_any_dirty_foreach(
954 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
955 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
956 gboolean *dirty_foundp) /* user_data */
960 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
961 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
962 g_return_if_fail(dirty_foundp!=NULL);
964 for (now=0;now<captive_shared_cache_map_object->AllocationSize;now+=PAGE_SIZE) {
965 CaptiveSharedCacheMapObject_page *page;
967 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
968 if (!page->data_valid)
972 *dirty_foundp=TRUE; /* FIXME: stop the traversal. */
978 gboolean captive_shared_cache_map_is_any_dirty(void)
980 gboolean dirty_found;
982 CaptiveSharedCacheMapObject_hash_init();
984 g_hash_table_foreach(
985 CaptiveSharedCacheMapObject_hash, /* hash_table */
986 (GHFunc)captive_shared_cache_map_is_any_dirty_foreach, /* func */
987 &dirty_found); /* user_data */
993 typedef struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param captive_shared_cache_map_CcGetDirtyPages_foreach_param;
994 struct _captive_shared_cache_map_CcGetDirtyPages_foreach_param {
995 PDIRTY_PAGE_ROUTINE DirtyPageRoutine;
1001 static void captive_shared_cache_map_CcGetDirtyPages_foreach(
1002 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
1003 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
1004 captive_shared_cache_map_CcGetDirtyPages_foreach_param *param) /* user_data */
1007 CaptiveSharedCacheMapObject_page *page;
1009 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
1010 g_return_if_fail(captive_shared_cache_map_object->FileObject!=NULL);
1011 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
1012 g_return_if_fail(param!=NULL);
1014 for (now=CAPTIVE_ROUND_DOWN64(captive_shared_cache_map_object->AllocationSize-1,PAGE_SIZE);now>=0;now-=PAGE_SIZE) {
1015 LARGE_INTEGER now_LargeInteger,lsn_oldest_LargeInteger,lsn_newest_LargeInteger;
1017 page=captive_shared_cache_map_object->pages+now/PAGE_SIZE;
1018 if (!page->data_valid)
1022 if (page->lsn_oldest && (!param->result || param->result>page->lsn_oldest))
1023 param->result=page->lsn_oldest;
1025 now_LargeInteger.QuadPart=now;
1026 lsn_oldest_LargeInteger.QuadPart=page->lsn_oldest;
1027 lsn_newest_LargeInteger.QuadPart=page->lsn_newest;
1028 (*param->DirtyPageRoutine)(
1029 captive_shared_cache_map_object->FileObject, /* FileObject */
1030 &now_LargeInteger, /* FileOffset */
1031 PAGE_SIZE, /* Length */
1032 &lsn_oldest_LargeInteger, /* OldestLsn */
1033 &lsn_newest_LargeInteger, /* NewestLsn */
1034 param->Context1, /* Context1 */
1035 param->Context2); /* Context2 */
1039 gint64 captive_shared_cache_map_CcGetDirtyPages(PDIRTY_PAGE_ROUTINE DirtyPageRoutine,VOID *Context1,VOID *Context2)
1041 captive_shared_cache_map_CcGetDirtyPages_foreach_param param;
1043 g_return_val_if_fail(DirtyPageRoutine!=NULL,0);
1045 param.DirtyPageRoutine=DirtyPageRoutine;
1046 param.Context1=Context1;
1047 param.Context2=Context2;
1050 CaptiveSharedCacheMapObject_hash_init();
1051 g_hash_table_foreach(
1052 CaptiveSharedCacheMapObject_hash, /* hash_table */
1053 (GHFunc)captive_shared_cache_map_CcGetDirtyPages_foreach, /* func */
1054 ¶m); /* user_data */
1056 return param.result;
1059 gpointer captive_shared_cache_map_get_buffer(CaptiveSharedCacheMapObject *captive_shared_cache_map_object)
1061 g_return_val_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object),NULL);
1062 g_return_val_if_fail(captive_shared_cache_map_object->buffer!=NULL,NULL);
1064 return captive_shared_cache_map_object->buffer;
1067 static void captive_cc_FileObject_delete_foreach(
1068 CaptiveSharedCacheMapObject *captive_shared_cache_map_object, /* key */
1069 CaptiveSharedCacheMapObject *captive_shared_cache_map_object_value, /* value */
1070 FILE_OBJECT *FileObject) /* user_data */
1072 g_return_if_fail(CAPTIVE_SHARED_CACHE_MAP_IS_OBJECT(captive_shared_cache_map_object));
1073 g_return_if_fail(captive_shared_cache_map_object==captive_shared_cache_map_object_value);
1074 g_return_if_fail(FileObject!=NULL);
1076 g_assert(FileObject!=captive_shared_cache_map_object->FileObject);
1079 BOOLEAN captive_cc_FileObject_delete(FILE_OBJECT *FileObject)
1081 g_return_val_if_fail(FileObject!=NULL,FALSE);
1083 CaptiveSharedCacheMapObject_hash_init();
1084 g_hash_table_foreach(
1085 CaptiveSharedCacheMapObject_hash, /* hash_table */
1086 (GHFunc)captive_cc_FileObject_delete_foreach, /* func */
1087 FileObject); /* user_data */
1089 return FALSE; /* FIXME: remove useless return code. */