1use crate::device::DeviceMode;
6use crate::device::block::canonicalize_ioctl_request;
7use crate::device::kobject::DeviceMetadata;
8use crate::fs::sysfs::{BlockDeviceInfo, build_block_device_directory};
9use crate::mm::MemoryAccessorExt;
10use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
11use crate::task::{CurrentTask, KernelThreads, LockedAndTask};
12use crate::vfs::buffers::{InputBuffer, OutputBuffer};
13use crate::vfs::{
14 FileObject, FileOps, FsString, NamespaceNode, SeekTarget, default_ioctl, default_seek,
15};
16use anyhow::{Context as _, Error};
17use block_client::{BlockClient, BufferSlice, MutableBufferSlice, RemoteBlockClient};
18use fidl::endpoints::ClientEnd;
19use fidl_fuchsia_storage_block::BlockMarker;
20use futures::channel::oneshot;
21use futures::executor::block_on;
22use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
23use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
24use starnix_uapi::device_type::{BLOCK_EXTENDED_MAJOR, DeviceType};
25use starnix_uapi::errors::Errno;
26use starnix_uapi::open_flags::OpenFlags;
27use starnix_uapi::user_address::{MultiArchUserRef, UserRef};
28use starnix_uapi::{BLKGETSIZE, BLKGETSIZE64, errno, from_status_like_fdio, off_t};
29use std::collections::btree_map::BTreeMap;
30use std::sync::Arc;
31use std::sync::atomic::{AtomicU32, Ordering};
32
33pub struct RemoteBlockDevice {
35 block_client: Arc<SyncBlockClient>,
36}
37
38impl RemoteBlockDevice {
39 pub fn read(&self, offset: u64, buf: &mut [u8]) -> Result<(), Error> {
40 self.block_client
41 .read_at(MutableBufferSlice::Memory(buf), offset as u64)
42 .context("read_at failed")
43 }
44
45 fn new<L>(
46 locked: &mut Locked<L>,
47 current_task: &CurrentTask,
48 minor: u32,
49 name: &str,
50 block: ClientEnd<BlockMarker>,
51 ) -> Result<Arc<Self>, Errno>
52 where
53 L: LockEqualOrBefore<FileOpsCore>,
54 {
55 let kernel = current_task.kernel();
56 let registry = &kernel.device_registry;
57 let device_name = FsString::from(name);
58 let virtual_block_class = registry.objects.virtual_block_class();
59 let block_client = SyncBlockClient::new(&kernel.kthreads, block)?;
60 let device = Arc::new(Self { block_client });
61 let device_weak = Arc::<RemoteBlockDevice>::downgrade(&device);
62 registry.add_device(
63 locked,
64 current_task,
65 device_name.as_ref(),
66 DeviceMetadata::new(
67 device_name.clone(),
68 DeviceType::new(BLOCK_EXTENDED_MAJOR, minor),
69 DeviceMode::Block,
70 ),
71 virtual_block_class,
72 |device, dir| build_block_device_directory(device, device_weak, dir),
73 )?;
74 Ok(device)
75 }
76
77 pub fn create_file_ops(&self) -> Box<dyn FileOps> {
78 Box::new(RemoteBlockDeviceFile { block_client: self.block_client.clone() })
79 }
80}
81
82impl BlockDeviceInfo for RemoteBlockDevice {
83 fn size(&self) -> Result<usize, Errno> {
84 (self.block_client.block_count() as usize)
85 .checked_mul(self.block_client.block_size() as usize)
86 .ok_or_else(|| errno!(EINVAL))
87 }
88}
89
90pub struct SyncBlockClient {
91 client: Arc<RemoteBlockClient>,
92 terminate_tx: Option<oneshot::Sender<()>>,
93}
94
95impl SyncBlockClient {
96 fn new(kthreads: &KernelThreads, block: ClientEnd<BlockMarker>) -> Result<Arc<Self>, Errno> {
97 let (init_tx, init_rx) = std::sync::mpsc::channel();
98 let (terminate_tx, terminate_rx) = oneshot::channel();
99
100 let closure = move |_: LockedAndTask<'_>| async move {
102 let proxy = block.into_proxy();
103 match RemoteBlockClient::new(proxy).await {
104 Ok(client) => {
105 let _ = init_tx.send(Ok(Arc::new(Self {
106 client: Arc::new(client),
107 terminate_tx: Some(terminate_tx),
108 })));
109 }
110 Err(e) => {
111 let _ = init_tx.send(Err(e));
112 return;
113 }
114 }
115 let _ = terminate_rx.await;
119 };
120
121 let req = SpawnRequestBuilder::new()
122 .with_debug_name("remote-block-client")
123 .with_async_closure(closure)
124 .build();
125 kthreads.spawner().spawn_from_request(req);
126
127 match init_rx.recv() {
128 Ok(Ok(client)) => Ok(client),
129 Ok(Err(status)) => Err(from_status_like_fdio!(status)),
130 Err(_) => Err(errno!(EINVAL)),
131 }
132 }
133
134 fn block_size(&self) -> u32 {
135 self.client.block_size()
136 }
137
138 fn block_count(&self) -> u64 {
139 self.client.block_count()
140 }
141
142 fn read_at(
143 &self,
144 buffer_slice: MutableBufferSlice<'_>,
145 device_offset: u64,
146 ) -> Result<(), zx::Status> {
147 block_on(self.client.read_at(buffer_slice, device_offset))
151 }
152
153 fn write_at(
154 &self,
155 buffer_slice: BufferSlice<'_>,
156 device_offset: u64,
157 ) -> Result<(), zx::Status> {
158 block_on(self.client.write_at(buffer_slice, device_offset))
162 }
163
164 fn flush(&self) -> Result<(), zx::Status> {
165 block_on(self.client.flush())
169 }
170}
171
172impl Drop for SyncBlockClient {
173 fn drop(&mut self) {
174 if let Some(tx) = self.terminate_tx.take() {
175 let _ = tx.send(());
176 }
177 }
178}
179
180struct RemoteBlockDeviceFile {
181 block_client: Arc<SyncBlockClient>,
182}
183
184impl RemoteBlockDeviceFile {
185 fn size(&self) -> Result<usize, Errno> {
186 (self.block_client.block_count() as usize)
187 .checked_mul(self.block_client.block_size() as usize)
188 .ok_or_else(|| errno!(EINVAL))
189 }
190}
191
192impl FileOps for RemoteBlockDeviceFile {
193 fn has_persistent_offsets(&self) -> bool {
194 true
195 }
196
197 fn is_seekable(&self) -> bool {
198 true
199 }
200
201 fn seek(
204 &self,
205 _locked: &mut Locked<FileOpsCore>,
206 _file: &FileObject,
207 _current_task: &CurrentTask,
208 current_offset: off_t,
209 target: SeekTarget,
210 ) -> Result<off_t, Errno> {
211 default_seek(current_offset, target, || self.size()?.try_into().map_err(|_| errno!(EINVAL)))
212 }
213
214 fn read(
215 &self,
216 _locked: &mut Locked<FileOpsCore>,
217 _file: &FileObject,
218 _current_task: &CurrentTask,
219 mut offset: usize,
220 data: &mut dyn OutputBuffer,
221 ) -> Result<usize, Errno> {
222 let size = self.size()?;
223 let block_size = self.block_client.block_size() as usize;
224 const MAX_CHUNK_SIZE: usize = 32 * 1024; let mut total_read = 0;
227 while data.available() > 0 && offset < size {
228 let chunk_len = std::cmp::min(data.available(), MAX_CHUNK_SIZE);
229 let chunk_len = std::cmp::min(chunk_len, size - offset);
230 if chunk_len == 0 {
231 break;
232 }
233
234 let aligned_offset = offset - offset % block_size;
235 let end_offset = offset + chunk_len;
236 let aligned_end_offset = std::cmp::min(
237 end_offset.checked_next_multiple_of(block_size).ok_or_else(|| errno!(EINVAL))?,
238 size,
239 );
240 let aligned_data_length = aligned_end_offset - aligned_offset;
241
242 let mut read_data = vec![0u8; aligned_data_length];
243 self.block_client
244 .read_at(MutableBufferSlice::Memory(&mut read_data), aligned_offset as u64)
245 .map_err(|status| from_status_like_fdio!(status))?;
246
247 let read_offset = offset - aligned_offset;
248 let read_end = read_offset + chunk_len;
249 let bytes_written = data.write(&read_data[read_offset..read_end])?;
250
251 offset += bytes_written;
252 total_read += bytes_written;
253
254 if bytes_written < chunk_len {
255 break;
256 }
257 }
258 Ok(total_read)
259 }
260
261 fn write(
262 &self,
263 _locked: &mut Locked<FileOpsCore>,
264 _file: &FileObject,
265 _current_task: &CurrentTask,
266 mut offset: usize,
267 data: &mut dyn InputBuffer,
268 ) -> Result<usize, Errno> {
269 let size = self.size()?;
270 let block_size = self.block_client.block_size() as usize;
271 const MAX_CHUNK_SIZE: usize = 32 * 1024; let mut total_written = 0;
274 while data.available() > 0 && offset < size {
275 let chunk_len = std::cmp::min(data.available(), MAX_CHUNK_SIZE);
276 let chunk_len = std::cmp::min(chunk_len, size - offset);
277 if chunk_len == 0 {
278 break;
279 }
280
281 let aligned_offset = offset - offset % block_size;
282 let end_offset = offset + chunk_len;
283 let aligned_end_offset = std::cmp::min(
284 end_offset.checked_next_multiple_of(block_size).ok_or_else(|| errno!(EINVAL))?,
285 size,
286 );
287 let aligned_data_length = aligned_end_offset - aligned_offset;
288
289 let mut write_buf = vec![0u8; aligned_data_length];
290
291 let head_unaligned = offset > aligned_offset;
294 let tail_unaligned = end_offset < aligned_end_offset;
295
296 if head_unaligned {
297 self.block_client
298 .read_at(
299 MutableBufferSlice::Memory(&mut write_buf[..block_size]),
300 aligned_offset as u64,
301 )
302 .map_err(|status| from_status_like_fdio!(status))?;
303 }
304
305 if tail_unaligned {
306 let last_block_start = aligned_data_length - block_size;
307 if !head_unaligned || last_block_start > 0 {
310 self.block_client
311 .read_at(
312 MutableBufferSlice::Memory(&mut write_buf[last_block_start..]),
313 (aligned_offset + last_block_start) as u64,
314 )
315 .map_err(|status| from_status_like_fdio!(status))?;
316 }
317 }
318
319 let write_offset = offset - aligned_offset;
320 let write_slice = &mut write_buf[write_offset..write_offset + chunk_len];
321 let write_slice_uninit = unsafe {
324 std::slice::from_raw_parts_mut(
325 write_slice.as_mut_ptr() as *mut std::mem::MaybeUninit<u8>,
326 write_slice.len(),
327 )
328 };
329 let bytes_read = data.read(write_slice_uninit)?;
330
331 self.block_client
332 .write_at(BufferSlice::Memory(&write_buf), aligned_offset as u64)
333 .map_err(|status| from_status_like_fdio!(status))?;
334
335 offset += bytes_read;
336 total_written += bytes_read;
337
338 if bytes_read < chunk_len {
339 break;
340 }
341 }
342 Ok(total_written)
343 }
344
345 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
346 self.block_client.flush().map_err(|status| from_status_like_fdio!(status))
347 }
348
349 fn ioctl(
350 &self,
351 locked: &mut Locked<Unlocked>,
352 file: &FileObject,
353 current_task: &CurrentTask,
354 request: u32,
355 arg: SyscallArg,
356 ) -> Result<SyscallResult, Errno> {
357 match canonicalize_ioctl_request(current_task, request) {
358 BLKGETSIZE => {
359 let user_size = MultiArchUserRef::<u64, u32>::new(current_task, arg);
360 let size = self.block_client.block_count();
361 current_task.write_multi_arch_object(user_size, size)?;
362 Ok(SUCCESS)
363 }
364 BLKGETSIZE64 => {
365 let user_size = UserRef::<u64>::from(arg);
366 let size = self.size()? as u64;
367 current_task.write_object(user_size, &size)?;
368 Ok(SUCCESS)
369 }
370 _ => default_ioctl(file, locked, current_task, request, arg),
371 }
372 }
373}
374
375fn open_remote_block_device(
376 _locked: &mut Locked<FileOpsCore>,
377 current_task: &CurrentTask,
378 id: DeviceType,
379 _node: &NamespaceNode,
380 _flags: OpenFlags,
381) -> Result<Box<dyn FileOps>, Errno> {
382 Ok(current_task.kernel().remote_block_device_registry.open(id.minor())?.create_file_ops())
383}
384
385pub fn remote_block_device_init(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) {
386 current_task
387 .kernel()
388 .device_registry
389 .register_major(
390 locked,
391 "remote-block".into(),
392 DeviceMode::Block,
393 BLOCK_EXTENDED_MAJOR,
394 open_remote_block_device,
395 )
396 .expect("remote block device register failed.");
397}
398
399#[derive(Default)]
400pub struct RemoteBlockDeviceRegistry {
401 devices: Mutex<BTreeMap<u32, Arc<RemoteBlockDevice>>>,
402 next_minor: AtomicU32,
403}
404
405impl RemoteBlockDeviceRegistry {
406 pub fn create_remote_block_device<L>(
407 &self,
408 locked: &mut Locked<L>,
409 current_task: &CurrentTask,
410 name: &str,
411 block: ClientEnd<BlockMarker>,
412 ) -> Result<(), Error>
413 where
414 L: LockEqualOrBefore<FileOpsCore>,
415 {
416 let mut devices = self.devices.lock();
417 let minor = self.next_minor.fetch_add(1, Ordering::Relaxed);
418 let device = RemoteBlockDevice::new(locked, current_task, minor, name, block)?;
419 devices.insert(minor, device);
420 Ok(())
421 }
422
423 pub fn open(&self, minor: u32) -> Result<Arc<RemoteBlockDevice>, Errno> {
424 self.devices.lock().get(&minor).ok_or_else(|| errno!(ENODEV)).cloned()
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431 use crate::testing::{anon_test_file, map_object_anywhere, spawn_kernel_and_run};
432 use crate::vfs::{SeekTarget, VecInputBuffer, VecOutputBuffer};
433 use starnix_uapi::open_flags::OpenFlags;
434 use starnix_uapi::{BLKGETSIZE, BLKGETSIZE64};
435 use vmo_backed_block_server::{VmoBackedServer, VmoBackedServerTestingExt};
436 use zerocopy::FromBytes as _;
437
438 #[::fuchsia::test]
439 async fn test_remote_block_device_registry() {
440 spawn_kernel_and_run(async |locked, current_task| {
441 let kernel = current_task.kernel();
442 remote_block_device_init(locked, ¤t_task);
443 let registry = kernel.remote_block_device_registry.clone();
444 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
445 let (client, server_end) = fidl::endpoints::create_endpoints::<BlockMarker>();
446 std::thread::spawn(move || {
447 let mut executor = fuchsia_async::LocalExecutor::default();
448 executor.run_singlethreaded(async move {
449 use fidl::endpoints::RequestStream;
450 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
451 });
452 });
453
454 registry
455 .create_remote_block_device(locked, ¤t_task, "test", client)
456 .expect("create_remote_block_device failed.");
457
458 let device = registry.open(0).expect("open failed.");
459 let file =
460 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
461
462 let arg_addr = map_object_anywhere(locked, ¤t_task, &0u64);
463 let mut arg = [0u8; 8];
464
465 file.ioctl(locked, ¤t_task, BLKGETSIZE64, arg_addr.into()).expect("ioctl failed");
466 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
467 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 1024);
468
469 file.ioctl(locked, ¤t_task, BLKGETSIZE, arg_addr.into()).expect("ioctl failed");
470 current_task.read_memory_to_slice(arg_addr, &mut arg).unwrap();
471 assert_eq!(u64::read_from_bytes(&arg).unwrap(), 2);
472
473 let mut buf = VecOutputBuffer::new(256);
476 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
477 assert_eq!(buf.data(), &[0u8; 256]);
478
479 let mut buf = VecInputBuffer::from(vec![1u8; 256]);
480 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
481 file.write(locked, ¤t_task, &mut buf).expect("write failed.");
482
483 let mut buf = VecOutputBuffer::new(256);
484 file.seek(locked, ¤t_task, SeekTarget::Set(0)).expect("seek failed");
485 file.read(locked, ¤t_task, &mut buf).expect("read failed.");
486 assert_eq!(buf.data(), &[1u8; 256]);
487 })
488 .await;
489 }
490
491 #[::fuchsia::test]
492 async fn test_read_write_past_eof() {
493 spawn_kernel_and_run(async |locked, current_task| {
494 let kernel = current_task.kernel();
495 remote_block_device_init(locked, ¤t_task);
496 let registry = kernel.remote_block_device_registry.clone();
497 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
498 let (client, server_end) = fidl::endpoints::create_endpoints::<BlockMarker>();
499 std::thread::spawn(move || {
500 let mut executor = fuchsia_async::LocalExecutor::default();
501 executor.run_singlethreaded(async move {
502 use fidl::endpoints::RequestStream;
503 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
504 });
505 });
506
507 registry
508 .create_remote_block_device(locked, ¤t_task, "test", client)
509 .expect("create_remote_block_device failed.");
510
511 let device = registry.open(0).expect("open failed.");
512 let file =
513 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
514
515 file.seek(locked, ¤t_task, SeekTarget::End(0)).expect("seek failed");
516 let mut buf = VecOutputBuffer::new(512);
517 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 0);
518
519 let mut buf = VecInputBuffer::from(vec![1u8; 512]);
520 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 0);
521 })
522 .await;
523 }
524
525 #[::fuchsia::test]
526 async fn test_unaligned_read_write_spanning_blocks() {
527 spawn_kernel_and_run(async |locked, current_task| {
528 let kernel = current_task.kernel();
529 remote_block_device_init(locked, ¤t_task);
530 let registry = kernel.remote_block_device_registry.clone();
531 let server = Arc::new(VmoBackedServer::new(3, 512, &[0u8; 1536]));
533 let (client, server_end) = fidl::endpoints::create_endpoints::<BlockMarker>();
534 std::thread::spawn(move || {
535 let mut executor = fuchsia_async::LocalExecutor::default();
536 executor.run_singlethreaded(async move {
537 use fidl::endpoints::RequestStream;
538 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
539 });
540 });
541
542 registry
543 .create_remote_block_device(locked, ¤t_task, "test", client)
544 .expect("create_remote_block_device failed.");
545
546 let device = registry.open(0).expect("open failed.");
547 let file =
548 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
549
550 let mut buf = VecInputBuffer::from(vec![0xAAu8; 516]);
554 file.seek(locked, ¤t_task, SeekTarget::Set(510)).expect("seek failed");
555 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 516);
556
557 let mut buf = VecOutputBuffer::new(516);
559 file.seek(locked, ¤t_task, SeekTarget::Set(510)).expect("seek failed");
560 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 516);
561 assert_eq!(buf.data(), &[0xAAu8; 516]);
562
563 let mut buf = VecOutputBuffer::new(1);
565 file.seek(locked, ¤t_task, SeekTarget::Set(509)).expect("seek failed");
566 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 1);
567 assert_eq!(buf.data(), &[0u8]);
568
569 let mut buf = VecOutputBuffer::new(1);
570 file.seek(locked, ¤t_task, SeekTarget::Set(1026)).expect("seek failed");
571 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 1);
572 assert_eq!(buf.data(), &[0u8]);
573 })
574 .await;
575 }
576
577 #[::fuchsia::test]
578 async fn test_exact_eof_boundary() {
579 spawn_kernel_and_run(async |locked, current_task| {
580 let kernel = current_task.kernel();
581 remote_block_device_init(locked, ¤t_task);
582 let registry = kernel.remote_block_device_registry.clone();
583 let server = Arc::new(VmoBackedServer::new(2, 512, &[0u8; 1024]));
585 let (client, server_end) = fidl::endpoints::create_endpoints::<BlockMarker>();
586 std::thread::spawn(move || {
587 let mut executor = fuchsia_async::LocalExecutor::default();
588 executor.run_singlethreaded(async move {
589 use fidl::endpoints::RequestStream;
590 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
591 });
592 });
593
594 registry
595 .create_remote_block_device(locked, ¤t_task, "test", client)
596 .expect("create_remote_block_device failed.");
597
598 let device = registry.open(0).expect("open failed.");
599 let file =
600 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
601
602 let mut buf = VecInputBuffer::from(vec![0xBBu8; 4]);
605 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
606 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 4);
607
608 let mut buf = VecOutputBuffer::new(4);
610 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
611 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 4);
612 assert_eq!(buf.data(), &[0xBBu8; 4]);
613
614 let mut buf = VecOutputBuffer::new(5);
616 file.seek(locked, ¤t_task, SeekTarget::Set(1020)).expect("seek failed");
617 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 4);
618 assert_eq!(buf.data()[..4], [0xBBu8; 4]);
619 })
620 .await;
621 }
622
623 #[::fuchsia::test]
624 async fn test_rmw_preserves_data() {
625 spawn_kernel_and_run(async |locked, current_task| {
626 let kernel = current_task.kernel();
627 remote_block_device_init(locked, ¤t_task);
628 let registry = kernel.remote_block_device_registry.clone();
629 let server = Arc::new(VmoBackedServer::new(3, 512, &[0xFFu8; 1536]));
632 let (client, server_end) = fidl::endpoints::create_endpoints::<BlockMarker>();
633 std::thread::spawn(move || {
634 let mut executor = fuchsia_async::LocalExecutor::default();
635 executor.run_singlethreaded(async move {
636 use fidl::endpoints::RequestStream;
637 server.serve(server_end.into_stream().cast_stream()).await.unwrap();
638 });
639 });
640
641 registry
642 .create_remote_block_device(locked, ¤t_task, "test", client)
643 .expect("create_remote_block_device failed.");
644
645 let device = registry.open(0).expect("open failed.");
646 let file =
647 anon_test_file(locked, ¤t_task, device.create_file_ops(), OpenFlags::RDWR);
648
649 let mut buf = VecInputBuffer::from(vec![0xAAu8; 100]);
653 file.seek(locked, ¤t_task, SeekTarget::Set(600)).expect("seek failed");
654 assert_eq!(file.write(locked, ¤t_task, &mut buf).expect("write failed."), 100);
655
656 let mut buf = VecOutputBuffer::new(512);
658 file.seek(locked, ¤t_task, SeekTarget::Set(512)).expect("seek failed");
659 assert_eq!(file.read(locked, ¤t_task, &mut buf).expect("read failed."), 512);
660 let data = buf.data();
661
662 assert_eq!(&data[0..88], &[0xFFu8; 88]);
664 assert_eq!(&data[88..188], &[0xAAu8; 100]);
666 assert_eq!(&data[188..512], &[0xFFu8; 324]);
668 })
669 .await;
670 }
671}