Skip to content

Commit 7fe4fb3

Browse files
authored
Added: Linux ioctls (#7)
1 parent 77f5da9 commit 7fe4fb3

File tree

8 files changed

+104
-39
lines changed

8 files changed

+104
-39
lines changed

.travis.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
sudo: false
22
language: rust
33
cache: cargo
4+
os:
5+
- linux
6+
- windows
47

58
rust:
69
- stable
710

11+
# NOTE: disable Travis' cache for now because it makes the Windows build fail
12+
cache:
13+
directories:
14+
815
before_script:
916
- rustup component add rustfmt-preview clippy
1017

Cargo.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "gptman"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
authors = ["Cecile Tonglet <[email protected]>"]
55
license = "MIT"
66
edition = "2018"
@@ -59,7 +59,7 @@ version = "0.6.0"
5959
optional = true
6060

6161
[dependencies.nix]
62-
version = "0.12.0"
62+
version = "0.14.1"
6363
optional = true
6464

6565
[dependencies.rand]
@@ -72,4 +72,7 @@ optional = true
7272

7373
[features]
7474
default = [ "cli" ]
75-
cli = [ "lazy_static", "ansi_term", "pad", "unicode-width", "clap", "linefeed", "nix", "rand", "structopt" ]
75+
cli = [ "lazy_static", "ansi_term", "pad", "unicode-width", "clap", "linefeed", "rand", "structopt" ]
76+
77+
[target.'cfg(target_os = "linux")'.dependencies]
78+
nix = "0.14.1"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Features
3636
* Swap partition indexes (s)
3737
* Randomize disk's GUID and all partition's GUID (Z)
3838
* Copy/clone all partitions from one disk and insert it to another (C)
39+
* Write protective MBR
3940

4041
Installation
4142
------------

src/cli/commands.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use crate::opt::Opt;
55
use crate::table::Table;
66
use crate::types::PartitionTypeGUID;
77
use crate::uuid::{convert_str_to_array, generate_random_uuid, UUID};
8+
#[cfg(target_os = "linux")]
9+
use gptman::linux::reread_partition_table;
810
use std::fs;
911
use std::io::{Read, Seek, SeekFrom};
1012
use std::path::PathBuf;
@@ -504,15 +506,7 @@ fn fix_partitions_order(gpt: &mut GPT) {
504506
gpt.sort();
505507
}
506508

507-
ioctl_none!(reread_partition_table, 0x12, 95);
508-
509-
const S_IFMT: u32 = 0o170_000;
510-
const S_IFBLK: u32 = 0o60_000;
511-
512509
fn write(gpt: &mut GPT, opt: &Opt) -> Result<()> {
513-
use std::os::linux::fs::MetadataExt;
514-
use std::os::unix::io::IntoRawFd;
515-
516510
let mut f = fs::OpenOptions::new().write(true).open(&opt.device)?;
517511
gpt.write_into(&mut f)?;
518512

@@ -521,12 +515,10 @@ fn write(gpt: &mut GPT, opt: &Opt) -> Result<()> {
521515
println!("protective MBR has been written");
522516
}
523517

524-
if fs::metadata(&opt.device)?.st_mode() & S_IFMT == S_IFBLK {
525-
println!("calling re-read ioctl");
526-
match unsafe { reread_partition_table(f.into_raw_fd()) } {
527-
Err(err) => println!("ioctl call failed: {}", err),
528-
Ok(0) => {}
529-
Ok(x) => println!("ioctl returned error code: {}", x),
518+
#[cfg(target_os = "linux")]
519+
{
520+
if let Err(err) = reread_partition_table(&mut f) {
521+
println!("rereading partition table failed: {}", err);
530522
}
531523
}
532524

src/cli/main.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
extern crate lazy_static;
33
#[macro_use]
44
extern crate clap;
5-
extern crate linefeed;
6-
#[macro_use]
7-
extern crate nix;
85
extern crate gptman;
6+
extern crate linefeed;
97
extern crate rand;
108
extern crate structopt;
119

@@ -21,19 +19,14 @@ use self::commands::{execute, print};
2119
use self::error::*;
2220
use self::opt::*;
2321
use self::uuid::generate_random_uuid;
22+
#[cfg(target_os = "linux")]
23+
use gptman::linux::get_sector_size;
2424
use gptman::GPT;
2525
use linefeed::{Interface, ReadResult, Signal};
2626
use std::fs;
2727
use std::io::{Seek, SeekFrom};
28-
use std::os::linux::fs::MetadataExt;
29-
use std::os::unix::io::AsRawFd;
3028
use structopt::StructOpt;
3129

32-
ioctl_read_bad!(blksszget, 0x1268, u64);
33-
34-
const S_IFMT: u32 = 0o170_000;
35-
const S_IFBLK: u32 = 0o60_000;
36-
3730
macro_rules! main_unwrap {
3831
($e:expr) => {{
3932
match $e {
@@ -122,16 +115,15 @@ where
122115

123116
let mut f = fs::File::open(&opt.device)?;
124117
let len = f.seek(SeekFrom::End(0))?;
125-
let metadata = fs::metadata(&opt.device).expect("could not get metadata");
126118

119+
#[allow(unused_mut)]
127120
let mut sector_size = opt.sector_size.unwrap_or(512);
128121

129-
if opt.sector_size.is_none() && metadata.st_mode() & S_IFMT == S_IFBLK {
130-
println!("getting sector size from device");
131-
match unsafe { blksszget(f.as_raw_fd(), &mut sector_size) } {
132-
Err(err) => println!("ioctl call failed: {}", err),
133-
Ok(0) => {}
134-
Ok(x) => println!("ioctl returned error code: {}", x),
122+
#[cfg(target_os = "linux")]
123+
{
124+
match get_sector_size(&mut f) {
125+
Err(err) => println!("failed to get sector size of device: {}", err),
126+
Ok(x) => sector_size = x,
135127
}
136128
}
137129

@@ -144,6 +136,8 @@ where
144136
ask("Do you wish to continue (yes/no)?").and_then(|x| {
145137
if x == "yes" {
146138
Ok(())
139+
} else if x == "no" {
140+
Err(Error::new("Aborted."))
147141
} else {
148142
Err(Error::new(&format!(
149143
"Invalid answer '{}'. Please type 'yes' or 'no'.",

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ extern crate serde;
7171
#[macro_use]
7272
extern crate serde_derive;
7373
extern crate crc;
74+
#[cfg(target_os = "linux")]
75+
#[macro_use]
76+
extern crate nix;
7477

7578
use bincode::{deserialize_from, serialize, serialize_into};
7679
use crc::{crc32, Hasher32};
@@ -83,6 +86,9 @@ use std::io;
8386
use std::io::{Read, Seek, SeekFrom, Write};
8487
use std::ops::{Index, IndexMut};
8588

89+
/// Linux specific helpers
90+
pub mod linux;
91+
8692
const DEFAULT_ALIGN: u64 = 2048;
8793
const MAX_ALIGN: u64 = 16384;
8894

src/linux.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![cfg(target_os = "linux")]
2+
3+
use std::fs;
4+
use std::io;
5+
use std::os::linux::fs::MetadataExt;
6+
use std::os::unix::io::AsRawFd;
7+
8+
mod ioctl {
9+
ioctl_read_bad!(blksszget, 0x1268, u64);
10+
ioctl_none!(blkrrpart, 0x12, 95);
11+
}
12+
13+
const S_IFMT: u32 = 0o170_000;
14+
const S_IFBLK: u32 = 0o60_000;
15+
16+
/// An error that can happen while doing an ioctl call with a block device
17+
#[derive(Debug, Error)]
18+
pub enum BlockError {
19+
/// An error that occurs when the metadata of the input file couldn't be retrieved
20+
#[error(display = "failed to get metadata of device fd")]
21+
Metadata(#[error(cause)] io::Error),
22+
/// An error that occurs when the partition table could not be reloaded by the OS
23+
#[error(display = "failed to reload partition table of device")]
24+
RereadTable(#[error(cause)] nix::Error),
25+
/// An error that occurs when an invalid return code has been received from an ioctl call
26+
#[error(display = "invalid return value of ioctl ({} != 0)", _0)]
27+
InvalidReturnValue(i32),
28+
/// An error that occurs when the file provided is not a block device
29+
#[error(display = "not a block device")]
30+
NotBlock,
31+
}
32+
33+
/// Makes an ioctl call to make the OS reread the partition table of a block device
34+
pub fn reread_partition_table(file: &mut fs::File) -> Result<(), BlockError> {
35+
let metadata = file.metadata().map_err(BlockError::Metadata)?;
36+
37+
if metadata.st_mode() & S_IFMT == S_IFBLK {
38+
match unsafe { ioctl::blkrrpart(file.as_raw_fd()) } {
39+
Err(err) => Err(BlockError::RereadTable(err)),
40+
Ok(0) => Ok(()),
41+
Ok(r) => Err(BlockError::InvalidReturnValue(r)),
42+
}
43+
} else {
44+
Err(BlockError::NotBlock)
45+
}
46+
}
47+
48+
/// Makes an ioctl call to obtain the sector size of a block device
49+
pub fn get_sector_size(file: &mut fs::File) -> Result<u64, BlockError> {
50+
let metadata = file.metadata().map_err(BlockError::Metadata)?;
51+
let mut sector_size = 512;
52+
53+
if metadata.st_mode() & S_IFMT == S_IFBLK {
54+
match unsafe { ioctl::blksszget(file.as_raw_fd(), &mut sector_size) } {
55+
Err(err) => Err(BlockError::RereadTable(err)),
56+
Ok(0) => Ok(sector_size),
57+
Ok(r) => Err(BlockError::InvalidReturnValue(r)),
58+
}
59+
} else {
60+
Err(BlockError::NotBlock)
61+
}
62+
}

0 commit comments

Comments
 (0)