diff --git a/src/context/memory.rs b/src/context/memory.rs
index c3c5423e791e81ce1a352210c4f53fa751c38107..749bb7755f07148d72c0a0468be5f019f63af447 100644
--- a/src/context/memory.rs
+++ b/src/context/memory.rs
@@ -700,41 +700,52 @@ impl Grant {
         })
     }
 
-    pub fn borrow_fmap(span: PageSpan, flags: PageFlags<RmmA>, file_ref: GrantFileRef, src: Option<BorrowedFmapSource<'_>>, mapper: &mut PageMapper, mut flusher: impl Flusher<RmmA>) -> Self {
+    pub fn borrow_fmap(span: PageSpan, new_flags: PageFlags<RmmA>, file_ref: GrantFileRef, src: Option<BorrowedFmapSource<'_>>, mapper: &mut PageMapper, mut flusher: impl Flusher<RmmA>) -> Result<Self> {
         if let Some(mut src) = src {
+            // FIXME: Iterate over grants rather than pages, to allow for not-yet-lazily mapped
+            // pages.
             for dst_page in span.pages() {
-                let src_page = src.src_page.next_by(dst_page.offset_from(span.base));
+                let src_page = src.src_base.next_by(dst_page.offset_from(span.base));
 
-                let (frame, _) = src.src_mapper.translate(src_page.start_address()).unwrap();
+                let (frame, is_cow) = match src.mode {
+                    MmapMode::Shared(src_mapper) => {
+                        // TODO: Error code for "scheme responded with unmapped page"?
+                        let (frame, _) = src_mapper.translate(src_page.start_address()).ok_or(Error::new(EIO))?;
 
-                if let Some(page_info) = get_page_info(Frame::containing_address(frame)) {
+                        (Frame::containing_address(frame), false)
+                    }
+                    MmapMode::Cow(ref mut src_mapper) => unsafe {
+                        let (_, frame, _) = src_mapper.remap_with(src_page.start_address(), |flags| flags.write(false)).ok_or(Error::new(EIO))?;
+
+                        (Frame::containing_address(frame), true)
+                    }
+                };
+
+                if let Some(page_info) = get_page_info(frame) {
                     page_info.add_ref(false);
                 }
 
                 unsafe {
-                    flusher.consume(mapper.map_phys(dst_page.start_address(), frame, flags).unwrap());
+                    flusher.consume(mapper.map_phys(dst_page.start_address(), frame.start_address(), new_flags.write(!is_cow)).unwrap());
                 }
             }
         }
 
-        Self {
+        Ok(Self {
             base: span.base,
             info: GrantInfo {
                 page_count: span.count,
                 mapped: true,
-                flags,
+                flags: new_flags,
                 provider: Provider::FmapBorrowed { file_ref },
             }
-        }
+        })
     }
 
-    // TODO: Do not return Vec, return an iterator perhaps? Referencing the source address space?
-
     /// Borrow all pages in the range `[src_base, src_base+page_count)` from `src_address_space`,
     /// mapping them into `[dst_base, dst_base+page_count)`. The destination pages will lazily read
     /// the page tables of the source pages, but once present in the destination address space,
     /// pages that are unmaped or moved will not be made visible to the destination address space.
-    // TODO: Return only one grant
     pub fn borrow(
         src_address_space_lock: Arc<RwLock<AddrSpace>>,
         src_address_space: &AddrSpace,
@@ -890,17 +901,19 @@ impl Grant {
             };
             let frame = Frame::containing_address(phys);
 
-            let is_cow_opt = match self.info.provider {
-                Provider::Allocated { .. } => Some(true),
-                Provider::External { .. } => Some(false),
-                Provider::PhysBorrowed { .. } => None,
-                Provider::FmapBorrowed { .. } => Some(false),
+            let (is_cow_opt, require_info) = match self.info.provider {
+                Provider::Allocated { .. } => (Some(true), true),
+                Provider::External { .. } => (Some(false), false),
+                Provider::PhysBorrowed { .. } => (None, false),
+                Provider::FmapBorrowed { .. } => (Some(false), false),
             };
 
             if let Some(is_cow) = is_cow_opt {
-                get_page_info(frame)
-                    .expect("allocated frame did not have an associated PageInfo")
-                    .remove_ref(is_cow);
+                if let Some(info) = get_page_info(frame) {
+                    info.remove_ref(is_cow);
+                } else {
+                    assert!(!require_info, "allocated frame did not have an associated PageInfo");
+                }
             }
 
 
@@ -1396,13 +1409,13 @@ fn correct_inner(addr_space_lock: Arc<RwLock<AddrSpace>>, faulting_page: Page, a
     Ok((frame, flush))
 }
 
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum MmapMode {
-    Cow,
-    Shared,
+#[derive(Debug)]
+pub enum MmapMode<'a> {
+    Cow(&'a mut PageMapper),
+    Shared(&'a PageMapper),
 }
 
 pub struct BorrowedFmapSource<'a> {
-    pub src_page: Page,
-    pub src_mapper: &'a PageMapper,
+    pub src_base: Page,
+    pub mode: MmapMode<'a>,
 }
diff --git a/src/scheme/user.rs b/src/scheme/user.rs
index 627f8b2685485f416383d5683739d022e9810336..6214064c72a274900ba1c21a3b92c0bd702aeb6c 100644
--- a/src/scheme/user.rs
+++ b/src/scheme/user.rs
@@ -457,16 +457,13 @@ impl UserInner {
             return Err(Error::new(EINVAL));
         }
 
-        let mode = if map.flags.contains(MapFlags::MAP_SHARED) {
-            MmapMode::Shared
-        } else {
-            MmapMode::Cow
-        };
-
         let src_address_space = Arc::clone(
             self.context.upgrade().ok_or(Error::new(ENODEV))?
                 .read().addr_space()?
         );
+        if Arc::ptr_eq(&src_address_space, &dst_addr_space) {
+            return Err(Error::new(EBUSY));
+        }
 
         let (pid, desc) = {
             let context_lock = context::current()?;
@@ -511,29 +508,34 @@ impl UserInner {
                 return Err(Error::new(EIO));
             }
         };
+        let file_ref = GrantFileRef {
+            description: desc,
+            base_offset: map.offset,
+        };
 
-        let dst_base = match mode {
-            MmapMode::Cow => todo!("mmap CoW"),
-            MmapMode::Shared => {
-                let file_ref = GrantFileRef {
-                    description: desc,
-                    base_offset: map.offset,
-                };
-                let src_guard = src_address_space.read();
-                let src = match base_page_opt {
-                    Some(base_addr) => Some(BorrowedFmapSource {
-                        src_page: Page::containing_address(VirtualAddress::new(base_addr)),
-                        src_mapper: &src_guard.table.utable,
-                    }),
-                    None => None,
-                };
-                let page_count_nz = NonZeroUsize::new(page_count).expect("already validated map.size != 0");
-                dst_addr_space.write().mmap(dst_base, page_count_nz, map.flags, |dst_base, flags, mapper, flusher| {
-                    Ok(Grant::borrow_fmap(PageSpan::new(dst_base, page_count), page_flags(map.flags), file_ref, src, mapper, flusher))
-                })?
-            }
+        let src_read_guard;
+        let mut src_write_guard;
+
+        let src = match base_page_opt {
+            Some(base_addr) => Some(BorrowedFmapSource {
+                src_base: Page::containing_address(VirtualAddress::new(base_addr)),
+                mode: if map.flags.contains(MapFlags::MAP_SHARED) {
+                    src_read_guard = src_address_space.read();
+                    MmapMode::Shared(&src_read_guard.table.utable)
+                } else {
+                    src_write_guard = src_address_space.write();
+                    MmapMode::Cow(&mut src_write_guard.table.utable)
+                },
+
+            }),
+            None => None,
         };
 
+        let page_count_nz = NonZeroUsize::new(page_count).expect("already validated map.size != 0");
+        let dst_base = dst_addr_space.write().mmap(dst_base, page_count_nz, map.flags, |dst_base, flags, mapper, flusher| {
+            Grant::borrow_fmap(PageSpan::new(dst_base, page_count), page_flags(map.flags), file_ref, src, mapper, flusher)
+        })?;
+
         Ok(dst_base.start_address().data())
     }
 }