@@ -41,6 +41,22 @@ use crate::mem::memory_region::{HostRegionBase, MemoryRegionKind};
4141use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags , MemoryRegionType } ;
4242use crate :: { Result , log_then_return} ;
4343
44+ /// Returns the guest-side permission flags for file mappings.
45+ ///
46+ /// With the `nanvix-unstable` feature, files are mapped as
47+ /// `READ | WRITE | EXECUTE` (copy-on-write). Otherwise the default
48+ /// is `READ | EXECUTE` (read-only + executable).
49+ fn file_mapping_flags ( ) -> MemoryRegionFlags {
50+ #[ cfg( feature = "nanvix-unstable" ) ]
51+ {
52+ MemoryRegionFlags :: READ | MemoryRegionFlags :: WRITE | MemoryRegionFlags :: EXECUTE
53+ }
54+ #[ cfg( not( feature = "nanvix-unstable" ) ) ]
55+ {
56+ MemoryRegionFlags :: READ | MemoryRegionFlags :: EXECUTE
57+ }
58+ }
59+
4460/// A prepared (host-side) file mapping ready to be applied to a VM.
4561///
4662/// Created by [`prepare_file_cow`]. The host-side OS resources (file
@@ -166,7 +182,7 @@ impl PreparedFileMapping {
166182 Ok ( MemoryRegion {
167183 host_region : host_base..host_end,
168184 guest_region : guest_start..guest_end,
169- flags : MemoryRegionFlags :: READ | MemoryRegionFlags :: EXECUTE ,
185+ flags : file_mapping_flags ( ) ,
170186 region_type : MemoryRegionType :: MappedFile ,
171187 } )
172188 }
@@ -186,7 +202,7 @@ impl PreparedFileMapping {
186202 host_region : * mmap_base as usize
187203 ..( * mmap_base as usize ) . wrapping_add ( * mmap_size) ,
188204 guest_region : guest_start..guest_end,
189- flags : MemoryRegionFlags :: READ | MemoryRegionFlags :: EXECUTE ,
205+ flags : file_mapping_flags ( ) ,
190206 region_type : MemoryRegionType :: MappedFile ,
191207 } )
192208 }
@@ -295,7 +311,8 @@ pub(crate) fn prepare_file_cow(
295311
296312 use windows:: Win32 :: Foundation :: HANDLE ;
297313 use windows:: Win32 :: System :: Memory :: {
298- CreateFileMappingW , FILE_MAP_READ , MapViewOfFile , PAGE_READONLY ,
314+ CreateFileMappingW , FILE_MAP_COPY , FILE_MAP_READ , MapViewOfFile , PAGE_READONLY ,
315+ PAGE_WRITECOPY ,
299316 } ;
300317
301318 let file = std:: fs:: File :: options ( ) . read ( true ) . open ( file_path) ?;
@@ -312,18 +329,23 @@ pub(crate) fn prepare_file_cow(
312329
313330 let file_handle = HANDLE ( file. as_raw_handle ( ) ) ;
314331
315- // Create a read-only file mapping object backed by the actual file.
332+ // nanvix-unstable maps files as RWX (CoW), so use
333+ // PAGE_WRITECOPY / FILE_MAP_COPY; otherwise PAGE_READONLY
334+ #[ cfg( feature = "nanvix-unstable" ) ]
335+ let ( page_prot, map_access) = ( PAGE_WRITECOPY , FILE_MAP_COPY ) ;
336+ #[ cfg( not( feature = "nanvix-unstable" ) ) ]
337+ let ( page_prot, map_access) = ( PAGE_READONLY , FILE_MAP_READ ) ;
338+
316339 // Pass 0,0 for size to use the file's actual size — Windows will
317340 // NOT extend a read-only file, so requesting page-aligned size
318341 // would fail for files smaller than one page.
319342 let mapping_handle =
320- unsafe { CreateFileMappingW ( file_handle, None , PAGE_READONLY , 0 , 0 , None ) }
343+ unsafe { CreateFileMappingW ( file_handle, None , page_prot , 0 , 0 , None ) }
321344 . map_err ( |e| HyperlightError :: Error ( format ! ( "CreateFileMappingW failed: {e}" ) ) ) ?;
322345
323- // Map a read-only view into the host process.
324346 // Passing 0 for dwNumberOfBytesToMap maps the entire file; the OS
325347 // rounds up to the next page boundary and zero-fills the remainder.
326- let view = unsafe { MapViewOfFile ( mapping_handle, FILE_MAP_READ , 0 , 0 , 0 ) } ;
348+ let view = unsafe { MapViewOfFile ( mapping_handle, map_access , 0 , 0 , 0 ) } ;
327349 if view. Value . is_null ( ) {
328350 unsafe {
329351 let _ = windows:: Win32 :: Foundation :: CloseHandle ( mapping_handle) ;
@@ -363,12 +385,15 @@ pub(crate) fn prepare_file_cow(
363385 // MSHV's map_user_memory requires host-writable pages (the
364386 // kernel module calls get_user_pages with write access).
365387 // KVM's KVM_MEM_READONLY slots work with read-only host pages.
388+ // nanvix-unstable needs PROT_WRITE for CoW guest mappings.
366389 // PROT_EXEC is never needed — the hypervisor backs guest R+X
367390 // pages without requiring host-side execute permission.
368- #[ cfg( mshv3) ]
369- let prot = libc:: PROT_READ | libc:: PROT_WRITE ;
370- #[ cfg( not( mshv3) ) ]
371- let prot = libc:: PROT_READ ;
391+ let needs_write = cfg ! ( mshv3) || cfg ! ( feature = "nanvix-unstable" ) ;
392+ let prot = if needs_write {
393+ libc:: PROT_READ | libc:: PROT_WRITE
394+ } else {
395+ libc:: PROT_READ
396+ } ;
372397
373398 libc:: mmap (
374399 std:: ptr:: null_mut ( ) ,
0 commit comments