Fix mmap file flush exceeding the file length
This commit is contained in:
		
							parent
							
								
									1e8c5a6d0a
								
							
						
					
					
						commit
						a99c63e21d
					
				| @ -279,7 +279,15 @@ impl VMArea { | ||||
| 
 | ||||
|         if need_flush { | ||||
|             let file_offset = file_offset.unwrap() + (target_range.start() - self.range.start()); | ||||
|             file.unwrap().write_at(file_offset, buf); | ||||
|             let file_len = file.unwrap().metadata().unwrap().size; | ||||
|             if file_offset < file_len { | ||||
|                 let effective_mem_len = std::cmp::min(target_range.size(), file_len - file_offset); | ||||
|                 let len = file | ||||
|                     .unwrap() | ||||
|                     .write_at(file_offset, &buf[..effective_mem_len]) | ||||
|                     .expect("flush file failure"); | ||||
|                 debug_assert!(len == effective_mem_len); | ||||
|             } // else file_offset >= file_len, no need to write file
 | ||||
|         } | ||||
| 
 | ||||
|         // reset zeros
 | ||||
| @ -541,16 +549,40 @@ impl VMArea { | ||||
|         if !cond_fn(file) { | ||||
|             return; | ||||
|         } | ||||
|         self.flush_file(file, file_offset) | ||||
|             .expect("flush memory to file failure"); | ||||
|     } | ||||
| 
 | ||||
|     fn flush_file(&self, file: &Arc<dyn File>, file_offset: usize) -> Result<()> { | ||||
|         let file_len = file.metadata().unwrap().size; | ||||
|         if file_offset >= file_len { | ||||
|             return Ok(()); | ||||
|         } | ||||
| 
 | ||||
|         if self.is_fully_committed() { | ||||
|             file.write_at(file_offset, unsafe { self.as_slice() }); | ||||
|             let effective_mem_len = std::cmp::min(self.range().size(), file_len - file_offset); | ||||
|             let len = file.write_at(file_offset, unsafe { | ||||
|                 &self.as_slice()[..effective_mem_len] | ||||
|             })?; | ||||
|             debug_assert!(len == effective_mem_len); | ||||
|         } else { | ||||
|             let committed = true; | ||||
|             let vm_range_start = self.range().start(); | ||||
|             for range in self.pages().get_ranges(committed) { | ||||
|                 let file_offset = file_offset + (range.start() - vm_range_start); | ||||
|                 file.write_at(file_offset, unsafe { range.as_slice() }); | ||||
|                 if file_offset >= file_len { | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 let effective_mem_len = std::cmp::min(range.size(), file_len - file_offset); | ||||
|                 let len = file.write_at(file_offset, unsafe { | ||||
|                     &range.as_slice()[..effective_mem_len] | ||||
|                 })?; | ||||
|                 debug_assert!(len == effective_mem_len); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_shared(&self) -> bool { | ||||
|  | ||||
| @ -1536,6 +1536,78 @@ int test_kernel_space_pf_trigger() { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int test_shared_file_mmap_small_file() { | ||||
|     int fd; | ||||
|     struct stat sb1, sb2; | ||||
|     char *mapped; | ||||
|     char write_buf[] = "hello world\n"; | ||||
|     size_t page_sz; | ||||
|     size_t file_sz; | ||||
|     size_t new_file_sz; | ||||
| 
 | ||||
|     if ((fd = open("/root/a.txt", O_RDWR | O_CREAT, S_IRWXU)) < 0) { | ||||
|         perror("open"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (write(fd, write_buf, strlen(write_buf)) < 0) { | ||||
|         perror("write"); | ||||
|         return -1; | ||||
|     } | ||||
|     if ((fstat(fd, &sb1)) == -1 ) { | ||||
|         perror("fstat"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     file_sz = sb1.st_size; | ||||
|     page_sz = getpagesize(); | ||||
| 
 | ||||
|     // Output the size before mmap
 | ||||
|     printf("before msync file_sz = %ld\n", file_sz); | ||||
|     if ((mapped = mmap(NULL, page_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, | ||||
|                        0)) == (void *) -1) { | ||||
|         perror("mmap"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     // Appending characters at the end of a file.
 | ||||
|     mapped[file_sz] = '9'; | ||||
|     // Synchronizing the modified contents of a file
 | ||||
|     if ((msync((void *)mapped, page_sz, MS_SYNC)) == -1) { | ||||
|         perror("msync"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if (fstat(fd, &sb2) == -1) { | ||||
|         perror("fstat"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     new_file_sz = sb2.st_size; | ||||
|     // Output the size after mmap
 | ||||
|     printf("msync after file_sz = %ld\n", new_file_sz); | ||||
|     if (new_file_sz != file_sz) { | ||||
|         THROW_ERROR("The file size doesn't match"); | ||||
|     } | ||||
| 
 | ||||
|     if ((munmap((void *)mapped, page_sz)) == -1) { | ||||
|         perror("munmap"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     if ((fstat(fd, &sb1)) == -1 ) { | ||||
|         perror("fstat"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     file_sz = sb1.st_size; | ||||
|     if (new_file_sz != file_sz) { | ||||
|         THROW_ERROR("The file size doesn't match"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| @ -1555,6 +1627,7 @@ static test_case_t test_cases[] = { | ||||
|     TEST_CASE(test_shared_file_mmap_flushing_with_munmap), | ||||
|     TEST_CASE(test_shared_file_mmap_flushing_with_fdatasync), | ||||
|     TEST_CASE(test_shared_file_mmap_flushing_with_fsync), | ||||
|     TEST_CASE(test_shared_file_mmap_small_file), | ||||
|     TEST_CASE(test_fixed_mmap_that_does_not_override_any_mmaping), | ||||
|     TEST_CASE(test_fixed_mmap_that_overrides_existing_mmaping), | ||||
|     TEST_CASE(test_fixed_mmap_with_non_page_aligned_addr), | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user