1 use libc::{c_int, c_void};
2 use nix::{Error, Result};
3 use nix::errno::*;
4 use nix::sys::aio::*;
5 use nix::sys::signal::{SaFlags, SigAction, sigaction, SigevNotify, SigHandler, Signal, SigSet};
6 use nix::sys::time::{TimeSpec, TimeValLike};
7 use std::io::{Write, Read, Seek, SeekFrom};
8 use std::ops::Deref;
9 use std::os::unix::io::AsRawFd;
10 use std::pin::Pin;
11 use std::sync::atomic::{AtomicBool, Ordering};
12 use std::{thread, time};
13 use tempfile::tempfile;
14 
15 // Helper that polls an AioCb for completion or error
16 fn poll_aio(aiocb: &mut Pin<Box<AioCb>>) -> Result<()> {
17     loop {
18         let err = aiocb.error();
19         if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
20         thread::sleep(time::Duration::from_millis(10));
21     }
22 }
23 
24 // Helper that polls a component of an LioCb for completion or error
25 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
26 fn poll_lio(liocb: &mut LioCb, i: usize) -> Result<()> {
27     loop {
28         let err = liocb.error(i);
29         if err != Err(Error::from(Errno::EINPROGRESS)) { return err; };
30         thread::sleep(time::Duration::from_millis(10));
31     }
32 }
33 
34 #[test]
35 fn test_accessors() {
36     let mut rbuf = vec![0; 4];
37     let aiocb = AioCb::from_mut_slice( 1001,
38                            2,   //offset
39                            &mut rbuf,
40                            42,   //priority
41                            SigevNotify::SigevSignal {
42                                signal: Signal::SIGUSR2,
43                                si_value: 99
44                            },
45                            LioOpcode::LIO_NOP);
46     assert_eq!(1001, aiocb.fd());
47     assert_eq!(Some(LioOpcode::LIO_NOP), aiocb.lio_opcode());
48     assert_eq!(4, aiocb.nbytes());
49     assert_eq!(2, aiocb.offset());
50     assert_eq!(42, aiocb.priority());
51     let sev = aiocb.sigevent().sigevent();
52     assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
53     assert_eq!(99, sev.sigev_value.sival_ptr as i64);
54 }
55 
56 // Tests AioCb.cancel.  We aren't trying to test the OS's implementation, only
57 // our bindings.  So it's sufficient to check that AioCb.cancel returned any
58 // AioCancelStat value.
59 #[test]
60 #[cfg_attr(target_env = "musl", ignore)]
61 fn test_cancel() {
62     let wbuf: &[u8] = b"CDEF";
63 
64     let f = tempfile().unwrap();
65     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
66                             0,   //offset
67                             wbuf,
68                             0,   //priority
69                             SigevNotify::SigevNone,
70                             LioOpcode::LIO_NOP);
71     aiocb.write().unwrap();
72     let err = aiocb.error();
73     assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
74 
75     let cancelstat = aiocb.cancel();
76     assert!(cancelstat.is_ok());
77 
78     // Wait for aiocb to complete, but don't care whether it succeeded
79     let _ = poll_aio(&mut aiocb);
80     let _ = aiocb.aio_return();
81 }
82 
83 // Tests using aio_cancel_all for all outstanding IOs.
84 #[test]
85 #[cfg_attr(target_env = "musl", ignore)]
86 fn test_aio_cancel_all() {
87     let wbuf: &[u8] = b"CDEF";
88 
89     let f = tempfile().unwrap();
90     let mut aiocb = AioCb::from_slice(f.as_raw_fd(),
91                             0,   //offset
92                             wbuf,
93                             0,   //priority
94                             SigevNotify::SigevNone,
95                             LioOpcode::LIO_NOP);
96     aiocb.write().unwrap();
97     let err = aiocb.error();
98     assert!(err == Ok(()) || err == Err(Error::from(Errno::EINPROGRESS)));
99 
100     let cancelstat = aio_cancel_all(f.as_raw_fd());
101     assert!(cancelstat.is_ok());
102 
103     // Wait for aiocb to complete, but don't care whether it succeeded
104     let _ = poll_aio(&mut aiocb);
105     let _ = aiocb.aio_return();
106 }
107 
108 #[test]
109 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
110 fn test_fsync() {
111     const INITIAL: &[u8] = b"abcdef123456";
112     let mut f = tempfile().unwrap();
113     f.write_all(INITIAL).unwrap();
114     let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
115                             0,   //priority
116                             SigevNotify::SigevNone);
117     let err = aiocb.fsync(AioFsyncMode::O_SYNC);
118     assert!(err.is_ok());
119     poll_aio(&mut aiocb).unwrap();
120     aiocb.aio_return().unwrap();
121 }
122 
123 /// `AioCb::fsync` should not modify the `AioCb` object if `libc::aio_fsync` returns
124 /// an error
125 // Skip on Linux, because Linux's AIO implementation can't detect errors
126 // synchronously
127 #[test]
128 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
129 fn test_fsync_error() {
130     use std::mem;
131 
132     const INITIAL: &[u8] = b"abcdef123456";
133     // Create an invalid AioFsyncMode
134     let mode = unsafe { mem::transmute(666) };
135     let mut f = tempfile().unwrap();
136     f.write_all(INITIAL).unwrap();
137     let mut aiocb = AioCb::from_fd( f.as_raw_fd(),
138                             0,   //priority
139                             SigevNotify::SigevNone);
140     let err = aiocb.fsync(mode);
141     assert!(err.is_err());
142 }
143 
144 #[test]
145 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
146 // On Travis, aio_suspend hits an assertion within glibc.  This is either a bug
147 // in Travis's version of glibc or Linux.  Either way, we must skip the test.
148 // https://github.com/nix-rust/nix/issues/1099
149 #[cfg_attr(target_os = "linux", ignore)]
150 // On Cirrus, aio_suspend is failing with EINVAL
151 // https://github.com/nix-rust/nix/issues/1361
152 #[cfg_attr(target_os = "macos", ignore)]
153 fn test_aio_suspend() {
154     const INITIAL: &[u8] = b"abcdef123456";
155     const WBUF: &[u8] = b"CDEFG";
156     let timeout = TimeSpec::seconds(10);
157     let mut rbuf = vec![0; 4];
158     let rlen = rbuf.len();
159     let mut f = tempfile().unwrap();
160     f.write_all(INITIAL).unwrap();
161 
162     let mut wcb = AioCb::from_slice( f.as_raw_fd(),
163                            2,   //offset
164                            WBUF,
165                            0,   //priority
166                            SigevNotify::SigevNone,
167                            LioOpcode::LIO_WRITE);
168 
169     let mut rcb = AioCb::from_mut_slice( f.as_raw_fd(),
170                             8,   //offset
171                             &mut rbuf,
172                             0,   //priority
173                             SigevNotify::SigevNone,
174                             LioOpcode::LIO_READ);
175     wcb.write().unwrap();
176     rcb.read().unwrap();
177     loop {
178         {
179             let cbbuf = [wcb.as_ref(), rcb.as_ref()];
180             let r = aio_suspend(&cbbuf[..], Some(timeout));
181             match r {
182                 Err(Error::Sys(Errno::EINTR)) => continue,
183                 Err(e) => panic!("aio_suspend returned {:?}", e),
184                 Ok(_) => ()
185             };
186         }
187         if rcb.error() != Err(Error::from(Errno::EINPROGRESS)) &&
188            wcb.error() != Err(Error::from(Errno::EINPROGRESS)) {
189             break
190         }
191     }
192 
193     assert_eq!(wcb.aio_return().unwrap() as usize, WBUF.len());
194     assert_eq!(rcb.aio_return().unwrap() as usize, rlen);
195 }
196 
197 // Test a simple aio operation with no completion notification.  We must poll
198 // for completion
199 #[test]
200 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
201 fn test_read() {
202     const INITIAL: &[u8] = b"abcdef123456";
203     let mut rbuf = vec![0; 4];
204     const EXPECT: &[u8] = b"cdef";
205     let mut f = tempfile().unwrap();
206     f.write_all(INITIAL).unwrap();
207     {
208         let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
209                                2,   //offset
210                                &mut rbuf,
211                                0,   //priority
212                                SigevNotify::SigevNone,
213                                LioOpcode::LIO_NOP);
214         aiocb.read().unwrap();
215 
216         let err = poll_aio(&mut aiocb);
217         assert_eq!(err, Ok(()));
218         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
219     }
220 
221     assert_eq!(EXPECT, rbuf.deref().deref());
222 }
223 
224 /// `AioCb::read` should not modify the `AioCb` object if `libc::aio_read`
225 /// returns an error
226 // Skip on Linux, because Linux's AIO implementation can't detect errors
227 // synchronously
228 #[test]
229 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
230 fn test_read_error() {
231     const INITIAL: &[u8] = b"abcdef123456";
232     let mut rbuf = vec![0; 4];
233     let mut f = tempfile().unwrap();
234     f.write_all(INITIAL).unwrap();
235     let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
236                            -1,   //an invalid offset
237                            &mut rbuf,
238                            0,   //priority
239                            SigevNotify::SigevNone,
240                            LioOpcode::LIO_NOP);
241     assert!(aiocb.read().is_err());
242 }
243 
244 // Tests from_mut_slice
245 #[test]
246 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
247 fn test_read_into_mut_slice() {
248     const INITIAL: &[u8] = b"abcdef123456";
249     let mut rbuf = vec![0; 4];
250     const EXPECT: &[u8] = b"cdef";
251     let mut f = tempfile().unwrap();
252     f.write_all(INITIAL).unwrap();
253     {
254         let mut aiocb = AioCb::from_mut_slice( f.as_raw_fd(),
255                                2,   //offset
256                                &mut rbuf,
257                                0,   //priority
258                                SigevNotify::SigevNone,
259                                LioOpcode::LIO_NOP);
260         aiocb.read().unwrap();
261 
262         let err = poll_aio(&mut aiocb);
263         assert_eq!(err, Ok(()));
264         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
265     }
266 
267     assert_eq!(rbuf, EXPECT);
268 }
269 
270 // Tests from_ptr
271 #[test]
272 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
273 fn test_read_into_pointer() {
274     const INITIAL: &[u8] = b"abcdef123456";
275     let mut rbuf = vec![0; 4];
276     const EXPECT: &[u8] = b"cdef";
277     let mut f = tempfile().unwrap();
278     f.write_all(INITIAL).unwrap();
279     {
280         // Safety: ok because rbuf lives until after poll_aio
281         let mut aiocb = unsafe {
282             AioCb::from_mut_ptr( f.as_raw_fd(),
283                                  2,   //offset
284                                  rbuf.as_mut_ptr() as *mut c_void,
285                                  rbuf.len(),
286                                  0,   //priority
287                                  SigevNotify::SigevNone,
288                                  LioOpcode::LIO_NOP)
289         };
290         aiocb.read().unwrap();
291 
292         let err = poll_aio(&mut aiocb);
293         assert_eq!(err, Ok(()));
294         assert_eq!(aiocb.aio_return().unwrap() as usize, EXPECT.len());
295     }
296 
297     assert_eq!(rbuf, EXPECT);
298 }
299 
300 // Test reading into an immutable buffer.  It should fail
301 // FIXME: This test fails to panic on Linux/musl
302 #[test]
303 #[should_panic(expected = "Can't read into an immutable buffer")]
304 #[cfg_attr(target_env = "musl", ignore)]
305 fn test_read_immutable_buffer() {
306     let rbuf: &[u8] = b"CDEF";
307     let f = tempfile().unwrap();
308     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
309                            2,   //offset
310                            rbuf,
311                            0,   //priority
312                            SigevNotify::SigevNone,
313                            LioOpcode::LIO_NOP);
314     aiocb.read().unwrap();
315 }
316 
317 
318 // Test a simple aio operation with no completion notification.  We must poll
319 // for completion.  Unlike test_aio_read, this test uses AioCb::from_slice
320 #[test]
321 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
322 fn test_write() {
323     const INITIAL: &[u8] = b"abcdef123456";
324     let wbuf = "CDEF".to_string().into_bytes();
325     let mut rbuf = Vec::new();
326     const EXPECT: &[u8] = b"abCDEF123456";
327 
328     let mut f = tempfile().unwrap();
329     f.write_all(INITIAL).unwrap();
330     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
331                            2,   //offset
332                            &wbuf,
333                            0,   //priority
334                            SigevNotify::SigevNone,
335                            LioOpcode::LIO_NOP);
336     aiocb.write().unwrap();
337 
338     let err = poll_aio(&mut aiocb);
339     assert_eq!(err, Ok(()));
340     assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
341 
342     f.seek(SeekFrom::Start(0)).unwrap();
343     let len = f.read_to_end(&mut rbuf).unwrap();
344     assert_eq!(len, EXPECT.len());
345     assert_eq!(rbuf, EXPECT);
346 }
347 
348 // Tests `AioCb::from_ptr`
349 #[test]
350 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
351 fn test_write_from_pointer() {
352     const INITIAL: &[u8] = b"abcdef123456";
353     let wbuf = "CDEF".to_string().into_bytes();
354     let mut rbuf = Vec::new();
355     const EXPECT: &[u8] = b"abCDEF123456";
356 
357     let mut f = tempfile().unwrap();
358     f.write_all(INITIAL).unwrap();
359     // Safety: ok because aiocb outlives poll_aio
360     let mut aiocb = unsafe {
361         AioCb::from_ptr( f.as_raw_fd(),
362                          2,   //offset
363                          wbuf.as_ptr() as *const c_void,
364                          wbuf.len(),
365                          0,   //priority
366                          SigevNotify::SigevNone,
367                          LioOpcode::LIO_NOP)
368     };
369     aiocb.write().unwrap();
370 
371     let err = poll_aio(&mut aiocb);
372     assert_eq!(err, Ok(()));
373     assert_eq!(aiocb.aio_return().unwrap() as usize, wbuf.len());
374 
375     f.seek(SeekFrom::Start(0)).unwrap();
376     let len = f.read_to_end(&mut rbuf).unwrap();
377     assert_eq!(len, EXPECT.len());
378     assert_eq!(rbuf, EXPECT);
379 }
380 
381 /// `AioCb::write` should not modify the `AioCb` object if `libc::aio_write`
382 /// returns an error
383 // Skip on Linux, because Linux's AIO implementation can't detect errors
384 // synchronously
385 #[test]
386 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
387 fn test_write_error() {
388     let wbuf = "CDEF".to_string().into_bytes();
389     let mut aiocb = AioCb::from_slice( 666, // An invalid file descriptor
390                            0,   //offset
391                            &wbuf,
392                            0,   //priority
393                            SigevNotify::SigevNone,
394                            LioOpcode::LIO_NOP);
395     assert!(aiocb.write().is_err());
396 }
397 
398 lazy_static! {
399     pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
400 }
401 
402 extern fn sigfunc(_: c_int) {
403     SIGNALED.store(true, Ordering::Relaxed);
404 }
405 
406 // Test an aio operation with completion delivered by a signal
407 // FIXME: This test is ignored on mips because of failures in qemu in CI
408 #[test]
409 #[cfg_attr(any(all(target_env = "musl", target_arch = "x86_64"), target_arch = "mips", target_arch = "mips64"), ignore)]
410 fn test_write_sigev_signal() {
411     let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
412     let sa = SigAction::new(SigHandler::Handler(sigfunc),
413                             SaFlags::SA_RESETHAND,
414                             SigSet::empty());
415     SIGNALED.store(false, Ordering::Relaxed);
416     unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
417 
418     const INITIAL: &[u8] = b"abcdef123456";
419     const WBUF: &[u8] = b"CDEF";
420     let mut rbuf = Vec::new();
421     const EXPECT: &[u8] = b"abCDEF123456";
422 
423     let mut f = tempfile().unwrap();
424     f.write_all(INITIAL).unwrap();
425     let mut aiocb = AioCb::from_slice( f.as_raw_fd(),
426                            2,   //offset
427                            WBUF,
428                            0,   //priority
429                            SigevNotify::SigevSignal {
430                                signal: Signal::SIGUSR2,
431                                si_value: 0  //TODO: validate in sigfunc
432                            },
433                            LioOpcode::LIO_NOP);
434     aiocb.write().unwrap();
435     while !SIGNALED.load(Ordering::Relaxed) {
436         thread::sleep(time::Duration::from_millis(10));
437     }
438 
439     assert_eq!(aiocb.aio_return().unwrap() as usize, WBUF.len());
440     f.seek(SeekFrom::Start(0)).unwrap();
441     let len = f.read_to_end(&mut rbuf).unwrap();
442     assert_eq!(len, EXPECT.len());
443     assert_eq!(rbuf, EXPECT);
444 }
445 
446 // Test LioCb::listio with LIO_WAIT, so all AIO ops should be complete by the
447 // time listio returns.
448 #[test]
449 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
450 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
451 fn test_liocb_listio_wait() {
452     const INITIAL: &[u8] = b"abcdef123456";
453     const WBUF: &[u8] = b"CDEF";
454     let mut rbuf = vec![0; 4];
455     let rlen = rbuf.len();
456     let mut rbuf2 = Vec::new();
457     const EXPECT: &[u8] = b"abCDEF123456";
458     let mut f = tempfile().unwrap();
459 
460     f.write_all(INITIAL).unwrap();
461 
462     {
463         let mut liocb = LioCbBuilder::with_capacity(2)
464             .emplace_slice(
465                 f.as_raw_fd(),
466                 2,   //offset
467                 WBUF,
468                 0,   //priority
469                 SigevNotify::SigevNone,
470                 LioOpcode::LIO_WRITE
471             ).emplace_mut_slice(
472                 f.as_raw_fd(),
473                 8,   //offset
474                 &mut rbuf,
475                 0,   //priority
476                 SigevNotify::SigevNone,
477                 LioOpcode::LIO_READ
478             ).finish();
479         let err = liocb.listio(LioMode::LIO_WAIT, SigevNotify::SigevNone);
480         err.expect("lio_listio");
481 
482         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
483         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
484     }
485     assert_eq!(rbuf.deref().deref(), b"3456");
486 
487     f.seek(SeekFrom::Start(0)).unwrap();
488     let len = f.read_to_end(&mut rbuf2).unwrap();
489     assert_eq!(len, EXPECT.len());
490     assert_eq!(rbuf2, EXPECT);
491 }
492 
493 // Test LioCb::listio with LIO_NOWAIT and no SigEvent, so we must use some other
494 // mechanism to check for the individual AioCb's completion.
495 #[test]
496 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
497 #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
498 fn test_liocb_listio_nowait() {
499     const INITIAL: &[u8] = b"abcdef123456";
500     const WBUF: &[u8] = b"CDEF";
501     let mut rbuf = vec![0; 4];
502     let rlen = rbuf.len();
503     let mut rbuf2 = Vec::new();
504     const EXPECT: &[u8] = b"abCDEF123456";
505     let mut f = tempfile().unwrap();
506 
507     f.write_all(INITIAL).unwrap();
508 
509     {
510         let mut liocb = LioCbBuilder::with_capacity(2)
511             .emplace_slice(
512                 f.as_raw_fd(),
513                 2,   //offset
514                 WBUF,
515                 0,   //priority
516                 SigevNotify::SigevNone,
517                 LioOpcode::LIO_WRITE
518             ).emplace_mut_slice(
519                 f.as_raw_fd(),
520                 8,   //offset
521                 &mut rbuf,
522                 0,   //priority
523                 SigevNotify::SigevNone,
524                 LioOpcode::LIO_READ
525             ).finish();
526         let err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
527         err.expect("lio_listio");
528 
529         poll_lio(&mut liocb, 0).unwrap();
530         poll_lio(&mut liocb, 1).unwrap();
531         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
532         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
533     }
534     assert_eq!(rbuf.deref().deref(), b"3456");
535 
536     f.seek(SeekFrom::Start(0)).unwrap();
537     let len = f.read_to_end(&mut rbuf2).unwrap();
538     assert_eq!(len, EXPECT.len());
539     assert_eq!(rbuf2, EXPECT);
540 }
541 
542 // Test LioCb::listio with LIO_NOWAIT and a SigEvent to indicate when all
543 // AioCb's are complete.
544 // FIXME: This test is ignored on mips/mips64 because of failures in qemu in CI.
545 #[test]
546 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
547 #[cfg_attr(any(target_arch = "mips", target_arch = "mips64", target_env = "musl"), ignore)]
548 fn test_liocb_listio_signal() {
549     let _m = crate::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
550     const INITIAL: &[u8] = b"abcdef123456";
551     const WBUF: &[u8] = b"CDEF";
552     let mut rbuf = vec![0; 4];
553     let rlen = rbuf.len();
554     let mut rbuf2 = Vec::new();
555     const EXPECT: &[u8] = b"abCDEF123456";
556     let mut f = tempfile().unwrap();
557     let sa = SigAction::new(SigHandler::Handler(sigfunc),
558                             SaFlags::SA_RESETHAND,
559                             SigSet::empty());
560     let sigev_notify = SigevNotify::SigevSignal { signal: Signal::SIGUSR2,
561                                                   si_value: 0 };
562 
563     f.write_all(INITIAL).unwrap();
564 
565     {
566         let mut liocb = LioCbBuilder::with_capacity(2)
567             .emplace_slice(
568                 f.as_raw_fd(),
569                 2,   //offset
570                 WBUF,
571                 0,   //priority
572                 SigevNotify::SigevNone,
573                 LioOpcode::LIO_WRITE
574             ).emplace_mut_slice(
575                 f.as_raw_fd(),
576                 8,   //offset
577                 &mut rbuf,
578                 0,   //priority
579                 SigevNotify::SigevNone,
580                 LioOpcode::LIO_READ
581             ).finish();
582         SIGNALED.store(false, Ordering::Relaxed);
583         unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
584         let err = liocb.listio(LioMode::LIO_NOWAIT, sigev_notify);
585         err.expect("lio_listio");
586         while !SIGNALED.load(Ordering::Relaxed) {
587             thread::sleep(time::Duration::from_millis(10));
588         }
589 
590         assert_eq!(liocb.aio_return(0).unwrap() as usize, WBUF.len());
591         assert_eq!(liocb.aio_return(1).unwrap() as usize, rlen);
592     }
593     assert_eq!(rbuf.deref().deref(), b"3456");
594 
595     f.seek(SeekFrom::Start(0)).unwrap();
596     let len = f.read_to_end(&mut rbuf2).unwrap();
597     assert_eq!(len, EXPECT.len());
598     assert_eq!(rbuf2, EXPECT);
599 }
600 
601 // Try to use LioCb::listio to read into an immutable buffer.  It should fail
602 // FIXME: This test fails to panic on Linux/musl
603 #[test]
604 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
605 #[should_panic(expected = "Can't read into an immutable buffer")]
606 #[cfg_attr(target_env = "musl", ignore)]
607 fn test_liocb_listio_read_immutable() {
608     let rbuf: &[u8] = b"abcd";
609     let f = tempfile().unwrap();
610 
611 
612     let mut liocb = LioCbBuilder::with_capacity(1)
613         .emplace_slice(
614             f.as_raw_fd(),
615             2,   //offset
616             rbuf,
617             0,   //priority
618             SigevNotify::SigevNone,
619             LioOpcode::LIO_READ
620         ).finish();
621     let _ = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
622 }
623