2 * reactos Cache Manager mapper emulation of libcaptive
3 * Copyright (C) 2002 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 "reactos/ddk/ccfuncs.h" /* self */
23 #include <glib/gmessages.h>
24 #include <glib/ghash.h>
25 #include <glib/gmem.h>
27 #include "reactos/ddk/mmfuncs.h" /* for MmCreateMdl() */
28 #include "reactos/ddk/kefuncs.h" /* for KeInitializeEvent() */
29 #include "reactos/ddk/iofuncs.h" /* for IoPageRead() */
30 #include "captive/macros.h"
31 #include <sys/types.h>
34 #include <glib/glist.h>
35 #include "reactos/internal/io.h" /* for IoSynchronousPageWrite() */
40 #define CAPTIVE_PUBLIC_BCB_NODETYPECODE 0xDE45 /* FIXME: unknown, undocumented */
43 static gboolean validate_Bcb(const PUBLIC_BCB *PublicBcb)
45 g_return_val_if_fail(PublicBcb!=NULL,FALSE);
46 g_return_val_if_fail(PublicBcb->NodeTypeCode==CAPTIVE_PUBLIC_BCB_NODETYPECODE,FALSE);
47 g_return_val_if_fail(PublicBcb->NodeByteSize==sizeof(*PublicBcb),FALSE);
48 g_return_val_if_fail(PublicBcb->MappedLength>0,FALSE);
49 g_return_val_if_fail(PublicBcb->MappedFileOffset.QuadPart>=0,FALSE);
55 /* position in file */
56 struct page_position {
57 FILE_OBJECT *FileObject;
58 LARGE_INTEGER FileOffset; /* always PAGE_SIZE aligned */
60 GList *privbcb_list; /* each mapped page has its one private_bcb */
63 /* map: (struct page_position *)pagepos -> (struct page_position *)pagepos */
64 static GHashTable *page_position_hash;
66 static gboolean validate_page_position(const struct page_position *pagepos)
69 struct shmid_ds shmid_ds;
71 g_return_val_if_fail(pagepos!=NULL,FALSE);
72 g_return_val_if_fail(pagepos->FileObject!=NULL,FALSE);
73 g_return_val_if_fail(pagepos->FileOffset.QuadPart>=0,FALSE);
74 g_return_val_if_fail(0==CAPTIVE_ROUND_DOWN_EXCEEDING64(pagepos->FileOffset.QuadPart,PAGE_SIZE),FALSE);
75 /* 'pagepos->shmid' may be -1 */
76 /* 'pagepos->privbcb_list' may be empty */
77 g_return_val_if_fail((pagepos->shmid==-1)==(pagepos->privbcb_list==NULL),FALSE); /* either deleted or alive */
79 if (pagepos->shmid!=-1) {
80 errint=shmctl(pagepos->shmid,
83 g_return_val_if_fail(errint==0,FALSE);
85 g_return_val_if_fail(shmid_ds.shm_perm.uid==geteuid(),FALSE);
86 g_return_val_if_fail(shmid_ds.shm_perm.gid==getegid(),FALSE);
87 g_return_val_if_fail(shmid_ds.shm_perm.cuid==geteuid(),FALSE);
88 g_return_val_if_fail(shmid_ds.shm_perm.cgid==getegid(),FALSE);
89 /* 'shm_perm.mode' was seen with sticky bit 01000: */
90 g_return_val_if_fail((shmid_ds.shm_perm.mode&0777)==0600,FALSE);
91 g_return_val_if_fail(shmid_ds.shm_segsz==PAGE_SIZE,FALSE);
92 g_return_val_if_fail(shmid_ds.shm_cpid==getpid(),FALSE);
93 g_return_val_if_fail(shmid_ds.shm_lpid==getpid(),FALSE);
94 g_return_val_if_fail(shmid_ds.shm_nattch==g_list_length(pagepos->privbcb_list),FALSE);
100 static guint page_position_hash_hash_func(const struct page_position *key)
102 g_return_val_if_fail(validate_page_position(key),0);
104 return ((guint)key->FileObject)^(key->FileOffset.QuadPart);
107 static gboolean page_position_hash_key_equal_func(const struct page_position *a,const struct page_position *b)
109 g_return_val_if_fail(validate_page_position(a),FALSE);
110 g_return_val_if_fail(validate_page_position(b),FALSE);
112 return (a->FileObject==b->FileObject && a->FileOffset.QuadPart==b->FileOffset.QuadPart);
115 static void page_position_hash_key_destroy_func(struct page_position *key)
117 g_return_if_fail(validate_page_position(key));
118 g_assert(key->privbcb_list==NULL);
119 g_assert(key->shmid==-1);
124 static void page_position_hash_init(void)
126 if (page_position_hash)
128 page_position_hash=g_hash_table_new_full(
129 (GHashFunc)page_position_hash_hash_func, /* hash_func */
130 (GEqualFunc)page_position_hash_key_equal_func, /* key_equal_func */
131 (GDestroyNotify)page_position_hash_key_destroy_func, /* key_destroy_func */
132 NULL); /* value_destroy_func */
137 PUBLIC_BCB *PublicBcb; /* ->MappedLength, ->MappedFileOffset */
138 FILE_OBJECT *FileObject;
140 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
141 ULONG MappedLength; /* It is the real requested size; it is not PAGE_SIZE aligned. */
142 /* we save it here as 'PublicBcb' may be already destroyed in private_bcb_hash_value_destroy_func(): */
143 LARGE_INTEGER MappedFileOffset; /* It is the real requested offset; it is not PAGE_SIZE aligned. */
144 gpointer base; /* It is the pointer corresponding to MappedFileOffset; it is not PAGE_SIZE aligned. */
148 /* map: (PUBLIC_BCB *)PublicBcb -> (struct private_bcb *)privbcb */
149 static GHashTable *private_bcb_hash;
151 static void private_bcb_hash_key_destroy_func(PUBLIC_BCB *key)
153 g_return_if_fail(validate_Bcb(key));
158 static void private_bcb_hash_value_destroy_func(struct private_bcb *value)
160 struct page_position pagepos_local;
164 gpointer base_aligned;
166 g_return_if_fail(value!=NULL);
167 /* We cannot do 'validate_Bcb(value->PublicBcb)' here as 'value->PublicBcb'
168 * may got already destroyed by 'private_bcb_hash_key_destroy_func(key)'
170 g_return_if_fail(value->PublicBcb!=NULL);
171 g_return_if_fail(value->FileObject!=NULL);
172 g_return_if_fail(value->ref_count==0);
173 g_return_if_fail(value->MappedLength>0);
174 g_return_if_fail(value->MappedFileOffset.QuadPart>=0);
175 g_return_if_fail(value->base!=NULL);
176 g_return_if_fail(value->dirty==FALSE);
178 page_position_hash_init();
180 base_aligned=((char *)value->base)-CAPTIVE_ROUND_DOWN_EXCEEDING64(value->MappedFileOffset.QuadPart,PAGE_SIZE);
182 pagepos_local.FileObject=value->FileObject;
183 pagepos_local.privbcb_list=NULL;
184 pagepos_local.shmid=-1;
187 offset<value->MappedLength;
189 struct page_position *pagepos;
191 pagepos_local.FileOffset.QuadPart=CAPTIVE_ROUND_DOWN64(value->MappedFileOffset.QuadPart+offset,PAGE_SIZE);
192 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
193 g_assert(validate_page_position(pagepos));
194 g_assert(pagepos->privbcb_list!=NULL);
195 errint=shmdt(((char *)base_aligned)+offset);
198 g_assert(g_list_find(pagepos->privbcb_list,value)!=NULL);
199 pagepos->privbcb_list=g_list_remove(pagepos->privbcb_list,value);
200 g_assert(g_list_find(pagepos->privbcb_list,value)==NULL);
202 if (!pagepos->privbcb_list) {
203 /* It should be destroyed automatically as IPC_RMID should be pending from its foundation. */
205 errbool=g_hash_table_remove(page_position_hash,&pagepos_local);
206 g_assert(errbool==TRUE);
209 g_assert(validate_page_position(pagepos));
215 static void private_bcb_hash_init(void)
217 if (private_bcb_hash)
219 private_bcb_hash=g_hash_table_new_full(
220 g_direct_hash, /* hash_func */
221 g_direct_equal, /* key_equal_func */
222 (GDestroyNotify)private_bcb_hash_key_destroy_func, /* key_destroy_func */
223 (GDestroyNotify)private_bcb_hash_value_destroy_func); /* value_destroy_func */
227 static ULONG captive_Cc_IoPageRead(FILE_OBJECT *FileObject,gpointer address,ULONG length,LARGE_INTEGER *FileOffset)
231 IO_STATUS_BLOCK IoStatus;
234 g_return_val_if_fail(FileObject!=NULL,0);
235 g_return_val_if_fail(address!=0,0);
236 g_return_val_if_fail(length!=0,0);
237 g_return_val_if_fail(FileOffset!=NULL,0);
239 Mdl=MmCreateMdl(NULL,address,PAGE_SIZE); /* FIXME: Depreacted in favor of IoAllocateMdl() */
241 KeInitializeEvent(&Event,NotificationEvent,FALSE);
242 /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
243 err=IoPageRead(FileObject,Mdl,FileOffset,&Event,&IoStatus);
244 g_assert(NT_SUCCESS(err));
245 g_assert(NT_SUCCESS(IoStatus.Status));
246 /* It is not == as the file may be shorter than requested */
247 g_assert(IoStatus.Information<=length);
250 /* zero the rest of the buffer */
251 memset(((char *)address)+IoStatus.Information,0,(length-IoStatus.Information));
253 return IoStatus.Information;
259 * @FileObject: Initialized open #FileObject to map.
260 * %NULL value is forbidden.
261 * @FileOffset: The @FileObject file offset from where to map the region from.
262 * Negative value is forbidden.
263 * @Length: Requested length of the region to map from @FileObject.
264 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
265 * @Flags: %MAP_WAIT means whether disk waiting is permitted for this function.
266 * %MAP_NO_READ currently ignored; we map the data always.
267 * Value without %MAP_WAIT is currently forbidden by libcaptive as we have no on-demand loading implemented.
268 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
269 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
270 * %NULL pointer is forbidden.
271 * @Buffer: Returns the mapped memory region start address.
272 * This address may not be %PAGE_SIZE aligned.
273 * %NULL pointer is forbidden.
275 * Maps the specified region of @FileObject to automatically chosen address space.
276 * FIXME: No on-demand loading implemented yet - the whole region is read at the time of this function call.
278 * WARNING: You are forbidden to access the returned @Buffer; you must enforce
279 * the memory accessibility by either CcPinRead() or CcPinMappedData().
281 * Returns: %TRUE if the region was successfuly mapped.
282 * @Bcb with the initialized new memory region.
283 * @Buffer with the address of the exact byte specified by @FileOffset.
285 BOOLEAN CcMapData(IN PFILE_OBJECT FileObject,
286 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
288 PUBLIC_BCB **PublicBcbp,*PublicBcb;
289 struct page_position pagepos_local,*pagepos;
290 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
291 gpointer base_aligned;
292 size_t offset,length_mapped_aligned;
295 struct private_bcb *privbcb;
296 gboolean after_eof=FALSE; /* Did we reached the end of file already? */
298 g_return_val_if_fail(FileObject!=NULL,FALSE);
299 g_return_val_if_fail(FileOffset!=NULL,FALSE);
300 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
301 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
302 g_return_val_if_fail(Flags&MAP_WAIT,FALSE); /* FIXME: on-demand loading not yet implemented */
303 g_return_val_if_fail(!(Flags&~(MAP_WAIT|MAP_NO_READ)),FALSE); /* unknown flags? */
304 g_return_val_if_fail(Bcb!=NULL,FALSE);
305 g_return_val_if_fail(Buffer!=NULL,FALSE);
307 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
308 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
310 g_return_val_if_fail(FileObject->SectionObjectPointers!=NULL,FALSE);
311 g_return_val_if_fail(FileObject->SectionObjectPointers->SharedCacheMap==NULL,FALSE);
312 g_return_val_if_fail(FileObject->DeviceObject!=NULL,FALSE);
313 /* Is PAGE_SIZE aligned with 'FileObject->DeviceObject->SectorSize'?
314 * 'SectorSize' may not yet be filled in.
316 g_return_val_if_fail(!FileObject->DeviceObject->SectorSize
317 || 0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,FileObject->DeviceObject->SectorSize),FALSE);
319 page_position_hash_init();
320 private_bcb_hash_init();
322 PublicBcbp=(PUBLIC_BCB **)Bcb;
323 /* extend 'FileOffset' and 'Length' to page boundaries */
324 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
325 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
326 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
328 /* Create 'base_aligned'; referenced as unaligned by 'privbcb'. */
329 /* TODO: on-demand loading */
330 /* Although we do zeroed-page mapping here we just reserve the linears
331 * space by it - all the page will be overriden by shmat(2) afterwards anyway.
335 length_mapped_aligned, /* length */
336 PROT_READ|PROT_WRITE, /* prot; FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
337 MAP_PRIVATE|MAP_ANONYMOUS, /* flags */
338 -1, /* fd; ignored due to MAP_ANONYMOUS */
339 0); /* offset; ignored due to MAP_ANONYMOUS */
340 g_assert(base_aligned!=NULL);
342 /* Create 'PublicBcb'; referenced by 'privbcb'. */
343 captive_new(PublicBcb);
344 PublicBcb->NodeTypeCode=CAPTIVE_PUBLIC_BCB_NODETYPECODE;
345 PublicBcb->NodeByteSize=sizeof(*PublicBcb); /* we have no extensions there */
346 PublicBcb->MappedLength=Length;
347 PublicBcb->MappedFileOffset=*FileOffset;
349 /* Create 'privbcb'; referenced by created 'pagepos'es. */
350 captive_new(privbcb);
351 privbcb->PublicBcb=PublicBcb;
352 privbcb->FileObject=FileObject;
353 privbcb->ref_count=1;
354 privbcb->MappedLength=PublicBcb->MappedLength;
355 privbcb->MappedFileOffset=PublicBcb->MappedFileOffset;
356 privbcb->base=base_aligned+CAPTIVE_ROUND_DOWN_EXCEEDING64(FileOffset->QuadPart,PAGE_SIZE);
357 privbcb->dirty=FALSE;
358 g_hash_table_insert(private_bcb_hash,
360 privbcb); /* value */
362 pagepos_local.FileObject=FileObject;
363 pagepos_local.shmid=-1;
364 pagepos_local.privbcb_list=NULL;
367 offset<length_mapped_aligned;
371 pagepos_local.FileOffset.QuadPart=FileOffset_bottom.QuadPart+offset;
372 if (!(pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local))) {
373 captive_new(pagepos);
374 /* FIXME: Reference 'FileObject' to not to leave broken references to it here */
375 *pagepos=pagepos_local;
376 pagepos->shmid=shmget(IPC_PRIVATE,PAGE_SIZE,IPC_CREAT|IPC_CREAT|0600);
377 g_assert(pagepos->shmid!=-1);
378 pagepos->privbcb_list=NULL;
380 pageaddr=(gpointer)(((char *)base_aligned)+offset);
381 /* It appears as shmat(2) cannot override previously mmap(2)ed memory;
382 * mmap(2) is still needed to get linear block of memory assignment.
384 /* TODO:thread; munmap()..shmat() window */
385 errint=munmap(pageaddr,PAGE_SIZE);
387 errptr=shmat(pagepos->shmid,
388 pageaddr, /* shmaddr */
389 0); /* shmflg; !SHM_RDONLY==r/w FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
390 g_assert(errptr==pageaddr);
392 g_assert(g_list_find(pagepos->privbcb_list,privbcb)==NULL);
393 pagepos->privbcb_list=g_list_prepend(pagepos->privbcb_list,privbcb); /* order not important */
394 g_assert(g_list_find(pagepos->privbcb_list,privbcb)!=NULL);
395 if (pagepos->privbcb_list->next==NULL) { /* exactly one item (we just added it now) */
398 errint=shmctl(pagepos->shmid,
403 /* Read the page content: */
404 got=captive_Cc_IoPageRead(FileObject,pageaddr,PAGE_SIZE,&pagepos_local.FileOffset);
408 g_assert(got<=PAGE_SIZE);
409 after_eof=(got<PAGE_SIZE);
411 g_hash_table_insert(page_position_hash,
413 pagepos); /* value */
415 g_assert(validate_page_position(pagepos));
418 /* offset _into_ page, may not be PAGE_SIZE aligned: */
419 *Buffer=privbcb->base;
420 *PublicBcbp=PublicBcb;
421 g_assert(validate_Bcb(PublicBcb)==TRUE);
422 FileObject->SectionObjectPointers->SharedCacheMap=PublicBcb;
430 * @FileObject: Initialized open #FileObject to map.
431 * %NULL value is forbidden.
432 * @MappedFileOffset: The @FileObject file offset from where to map the region from.
433 * Negative value is forbidden.
434 * @MappedLength: Requested length of the region to map from @FileObject.
435 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
436 * @Wait: Whether disk waiting is permitted for this function.
437 * Value currently ignored by libcaptive as the data must have been mapped by CcMapData() already anyway.
438 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
439 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
440 * %PIN_NO_READ currently ignored; we map the data always.
441 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
442 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
443 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
444 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
445 * %NULL pointer is forbidden.
446 * @Buffer: Returns the mapped memory region start address.
447 * This address may not be %PAGE_SIZE aligned.
448 * %NULL pointer is forbidden.
450 * This function should map the memory from CcMapData() as CcMapData() does not provide
451 * you any access to the reserved memory space.
452 * libcaptive will map all the data by CcMapData() already and this function does not
453 * have any effect there.
455 * Returns: %TRUE if the region was successfuly mapped.
456 * @Bcb with the initialized new memory region.
457 * @Buffer with the address of the exact byte specified by @FileOffset.
459 BOOLEAN CcPinMappedData
460 (IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb)
462 struct page_position pagepos_local,*pagepos;
463 struct private_bcb *privbcb,*privbcb_listitem;
465 LARGE_INTEGER FileOffset_bottom,FileOffset_top;
466 size_t length_mapped_aligned;
467 gpointer base_aligned;
470 g_return_val_if_fail(FileObject!=NULL,FALSE);
471 g_return_val_if_fail(FileOffset!=NULL,FALSE);
472 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
473 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
474 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
475 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
476 g_return_val_if_fail(Bcb!=NULL,FALSE);
478 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
479 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
481 /* extend 'FileOffset' and 'Length' to page boundaries */
482 FileOffset_bottom.QuadPart=CAPTIVE_ROUND_DOWN64(FileOffset->QuadPart,PAGE_SIZE);
483 FileOffset_top.QuadPart=CAPTIVE_ROUND_UP64(FileOffset->QuadPart+Length,PAGE_SIZE);
484 length_mapped_aligned=(FileOffset_top.QuadPart-FileOffset_bottom.QuadPart);
486 page_position_hash_init();
488 pagepos_local.FileObject=FileObject;
489 pagepos_local.FileOffset=FileOffset_bottom;
490 pagepos_local.privbcb_list=NULL;
491 pagepos_local.shmid=-1;
492 pagepos=g_hash_table_lookup(page_position_hash,&pagepos_local);
493 if (!pagepos && (Flags & PIN_IF_BCB)) /* BCB does not exist */
495 g_assert(validate_page_position(pagepos));
496 g_assert(pagepos->privbcb_list!=NULL);
499 for (privbcb_list=pagepos->privbcb_list;privbcb_list;privbcb_list=privbcb_list->next) {
500 privbcb_listitem=privbcb_list->data;
502 && privbcb_listitem->MappedFileOffset.QuadPart==FileOffset->QuadPart
503 && privbcb_listitem->MappedLength==Length) {
504 g_assert(privbcb==NULL); /* appropriate privbcb found twice */
505 privbcb=privbcb_listitem;
508 if (!privbcb && (Flags & PIN_IF_BCB)) /* BCB does not exist */
510 g_return_val_if_fail(privbcb!=NULL,FALSE); /* appropriate privbcb not found */
512 /* Sanity check 'privbcb': */
513 g_return_val_if_fail(FileObject==privbcb->FileObject,FALSE);
515 base_aligned=CAPTIVE_ROUND_DOWN(privbcb->base,PAGE_SIZE);
517 got=captive_Cc_IoPageRead(
518 privbcb->FileObject, /* FILE_OBJECT */
519 base_aligned, /* address */
520 PAGE_SIZE, /* length */
521 &pagepos_local.FileOffset); /* FileOffset */
522 /* FIXME: sanitycheck 'got' agains CcMapData() state */
524 /* Memory already mapped by CcMapData(). */
525 *Bcb=privbcb->PublicBcb;
532 * @FileObject: Initialized open #FileObject to map.
533 * %NULL value is forbidden.
534 * @FileOffset: The @FileObject file offset from where to map the region from.
535 * Negative value is forbidden.
536 * @Length: Requested length of the region to map from @FileObject.
537 * FIXME: Value %0 is currently forbidden by libcaptive; it should be allowed.
538 * @Flags: %PIN_WAIT means whether disk waiting is permitted for this function.
539 * Value without %PIN_WAIT is currently permtted by libcaptive as the data must have been mapped by CcMapData() already anyway.
540 * %PIN_NO_READ currently ignored; we map the data always.
541 * FIXME: %PIN_EXCLUSIVE for exclusive @Bcb access is now ignored by libcaptive.
542 * %PIN_IF_BCB if @Bcb should never be created; function is successful only if @Bcb already exists.
543 * @Bcb: Returns initialized #PUBLIC_BCB to refer to the mapped region.
544 * The memory region can be larger than requested as it is %PAGE_SIZE aligned.
545 * %NULL pointer is forbidden.
546 * @Buffer: Returns the mapped memory region start address.
547 * This address may not be %PAGE_SIZE aligned.
548 * %NULL pointer is forbidden.
550 * Just a shortcut call for CcMapData() and CcPinMappedData() afterwards.
551 * See these two functions for the details.
553 * Returns: %TRUE if the region was successfuly mapped.
554 * @Bcb with the initialized new memory region.
555 * @Buffer with the address of the exact byte specified by @FileOffset.
557 BOOLEAN CcPinRead(IN PFILE_OBJECT FileObject,
558 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN ULONG Flags,OUT PVOID *Bcb,OUT PVOID *Buffer)
560 PVOID Bcb_CcPinMappedData;
563 g_return_val_if_fail(FileObject!=NULL,FALSE);
564 g_return_val_if_fail(FileOffset!=NULL,FALSE);
565 g_return_val_if_fail(FileOffset->QuadPart>=0,FALSE);
566 g_return_val_if_fail(Length>0,FALSE); /* FIXME: not handled below; 0 should be allowed */
567 /* 'Flags&PIN_WAIT' ignored as we already must have the data mapped */
568 g_return_val_if_fail(!(Flags&~(PIN_WAIT|PIN_EXCLUSIVE|PIN_NO_READ|PIN_IF_BCB)),FALSE); /* unknown flags? */
569 g_return_val_if_fail(Bcb!=NULL,FALSE);
570 g_return_val_if_fail(Buffer!=NULL,FALSE);
572 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Flags=0x%lX",G_STRLOC,
573 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gulong)Flags);
575 if (!(Flags&PIN_IF_BCB)) {
576 errbool=CcMapData(FileObject,FileOffset,Length,
578 | (Flags&PIN_WAIT ? MAP_WAIT : 0),
580 g_return_val_if_fail(errbool==TRUE,FALSE);
583 errbool=CcPinMappedData(FileObject,FileOffset,Length,Flags,&Bcb_CcPinMappedData);
584 if (!(Flags&PIN_IF_BCB)) {
585 g_return_val_if_fail(errbool==TRUE,FALSE);
586 g_return_val_if_fail(Bcb_CcPinMappedData==*Bcb,FALSE);
589 if (errbool==FALSE) /* FALSE permitted; We may fail if Bcb does not exist yet. */
591 *Bcb=Bcb_CcPinMappedData;
598 static void captive_privbcb_flush(struct private_bcb *privbcb)
602 IO_STATUS_BLOCK IoStatus;
604 gpointer base_sectoraligned;
605 gsize length_sectoraligned;
606 LARGE_INTEGER FileOffset_sectoraligned;
611 g_assert(privbcb->FileObject->DeviceObject!=NULL);
612 /* Is PAGE_SIZE aligned with 'privbcb->FileObject->DeviceObject->SectorSize'? */
613 g_assert(privbcb->FileObject->DeviceObject->SectorSize>0);
614 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(PAGE_SIZE,privbcb->FileObject->DeviceObject->SectorSize));
615 /* We align here directly the 'privbcb->base' which is not correct.
616 * We should rather aligned according to 'privbcb->MappedOffset' but
617 * as 'privbcb->base' with PAGE_SIZE alignment is just a possibly
618 * better alignment than 'privbcb->FileObject->DeviceObject->SectorSize' it must the same operation.
620 g_assert(CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize)
621 ==CAPTIVE_ROUND_DOWN_EXCEEDING64(privbcb->MappedFileOffset.QuadPart,privbcb->FileObject->DeviceObject->SectorSize));
622 base_sectoraligned =CAPTIVE_ROUND_DOWN(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize);
623 length_sectoraligned=CAPTIVE_ROUND_UP(((char *)privbcb->base)+privbcb->MappedLength,privbcb->FileObject->DeviceObject->SectorSize)
624 -((char *)base_sectoraligned);
625 g_assert(0==CAPTIVE_ROUND_DOWN_EXCEEDING(length_sectoraligned,privbcb->FileObject->DeviceObject->SectorSize));
626 FileOffset_sectoraligned.QuadPart=CAPTIVE_ROUND_DOWN64(privbcb->MappedFileOffset.QuadPart,privbcb->FileObject->DeviceObject->SectorSize);
628 Mdl=MmCreateMdl(NULL,base_sectoraligned,length_sectoraligned);
631 KeInitializeEvent(&Event,NotificationEvent,FALSE);
633 /* FIXME: read/write should be possible after CcSetDirtyPinnedData() etc. */
634 /* Use rather IoSynchronousPageWrite() than IoPageWrite() to prevent STATUS_PENDING. */
635 err=IoSynchronousPageWrite(privbcb->FileObject,Mdl,&FileOffset_sectoraligned,&Event,&IoStatus);
636 g_assert(NT_SUCCESS(err));
637 g_assert(NT_SUCCESS(IoStatus.Status));
638 /* We should write at least the unaligned mapped data although we
639 * do not need to successfuly write the whole aligned amount.
641 g_assert(IoStatus.Information>=CAPTIVE_ROUND_DOWN_EXCEEDING(privbcb->base,privbcb->FileObject->DeviceObject->SectorSize)
642 +privbcb->MappedLength);
643 g_assert(IoStatus.Information<=length_sectoraligned);
645 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: 'dirty' flush: FileObject=%p,MappedFileOffset=0x%llX,MappedLength=0x%lX,base=%p"
646 "; base_sectoraligned=%p,FileOffset_sectoraligned=0x%llX,length_sectoraligned=0x%lX; ->Information=0x%lX",G_STRLOC,
647 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength,privbcb->base,
648 base_sectoraligned,(guint64)FileOffset_sectoraligned.QuadPart,(gulong)length_sectoraligned,
649 (gulong)IoStatus.Information);
651 privbcb->dirty=FALSE;
657 * @Bcb: Initialized #PUBLIC_BCB structure.
658 * %NULL value is forbidden.
660 * Dereferences @Bcb with the possible cleanup operations if you were the last owner.
662 VOID CcUnpinData(IN PVOID Bcb)
664 PUBLIC_BCB *PublicBcb;
665 struct private_bcb *privbcb;
668 g_return_if_fail(validate_Bcb(Bcb));
670 private_bcb_hash_init();
672 PublicBcb=(PUBLIC_BCB *)Bcb;
673 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
674 g_return_if_fail(privbcb!=NULL);
676 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
677 g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
679 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,
680 "%s: privbcb->FileObject=%p,privbcb->MappedFileOffset=0x%llX,privbcb->MappedLength=0x%lX",G_STRLOC,
681 privbcb->FileObject,(guint64)privbcb->MappedFileOffset.QuadPart,(gulong)privbcb->MappedLength);
683 g_assert(privbcb->ref_count>0);
684 /* Do not write back the contents if this is not the final unpin.
685 * FIXME: Is it correct?
687 if (--privbcb->ref_count)
690 captive_privbcb_flush(privbcb);
692 g_assert(privbcb->FileObject->SectionObjectPointers!=NULL);
693 g_assert(privbcb->FileObject->SectionObjectPointers->SharedCacheMap==PublicBcb);
694 privbcb->FileObject->SectionObjectPointers->SharedCacheMap=NULL;
696 errbool=g_hash_table_remove(private_bcb_hash,PublicBcb);
697 g_assert(errbool==TRUE);
701 VOID CcRepinBcb(IN PVOID Bcb)
703 PUBLIC_BCB *PublicBcb;
704 struct private_bcb *privbcb;
706 g_return_if_fail(validate_Bcb(Bcb));
708 private_bcb_hash_init();
710 PublicBcb=(PUBLIC_BCB *)Bcb;
711 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
712 g_return_if_fail(privbcb!=NULL);
714 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p; privbcb->FileObject=%p",G_STRLOC,
715 Bcb,privbcb->FileObject);
717 privbcb->ref_count++;
721 VOID CcUnpinRepinnedBcb(IN PVOID Bcb,IN BOOLEAN WriteThrough,IN PIO_STATUS_BLOCK IoStatus)
723 PUBLIC_BCB *PublicBcb;
724 struct private_bcb *privbcb;
726 g_return_if_fail(validate_Bcb(Bcb));
727 g_return_if_fail(IoStatus!=NULL);
729 private_bcb_hash_init();
731 PublicBcb=(PUBLIC_BCB *)Bcb;
732 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
733 g_return_if_fail(privbcb!=NULL);
735 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,WriteThrough=%d,IoStatus=%p; privbcb->FileObject=%p",G_STRLOC,
736 Bcb,(gint)WriteThrough,IoStatus,privbcb->FileObject);
738 IoStatus->Status=STATUS_SUCCESS;
739 IoStatus->Information=privbcb->MappedLength;
745 VOID CcSetDirtyPinnedData(IN PVOID Bcb,IN PLARGE_INTEGER Lsn OPTIONAL)
747 PUBLIC_BCB *PublicBcb;
748 struct private_bcb *privbcb;
750 g_return_if_fail(validate_Bcb(Bcb));
751 g_return_if_fail(Lsn==NULL); /* TODO: NOT IMPLEMENTED YET */
753 private_bcb_hash_init();
755 PublicBcb=(PUBLIC_BCB *)Bcb;
756 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
757 g_return_if_fail(privbcb!=NULL);
759 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: Bcb=%p,Lsn=0x%llX; privbcb->FileObject=%p",G_STRLOC,
760 Bcb,(guint64)(!Lsn ? -1 : Lsn->QuadPart),privbcb->FileObject);
768 * @FileObject: Initialized open #FileObject to update file sizes of.
769 * %NULL value is forbidden.
770 * @FileSizes: New file sizes to update cache to.
771 * %NULL value is forbidden.
773 * Update cache properties after file sizes were updated.
774 * Probably only the exceeding pages need to be unmapped and BCBs updated
775 * if FileSizes->AllocationSize gets shrunk.
777 * FIXME: Currently a NOP with no effect by libcaptive.
779 VOID CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)
781 g_return_if_fail(FileObject!=NULL);
782 g_return_if_fail(FileSizes!=NULL);
784 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,"
785 "FileSizes->AllocationSize=0x%llX,FileSizes->FileSize=0x%llX,FileSizes->ValidDataLength=0x%llX",G_STRLOC,
786 FileObject,(guint64)FileSizes->AllocationSize.QuadPart,(guint64)FileSizes->FileSize.QuadPart,
787 (guint64)FileSizes->ValidDataLength.QuadPart);
789 /* FIXME: check BCB && 'struct page_position' invalidities */
794 * CcPurgeCacheSection:
795 * @SectionObjectPointer: Pointer specifying file to purge;
796 * %NULL value is forbidden.
797 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
798 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP with %FALSE return code in such case.
799 * @FileOffset: Starting offset of the ranger to purge.
800 * %NULL pointer is permitted and it means to purge the whole whole.
801 * FIXME: Non %NULL pointer is NOT IMPLEMENTED YET by libcaptive.
802 * @Length: Length of the range to purge. Ignored if @FileOffset==NULL.
803 * @UninitializeCacheMaps: Purge also private cache maps (FIXME: ???).
805 * Drop any caching for shrunken file which is not being deleted.
806 * libcaptive will no longer consider such #BCB as dirty.
808 * Returns: %TRUE if the range was purged successfuly.
810 BOOLEAN CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
811 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)
813 PUBLIC_BCB *PublicBcb;
814 struct private_bcb *privbcb;
816 g_return_val_if_fail(SectionObjectPointer!=NULL,FALSE);
817 if (SectionObjectPointer->SharedCacheMap==NULL)
818 return FALSE; /* failed - nothing to purge */
819 g_return_val_if_fail(FileOffset==NULL,FALSE); /* NOT IMPLEMENTED YET */
821 PublicBcb=SectionObjectPointer->SharedCacheMap;
822 g_return_val_if_fail(validate_Bcb(PublicBcb),FALSE);
824 private_bcb_hash_init();
826 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
827 g_return_val_if_fail(privbcb!=NULL,FALSE);
829 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
830 "FileOffset=0x%llX,Length=0x%lX,UninitializeCacheMaps=%d",G_STRLOC,
831 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
832 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,(gint)UninitializeCacheMaps);
834 privbcb->dirty=FALSE; /* purge it */
842 * @FileObject: Initialized open #FileObject to map.
843 * %NULL value is forbidden.
844 * @FileOffset: The @FileObject file offset from where to map the region from.
845 * Negative value is forbidden.
846 * @Length: Requested length of the region to map from @FileObject.
847 * Value %0 is permitted (no effect of this function call).
848 * @Wait: Whether disk waiting is permitted for this function.
849 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
850 * @Buffer: Address of memory region with already allocated memory of size @Length.
851 * This address may not be %PAGE_SIZE aligned.
852 * %NULL pointer is forbidden.
853 * @IoStatus: #PIO_STATUS_BLOCK to return status of this operation.
854 * %NULL pointer is forbidden.
856 * Reads the specified region of @FileObject to the given @Buffer.
857 * No on-demand loading is in effect.
859 * Returns: %TRUE if the region was successfuly filled with @Length bytes.
860 * @IoStatus.Status initialized by %STATUS_SUCCESS if successful.
861 * @IoStatus.Information initialized by @Length if successful.
863 BOOLEAN CcCopyRead(IN PFILE_OBJECT FileObject,
864 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,OUT PVOID Buffer,OUT PIO_STATUS_BLOCK IoStatus)
870 g_return_val_if_fail(FileObject!=NULL,FALSE);
871 g_return_val_if_fail(FileOffset!=NULL,FALSE);
872 g_return_val_if_fail(Buffer!=NULL,FALSE);
873 g_return_val_if_fail(IoStatus!=NULL,FALSE);
875 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
876 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
878 IoStatus->Status=STATUS_UNSUCCESSFUL;
879 IoStatus->Information=0;
883 FileObject, /* FileObject */
884 FileOffset, /* FileOffset */
887 &MappedBcb, /* Bcb */
888 &MappedBuffer); /* Buffer */
889 g_return_val_if_fail(errbool==TRUE,FALSE);
891 memcpy(Buffer,MappedBuffer,Length);
893 CcUnpinData(MappedBcb); /* no error code */
896 IoStatus->Status=STATUS_SUCCESS;
897 IoStatus->Information=Length;
905 * @FileObject: Initialized open #FileObject to map.
906 * %NULL value is forbidden.
907 * @FileOffset: The @FileObject file offset from where to map the region from.
908 * Negative value is forbidden.
909 * @Length: Requested length of the region to map from @FileObject.
910 * Value %0 is permitted (no effect of this function call).
911 * @Wait: Whether disk waiting is permitted for this function.
912 * Value %FALSE is currently forbidden by libcaptive as we have no on-demand loading implemented.
913 * @Buffer: Address of memory region with already allocated memory of size @Length.
914 * This address may not be %PAGE_SIZE aligned.
915 * %NULL pointer is forbidden.
917 * Writes the specified region of the given @Buffer to @FileObject.
919 * Returns: %TRUE if the region was successfuly written with @Length bytes.
921 BOOLEAN CcCopyWrite(IN PFILE_OBJECT FileObject,
922 IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN PVOID Buffer)
928 g_return_val_if_fail(FileObject!=NULL,FALSE);
929 g_return_val_if_fail(FileOffset!=NULL,FALSE);
930 g_return_val_if_fail(Buffer!=NULL,FALSE);
932 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,FileOffset=0x%llX,Length=0x%lX,Wait=%d",G_STRLOC,
933 FileObject,(guint64)FileOffset->QuadPart,(gulong)Length,(gint)Wait);
937 FileObject, /* FileObject */
938 FileOffset, /* FileOffset */
941 &MappedBcb, /* Bcb */
942 &MappedBuffer); /* Buffer */
943 g_return_val_if_fail(errbool==TRUE,FALSE);
945 memcpy(MappedBuffer,Buffer,Length);
947 CcSetDirtyPinnedData(
950 CcUnpinData(MappedBcb); /* no error code */
959 * @FileObject: Initialized open #FileObject to map.
960 * %NULL value is forbidden.
961 * @BytesToWrite: Amount of data to be asked whether it will be accepted.
962 * Value %0 is permitted.
963 * @Wait: Whether disk waiting would be permitted during the forthcoming write call.
964 * @Retrying: Use %TRUE iff calling this function for the second and further times for one request.
966 * Asks cache manager if it would currently accept write request to @FileObject
967 * of @BytesToWrite bytes with @Wait condition.
968 * libcaptive will always accept any writes. This function is a NOP.
970 * Returns: libcaptive always returns %TRUE.
972 BOOLEAN CcCanIWrite(IN PFILE_OBJECT FileObject,IN ULONG BytesToWrite,IN BOOLEAN Wait,IN BOOLEAN Retrying)
974 g_return_val_if_fail(FileObject!=NULL,FALSE);
976 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,BytesToWrite=0x%lX,Wait=%d,Retrying=%d",G_STRLOC,
977 FileObject,(gulong)BytesToWrite,(gint)Wait,(gint)Retrying);
984 * CcSetReadAheadGranularity:
985 * @FileObject: Initialized open #FileObject to map.
986 * %NULL value is forbidden.
987 * @Granularity: Suggested size of the cache element.
988 * Value must be larger or requal to %PAGE_SIZE and it must be even power of two.
990 * libcaptive does not implement any caching and therefore this function
993 VOID CcSetReadAheadGranularity(IN PFILE_OBJECT FileObject,IN ULONG Granularity)
995 g_return_if_fail(FileObject!=NULL);
996 g_return_if_fail(Granularity>=PAGE_SIZE);
997 g_return_if_fail((Granularity&(Granularity-1))==0); /* Power of two */
999 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: FileObject=%p,Granularity=0x%lX",G_STRLOC,
1000 FileObject,(gulong)Granularity);
1002 /* NOP; no caching by libcaptive */
1008 * @SectionObjectPointer: Pointer specifying file to flush;
1009 * %NULL value is forbidden.
1010 * libcaptive interprets only #SharedCacheMap field as #PUBLIC_BCB pointer.
1011 * Field #SharedCacheMap value %NULL is permitted; libcaptive does a NOP in such case.
1012 * @FileOffset: Optional starting point of the range to flush.
1013 * %NULL value is permitted.
1014 * @Length: Length of the range to flush. Ignored if @FileOffset is %NULL.
1015 * @IoStatus: Optionally returns the resulting operation status.
1016 * #Information field will contain the number of bytes flushed.
1017 * %NULL value is permitted.
1019 * Flushes out any pending dirty data in cache manager BCB mapping.
1020 * FIXME: libcaptive currently always flushes the full file ignoring any @FileOffset or @Length.
1022 VOID CcFlushCache(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
1023 IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,OUT PIO_STATUS_BLOCK IoStatus OPTIONAL)
1025 PUBLIC_BCB *PublicBcb;
1026 struct private_bcb *privbcb;
1028 g_return_if_fail(SectionObjectPointer!=NULL);
1030 if (SectionObjectPointer->SharedCacheMap==NULL) {
1032 IoStatus->Status=STATUS_SUCCESS;
1033 IoStatus->Information=0;
1038 PublicBcb=SectionObjectPointer->SharedCacheMap;
1039 g_return_if_fail(validate_Bcb(PublicBcb));
1041 private_bcb_hash_init();
1043 privbcb=g_hash_table_lookup(private_bcb_hash,PublicBcb);
1044 g_return_if_fail(privbcb!=NULL);
1046 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: SectionObjectPointer=%p(Bcb=%p,privbcb=%p,privbcb->FileObject=%p),"
1047 "FileOffset=0x%llX,Length=0x%lX,IoStatus=%p",G_STRLOC,
1048 SectionObjectPointer,PublicBcb,privbcb,privbcb->FileObject,
1049 (guint64)(!FileOffset ? -1 : FileOffset->QuadPart),(gulong)Length,IoStatus);
1052 g_assert(FileOffset->QuadPart >=privbcb->MappedFileOffset.QuadPart);
1053 g_assert(FileOffset->QuadPart+Length<=privbcb->MappedFileOffset.QuadPart+privbcb->MappedLength);
1056 /* FIXME: Flush just FileOffset..FileOfset+Length part */
1057 captive_privbcb_flush(privbcb);
1060 IoStatus->Status=STATUS_SUCCESS;
1061 IoStatus->Information=(FileOffset && Length ? MIN(privbcb->MappedLength,Length) : privbcb->MappedLength);