Skip to content

Commit

Permalink
read and validate index names contained in the multi-pack index (#279)
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Dec 20, 2021
1 parent 3ca04e3 commit 24a9790
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions git-pack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ filebuffer = "0.4.0"
byteorder = "1.2.3"
serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
bytesize = "1.0.1"
os_str_bytes = "6.0.0"
parking_lot = { version = "0.11.0", default-features = false }
thiserror = "1.0.26"
uluru = { version = "3.0.0", optional = true }
Expand Down
62 changes: 60 additions & 2 deletions git-pack/src/multi_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use filebuffer::FileBuffer;
use std::ops::Range;
use std::path::PathBuf;

/// Known multi-index file versions
#[derive(PartialEq, Eq, Ord, PartialOrd, Debug, Hash, Clone, Copy)]
Expand All @@ -28,6 +29,8 @@ pub struct File {
/// The amount of pack files contained within
num_packs: u32,
fan: [u32; 256],
num_objects: u32,
index_names: Vec<PathBuf>,
}

///
Expand All @@ -44,9 +47,56 @@ pub mod access {
}
}

mod chunk {
pub mod chunk {
pub mod pack_names {
use git_object::bstr::{BString, ByteSlice};
use os_str_bytes::OsStrBytes;
use std::path::{Path, PathBuf};

pub const ID: git_chunk::Kind = 0x504e414d; /* "PNAM" */

pub mod from_slice {
use git_object::bstr::BString;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("The pack names were not ordered alphabetically.")]
NotOrderedAlphabetically,
#[error("Each pack path name must be terminated with a null byte")]
MissingNullByte,
#[error("Couldn't turn path '{path}' into OS path due to encoding issues")]
PathEncoding { path: BString },
}
}

pub fn from_slice(mut chunk: &[u8], num_packs: u32) -> Result<Vec<PathBuf>, from_slice::Error> {
let mut out = Vec::new();
for _ in 0..num_packs {
let null_byte_pos = chunk
.find_byte(b'\0')
.ok_or_else(|| from_slice::Error::MissingNullByte)?;

let path = &chunk[..null_byte_pos];
let path = Path::from_raw_bytes(path)
.map_err(|_| from_slice::Error::PathEncoding {
path: BString::from(path),
})?
.into_owned();

if let Some(previous) = out.last() {
if !(previous < &path) {
return Err(from_slice::Error::NotOrderedAlphabetically);
}
}
out.push(path);

chunk = &chunk[null_byte_pos + 1..];
}

// NOTE: git writes garbage into this chunk, usually extra \0 bytes, which we simply ignore. If we were strict
// about it we couldn't read this chunk data at all.
Ok(out)
}
}
pub mod fanout {
use std::convert::TryInto;
Expand Down Expand Up @@ -84,6 +134,8 @@ pub mod init {
use std::path::Path;

mod error {
use crate::multi_index::chunk;

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Could not open multi-index file at '{path}'")]
Expand All @@ -105,6 +157,8 @@ pub mod init {
FileTooLarge(#[from] git_chunk::file::index::data_by_kind::Error),
#[error("The multi-pack fan doesn't have the correct size of 256 * 4 bytes")]
MultiPackFanSize,
#[error(transparent)]
PackNames(#[from] chunk::pack_names::from_slice::Error),
}
}
pub use error::Error;
Expand Down Expand Up @@ -168,10 +222,12 @@ pub mod init {
};

let chunks = git_chunk::file::Index::from_bytes(&data, HEADER_LEN, num_chunks as u32)?;
let pack_names = chunks.offset_by_kind(chunk::pack_names::ID, "PNAM")?;
let pack_names = chunks.data_by_kind(&data, chunk::pack_names::ID, "PNAM")?;
let index_names = chunk::pack_names::from_slice(pack_names, num_packs)?;

let fan = chunks.data_by_kind(&data, chunk::fanout::ID, "OIDF")?;
let fan = chunk::fanout::from_slice(fan).ok_or_else(|| Error::MultiPackFanSize)?;
let num_objects = fan[255];

let lookup = chunks.offset_by_kind(chunk::lookup::ID, "OIDL")?;
let offsets = chunks.offset_by_kind(chunk::offsets::ID, "OOFF")?;
Expand All @@ -183,6 +239,8 @@ pub mod init {
version,
hash_kind,
fan,
index_names,
num_objects,
num_chunks,
num_packs,
})
Expand Down

0 comments on commit 24a9790

Please sign in to comment.