libpart/
gpt.rs

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
16/// Options for parsing GPT
17pub struct GPTOptions {
18    /// The block size to use. Defaults to 512
19    pub block_size: u16,
20    /// Make checksum errors non-fatal.
21    pub ignore_csum: bool,
22    /// Make UTF-16 error non-fatal
23    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    /// The type UUID of the partition
50    pub part_type: UUID,
51    /// The PARTUUID of the partition
52    pub part_id: UUID,
53    /// The first block of the partition
54    pub start: Block,
55    /// The last block of the partition
56    pub end: Block,
57    /// The flags of the partition
58    pub flags: u64,
59    /// The human readable name of the partition. At most 19 chars.
60    pub name: String
61}
62
63impl PartitionEntry {
64    /// Creates a new empty partition entry
65    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    /// There was no GPT found
80    NoTable,
81    /// The checksum of both the primary and the backup GPT was incorrect
82    ChecksumError,
83    /// The Version of the GPT is incompatible with this implementation
84    InvalidVersion,
85    /// The header in itself is invalid
86    InvalidHeader,
87    /// During parsing or writing a GPT an IO Error occured
88    IOError(IOError),
89    /// One of the UUIDs is not valid
90    UUIDError(UUIDError),
91    /// One of the strings is invalid UTF-16
92    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    /// Load a GPT from file or stream
168    pub fn load<T: Read + Seek>(read: &mut T, options: &GPTOptions) -> Result<GPTTable, GPTError> {
169
170        let block_size = options.block_size;
171
172        // Actually go to the start of the GPT
173        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        // FIXME: ignoring checksum for now
196        let crc = try!(read.read_u32::<LittleEndian>());
197
198        // Reserved. Let's ignore it.
199        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            // In primary GPT this is ALWAYS 2
214            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            // Time to verify checksum
230            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            // Zero out checksum field
235            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            // Time to checksum the partition table
244            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        // Okay, Lets read the actual partition table
257        
258        try!(read.seek(SeekFrom::Start(part_start.to_bytes(block_size))));
259
260        // Stuff might break on 64 bit once we get huuuuuge hard disks.
261        // But eh, 32 bit will be gone by then anyways
262        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    /// Write a GPT to file. will write both primary and backup
300    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        // Magic Bytes
314        try!(cur.write(&GPT_MAGIC));
315        // Revision
316        try!(cur.write(&[0x00, 0x00, 0x01, 0x00]));
317        // Header size
318        try!(cur.write_u32::<LittleEndian>(92));
319        // CRC32 sum - for now 0
320        try!(cur.write_u32::<LittleEndian>(0));
321        // Reserved
322        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        // Write part table
353        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        // Write CRC of partition table
377        try!(cur.write_u32::<LittleEndian>(part_crc));
378
379        // Now we actually write the table to disk
380        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        // Fully zero the sector for the actual GPT
392        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        // Write the actual GPT
398        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    /// Gets the amount of partitions that are in use.
410    ///
411    /// Please note that if there is an empty part inbetween, it is not counted. So say
412    /// you have /dev/sda1, /dev/sda2 and /dev/sda4, but no /dev/sda3, this would still return 3
413    pub fn part_count(&self) -> u64 {
414        self.partitions.iter().filter(|p| p.is_some()).count() as u64
415    }
416
417    /// Gives you readonly access to all partitions
418    pub fn partitions(&self) -> &[Option<PartitionEntry>] {
419        &self.partitions
420    }
421
422    /// Get the first free partition ID
423    ///
424    /// Returns Some(id) if there is still space  
425    /// Returns None if all partition slots are occupied
426    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    /// Set a partition entry to whatever you specified
436    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    /// Mark a partition slot as empty
445    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    // Lets fix endianness
500    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}