Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions libcdio-rs/src/cdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,29 @@ pub(crate) struct Cdio {
}

impl Cdio {
/// Create a new Cdio object with the given parameters.
pub(crate) fn new(device: Option<&CStr>, driver: driver_id_t) -> Option<Self> {
/// Initialize a hardware Cdio resource with read-write access.
pub(crate) fn with_device(device: Option<&CStr>) -> Option<Self> {
let source = device.map(|s| s.as_ptr()).unwrap_or(ptr::null());
NonNull::new(Self::open(source, driver)).map(|cdio| Self { cdio })
NonNull::new(Self::open(true, source, driver_id_t_DRIVER_DEVICE)).map(|cdio| Self { cdio })
}

fn open(source: *const c_char, driver: driver_id_t) -> *mut CdIo_t {
fn open(allow_writes: bool, source: *const c_char, driver: driver_id_t) -> *mut CdIo_t {
logging::init_logger();
let access_mode = if allow_writes {
RW_ACCESS_MODE.as_ptr()
} else {
ptr::null()
};

// SAFETY: This invokes cdio_init(), which mutates a static variable.
// CDIO_LAST_DRIVER_LOCK is held to prevent data races.
let _lock = CDIO_LAST_DRIVER_LOCK.lock().unwrap();
unsafe { libcdio_sys::cdio_open(source, driver) }
}
return unsafe { libcdio_sys::cdio_open_am(source, driver, access_mode) };

pub(crate) const DEVICE_DRIVER: driver_id_t = driver_id_t_DRIVER_DEVICE;
/// Although prefixed "MMC", this does imply read-write for all
/// operations
static RW_ACCESS_MODE: &CStr = c"MMC_RDWR";
}
}

impl Deref for Cdio {
Expand Down
7 changes: 4 additions & 3 deletions libcdio-rs/src/drive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ impl Drive {
/// Get a list of connected drives.
/// The values could be used with [`Self::with_drive()`].
pub fn drives() -> Vec<PathBuf> {
let drive_list = unsafe { libcdio_sys::cdio_get_devices(Cdio::DEVICE_DRIVER) };
let drive_list =
unsafe { libcdio_sys::cdio_get_devices(libcdio_sys::driver_id_t_DRIVER_DEVICE) };
if drive_list.is_null() {
return vec![];
}
Expand Down Expand Up @@ -72,7 +73,7 @@ impl Drive {
/// # Errors
/// If there are no drives connected, or the drive could not be opened.
pub fn new() -> Result<Self, DriveNotFoundError> {
Cdio::new(None, Cdio::DEVICE_DRIVER)
Cdio::with_device(None)
.ok_or(DriveNotFoundError)
.map(|cdio| Self { cdio })
}
Expand All @@ -91,7 +92,7 @@ impl Drive {
source: WithDriveErrorKind::DriveHasNullChar(err),
}
})?;
let cdio = Cdio::new(Some(&drive), Cdio::DEVICE_DRIVER).ok_or_else(|| WithDriveError {
let cdio = Cdio::with_device(Some(&drive)).ok_or_else(|| WithDriveError {
drive: os_string_from_bytes_safe(drive.into_bytes()).into(),
source: WithDriveErrorKind::CouldNotOpenAsDrive,
})?;
Expand Down
7 changes: 5 additions & 2 deletions libcdio-rs/src/mmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ use std::{

pub use get_config::*;
pub use get_event_status::*;
pub use prevent_allow_medium_removal::*;
pub use read_disc_info::*;
pub use read_subchannel::*;
pub use test_unit_ready::*;
pub use read_toc::*;

mod get_config;
mod get_event_status;
mod prevent_allow_medium_removal;
mod read_disc_info;
mod read_subchannel;
mod test_unit_ready;
Expand Down Expand Up @@ -77,7 +79,7 @@ impl Mmc {
/// # Errors
/// If an MMC capable device could not be found.
pub fn new() -> Result<Mmc, MmcNotFoundError> {
Cdio::new(None, Cdio::DEVICE_DRIVER)
Cdio::with_device(None)
.map(|cdio| Self { cdio })
.filter(|mmc| mmc.level().is_ok())
.ok_or(MmcNotFoundError)
Expand All @@ -95,7 +97,7 @@ impl Mmc {
source: WithDeviceErrorKind::DeviceHasNullChar(err),
}
})?;
let Some(cdio) = Cdio::new(Some(&device), Cdio::DEVICE_DRIVER) else {
let Some(cdio) = Cdio::with_device(Some(&device)) else {
return Err(WithDeviceError {
device: os_string_from_bytes_safe(device.into_bytes()).into(),
source: WithDeviceErrorKind::CouldNotOpenDevice,
Expand Down Expand Up @@ -365,6 +367,7 @@ pub enum OsError {
enum MmcCommand {
#[allow(unused)]
GetConfiguration = 0x46,
PreventAllowMediumRemoval = 0x1E,
TestUnitReady = 0x00,
ReadDiscInfo = 0x51,
ReadToc = 0x43,
Expand Down
73 changes: 73 additions & 0 deletions libcdio-rs/src/mmc/prevent_allow_medium_removal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 2026 Shiva Kiran Koninty <shiva@skran.xyz>
//
// This file is part of libcdio-rs.
//
// libcdio-rs is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// libcdio-rs is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with libcdio-rs. If not, see <https://www.gnu.org/licenses/>.

//! Routines based on MMC `PREVENT ALLOW MEDIUM REMOVAL`.

use displaydoc::Display;
use thiserror::Error;

use crate::{
Mmc,
mmc::{Cdb, MmcCommand, MmcDirection, MmcError},
};

/// Routines based on MMC `PREVENT ALLOW MEDIUM REMOVAL`
impl Mmc {
/// Allow media removal.
pub fn allow_media_removal(&self) -> Result<(), MmcMediaRemovalError> {
self.prevent_allow_medium_removal(PreventOption::Clear)
}

/// Prevent media removal.
pub fn prevent_media_removal(&self) -> Result<(), MmcMediaRemovalError> {
self.prevent_allow_medium_removal(PreventOption::Set)
}

fn prevent_allow_medium_removal(
&self,
prevent: PreventOption,
) -> Result<(), MmcMediaRemovalError> {
let mut cdb = Cdb::default();
cdb[0] = MmcCommand::PreventAllowMediumRemoval as u8;
cdb[4] = prevent as u8;

self.run_command(Some(MmcDirection::Write), &mut [], cdb)?;

Ok(())
}
}

/// error from a `PREVENT ALLOW MEDIUM REMOVAL` command.
#[non_exhaustive]
#[derive(Debug, Display, Error)]
pub struct MmcMediaRemovalError {
#[from]
pub source: MmcError,
}

/// Operations of the `PREVENT ALLOW MEDIUM REMOVAL` command
#[allow(unused)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
enum PreventOption {
Clear = 0b00,
Set = 0b01,
ClearPersistent = 0b10,
SetPersistent = 0b11,
}

// TODO: add manual tests