diff options
Diffstat (limited to 'build/liblowercase')
-rw-r--r-- | build/liblowercase/Cargo.lock | 237 | ||||
-rw-r--r-- | build/liblowercase/Cargo.toml | 22 | ||||
-rw-r--r-- | build/liblowercase/lib.rs | 252 |
3 files changed, 511 insertions, 0 deletions
diff --git a/build/liblowercase/Cargo.lock b/build/liblowercase/Cargo.lock new file mode 100644 index 0000000000..4449d1ebc7 --- /dev/null +++ b/build/liblowercase/Cargo.lock @@ -0,0 +1,237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +dependencies = [ + "ppv-lite86", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" + +[[package]] +name = "lowercase" +version = "0.1.0" +dependencies = [ + "libc", + "once_cell", + "paste", + "path-dedot", + "tempfile", +] + +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + +[[package]] +name = "paste" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "path-dedot" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf32f6a3b529384739d9c11c230ad760aeb553061e7834f58de63a7c507f24f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +dependencies = [ + "c2-chacha", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/build/liblowercase/Cargo.toml b/build/liblowercase/Cargo.toml new file mode 100644 index 0000000000..2f26edd718 --- /dev/null +++ b/build/liblowercase/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "lowercase" +version = "0.1.0" +authors = ["Mike Hommey <mhommey@glandium.org>"] +edition = "2018" +license = "MPL-2.0" + +[lib] +crate-type = ["cdylib"] +path = "lib.rs" + +[dependencies] +libc = "0.2" +once_cell = "1" +paste = "0.1" +path-dedot = "1" + +[dev-dependencies] +tempfile = "3" + +[profile.release] +lto = true diff --git a/build/liblowercase/lib.rs b/build/liblowercase/lib.rs new file mode 100644 index 0000000000..9e068a8c38 --- /dev/null +++ b/build/liblowercase/lib.rs @@ -0,0 +1,252 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* LD_PRELOAD library that intercepts some libc functions and lowercases + * paths under a given set of directories before calling the real libc + * functions. + * + * The set of directories is defined with the LOWERCASE_DIRS environment + * variable, separated with a `:`. + * + * Only the parts of the directories below the LOWERCASE_DIRS directories + * are lowercased. + * + * For example, with LOWERCASE_DIRS=/Foo:/Bar: + * `/home/QuX` is unchanged. + * `/Foo/QuX` becomes `/Foo/qux`. + * `/foo/QuX` is unchanged. + * `/Bar/QuX` becomes `/Bar/qux`. + * etc. + * + * This is, by no means, supposed to be a generic LD_PRELOAD library. It + * only intercepts the libc functions that matter in order to build Firefox. + */ + +use std::borrow::Cow; +use std::env::{self, current_dir}; +use std::ffi::{c_void, CStr, CString, OsStr, OsString}; +use std::mem::transmute; +use std::os::raw::{c_char, c_int}; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; +use std::path::{Path, PathBuf}; +use std::ptr::null; + +use once_cell::sync::Lazy; +use path_dedot::ParseDot; + +#[cfg(not(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu")))] +compile_error!("Platform is not supported"); + +static LOWERCASE_DIRS: Lazy<Vec<PathBuf>> = Lazy::new(|| match env::var_os("LOWERCASE_DIRS") { + None => Vec::new(), + Some(value) => value + .as_bytes() + .split(|&c| c == b':') + .map(|p| canonicalize_path(Path::new(OsStr::from_bytes(p))).into_owned()) + .collect(), +}); + +fn canonicalize_path(path: &Path) -> Cow<Path> { + let path = if path.is_absolute() { + Cow::Borrowed(path) + } else { + match current_dir() { + Ok(cwd) => Cow::Owned(cwd.join(path)), + Err(_) => Cow::Borrowed(path), + } + }; + + // TODO: avoid allocation when the path doesn't need .. / . removals. + Cow::Owned(path.parse_dot().unwrap()) +} + +#[test] +fn test_canonicalize_path() { + use std::env::set_current_dir; + use std::iter::repeat; + use tempfile::tempdir; + + fn do_test(curdir: &Path) { + let foobarbaz = curdir.join("foo/bar/baz"); + + assert_eq!(foobarbaz, canonicalize_path(Path::new("foo/bar/baz"))); + assert_eq!(foobarbaz, canonicalize_path(Path::new("./foo/bar/baz"))); + assert_eq!(foobarbaz, canonicalize_path(Path::new("foo/./bar/baz"))); + assert_eq!(foobarbaz, canonicalize_path(Path::new("foo/././bar/baz"))); + assert_eq!( + foobarbaz, + canonicalize_path(Path::new("foo/././bar/qux/../baz")) + ); + assert_eq!( + foobarbaz, + canonicalize_path(Path::new("foo/./bar/../qux/../bar/baz")) + ); + assert_eq!( + foobarbaz, + canonicalize_path(Path::new("foo/bar/./../../foo/bar/baz")) + ); + + let depth = curdir.components().count(); + for depth in depth..=depth + 1 { + let path = repeat("..").take(depth).collect::<Vec<_>>(); + let mut path = path.join("/"); + path.push_str("/foo/bar/baz"); + + assert_eq!( + Path::new("/foo/bar/baz"), + canonicalize_path(Path::new(&path)) + ); + } + } + + let orig_curdir = current_dir().unwrap(); + + do_test(&orig_curdir); + + let tempdir = tempdir().unwrap(); + set_current_dir(&tempdir).unwrap(); + + do_test(tempdir.path()); + + set_current_dir(orig_curdir).unwrap(); +} + +fn normalize_path(path: &CStr) -> Cow<CStr> { + let orig_path = path; + let path = Path::new(OsStr::from_bytes(orig_path.to_bytes())); + match normalize_path_for_dirs(&path, &LOWERCASE_DIRS) { + Cow::Borrowed(_) => Cow::Borrowed(orig_path), + Cow::Owned(p) => Cow::Owned(CString::new(p.into_os_string().into_vec()).unwrap()), + } +} + +fn normalize_path_for_dirs<'a>(path: &'a Path, dirs: &[PathBuf]) -> Cow<'a, Path> { + let orig_path = path; + let path = canonicalize_path(path); + + for lowercase_dir in dirs.iter() { + if path.starts_with(lowercase_dir) { + // TODO: avoid allocation when the string doesn't actually need + // modification. + let mut lowercased_path = path.into_owned().into_os_string().into_vec(); + lowercased_path[lowercase_dir.as_os_str().as_bytes().len()..].make_ascii_lowercase(); + return Cow::Owned(OsString::from_vec(lowercased_path).into()); + } + } + + Cow::Borrowed(orig_path) +} + +#[test] +fn test_normalize_path() { + let paths = vec![ + Path::new("/Foo/Bar").to_owned(), + Path::new("/Qux").to_owned(), + current_dir().unwrap().join("Fuga"), + ]; + + assert_eq!( + normalize_path_for_dirs(Path::new("/foo/bar/Baz"), &paths), + Path::new("/foo/bar/Baz") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Foo/Bar/Baz"), &paths), + Path::new("/Foo/Bar/baz") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Foo/BarBaz"), &paths), + Path::new("/Foo/BarBaz") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Foo/Bar"), &paths), + Path::new("/Foo/Bar") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Foo/Bar/Baz/../Qux"), &paths), + Path::new("/Foo/Bar/qux") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Foo/Bar/Baz/../../Qux"), &paths), + Path::new("/Foo/Bar/Baz/../../Qux") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/Qux/Foo/Bar/Baz"), &paths), + Path::new("/Qux/foo/bar/baz") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("/foo/../Qux/Baz"), &paths), + Path::new("/Qux/baz") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("fuga/Foo/Bar"), &paths), + Path::new("fuga/Foo/Bar") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("Fuga/Foo/Bar"), &paths), + current_dir().unwrap().join("Fuga/foo/bar") + ); + assert_eq!( + normalize_path_for_dirs(Path::new("Fuga/../Foo/Bar"), &paths), + Path::new("Fuga/../Foo/Bar") + ); +} + +macro_rules! wrappers { + ($(fn $name:ident($( $a:ident : $t:ty ),*) $( -> $ret:ty)?;)*) => { + $( + paste::item! { + #[allow(non_upper_case_globals)] + static [< real $name >]: Lazy<extern "C" fn($($t),*) $( -> $ret)?> = + Lazy::new(|| unsafe { + transmute(libc::dlsym( + libc::RTLD_NEXT, + concat!(stringify!($name), "\0").as_ptr() as _ + )) + }); + #[no_mangle] + unsafe extern "C" fn $name($($a : $t),*) $(-> $ret)? { + $( wrappers!(@normalize ($a: $t)); )* + [< real $name >]($($a),*) + } + } + )* + }; + (@normalize ($a:ident: *const c_char)) => { + let $a = if $a.is_null() { + None + } else { + Some(normalize_path(CStr::from_ptr($a))) + }; + let $a = $a.as_ref().map(|p| p.as_ptr()).unwrap_or(null()); + }; + (@normalize ($a:ident: $t:ty)) => {} +} + +// Note: actual definitions for e.g. fopen/fopen64 would be using c_char +// instead of c_void for mode, but the wrappers macro treats all `*const c_char`s +// as "to maybe be lowercased". +wrappers! { + fn open(path: *const c_char, flags: c_int, mode: libc::mode_t) -> c_int; + fn open64(path: *const c_char, flags: c_int, mode: libc::mode_t) -> c_int; + fn fopen(path: *const c_char, mode: *const c_void) -> *mut libc::FILE; + fn fopen64(path: *const c_char, mode: *const c_void) -> *mut libc::FILE; + + fn opendir(path: *const c_char) -> *mut libc::DIR; + + fn __xstat(ver: c_int, path: *const c_char, buf: *mut libc::stat) -> c_int; + fn __xstat64(ver: c_int, path: *const c_char, buf: *mut libc::stat64) -> c_int; + + fn __lxstat(ver: c_int, path: *const c_char, buf: *mut libc::stat) -> c_int; + fn __lxstat64(ver: c_int, path: *const c_char, buf: *mut libc::stat64) -> c_int; + fn __fxstatat(ver: c_int, fd: c_int, path: *const c_char, buf: *mut libc::stat, flag: c_int) -> c_int; + fn __fxstatat64(ver: c_int, fd: c_int, path: *const c_char, buf: *mut libc::stat64, flag: c_int) -> c_int; + + fn access(path: *const c_char, mode: c_int) -> c_int; + + fn mkdir(path: *const c_char, mode: libc::mode_t) -> c_int; + + fn chdir(path: *const c_char) -> c_int; + + fn symlink(target: *const c_char, linkpath: *const c_char) -> c_int; +} |