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