1use crate::device::DeviceMode;
6use crate::device::kobject::DeviceMetadata;
7use crate::fs::sysfs::{BlockDeviceInfo, build_block_device_directory};
8use crate::mm::memory::MemoryObject;
9use crate::mm::{MemoryAccessorExt, ProtectionFlags};
10use crate::task::CurrentTask;
11use crate::vfs::buffers::{InputBuffer, OutputBuffer};
12use crate::vfs::{
13 FileObject, FileOps, FsString, NamespaceNode, SeekTarget, default_ioctl, default_seek,
14};
15use anyhow::{Context as _, Error};
16use block_client::{BufferSlice, MutableBufferSlice, RemoteBlockClientSync};
17use fidl::endpoints::ClientEnd;
18use fidl_fuchsia_hardware_block_volume::VolumeMarker;
19use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
20use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
21use starnix_uapi::device_type::{BLOCK_EXTENDED_MAJOR, DeviceType};
22use starnix_uapi::errors::Errno;
23use starnix_uapi::open_flags::OpenFlags;
24use starnix_uapi::user_address::UserRef;
25use starnix_uapi::{BLKGETSIZE, BLKGETSIZE64, errno, from_status_like_fdio, off_t};
26use std::collections::btree_map::BTreeMap;
27use std::sync::atomic::{AtomicU32, Ordering};
28use std::sync::{Arc, OnceLock};
29
30pub struct RemoteBlockDeviceVmo {
31 backing_memory: MemoryObject,
32 backing_memory_size: usize,
33 block_size: u32,
34}
35
36pub enum RemoteBlockDevice {
40 Vmo(Arc<RemoteBlockDeviceVmo>),
41 Fuchsia(Arc<RemoteBlockClientSync>),
42}
43
44const BLOCK_SIZE: u32 = 512;
45
46impl RemoteBlockDevice {
47 pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), Error> {
48 match self {
49 RemoteBlockDevice::Vmo(device) => Ok(device.backing_memory.read(buf, offset)?),
50 RemoteBlockDevice::Fuchsia(block_client) => block_client
51 .read_at(MutableBufferSlice::Memory(buf), offset as u64)
52 .context("read_at failed"),
53 }
54 }
55
56 fn new_from_vmo<L>(
57 locked: &mut Locked<L>,
58 current_task: &CurrentTask,
59 minor: u32,
60 name: &str,
61 backing_memory: MemoryObject,
62 ) -> Result<Arc<Self>, Errno>
63 where
64 L: LockEqualOrBefore<FileOpsCore>,
65 {
66 let kernel = current_task.kernel();
67 let registry = &kernel.device_registry;
68 let device_name = FsString::from(format!("remoteblk-{name}"));
69 let virtual_block_class = registry.objects.virtual_block_class();
70 let backing_memory_size = backing_memory.get_content_size() as usize;
71 let device = Arc::new(Self::Vmo(Arc::new(RemoteBlockDeviceVmo {
72 backing_memory,
73 backing_memory_size,
74 block_size: BLOCK_SIZE,
75 })));
76 let device_weak = Arc::<RemoteBlockDevice>::downgrade(&device);
77 registry.add_device(
78 locked,
79 current_task,
80 device_name.as_ref(),
81 DeviceMetadata::new(
82 device_name.clone(),
83 DeviceType::new(BLOCK_EXTENDED_MAJOR, minor),
84 DeviceMode::Block,
85 ),
86 virtual_block_class,
87 |device, dir| build_block_device_directory(device, device_weak, dir),
88 )?;
89 Ok(device)
90 }
91
92 fn new<L>(
93 locked: &mut Locked<L>,
94 current_task: &CurrentTask,
95 minor: u32,
96 name: &str,
97 block: ClientEnd<VolumeMarker>,
98 ) -> Result<Arc<Self>, Errno>
99 where
100 L: LockEqualOrBefore<FileOpsCore>,
101 {
102 let kernel = current_task.kernel();
103 let registry = &kernel.device_registry;
104 let device_name = FsString::from(name);
105 let virtual_block_class = registry.objects.virtual_block_class();
106 let block_client = RemoteBlockClientSync::new(block.into_channel().into())
107 .map_err(|status| from_status_like_fdio!(status))?;
108 let device = Arc::new(Self::Fuchsia(Arc::new(block_client)));
109 let device_weak = Arc::<RemoteBlockDevice>::downgrade(&device);
110 registry.add_device(
111 locked,
112 current_task,
113 device_name.as_ref(),
114 DeviceMetadata::new(
115 device_name.clone(),
116 DeviceType::new(BLOCK_EXTENDED_MAJOR, minor),
117 DeviceMode::Block,
118 ),
119 virtual_block_class,
120 |device, dir| build_block_device_directory(device, device_weak, dir),
121 )?;
122 Ok(device)
123 }
124
125 pub fn create_file_ops(&self) -> Box<dyn FileOps> {
126 match self {
127 RemoteBlockDevice::Vmo(device) => {
128 Box::new(VmoBlockDeviceFile { device: device.clone() })
129 }
130 RemoteBlockDevice::Fuchsia(block_client) => {
131 Box::new(RemoteBlockDeviceFile { block_client: block_client.clone() })
132 }
133 }
134 }
135}
136
137impl BlockDeviceInfo for RemoteBlockDevice {
138 fn size(&self) -> Result<usize, Errno> {
139 match self {
140 RemoteBlockDevice::Vmo(device) => Ok(device.backing_memory.get_size() as usize),
141 RemoteBlockDevice::Fuchsia(block_client) => (block_client.block_count() as usize)
142 .checked_mul(block_client.block_size() as usize)
143 .ok_or_else(|| errno!(EINVAL)),
144 }
145 }
146}
147
148struct VmoBlockDeviceFile {
149 device: Arc<RemoteBlockDeviceVmo>,
150}
151
152impl FileOps for VmoBlockDeviceFile {
153 fn has_persistent_offsets(&self) -> bool {
154 true
155 }
156
157 fn is_seekable(&self) -> bool {
158 true
159 }
160
161 fn seek(
164 &self,
165 _locked: &mut Locked<FileOpsCore>,
166 _file: &FileObject,
167 _current_task: &CurrentTask,
168 current_offset: off_t,
169 target: SeekTarget,
170 ) -> Result<off_t, Errno> {
171 default_seek(current_offset, target, || {
172 self.device.backing_memory_size.try_into().map_err(|_| errno!(EINVAL))
173 })
174 }
175
176 fn read(
177 &self,
178 _locked: &mut Locked<FileOpsCore>,
179 _file: &FileObject,
180 _current_task: &CurrentTask,
181 mut offset: usize,
182 data: &mut dyn OutputBuffer,
183 ) -> Result<usize, Errno> {
184 data.write_each(&mut move |buf| {
185 let buflen = buf.len();
186 let buf = &mut buf
187 [..std::cmp::min(self.device.backing_memory_size.saturating_sub(offset), buflen)];
188 if !buf.is_empty() {
189 self.device
190 .backing_memory
191 .read_uninit(buf, offset as u64)
192 .map_err(|status| from_status_like_fdio!(status))?;
193 offset = offset.checked_add(buf.len()).ok_or_else(|| errno!(EINVAL))?;
194 }
195 Ok(buf.len())
196 })
197 }
198
199 fn write(
200 &self,
201 _locked: &mut Locked<FileOpsCore>,
202 _file: &FileObject,
203 _current_task: &CurrentTask,
204 mut offset: usize,
205 data: &mut dyn InputBuffer,
206 ) -> Result<usize, Errno> {
207 data.read_each(&mut move |buf| {
208 let to_write =
209 std::cmp::min(self.device.backing_memory_size.saturating_sub(offset), buf.len());
210 self.device
211 .backing_memory
212 .write(&buf[..to_write], offset as u64)
213 .map_err(|status| from_status_like_fdio!(status))?;
214 offset = offset.checked_add(to_write).ok_or_else(|| errno!(EINVAL))?;
215 Ok(to_write)
216 })
217 }
218
219 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
220 Ok(())
221 }
222
223 fn get_memory(
224 &self,
225 _locked: &mut Locked<FileOpsCore>,
226 _file: &FileObject,
227 _current_task: &CurrentTask,
228 requested_length: Option<usize>,
229 _prot: ProtectionFlags,
230 ) -> Result<Arc<MemoryObject>, Errno> {
231 let slice_len =
232 std::cmp::min(self.device.backing_memory_size, requested_length.unwrap_or(usize::MAX))
233 as u64;
234 self.device
235 .backing_memory
236 .create_child(zx::VmoChildOptions::SLICE, 0, slice_len)
237 .map(Arc::new)
238 .map_err(|status| from_status_like_fdio!(status))
239 }
240
241 fn ioctl(
242 &self,
243 locked: &mut Locked<Unlocked>,
244 file: &FileObject,
245 current_task: &CurrentTask,
246 request: u32,
247 arg: SyscallArg,
248 ) -> Result<SyscallResult, Errno> {
249 match request {
250 BLKGETSIZE => {
251 let user_size = UserRef::<u64>::from(arg);
252 let size =
253 (self.device.backing_memory_size / self.device.block_size as usize) as u64;
254 current_task.write_object(user_size, &size)?;
255 Ok(SUCCESS)
256 }
257 BLKGETSIZE64 => {
258 let user_size = UserRef::<u64>::from(arg);
259 let size = self.device.backing_memory_size as u64;
260 current_task.write_object(user_size, &size)?;
261 Ok(SUCCESS)
262 }
263 _ => default_ioctl(file, locked, current_task, request, arg),
264 }
265 }
266}
267
268struct RemoteBlockDeviceFile {
269 block_client: Arc<RemoteBlockClientSync>,
270}
271
272impl RemoteBlockDeviceFile {
273 fn size(&self) -> Result<usize, Errno> {
274 (self.block_client.block_count() as usize)
275 .checked_mul(self.block_client.block_size() as usize)
276 .ok_or_else(|| errno!(EINVAL))
277 }
278}
279
280impl FileOps for RemoteBlockDeviceFile {
281 fn has_persistent_offsets(&self) -> bool {
282 true
283 }
284
285 fn is_seekable(&self) -> bool {
286 true
287 }
288
289 fn seek(
292 &self,
293 _locked: &mut Locked<FileOpsCore>,
294 _file: &FileObject,
295 _current_task: &CurrentTask,
296 current_offset: off_t,
297 target: SeekTarget,
298 ) -> Result<off_t, Errno> {
299 default_seek(current_offset, target, || self.size()?.try_into().map_err(|_| errno!(EINVAL)))
300 }
301
302 fn read(
303 &self,
304 _locked: &mut Locked<FileOpsCore>,
305 _file: &FileObject,
306 _current_task: &CurrentTask,
307 mut offset: usize,
308 data: &mut dyn OutputBuffer,
309 ) -> Result<usize, Errno> {
310 let size = self.size()?;
311 let block_size = self.block_client.block_size() as usize;
312 const MAX_CHUNK_SIZE: usize = 32 * 1024; let mut total_read = 0;
315 while data.available() > 0 && offset < size {
316 let chunk_len = std::cmp::min(data.available(), MAX_CHUNK_SIZE);
317 let chunk_len = std::cmp::min(chunk_len, size - offset);
318 if chunk_len == 0 {
319 break;
320 }
321
322 let aligned_offset = offset - offset % block_size;
323 let end_offset = offset + chunk_len;
324 let aligned_end_offset = std::cmp::min(
325 end_offset.checked_next_multiple_of(block_size).ok_or_else(|| errno!(EINVAL))?,
326 size,
327 );
328 let aligned_data_length = aligned_end_offset - aligned_offset;
329
330 let mut read_data = vec![0u8; aligned_data_length];
331 self.block_client
332 .read_at(MutableBufferSlice::Memory(&mut read_data), aligned_offset as u64)
333 .map_err(|status| from_status_like_fdio!(status))?;
334
335 let read_offset = offset - aligned_offset;
336 let read_end = read_offset + chunk_len;
337 let bytes_written = data.write(&read_data[read_offset..read_end])?;
338
339 offset += bytes_written;
340 total_read += bytes_written;
341
342 if bytes_written < chunk_len {
343 break;
344 }
345 }
346 Ok(total_read)
347 }
348
349 fn write(
350 &self,
351 _locked: &mut Locked<FileOpsCore>,
352 _file: &FileObject,
353 _current_task: &CurrentTask,
354 mut offset: usize,
355 data: &mut dyn InputBuffer,
356 ) -> Result<usize, Errno> {
357 let size = self.size()?;
358 let block_size = self.block_client.block_size() as usize;
359 const MAX_CHUNK_SIZE: usize = 32 * 1024; let mut total_written = 0;
362 while data.available() > 0 && offset < size {
363 let chunk_len = std::cmp::min(data.available(), MAX_CHUNK_SIZE);
364 let chunk_len = std::cmp::min(chunk_len, size - offset);
365 if chunk_len == 0 {
366 break;
367 }
368
369 let aligned_offset = offset - offset % block_size;
370 let end_offset = offset + chunk_len;
371 let aligned_end_offset = std::cmp::min(
372 end_offset.checked_next_multiple_of(block_size).ok_or_else(|| errno!(EINVAL))?,
373 size,
374 );
375 let aligned_data_length = aligned_end_offset - aligned_offset;
376
377 let mut write_buf = vec![0u8; aligned_data_length];
378
379 let head_unaligned = offset > aligned_offset;
382 let tail_unaligned = end_offset < aligned_end_offset;
383
384 if head_unaligned {
385 self.block_client
386 .read_at(
387 MutableBufferSlice::Memory(&mut write_buf[..block_size]),
388 aligned_offset as u64,
389 )
390 .map_err(|status| from_status_like_fdio!(status))?;
391 }
392
393 if tail_unaligned {
394 let last_block_start = aligned_data_length - block_size;
395 if !head_unaligned || last_block_start > 0 {
398 self.block_client
399 .read_at(
400 MutableBufferSlice::Memory(&mut write_buf[last_block_start..]),
401 (aligned_offset + last_block_start) as u64,
402 )
403 .map_err(|status| from_status_like_fdio!(status))?;
404 }
405 }
406
407 let write_offset = offset - aligned_offset;
408 let write_slice = &mut write_buf[write_offset..write_offset + chunk_len];
409 let write_slice_uninit = unsafe {
412 std::slice::from_raw_parts_mut(
413 write_slice.as_mut_ptr() as *mut std::mem::MaybeUninit<u8>,
414 write_slice.len(),
415 )
416 };
417 let bytes_read = data.read(write_slice_uninit)?;
418
419 self.block_client
420 .write_at(BufferSlice::Memory(&write_buf), aligned_offset as u64)
421 .map_err(|status| from_status_like_fdio!(status))?;
422
423 offset += bytes_read;
424 total_written += bytes_read;
425
426 if bytes_read < chunk_len {
427 break;
428 }
429 }
430 Ok(total_written)
431 }
432
433 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
434 self.block_client.flush().map_err(|status| from_status_like_fdio!(status))
435 }
436
437 fn ioctl(
438 &self,
439 locked: &mut Locked<Unlocked>,
440 file: &FileObject,
441 current_task: &CurrentTask,
442 request: u32,
443 arg: SyscallArg,
444 ) -> Result<SyscallResult, Errno> {
445 match request {
446 BLKGETSIZE => {
447 let user_size = UserRef::<u64>::from(arg);
448 let size = self.block_client.block_count();
449 current_task.write_object(user_size, &size)?;
450 Ok(SUCCESS)
451 }
452 BLKGETSIZE64 => {
453 let user_size = UserRef::<u64>::from(arg);
454 let size = self.size()? as u64;
455 current_task.write_object(user_size, &size)?;
456 Ok(SUCCESS)
457 }
458 _ => default_ioctl(file, locked, current_task, request, arg),
459 }
460 }
461}
462
463fn open_remote_block_device(
464 _locked: &mut Locked<FileOpsCore>,
465 current_task: &CurrentTask,
466 id: DeviceType,
467 _node: &NamespaceNode,
468 _flags: OpenFlags,
469) -> Result<Box<dyn FileOps>, Errno> {
470 Ok(current_task.kernel().remote_block_device_registry.open(id.minor())?.create_file_ops())
471}
472
473pub fn remote_block_device_init(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) {
474 current_task
475 .kernel()
476 .device_registry
477 .register_major(
478 locked,
479 "remote-block".into(),
480 DeviceMode::Block,
481 BLOCK_EXTENDED_MAJOR,
482 open_remote_block_device,
483 )
484 .expect("remote block device register failed.");
485}
486
487#[derive(Default)]
488pub struct RemoteBlockDeviceRegistry {
489 devices: Mutex<BTreeMap<u32, Arc<RemoteBlockDevice>>>,
490 next_minor: AtomicU32,
491 device_added_fn: OnceLock<RemoteBlockDeviceAddedFn>,
492}
493
494pub type RemoteBlockDeviceAddedFn = Box<dyn Fn(&str, u32, &Arc<RemoteBlockDevice>) + Send + Sync>;
496
497impl RemoteBlockDeviceRegistry {
498 pub fn on_device_added(&self, callback: RemoteBlockDeviceAddedFn) {
500 self.device_added_fn.set(callback).map_err(|_| ()).expect("Callback already set");
501 }
502
503 pub fn create_vmo_block_device<L>(
506 &self,
507 locked: &mut Locked<L>,
508 current_task: &CurrentTask,
509 name: &str,
510 initial_size: u64,
511 ) -> Result<(), Error>
512 where
513 L: LockEqualOrBefore<FileOpsCore>,
514 {
515 let mut devices = self.devices.lock();
516 let backing_memory = MemoryObject::from(zx::Vmo::create(initial_size)?)
517 .with_zx_name(b"starnix:remote_block_device");
518 let minor = self.next_minor.fetch_add(1, Ordering::Relaxed);
519 let device =
520 RemoteBlockDevice::new_from_vmo(locked, current_task, minor, name, backing_memory)?;
521 if let Some(callback) = self.device_added_fn.get() {
522 callback(name, minor, &device);
523 }
524 devices.insert(minor, device);
525 Ok(())
526 }
527
528 pub fn create_remote_block_device<L>(
529 &self,
530 locked: &mut Locked<L>,
531 current_task: &CurrentTask,
532 name: &str,
533 block: ClientEnd<VolumeMarker>,
534 ) -> Result<(), Error>
535 where
536 L: LockEqualOrBefore<FileOpsCore>,
537 {
538 let mut devices = self.devices.lock();
539 let minor = self.next_minor.fetch_add(1, Ordering::Relaxed);
540 let device = RemoteBlockDevice::new(locked, current_task, minor, name, block)?;
541 devices.insert(minor, device);
542 Ok(())
543 }
544
545 pub fn open(&self, minor: u32) -> Result<Arc<RemoteBlockDevice>, Errno> {
546 self.devices.lock().get(&minor).ok_or_else(|| errno!(ENODEV)).cloned()
547 }
548}
549
550#[cfg(test)]
551mod tests {
552 use super::*;
553 use crate::testing::{anon_test_file, map_object_anywhere, spawn_kernel_and_run};
554 use crate::vfs::{SeekTarget, VecInputBuffer, VecOutputBuffer};
555 use starnix_uapi::open_flags::OpenFlags;
556 use starnix_uapi::{BLKGETSIZE, BLKGETSIZE64};
557 use vmo_backed_block_server::{VmoBackedServer, VmoBackedServerTestingExt};
558 use zerocopy::FromBytes as _;
559
560 #[::fuchsia::test]
561 async fn test_vmo_block_device_registry() {
562 spawn_kernel_and_run(async |locked, current_task| {
563 let kernel = current_task.kernel();
564 remote_block_device_init(locked, ¤t_task);
565 let registry = kernel.remote_block_device_registry.clone();
566
567 registry
568 .create_vmo_block_device(locked, ¤t_task, "test", 1024)
569 .expect("create_vmo_block_device failed.");
570
571 let device = registry.open(0).expect("open failed.");
572 let file =
573 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
574
575 let arg_addr = map_object_anywhere(locked, ¤t_task, &0u64);
576 let mut arg = [0u8; 8];
577
578 file.ioctl(locked, ¤t_task, BLKGETSIZE64, arg_addr.into()).expect("ioctl failed");
579 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
580 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 1024);
581
582 file.ioctl(locked, ¤t_task, BLKGETSIZE, arg_addr.into()).expect("ioctl failed");
583 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
584 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 2);
585
586 let mut buf = VecOutputBuffer::new(512);
587 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
588 assert_eq!(buf.data(), &[0u8; 512]);
589
590 let mut buf = VecInputBuffer::from(vec![1u8; 512]);
591 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
592 file.write(locked, ¤t_task, &mut buf).expect("write failed.");
593
594 let mut buf = VecOutputBuffer::new(512);
595 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
596 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
597 assert_eq!(buf.data(), &[1u8; 512]);
598 })
599 .await;
600 }
601
602 #[::fuchsia::test]
603 async fn test_vmo_read_write_past_eof() {
604 spawn_kernel_and_run(async |locked, current_task| {
605 let kernel = current_task.kernel();
606 remote_block_device_init(locked, ¤t_task);
607 let registry = kernel.remote_block_device_registry.clone();
608
609 registry
610 .create_vmo_block_device(locked, ¤t_task, "test", 1024)
611 .expect("create_vmo_block_device failed.");
612
613 let device = registry.open(0).expect("open failed.");
614 let file =
615 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
616
617 file.seek(locked, ¤t_task, SeekTarget::End(0)).expect("seek failed");
618 let mut buf = VecOutputBuffer::new(512);
619 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 0);
620
621 let mut buf = VecInputBuffer::from(vec![1u8; 512]);
622 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 0);
623 })
624 .await;
625 }
626
627 #[::fuchsia::test]
628 async fn test_remote_block_device_registry() {
629 spawn_kernel_and_run(async |locked, current_task| {
630 let kernel = current_task.kernel();
631 remote_block_device_init(locked, ¤t_task);
632 let registry = kernel.remote_block_device_registry.clone();
633 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
634 let (client, server_end) = fidl::endpoints::create_endpoints::<VolumeMarker>();
635 std::thread::spawn(move || {
636 let mut executor = fuchsia_async::LocalExecutor::default();
637 executor.run_singlethreaded(async move {
638 use fidl::endpoints::RequestStream;
639 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
640 });
641 });
642
643 registry
644 .create_remote_block_device(locked, ¤t_task, "test", client)
645 .expect("create_remote_block_device failed.");
646
647 let device = registry.open(0).expect("open failed.");
648 let file =
649 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
650
651 let arg_addr = map_object_anywhere(locked, ¤t_task, &0u64);
652 let mut arg = [0u8; 8];
653
654 file.ioctl(locked, ¤t_task, BLKGETSIZE64, arg_addr.into()).expect("ioctl failed");
655 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
656 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 1024);
657
658 file.ioctl(locked, ¤t_task, BLKGETSIZE, arg_addr.into()).expect("ioctl failed");
659 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
660 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 2);
661
662 let mut buf = VecOutputBuffer::new(256);
665 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
666 assert_eq!(buf.data(), &[0u8; 256]);
667
668 let mut buf = VecInputBuffer::from(vec![1u8; 256]);
669 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
670 file.write(locked, ¤t_task, &mut buf).expect("write failed.");
671
672 let mut buf = VecOutputBuffer::new(256);
673 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
674 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
675 assert_eq!(buf.data(), &[1u8; 256]);
676 })
677 .await;
678 }
679
680 #[::fuchsia::test]
681 async fn test_read_write_past_eof() {
682 spawn_kernel_and_run(async |locked, current_task| {
683 let kernel = current_task.kernel();
684 remote_block_device_init(locked, ¤t_task);
685 let registry = kernel.remote_block_device_registry.clone();
686 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
687 let (client, server_end) = fidl::endpoints::create_endpoints::<VolumeMarker>();
688 std::thread::spawn(move || {
689 let mut executor = fuchsia_async::LocalExecutor::default();
690 executor.run_singlethreaded(async move {
691 use fidl::endpoints::RequestStream;
692 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
693 });
694 });
695
696 registry
697 .create_remote_block_device(locked, ¤t_task, "test", client)
698 .expect("create_remote_block_device failed.");
699
700 let device = registry.open(0).expect("open failed.");
701 let file =
702 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
703
704 file.seek(locked, ¤t_task, SeekTarget::End(0)).expect("seek failed");
705 let mut buf = VecOutputBuffer::new(512);
706 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 0);
707
708 let mut buf = VecInputBuffer::from(vec![1u8; 512]);
709 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 0);
710 })
711 .await;
712 }
713
714 #[::fuchsia::test]
715 async fn test_unaligned_read_write_spanning_blocks() {
716 spawn_kernel_and_run(async |locked, current_task| {
717 let kernel = current_task.kernel();
718 remote_block_device_init(locked, ¤t_task);
719 let registry = kernel.remote_block_device_registry.clone();
720 let server = Arc::new(VmoBackedServer::new(3, 512, &[0u8; 1536]));
722 let (client, server_end) = fidl::endpoints::create_endpoints::<VolumeMarker>();
723 std::thread::spawn(move || {
724 let mut executor = fuchsia_async::LocalExecutor::default();
725 executor.run_singlethreaded(async move {
726 use fidl::endpoints::RequestStream;
727 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
728 });
729 });
730
731 registry
732 .create_remote_block_device(locked, ¤t_task, "test", client)
733 .expect("create_remote_block_device failed.");
734
735 let device = registry.open(0).expect("open failed.");
736 let file =
737 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
738
739 let mut buf = VecInputBuffer::from(vec![0xAAu8; 516]);
743 file.seek(locked, ¤t_task, SeekTarget::Set(510)).expect("seek failed");
744 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 516);
745
746 let mut buf = VecOutputBuffer::new(516);
748 file.seek(locked, ¤t_task, SeekTarget::Set(510)).expect("seek failed");
749 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 516);
750 assert_eq!(buf.data(), &[0xAAu8; 516]);
751
752 let mut buf = VecOutputBuffer::new(1);
754 file.seek(locked, ¤t_task, SeekTarget::Set(509)).expect("seek failed");
755 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 1);
756 assert_eq!(buf.data(), &[0u8]);
757
758 let mut buf = VecOutputBuffer::new(1);
759 file.seek(locked, ¤t_task, SeekTarget::Set(1026)).expect("seek failed");
760 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 1);
761 assert_eq!(buf.data(), &[0u8]);
762 })
763 .await;
764 }
765
766 #[::fuchsia::test]
767 async fn test_exact_eof_boundary() {
768 spawn_kernel_and_run(async |locked, current_task| {
769 let kernel = current_task.kernel();
770 remote_block_device_init(locked, ¤t_task);
771 let registry = kernel.remote_block_device_registry.clone();
772 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
774 let (client, server_end) = fidl::endpoints::create_endpoints::<VolumeMarker>();
775 std::thread::spawn(move || {
776 let mut executor = fuchsia_async::LocalExecutor::default();
777 executor.run_singlethreaded(async move {
778 use fidl::endpoints::RequestStream;
779 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
780 });
781 });
782
783 registry
784 .create_remote_block_device(locked, ¤t_task, "test", client)
785 .expect("create_remote_block_device failed.");
786
787 let device = registry.open(0).expect("open failed.");
788 let file =
789 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
790
791 let mut buf = VecInputBuffer::from(vec![0xBBu8; 4]);
794 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
795 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 4);
796
797 let mut buf = VecOutputBuffer::new(4);
799 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
800 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 4);
801 assert_eq!(buf.data(), &[0xBBu8; 4]);
802
803 let mut buf = VecOutputBuffer::new(5);
805 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
806 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 4);
807 assert_eq!(buf.data()[..4], [0xBBu8; 4]);
808 })
809 .await;
810 }
811
812 #[::fuchsia::test]
813 async fn test_rmw_preserves_data() {
814 spawn_kernel_and_run(async |locked, current_task| {
815 let kernel = current_task.kernel();
816 remote_block_device_init(locked, ¤t_task);
817 let registry = kernel.remote_block_device_registry.clone();
818 let server = Arc::new(VmoBackedServer::new(3, 512, &[0xFFu8; 1536]));
821 let (client, server_end) = fidl::endpoints::create_endpoints::<VolumeMarker>();
822 std::thread::spawn(move || {
823 let mut executor = fuchsia_async::LocalExecutor::default();
824 executor.run_singlethreaded(async move {
825 use fidl::endpoints::RequestStream;
826 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
827 });
828 });
829
830 registry
831 .create_remote_block_device(locked, ¤t_task, "test", client)
832 .expect("create_remote_block_device failed.");
833
834 let device = registry.open(0).expect("open failed.");
835 let file =
836 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
837
838 let mut buf = VecInputBuffer::from(vec![0xAAu8; 100]);
842 file.seek(locked, ¤t_task, SeekTarget::Set(600)).expect("seek failed");
843 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 100);
844
845 let mut buf = VecOutputBuffer::new(512);
847 file.seek(locked, ¤t_task, SeekTarget::Set(512)).expect("seek failed");
848 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 512);
849 let data = buf.data();
850
851 assert_eq!(&data[0..88], &[0xFFu8; 88]);
853 assert_eq!(&data[88..188], &[0xAAu8; 100]);
855 assert_eq!(&data[188..512], &[0xFFu8; 324]);
857 })
858 .await;
859 }
860}