1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! Interface to represent both hard disks and disk drives
//! (with or without removable media).
//!
//! This interface should not to be confused with the `org.freedesktop.UDisks2.Block`
//! interface that is used for low-level block devices the OS knows about.
//! For example, if `/dev/sda` and `/dev/sdb` are block devices for two paths
//! to the same drive, there will be only one `org.freedesktop.UDisks2.Drive`
//! object but two `org.freedesktop.UDisks2.Block` objects.
use std::str::FromStr;
use serde::{de::IntoDeserializer, Deserialize, Serialize};
use zbus::{
proxy,
zvariant::{OwnedValue, Type, Value},
};
use crate::error;
/// Rotational rate of a drive.
#[derive(Debug, Default, PartialEq, Eq)]
pub enum RotationRate {
/// The drive is known to be rotating media but rotation rate isn't known.
Unknown,
/// The drive is known to be non-rotating media.
#[default]
NonRotating,
/// The rotation rate in rounds per minute.
Rotating(i32),
}
impl TryFrom<OwnedValue> for RotationRate {
type Error = <i32 as TryFrom<OwnedValue>>::Error;
fn try_from(v: OwnedValue) -> Result<Self, Self::Error> {
Ok(match v.try_into()? {
-1 => RotationRate::Unknown,
0 => RotationRate::NonRotating,
v => RotationRate::Rotating(v),
})
}
}
/// The physical kind of media a drive uses or the type of the drive.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Eq, Type)]
#[zvariant(signature = "s")]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum MediaCompatibility {
/// The device is a thumb-drive with non-removable media (e.g. a USB stick)
Thumb,
/// Flash Card
Flash,
/// CompactFlash
FlashCf,
/// MemoryStick
FlashMs,
/// SmartMedia
FlashSm,
/// Secure Digital
FlashSd,
/// Secure Digital High Capacity
FlashSdhc,
/// Secure Digital eXtended Capacity
FlashSdxc,
/// Secure Digital Input Output
FlashSdio,
/// Secure Digital Input Output combo card with storage and I/O functionality
FlashSdCombo,
/// MultiMediaCard
FlashMmc,
/// Floppy Disk
Floppy,
/// Zip Disk
FloppyZip,
/// Jaz Disk
FloppyJaz,
/// Optical Disc
Optical,
/// Compact Disc
OpticalCd,
/// Compact Disc Recordable
OpticalCdR,
/// Compact Disc Rewritable
OpticalCdRw,
/// Digital Versatile Disc
OpticalDvd,
/// DVD-R
OpticalDvdR,
/// DVD-RW
OpticalDvdRw,
/// DVD-RAM
OpticalDvdRam,
/// DVD+R
OpticalDvdPlusR,
/// DVD+RW
OpticalDvdPlusRw,
/// DVD+R Dual Layer
OpticalDvdPlusRDl,
/// DVD+RW Dual Layer
OpticalDvdPlusRwDl,
/// Blu-ray Disc
OpticalBd,
/// Blu-ray Recordable
OpticalBdR,
/// Blu-ray Rewritable
OpticalBdRe,
/// HD-DVD
OpticalHddvd,
/// HD-DVD Recordable
OpticalHddvdR,
/// HD-DVD Rewritable
OpticalHddvdRw,
/// Magneto Optical
OpticalMo,
/// Can read Mount Rainer media
OpticalMrw,
/// Can write Mount Rainer media
OpticalMrwW,
/// Media is unknown
#[serde(rename(deserialize = ""))] // unknow types are blank
Unknown,
}
impl FromStr for MediaCompatibility {
type Err = serde::de::value::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let res: Result<_, Self::Err> = Self::deserialize(s.into_deserializer());
Ok(res.unwrap_or(Self::Unknown))
}
}
//TODO: use macro when this is used for multiple types
impl TryFrom<Value<'_>> for MediaCompatibility {
type Error = <String as TryFrom<Value<'static>>>::Error;
fn try_from(value: Value<'_>) -> Result<Self, Self::Error> {
let val: String = value.downcast_ref()?;
Ok(Self::from_str(&val).unwrap_or(Self::Unknown))
}
}
impl TryFrom<OwnedValue> for MediaCompatibility {
type Error = <String as TryFrom<OwnedValue>>::Error;
fn try_from(v: OwnedValue) -> Result<Self, Self::Error> {
Self::try_from(Into::<Value<'_>>::into(v))
}
}
#[proxy(
interface = "org.freedesktop.UDisks2.Drive",
default_service = "org.freedesktop.UDisks2",
default_path = "/org/freedesktop/UDisks2/Drive"
)]
pub trait Drive {
/// Ejects media from the drive. This is only meaningful to do on drives with removable media.
/// There are not a lot of guarantees associated with this method so it should only be called in response to an user action.
///
/// On some hardware the media may be physically ejected while on other hardware may simply eject the disc. On some hardware it may not do anything physical but it may cause e.g. a display on the hardware to show e.g. “It is now safe to remove the device”.
fn eject(
&self,
options: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> error::Result<()>;
/// Arranges for the drive to be safely removed and powered off.
/// On the OS side this includes ensuring that no process is using the drive,
/// then requesting that in-flight buffers and caches are committed to stable storage.
/// The exact steps for powering off the drive depends on the drive itself and the interconnect used.
/// For drives connected through USB, the effect is that the USB device will be deconfigured followed by disabling the upstream hub port it is connected to.
///
/// Note that as some physical devices contain multiple drives (for example 4-in-1 flash card reader USB devices) powering off one drive may affect other drives. Applications can examine the "SiblingId" property to determine such relationships.
///
/// There are not a lot of guarantees associated with this method so it should only be called in response to an user action. Usually the effect is that the drive disappears as if it was unplugged.
///
/// This method only works if the [`Self::can_power_off`] property is set to `true`.
fn power_off(
&self,
options: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> error::Result<()>;
/// Sets the configuration for the drive.
/// This will store the configuration in the file-system and also apply it to the drive.
///
/// See the [Self::configuration] property for details about valid values and the location of the configuration file that value will be written to.
fn set_configuration(
&self,
value: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
options: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> error::Result<()>;
/// Whether the drive can be safely removed / powered off. See the [Self::power_off] function for more information.
///
/// See [udisks(8)](http://storaged.org/doc/udisks2-api/latest/udisks.8.html) for how to influence the value of this property.
#[zbus(property)]
fn can_power_off(&self) -> error::Result<bool>;
/// Configuration directives that are applied to the drive
/// when it's connected (i.e. start-up, hotplug or resume).
//TODO: since the confi. are known, use a struct?
#[zbus(property)]
fn configuration(
&self,
) -> error::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
/// Physical connection bus used for the drive as seen by the user.
/// This is typically used to draw a USB or
/// Firewire emblem on top of an icon in an user interface.
///
/// Note that this property has _nothing_ to do with the low-level
/// command-set used (such as ATA-8) or what low-level connection bus
/// (such as SATA, eSATA, PATA, SAS2 etc) is used.
#[zbus(property)]
fn connection_bus(&self) -> error::Result<String>;
/// Whether the media can be ejected from the drive or the drive accepts an eject command to switch its state so it displays e.g. a "Safe To Remove" message to the user.
///
/// Note that this is only a _guess_.
#[zbus(property)]
fn ejectable(&self) -> error::Result<bool>;
/// Unique and persistent identifier for the device or blank if no identifier is available.
/// This identifier is guaranteed to not include the slash character `/` (U+002F SOLIDUS) which means it can be used as a filename.
///
/// # Examples
/// - `ST32000542AS-6XW00W51`
/// - `HITACHI-HTS723232A7A364-E3834563KRG2HN`
/// - `INTEL-SSDSA2MH080G1GC-CVEM842101HD080DGN`
#[zbus(property)]
fn id(&self) -> error::Result<String>;
/// Media currently in the drive or black if unknown.
#[zbus(property)]
fn media(&self) -> error::Result<MediaCompatibility>;
/// If the medium is available.
///
/// Will always be `true` if [`Self::media_change_detected`] is `false`
#[zbus(property)]
fn media_available(&self) -> error::Result<bool>;
/// If media changes are detected.
///
/// Media changes are detected on all modern disk drives through
/// either polling or an asynchronous notification mechanism.
/// The only known disk drives that cannot report
/// media changes are PC floppy drives.
#[zbus(property)]
fn media_change_detected(&self) -> error::Result<bool>;
/// The physical kind of media the drive uses or the type of the drive.
/// Blank if unknown.
#[zbus(property)]
fn media_compatibility(&self) -> error::Result<Vec<MediaCompatibility>>;
/// Hint whether the drive and/or its media is considered removable by the user.
///
/// This includes drives with removable media (cf. the [`Self::media_removable`] property),
/// Flash media such as SD cards and drives on hotpluggable buses such as USB or Firewire (cf. the [`Self::connection_bus`] property).
///
/// Note that this is only a guess.
#[zbus(property)]
fn media_removable(&self) -> error::Result<bool>;
/// Name for the model of the drive or blank if unknown.
#[zbus(property)]
fn model(&self) -> error::Result<String>;
/// Whether the drive contains an optical disc.
#[zbus(property)]
fn optical(&self) -> error::Result<bool>;
/// Whether the disc is blank.
///
/// This is only valid if the property [Self::optical] is true`.
#[zbus(property)]
fn optical_blank(&self) -> error::Result<bool>;
/// Number of audio tracks.
///
/// This is only valid if the property [Self::optical] is `true`.
#[zbus(property)]
fn optical_num_audio_tracks(&self) -> error::Result<u32>;
/// Number of data tracks.
///
/// This is only valid if the property [Self::optical] is `true`.
#[zbus(property)]
fn optical_num_data_tracks(&self) -> error::Result<u32>;
/// Number of sessions.
///
/// This is only valid if the property [Self::optical] is `true`.
#[zbus(property)]
fn optical_num_sessions(&self) -> error::Result<u32>;
/// Number of of tracks.
///
/// This is only valid if the property [Self::optical] is `true`.
#[zbus(property)]
fn optical_num_tracks(&self) -> error::Result<u32>;
/// Hint whether the drive and/or its media is considered removable by the user.
///
/// This includes drives with removable media (cf. the [Self::media_removable] property),
/// Flash media such as SD cards and drives on hotpluggable buses
/// such as USB or Firewire (cf. the [Self::connection_bus] property).
///
/// Note that this is only a guess.
#[zbus(property)]
fn removable(&self) -> error::Result<bool>;
/// Firmware Revision
///
/// Blank if unknown.
#[zbus(property)]
fn revision(&self) -> error::Result<String>;
/// Rotational rate of the drive.
#[zbus(property)]
fn rotation_rate(&self) -> error::Result<RotationRate>;
/// String identifying what seat the drive is plugged into, if any.
#[zbus(property)]
fn seat(&self) -> error::Result<String>;
/// Serial number of the drive.
///
/// Blank if unknown.
#[zbus(property)]
fn serial(&self) -> error::Result<String>;
/// Opaque token that, if non-blank,
/// is used to group drives that are part of the same physical device.
#[zbus(property)]
fn sibling_id(&self) -> error::Result<String>;
/// Size of the drive (or the media currently in the drive).
///
/// In case of NVMe this value indicates the total NVM capacity that is accessible by the NVMe controller.
/// This is always `0` if [`Self::media_change_detected`] is `false`.
#[zbus(property)]
fn size(&self) -> error::Result<u64>;
/// String that can be used for sorting drive objects.
#[zbus(property)]
fn sort_key(&self) -> error::Result<String>;
/// The time the drive was first detected.
///
/// This is expressed in micro-seconds since [std::time::UNIX_EPOCH].
#[zbus(property)]
fn time_detected(&self) -> error::Result<u64>;
/// The earliest time media was last detected or 0 if media is not available.
///
/// This is expressed in micro-seconds since [std::time::UNIX_EPOCH].
#[zbus(property)]
fn time_media_detected(&self) -> error::Result<u64>;
/// Name for the vendor of the drive or blank if unknown.
#[zbus(property)]
fn vendor(&self) -> error::Result<String>;
/// [World Wide Name](http://en.wikipedia.org/wiki/World_Wide_Name) of the drive or blank if unknown.
///
/// In case of NVMe drives please refer to namespace-level WWN properties.
#[zbus(property, name = "WWN")]
fn wwn(&self) -> error::Result<String>;
}