Skip to main content

libcdio_rs/
cdio.rs

1// Copyright (C) 2026 Shiva Kiran Koninty <shiva@skran.xyz>
2//
3// This file is part of libcdio-rs.
4//
5// libcdio-rs is free software: you can redistribute it and/or
6// modify it under the terms of the GNU General Public License as
7// published by the Free Software Foundation, either version 3 of the
8// License, or (at your option) any later version.
9//
10// libcdio-rs is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13// General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with libcdio-rs. If not, see <https://www.gnu.org/licenses/>.
17
18use std::{
19    ffi::{CStr, c_char},
20    ops::Deref,
21    ptr::{self, NonNull},
22    sync::Mutex,
23};
24
25use libcdio_sys::{CdIo_t, driver_id_t, driver_id_t_DRIVER_DEVICE};
26
27use crate::logging;
28
29/// The Cdio type.
30pub(crate) struct Cdio {
31    pub(crate) cdio: NonNull<CdIo_t>,
32}
33
34impl Cdio {
35    /// Initialize a hardware Cdio resource with read-write access.
36    pub(crate) fn with_device(device: Option<&CStr>) -> Option<Self> {
37        let source = device.map(|s| s.as_ptr()).unwrap_or(ptr::null());
38        NonNull::new(Self::open(true, source, driver_id_t_DRIVER_DEVICE)).map(|cdio| Self { cdio })
39    }
40
41    fn open(allow_writes: bool, source: *const c_char, driver: driver_id_t) -> *mut CdIo_t {
42        logging::init_logger();
43        let access_mode = if allow_writes {
44            RW_ACCESS_MODE.as_ptr()
45        } else {
46            ptr::null()
47        };
48
49        // SAFETY: This invokes cdio_init(), which mutates a static variable.
50        // CDIO_LAST_DRIVER_LOCK is held to prevent data races.
51        let _lock = CDIO_INIT_LOCK.lock().unwrap();
52        return unsafe { libcdio_sys::cdio_open_am(source, driver, access_mode) };
53
54        /// Although prefixed "MMC", this does imply read-write for all
55        /// operations
56        static RW_ACCESS_MODE: &CStr = c"MMC_RDWR";
57    }
58}
59
60impl Deref for Cdio {
61    type Target = NonNull<CdIo_t>;
62
63    fn deref(&self) -> &Self::Target {
64        &self.cdio
65    }
66}
67
68impl Drop for Cdio {
69    fn drop(&mut self) {
70        let _lock = CDIO_INIT_LOCK.lock().unwrap();
71
72        // SAFETY: This method invokes modifies a static variable.
73        // CDIO_LAST_DRIVER_LOCK is held to prevent data races.
74        unsafe { libcdio_sys::cdio_destroy(self.cdio.as_ptr()) }
75    }
76}
77
78/// A lock that must be held before any routine that initializes or
79/// destroys `CdIo_t`.
80/// It was found that the GNU/Linux driver initialization routine,
81/// is NOT thread safe as of libcdio v2.4.0.
82/// Apart from that, this also guards the use of a private static
83/// named `CdIo_last_driver`, used by `CdIo_t` during init
84/// and cleanup.
85pub(crate) static CDIO_INIT_LOCK: Mutex<()> = Mutex::new(());