1extern crate checksum;
2extern crate uuid;
3extern crate byteorder;
4
5use super::util::Block;
6use std::cmp;
7use self::checksum::crc32::Crc32 as CRC32;
8use self::uuid::{Uuid as UUID, ParseError as UUIDError};
9use self::byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian, ByteOrder};
10use std::io::{Result as IOResult, Write, Read, Error as IOError, Seek, SeekFrom, Cursor};
11use std::error::Error;
12use std::fmt;
13
14const GPT_MAGIC: [u8; 8] = [0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54];
15
16pub struct GPTOptions {
18 pub block_size: u16,
20 pub ignore_csum: bool,
22 pub ignore_utf16_errors: bool
24}
25
26impl Default for GPTOptions {
27 fn default() -> GPTOptions {
28 GPTOptions {
29 block_size: 512,
30 ignore_csum: false,
31 ignore_utf16_errors: false
32 }
33 }
34}
35
36#[derive(Debug)]
37pub struct GPTTable {
38 primary_gpt: Block,
39 backup_gpt: Block,
40 first_usable: Block,
41 last_usable: Block,
42 gpt_uuid: UUID,
43 partitions: Vec<Option<PartitionEntry>>,
44 checksum: u32
45}
46
47#[derive(Debug)]
48pub struct PartitionEntry {
49 pub part_type: UUID,
51 pub part_id: UUID,
53 pub start: Block,
55 pub end: Block,
57 pub flags: u64,
59 pub name: String
61}
62
63impl PartitionEntry {
64 fn empty() -> PartitionEntry {
66 PartitionEntry {
67 part_type: UUID::nil(),
68 part_id: UUID::nil(),
69 start: Block(0),
70 end: Block(0),
71 flags: 0,
72 name: String::new()
73 }
74 }
75}
76
77#[derive(Debug)]
78pub enum ErrorType {
79 NoTable,
81 ChecksumError,
83 InvalidVersion,
85 InvalidHeader,
87 IOError(IOError),
89 UUIDError(UUIDError),
91 UTF16Error,
93 InvalidID
94}
95
96#[derive(Debug)]
97pub struct GPTError {
98 error_type: ErrorType,
99 desc: String
100}
101
102impl GPTError {
103 fn new(t: ErrorType) -> GPTError {
104 let desc = String::from(match &t {
105 &ErrorType::NoTable => String::from("No GPT found"),
106 &ErrorType::ChecksumError => String::from("GPT corrupt"),
107 &ErrorType::InvalidVersion => String::from("Invalid GPT Version"),
108 &ErrorType::InvalidHeader => String::from("Invalid GPT Header"),
109 &ErrorType::UTF16Error => String::from("Encoding Error in GPT: Invalid UTF-16"),
110 &ErrorType::InvalidID => String::from("Invalid ID"),
111 &ErrorType::IOError(ref e) => format!("IO Error while processing GPT: {}", e.description()),
112 &ErrorType::UUIDError(ref e) => format!("Invalid UUID: {}", e.description())
113 });
114 GPTError {
115 error_type: t,
116 desc: desc
117 }
118 }
119}
120
121impl From<IOError> for GPTError {
122 fn from(err: IOError) -> GPTError {
123 GPTError::new(ErrorType::IOError(err))
124 }
125}
126
127impl From<UUIDError> for GPTError {
128 fn from(err: UUIDError) -> GPTError {
129 GPTError::new(ErrorType::UUIDError(err))
130 }
131}
132
133impl Error for GPTError {
134 fn description(&self) -> &str {
135 &self.desc
136 }
137
138 fn cause(&self) -> Option<&Error> {
139 match &self.error_type {
140 &ErrorType::IOError(ref e) => Some(e),
141 &ErrorType::UUIDError(ref e) => Some(e),
142 _ => None
143 }
144 }
145}
146
147impl fmt::Display for GPTError {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "{}", self.description())
150 }
151}
152
153impl GPTTable {
154
155
156 pub fn exists<T: Read + Seek>(read: &mut T, options: &GPTOptions) -> Result<bool, IOError> {
157 let block_size = options.block_size;
158
159 read.seek(SeekFrom::Start(block_size as u64))?;
160
161 let mut buf = [0u8; 8];
162 try!(read.read(&mut buf));
163
164 Ok(buf == GPT_MAGIC)
165 }
166
167 pub fn load<T: Read + Seek>(read: &mut T, options: &GPTOptions) -> Result<GPTTable, GPTError> {
169
170 let block_size = options.block_size;
171
172 try!(read.seek(SeekFrom::Start(block_size as u64)));
174
175 let mut buf = [0u8; 8];
176 try!(read.read(&mut buf));
177 if buf != GPT_MAGIC {
178 return Err(GPTError::new(ErrorType::NoTable));
179 }
180
181 let mut buf = [0u8; 4];
182 try!(read.read(&mut buf));
183 if buf != [0x00, 0x00, 0x01, 0x00] {
184 println!("Invalid header version");
185 return Err(GPTError::new(ErrorType::InvalidVersion));
186 }
187
188 let hlen = try!(read.read_u32::<LittleEndian>());
189
190 if hlen != 92 {
191 println!("Header length is not 92");
192 return Err(GPTError::new(ErrorType::InvalidHeader));
193 }
194
195 let crc = try!(read.read_u32::<LittleEndian>());
197
198 try!(read.read_i32::<LittleEndian>());
200
201 let mypos = Block(try!(read.read_u64::<LittleEndian>()));
202
203 let otherpos = Block(try!(read.read_u64::<LittleEndian>()));
204
205 let first_usable = Block(try!(read.read_u64::<LittleEndian>()));
206
207 let last_usable = Block(try!(read.read_u64::<LittleEndian>()));
208
209 let uuid = try!(read_uuid(read));
210
211 let part_start = Block(try!(read.read_u64::<LittleEndian>()));
212 if part_start != Block(2) {
213 println!("Invalid start of partition table");
215 return Err(GPTError::new(ErrorType::InvalidHeader));
216 }
217
218 let part_count = try!(read.read_u32::<LittleEndian>());
219
220 let part_size = try!(read.read_u32::<LittleEndian>());
221 if part_size != 128 {
222 println!("Invalid partition table entry size");
223 return Err(GPTError::new(ErrorType::InvalidHeader));
224 }
225
226 let part_checksum = try!(read.read_u32::<LittleEndian>());
227
228 if !options.ignore_csum {
229 try!(read.seek(SeekFrom::Start(block_size as u64)));
231 let mut buf = Vec::new();
232 buf.resize(hlen as usize, 0u8);
233 try!(read.read(&mut buf));
234 cp(&[0x00, 0x00, 0x00, 0x00], &mut buf[16..20]);
236
237 let csum = CRC32::new().checksum(&buf);
238
239 if csum != crc {
240 return Err(GPTError::new(ErrorType::ChecksumError));
241 }
242
243 try!(read.seek(SeekFrom::Start(part_start.to_bytes(block_size))));
245
246 let mut buf = Vec::new();
247 buf.resize(part_size as usize * part_count as usize, 0u8);
248 try!(read.read(&mut buf));
249
250 let csum = CRC32::new().checksum(&buf);
251 if csum != part_checksum {
252 return Err(GPTError::new(ErrorType::ChecksumError));
253 }
254 }
255
256 try!(read.seek(SeekFrom::Start(part_start.to_bytes(block_size))));
259
260 let mut partitions = Vec::with_capacity(part_count as usize);
263
264 for _ in 0..part_count {
265 let part_type = try!(read_uuid(read));
266 let part_id = try!(read_uuid(read));
267 let part_start = Block(try!(read.read_u64::<LittleEndian>()));
268 let part_end = Block(try!(read.read_u64::<LittleEndian>()));
269 let part_flags = try!(read.read_u64::<LittleEndian>());
270 let part_label = try!(read_utf16_le(read, options.ignore_utf16_errors));
271
272
273 if part_type.is_nil() {
274 partitions.push(None)
275 } else {
276 partitions.push(Some(PartitionEntry {
277 part_type: part_type,
278 part_id: part_id,
279 start: part_start,
280 end: part_end,
281 flags: part_flags,
282 name: part_label
283 }));
284 }
285 }
286
287
288 Ok(GPTTable {
289 primary_gpt: mypos,
290 backup_gpt: otherpos,
291 first_usable: first_usable,
292 last_usable: last_usable,
293 gpt_uuid: uuid,
294 partitions: partitions,
295 checksum: crc
296 })
297 }
298
299 pub fn write<W: Write + Seek>(&self, write: &mut W, options: &GPTOptions) -> Result<(), GPTError> {
301 try!(self.write_gpt(write, options, true));
302 try!(self.write_gpt(write, options, false));
303 Ok(())
304 }
305
306 fn write_gpt<W: Write + Seek>(&self, write: &mut W, options: &GPTOptions, primary: bool) -> Result<(), GPTError> {
307
308 let mut gpt = Vec::new();
309 gpt.resize(92, 0u8);
310
311 let mut cur = Cursor::new(gpt);
312
313 try!(cur.write(&GPT_MAGIC));
315 try!(cur.write(&[0x00, 0x00, 0x01, 0x00]));
317 try!(cur.write_u32::<LittleEndian>(92));
319 try!(cur.write_u32::<LittleEndian>(0));
321 try!(cur.write_i32::<LittleEndian>(0));
323
324 let mypos = if primary {
325 try!(cur.write_u64::<LittleEndian>(self.primary_gpt.0));
326 try!(cur.write_u64::<LittleEndian>(self.backup_gpt.0));
327 self.primary_gpt
328 } else {
329 try!(cur.write_u64::<LittleEndian>(self.backup_gpt.0));
330 try!(cur.write_u64::<LittleEndian>(self.primary_gpt.0));
331 self.backup_gpt
332 };
333
334 try!(cur.write_u64::<LittleEndian>(self.first_usable.0));
335 try!(cur.write_u64::<LittleEndian>(self.last_usable.0));
336
337 try!(write_uuid(&mut cur, self.gpt_uuid));
338
339 let part_start = if primary {
340 Block(2)
341 } else {
342 self.backup_gpt - self.ptable_len(self.partitions.len() as u64, options)
343 };
344
345 try!(cur.write_u64::<LittleEndian>(part_start.0));
346
347 try!(cur.write_u32::<LittleEndian>(self.partitions.len() as u32));
348
349 try!(cur.write_u32::<LittleEndian>(128));
350
351
352 let mut part_tab = Vec::new();
354 part_tab.resize(self.partitions.len() * 128, 0u8);
355
356 let mut pcur = Cursor::new(part_tab);
357
358 let empty = PartitionEntry::empty();
359
360 for p in &self.partitions {
361 let p = match p {
362 &Some(ref p) => p,
363 &None => &empty
364 };
365
366 try!(write_uuid(&mut pcur, p.part_type));
367 try!(write_uuid(&mut pcur, p.part_id));
368 try!(pcur.write_u64::<LittleEndian>(p.start.0));
369 try!(pcur.write_u64::<LittleEndian>(p.end.0));
370 try!(pcur.write_u64::<LittleEndian>(p.flags));
371 try!(write_utf16_le(&mut pcur, &p.name));
372 }
373
374 let part_crc = CRC32::new().checksum(pcur.get_ref());
375
376 try!(cur.write_u32::<LittleEndian>(part_crc));
378
379 try!(write.seek(SeekFrom::Start(part_start.to_bytes(options.block_size))));
381 try!(write.write(pcur.get_ref()));
382
383 try!(cur.seek(SeekFrom::Start(16)));
384
385 let hdr_crc = {
386 let buf = cur.get_ref();
387 CRC32::new().checksum(&buf)
388 };
389 try!(cur.write_u32::<LittleEndian>(hdr_crc));
390
391 let mut buf = Vec::new();
393 buf.resize(options.block_size as usize, 0u8);
394 try!(write.seek(SeekFrom::Start(mypos.to_bytes(options.block_size))));
395 try!(write.write(&buf));
396
397 try!(write.seek(SeekFrom::Start(mypos.to_bytes(options.block_size))));
399 try!(write.write(cur.get_ref()));
400
401 Ok(())
402
403 }
404
405 fn ptable_len(&self, pcount: u64, options: &GPTOptions) -> Block {
406 Block::from_bytes(pcount * 128, options.block_size).expect("Partition count must be devidable by 4")
407 }
408
409 pub fn part_count(&self) -> u64 {
414 self.partitions.iter().filter(|p| p.is_some()).count() as u64
415 }
416
417 pub fn partitions(&self) -> &[Option<PartitionEntry>] {
419 &self.partitions
420 }
421
422 pub fn next_id(&self) -> Option<u64> {
427 for p in self.partitions.iter().enumerate() {
428 if p.1.is_none() {
429 return Some(p.0 as u64);
430 }
431 }
432 None
433 }
434
435 pub fn set_partition(&mut self, id: u64, part: PartitionEntry) -> Result<(), GPTError> {
437 if id as usize > self.partitions.len() - 1 {
438 return Err(GPTError::new(ErrorType::InvalidID));
439 }
440 self.partitions[id as usize] = Some(part);
441 Ok(())
442 }
443
444 pub fn delete_partition(&mut self, id: u64) -> Result<(), GPTError> {
446 if id as usize > self.partitions.len() - 1 {
447 return Err(GPTError::new(ErrorType::InvalidID));
448 }
449 self.partitions[id as usize] = None;
450 Ok(())
451 }
452}
453
454fn write_utf16_le(write: &mut Write, s: &str) -> Result<(), GPTError> {
455 let buf = s.encode_utf16().take(36).collect::<Vec<_>>();
456 let mut buf2 = [0u16; 36];
457 cp(&buf, &mut buf2);
458 try!(write_u16_buf::<LittleEndian>(write, &buf2));
459 Ok(())
460}
461
462fn write_u16_buf<T: ByteOrder>(write: &mut Write, buf: &[u16]) -> IOResult<()> {
463 for i in 0..buf.len() {
464 try!(write.write_u16::<T>(buf[i]));
465 }
466 Ok(())
467}
468
469fn read_utf16_le(read: &mut Read, ignore_err: bool) -> Result<String, GPTError> {
470 let mut buf = [0u16; 36];
471 try!(read_u16_buf::<LittleEndian>(read, &mut buf));
472 let ret = match String::from_utf16(&buf) {
473 Ok(x) => x,
474 Err(_) => if ignore_err {
475 String::new()
476 } else {
477 return Err(GPTError::new(ErrorType::UTF16Error))
478 }
479 };
480
481 Ok(match ret.find('\0') {
482 Some(x) => String::from(&ret[0..x]),
483 None => ret
484 })
485}
486
487fn read_u16_buf<T: ByteOrder>(read: &mut Read, buf: &mut[u16]) -> IOResult<()> {
488 for i in 0..buf.len() {
489 buf[i] = try!(read.read_u16::<T>());
490 }
491 Ok(())
492}
493
494fn read_uuid(read: &mut Read) -> Result<UUID, GPTError> {
495 let mut buf = [0u8; 16];
496 let mut buf_endian_ffs = [0u8; 16];
497 try!(read.read(&mut buf));
498 cp(&buf, &mut buf_endian_ffs);
499 swap_endian(&buf[0..4], &mut buf_endian_ffs[0..4]);
501 swap_endian(&buf[4..6], &mut buf_endian_ffs[4..6]);
502 swap_endian(&buf[6..8], &mut buf_endian_ffs[6..8]);
503
504 Ok(try!(UUID::from_bytes(&buf_endian_ffs)))
505}
506
507fn write_uuid(write: &mut Write, uuid: UUID) -> Result<(), GPTError> {
508 let buf = uuid.as_bytes();
509 let mut buf_out = [0u8; 16];
510 cp(buf, &mut buf_out);
511 swap_endian(&buf[0..4], &mut buf_out[0..4]);
512 swap_endian(&buf[4..6], &mut buf_out[4..6]);
513 swap_endian(&buf[6..8], &mut buf_out[6..8]);
514
515 try!(write.write(&buf_out));
516 Ok(())
517}
518
519fn swap_endian(input: &[u8], output: &mut [u8]) {
520 let len = cmp::min(input.len(), output.len());
521 for i in 0..len {
522 output[len - 1 - i] = input[i];
523 }
524}
525
526fn cp<T: Copy>(input: &[T], output: &mut [T]) {
527 let len = cmp::min(input.len(), output.len());
528 for i in 0..len {
529 output[i] = input[i];
530 }
531}