├── .cargo └── config.toml ├── .deny.toml ├── .github ├── renovate.json └── workflows │ ├── ci.yaml │ └── release.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── docs └── release_process.md ├── gpt_disk_io ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples │ └── reader.rs ├── src │ ├── block_io.rs │ ├── block_io │ │ ├── slice_block_io.rs │ │ └── std_block_io.rs │ ├── disk.rs │ └── lib.rs └── tests │ ├── common │ └── mod.rs │ ├── test_block.rs │ ├── test_block_io.rs │ ├── test_crc32.rs │ ├── test_disk.rs │ ├── test_guid.rs │ ├── test_header.rs │ ├── test_layout.rs │ ├── test_mbr.rs │ ├── test_num.rs │ ├── test_partition_array.rs │ └── test_partition_entry.rs ├── gpt_disk_types ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── block.rs │ ├── crc32.rs │ ├── header.rs │ ├── lib.rs │ ├── mbr.rs │ ├── num.rs │ ├── partition_array.rs │ └── partition_entry.rs ├── uguid ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples │ └── guid_info.rs ├── src │ ├── error.rs │ ├── guid.rs │ ├── lib.rs │ └── util.rs └── tests │ ├── test_guid.rs │ ├── test_macro_error.rs │ ├── test_serde.rs │ └── ui │ ├── guid_hex.rs │ ├── guid_hex.stderr │ ├── guid_len.rs │ ├── guid_len.stderr │ ├── guid_sep.rs │ └── guid_sep.stderr └── xtask ├── Cargo.toml └── src ├── main.rs ├── package.rs └── util.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | -------------------------------------------------------------------------------- /.deny.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [licenses] 10 | allow = [ 11 | "Apache-2.0", 12 | "MIT", 13 | "MPL-2.0", 14 | "Unicode-3.0", 15 | ] 16 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "rangeStrategy": "update-lockfile", 7 | "automerge": true, 8 | "lockFileMaintenance": { 9 | "enabled": true 10 | }, 11 | "packageRules": [ 12 | { 13 | "matchPackageNames": ["crate-ci/typos"], 14 | "extends": ["schedule:monthly"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | on: 10 | push: 11 | branches: 12 | - main 13 | pull_request: 14 | schedule: 15 | - cron: '0 0 * * 0' 16 | 17 | name: CI 18 | 19 | env: 20 | RUSTFLAGS: "-D warnings" 21 | 22 | jobs: 23 | msrv: 24 | name: MSRV 25 | runs-on: ubuntu-latest 26 | env: 27 | rust_version: "1.81" 28 | steps: 29 | - uses: actions/checkout@v4 30 | - run: rustup toolchain install $rust_version --profile minimal --no-self-update 31 | - run: rustup default $rust_version 32 | - uses: Swatinem/rust-cache@v2 33 | - run: rustc --version 34 | - run: cargo build -p uguid -p gpt_disk_types -p gpt_disk_io --all-features 35 | 36 | check: 37 | name: Check 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v4 41 | - uses: Swatinem/rust-cache@v2 42 | - run: cargo check 43 | 44 | test-uguid: 45 | name: Test uguid 46 | runs-on: ubuntu-latest 47 | steps: 48 | - uses: actions/checkout@v4 49 | - uses: Swatinem/rust-cache@v2 50 | - run: | 51 | cargo xtask test_uguid 52 | cargo test --examples 53 | 54 | test-gpt-disk-types: 55 | name: Test gpt_disk_types 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v4 59 | - uses: Swatinem/rust-cache@v2 60 | - run: cargo xtask test_gpt_disk_types 61 | 62 | test-gpt-disk-io: 63 | name: Test gpt_disk_io 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v4 67 | - uses: Swatinem/rust-cache@v2 68 | - run: cargo xtask test_gpt_disk_io 69 | 70 | fmt: 71 | name: Rustfmt 72 | runs-on: ubuntu-latest 73 | steps: 74 | - uses: actions/checkout@v4 75 | - run: cargo fmt --all -- --check --config format_code_in_doc_comments=true 76 | 77 | doc: 78 | name: Docs 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v4 82 | - uses: Swatinem/rust-cache@v2 83 | - run: cargo doc --all-features 84 | env: 85 | RUSTDOCFLAGS: -Dwarnings 86 | 87 | cargo-deny: 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v4 91 | - uses: EmbarkStudios/cargo-deny-action@v2 92 | 93 | coverage: 94 | runs-on: ubuntu-latest 95 | env: 96 | CARGO_TERM_COLOR: always 97 | steps: 98 | - uses: actions/checkout@v4 99 | - name: Install Rust 100 | run: rustup toolchain install stable --component llvm-tools-preview 101 | - name: Install cargo-llvm-cov 102 | uses: taiki-e/install-action@cargo-llvm-cov 103 | - name: Generate code coverage 104 | run: cargo llvm-cov --all-features --workspace --exclude xtask --lcov --output-path lcov.info 105 | - name: Coveralls 106 | uses: coverallsapp/github-action@v2 107 | 108 | spellcheck: 109 | runs-on: ubuntu-24.04 110 | steps: 111 | - uses: actions/checkout@v4 112 | - uses: crate-ci/typos@v1.32.0 113 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | name: Release 10 | on: 11 | push: 12 | branches: 13 | - main 14 | permissions: 15 | contents: write 16 | jobs: 17 | release: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - run: | 22 | cargo install auto-release 23 | auto-release --condition subject -p uguid -p gpt_disk_types -p gpt_disk_io 24 | env: 25 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | * [`uguid/CHANGELOG.md`](uguid/CHANGELOG.md) 4 | * [`gpt_disk_types/CHANGELOG.md`](gpt_disk_types/CHANGELOG.md) 5 | * [`gpt_disk_io/CHANGELOG.md`](gpt_disk_io/CHANGELOG.md) 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.98" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 10 | 11 | [[package]] 12 | name = "bit_field" 13 | version = "0.10.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 16 | 17 | [[package]] 18 | name = "bytemuck" 19 | version = "1.23.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" 22 | dependencies = [ 23 | "bytemuck_derive", 24 | ] 25 | 26 | [[package]] 27 | name = "bytemuck_derive" 28 | version = "1.9.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" 31 | dependencies = [ 32 | "proc-macro2", 33 | "quote", 34 | "syn", 35 | ] 36 | 37 | [[package]] 38 | name = "crc" 39 | version = "3.2.1" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" 42 | dependencies = [ 43 | "crc-catalog", 44 | ] 45 | 46 | [[package]] 47 | name = "crc-catalog" 48 | version = "2.4.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" 51 | 52 | [[package]] 53 | name = "equivalent" 54 | version = "1.0.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 57 | 58 | [[package]] 59 | name = "glob" 60 | version = "0.3.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 63 | 64 | [[package]] 65 | name = "gpt_disk_io" 66 | version = "0.16.2" 67 | dependencies = [ 68 | "bytemuck", 69 | "gpt_disk_types", 70 | ] 71 | 72 | [[package]] 73 | name = "gpt_disk_types" 74 | version = "0.16.1" 75 | dependencies = [ 76 | "bytemuck", 77 | "crc", 78 | "ucs2", 79 | "uguid", 80 | ] 81 | 82 | [[package]] 83 | name = "hashbrown" 84 | version = "0.15.3" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 87 | 88 | [[package]] 89 | name = "indexmap" 90 | version = "2.9.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 93 | dependencies = [ 94 | "equivalent", 95 | "hashbrown", 96 | ] 97 | 98 | [[package]] 99 | name = "itoa" 100 | version = "1.0.15" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 103 | 104 | [[package]] 105 | name = "memchr" 106 | version = "2.7.4" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 109 | 110 | [[package]] 111 | name = "proc-macro2" 112 | version = "1.0.95" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 115 | dependencies = [ 116 | "unicode-ident", 117 | ] 118 | 119 | [[package]] 120 | name = "quote" 121 | version = "1.0.40" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 124 | dependencies = [ 125 | "proc-macro2", 126 | ] 127 | 128 | [[package]] 129 | name = "ryu" 130 | version = "1.0.20" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 133 | 134 | [[package]] 135 | name = "serde" 136 | version = "1.0.219" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 139 | dependencies = [ 140 | "serde_derive", 141 | ] 142 | 143 | [[package]] 144 | name = "serde_derive" 145 | version = "1.0.219" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 148 | dependencies = [ 149 | "proc-macro2", 150 | "quote", 151 | "syn", 152 | ] 153 | 154 | [[package]] 155 | name = "serde_json" 156 | version = "1.0.140" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 159 | dependencies = [ 160 | "itoa", 161 | "memchr", 162 | "ryu", 163 | "serde", 164 | ] 165 | 166 | [[package]] 167 | name = "serde_spanned" 168 | version = "0.6.8" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 171 | dependencies = [ 172 | "serde", 173 | ] 174 | 175 | [[package]] 176 | name = "serde_test" 177 | version = "1.0.177" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" 180 | dependencies = [ 181 | "serde", 182 | ] 183 | 184 | [[package]] 185 | name = "syn" 186 | version = "2.0.101" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 189 | dependencies = [ 190 | "proc-macro2", 191 | "quote", 192 | "unicode-ident", 193 | ] 194 | 195 | [[package]] 196 | name = "target-triple" 197 | version = "0.1.4" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" 200 | 201 | [[package]] 202 | name = "termcolor" 203 | version = "1.4.1" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 206 | dependencies = [ 207 | "winapi-util", 208 | ] 209 | 210 | [[package]] 211 | name = "toml" 212 | version = "0.8.22" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 215 | dependencies = [ 216 | "serde", 217 | "serde_spanned", 218 | "toml_datetime", 219 | "toml_edit", 220 | ] 221 | 222 | [[package]] 223 | name = "toml_datetime" 224 | version = "0.6.9" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 227 | dependencies = [ 228 | "serde", 229 | ] 230 | 231 | [[package]] 232 | name = "toml_edit" 233 | version = "0.22.26" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 236 | dependencies = [ 237 | "indexmap", 238 | "serde", 239 | "serde_spanned", 240 | "toml_datetime", 241 | "toml_write", 242 | "winnow", 243 | ] 244 | 245 | [[package]] 246 | name = "toml_write" 247 | version = "0.1.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 250 | 251 | [[package]] 252 | name = "trybuild" 253 | version = "1.0.104" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "6ae08be68c056db96f0e6c6dd820727cca756ced9e1f4cc7fdd20e2a55e23898" 256 | dependencies = [ 257 | "glob", 258 | "serde", 259 | "serde_derive", 260 | "serde_json", 261 | "target-triple", 262 | "termcolor", 263 | "toml", 264 | ] 265 | 266 | [[package]] 267 | name = "ucs2" 268 | version = "0.3.3" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" 271 | dependencies = [ 272 | "bit_field", 273 | ] 274 | 275 | [[package]] 276 | name = "uguid" 277 | version = "2.2.1" 278 | dependencies = [ 279 | "bytemuck", 280 | "serde", 281 | "serde_test", 282 | "trybuild", 283 | ] 284 | 285 | [[package]] 286 | name = "unicode-ident" 287 | version = "1.0.18" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 290 | 291 | [[package]] 292 | name = "winapi-util" 293 | version = "0.1.9" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 296 | dependencies = [ 297 | "windows-sys", 298 | ] 299 | 300 | [[package]] 301 | name = "windows-sys" 302 | version = "0.59.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 305 | dependencies = [ 306 | "windows-targets", 307 | ] 308 | 309 | [[package]] 310 | name = "windows-targets" 311 | version = "0.52.6" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 314 | dependencies = [ 315 | "windows_aarch64_gnullvm", 316 | "windows_aarch64_msvc", 317 | "windows_i686_gnu", 318 | "windows_i686_gnullvm", 319 | "windows_i686_msvc", 320 | "windows_x86_64_gnu", 321 | "windows_x86_64_gnullvm", 322 | "windows_x86_64_msvc", 323 | ] 324 | 325 | [[package]] 326 | name = "windows_aarch64_gnullvm" 327 | version = "0.52.6" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 330 | 331 | [[package]] 332 | name = "windows_aarch64_msvc" 333 | version = "0.52.6" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 336 | 337 | [[package]] 338 | name = "windows_i686_gnu" 339 | version = "0.52.6" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 342 | 343 | [[package]] 344 | name = "windows_i686_gnullvm" 345 | version = "0.52.6" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 348 | 349 | [[package]] 350 | name = "windows_i686_msvc" 351 | version = "0.52.6" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 354 | 355 | [[package]] 356 | name = "windows_x86_64_gnu" 357 | version = "0.52.6" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 360 | 361 | [[package]] 362 | name = "windows_x86_64_gnullvm" 363 | version = "0.52.6" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 366 | 367 | [[package]] 368 | name = "windows_x86_64_msvc" 369 | version = "0.52.6" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 372 | 373 | [[package]] 374 | name = "winnow" 375 | version = "0.7.9" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" 378 | dependencies = [ 379 | "memchr", 380 | ] 381 | 382 | [[package]] 383 | name = "xtask" 384 | version = "0.0.0" 385 | dependencies = [ 386 | "anyhow", 387 | ] 388 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [workspace] 10 | members = [ 11 | "gpt_disk_io", 12 | "gpt_disk_types", 13 | "uguid", 14 | "xtask", 15 | ] 16 | resolver = "2" 17 | 18 | [workspace.package] 19 | edition = "2021" 20 | license = "MIT OR Apache-2.0" 21 | repository = "https://github.com/google/gpt-disk-rs" 22 | rust-version = "1.81" 23 | 24 | [workspace.dependencies] 25 | bytemuck = { version = "1.4.0", default-features = false } 26 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2022 Google LLC 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpt-disk-rs 2 | 3 | [![Coverage Status](https://coveralls.io/repos/github/google/gpt-disk-rs/badge.svg?branch=main)](https://coveralls.io/github/google/gpt-disk-rs?branch=main) 4 | 5 | `no_std` libraries related to [GPT] (GUID Partition Table) disk 6 | data. There are three Rust packages in this repository: 7 | 8 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 9 | 10 | ## [`uguid`] 11 | 12 | [![Crates.io](https://img.shields.io/crates/v/uguid)](https://crates.io/crates/uguid) 13 | [![Docs.rs](https://docs.rs/uguid/badge.svg)](https://docs.rs/uguid) 14 | 15 | The [`uguid`] package provides the GUID type used in the [UEFI 16 | Specification], and also in Microsoft Windows. 17 | 18 | ## [`gpt_disk_types`] 19 | 20 | [![Crates.io](https://img.shields.io/crates/v/gpt_disk_types)](https://crates.io/crates/gpt_disk_types) 21 | [![Docs.rs](https://docs.rs/gpt_disk_types/badge.svg)](https://docs.rs/gpt_disk_types) 22 | 23 | The [`gpt_disk_types`] package provides flexible types for 24 | all the GPT types defined in the [UEFI Specification]. The types are 25 | designed to ensure correct endianness regardless of host platform, and 26 | can be used even with corrupted input data. 27 | 28 | [UEFI Specification]: https://uefi.org/specifications 29 | 30 | ## [`gpt_disk_io`] 31 | 32 | [![Crates.io](https://img.shields.io/crates/v/gpt_disk_io)](https://crates.io/crates/gpt_disk_io) 33 | [![Docs.rs](https://docs.rs/gpt_disk_io/badge.svg)](https://docs.rs/gpt_disk_io) 34 | 35 | The [`gpt_disk_io`] package depends on `gpt_disk_types` and adds types for 36 | reading and writing GPT data to an abstract disk interface. This 37 | interface can be implemented for any backend that supports block-level 38 | IO. 39 | 40 | ## Code layout 41 | 42 | [`uguid/src`](uguid/src): 43 | * `error.rs`: Parse error type. 44 | * `guid.rs`: Provides the `Guid` type. 45 | * `lib.rs`: Contains the public GUID-creation macros. 46 | * `util.rs`: ASCII conversion utilities. 47 | 48 | [`gpt_disk_types/src`](gpt_disk_types/src): 49 | * `block.rs`: Numeric types for addressing blocks. 50 | * `crc32.rs`: CRC32 type. 51 | * `header.rs`: GPT header and related types. 52 | * `mbr.rs`: Legacy master boot record types. 53 | * `num.rs`: Little-endian integer types. 54 | * `partition_array.rs`: GPT partition array types. 55 | * `partition_entry.rs`: GPT partition array entry types. 56 | * `std_support.rs`: Provides `std` trait impls when the `std` feature is enabled. 57 | 58 | [`gpt_disk_io/src`](gpt_disk_io/src): 59 | * `block_io.rs`: BlockIo trait for generic read/write operations. 60 | * `block_io/slice_block_io.rs`: In-memory byte slice implementations of BlockIo. 61 | * `block_io/std_block_io.rs`: `std::io`-backed implementation of BlockIo (requires `std` feature). 62 | * `disk.rs`: Read and write GPT data from a block device. 63 | * `std_support.rs`: Provides `std` trait impls when the `std` feature is enabled. 64 | 65 | Most of the tests are under `gpt_disk_io/tests`, including the tests for 66 | `gpt_disk_types`. Having all the tests in one place allow them to share code. 67 | 68 | ## License 69 | 70 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) 71 | or [MIT license](LICENSE-MIT) at your option. 72 | 73 | ## Disclaimer 74 | 75 | This project is not an official Google project. It is not supported by 76 | Google and Google specifically disclaims all warranties as to its quality, 77 | merchantability, or fitness for a particular purpose. 78 | 79 | [`uguid`]: uguid 80 | [`gpt_disk_types`]: gpt_disk_types 81 | [`gpt_disk_io`]: gpt_disk_io 82 | -------------------------------------------------------------------------------- /docs/release_process.md: -------------------------------------------------------------------------------- 1 | # Release process 2 | 3 | To release new versions of one or more packages in the workspace: 4 | 5 | 1. Create a local branch and make these changes: 6 | 1. Update the version of each package you want to release in the 7 | corresponding `Cargo.toml`. Follow semver. 8 | 2. If needed, update the dependency version in packages that depend 9 | on the updated package. For example, if you are releasing changes 10 | to `uguid` that require a major version bump, the dependency on 11 | `uguid` in `gpt_disk_types` should be updated. Ditto for a minor 12 | version bump if the dependent package requires any of the new 13 | functionality. Patch version bumps do not require a change in the 14 | dependent package. Note that in cargo's version of semver, `0.x.y` 15 | treats `x` as the major version number. 16 | 3. Run `cargo build` to ensure `Cargo.lock` is updated. 17 | 4. Update the changelogs. 18 | 2. Commit those changes. The commit message subject must start with 19 | `release:`. Without that prefix, the automatic part of the release 20 | process will not run. 21 | 3. Push the branch and create a PR. See for example [#158]. 22 | 4. When the PR is reviewed and merged, the automatic release process 23 | will kick off. 24 | 25 | [#158]: https://github.com/google/gpt-disk-rs/pull/158 26 | 27 | ## The automatic part 28 | 29 | The rest of the release process is triggered by 30 | `.github/workflows/release.yml`, which runs when commits are pushed to 31 | the main branch. It runs `cargo xtask auto_publish`. 32 | 33 | The `auto_publish` command gets the local version of each package and, 34 | if necessary, creates a remote git tag and publishes to crates.io. If 35 | the git tag already exists, that part is skipped. If the crates.io 36 | release already exists, that part is skipped. 37 | 38 | ## Crates.io token 39 | 40 | Releasing to crates.io requires an API token. That is passed to the job 41 | via a repository secret called `CARGO_REGISTRY_TOKEN`. The secret is 42 | configured in the repo settings. When creating an API token, restrict 43 | the scope to `publish-update`. 44 | -------------------------------------------------------------------------------- /gpt_disk_io/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.16.2 2 | 3 | * MSRV increased to 1.81. 4 | * The `Error` trait is now unconditionally implemented for all error types. 5 | 6 | # 0.16.1 7 | 8 | * Derive `PartialEq` for `DiskError`. 9 | 10 | # 0.16.0 11 | 12 | * Bump MSRV to 1.68. 13 | * Relax the requirements of `BlockIo::read_blocks` and 14 | `BlockIo::write_blocks` such that reading/writing zero blocks is 15 | allowed. 16 | * Add `BlockIoAdapter`. This struct impls `BlockIo` for types that 17 | don't have a block size. For example, `&mut [u8]`, `Vec`, and 18 | `File`. 19 | * Add `alloc` feature. This enables the `BlockIoAdapter>` impl. 20 | * Remove `SliceBlockIo`, `MutSliceBlockIo`, and `StdBlockIo`. Use 21 | `BlockIoAdapter` instead. 22 | * Remove `BlockIo::assert_valid_buffer`. Use 23 | `BlockSize::assert_valid_block_buffer` instead. 24 | * Update to latest `gpt_disk_types`. 25 | 26 | # 0.15.0 27 | 28 | * Update to latest `gpt_disk_types`. 29 | * Copied the license files into each package so that the archives on 30 | crates.io include them. 31 | 32 | # 0.14.0 33 | 34 | * Relax version requirement for `bytemuck`. 35 | * When the `std` feature is enabled, also enable it in `gpt_disk_types`. 36 | * Enable `doc_auto_cfg` on docs.rs. 37 | * Updated to latest `gpt_disk_types`. 38 | 39 | # 0.13.0 40 | 41 | * Allow the MIT license to be used in addition to Apache-2.0. 42 | * Updated to latest `gpt_disk_types`. 43 | 44 | # 0.12.0 45 | 46 | * Updated to latest `gpt_disk_types`. 47 | 48 | # 0.11.0 49 | 50 | * Updated to latest `gpt_disk_types`. 51 | 52 | # 0.10.0 53 | 54 | * Updated to latest `gpt_disk_types`. 55 | 56 | # 0.9.0 57 | 58 | * Updated to latest `gpt_disk_types`. 59 | 60 | # 0.8.0 61 | 62 | * Updated to latest `gpt_disk_types`. 63 | 64 | # 0.7.0 65 | 66 | * Updated to latest `gpt_disk_types`. 67 | 68 | # 0.6.0 69 | 70 | * Updated documentation. 71 | * Updated to latest `gpt_disk_types`. 72 | -------------------------------------------------------------------------------- /gpt_disk_io/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [package] 10 | name = "gpt_disk_io" 11 | version = "0.16.2" 12 | categories = ["data-structures", "embedded", "no-std"] 13 | description = "GPT (GUID Partition Table) disk IO no_std library" 14 | keywords = ["disk", "gpt", "no_std", "partition", "uefi"] 15 | 16 | edition.workspace = true 17 | rust-version.workspace = true 18 | license.workspace = true 19 | repository.workspace = true 20 | 21 | [dependencies] 22 | bytemuck.workspace = true 23 | gpt_disk_types = { version = "0.16.0", path = "../gpt_disk_types", features = ["bytemuck"] } 24 | 25 | [features] 26 | # See module docstring in src/lib.rs for details of what these feature do. 27 | alloc = [] 28 | std = ["alloc", "gpt_disk_types/std"] 29 | 30 | [package.metadata.docs.rs] 31 | all-features = true 32 | rustdoc-args = ["--cfg", "docsrs"] 33 | -------------------------------------------------------------------------------- /gpt_disk_io/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2022 Google LLC 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /gpt_disk_io/README.md: -------------------------------------------------------------------------------- 1 | # `gpt_disk_io` 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/gpt_disk_io)](https://crates.io/crates/gpt_disk_io) 4 | [![Docs.rs](https://docs.rs/gpt_disk_io/badge.svg)](https://docs.rs/gpt_disk_io) 5 | 6 | `no_std` library for reading and writing [GPT] (GUID Partition Table) 7 | disk data structures through a block IO interface. 8 | 9 | See also the [`gpt_disk_types`] package. 10 | 11 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 12 | [`gpt_disk_types`]: https://crates.io/crates/gpt_disk_types 13 | 14 | ## Features 15 | 16 | No features are enabled by default. 17 | 18 | * `alloc`: Enables `Vec` implementation of `BlockIoAdapter`. 19 | * `std`: Enables `std::io` implementations of `BlockIoAdapter`. 20 | 21 | ## Minimum Supported Rust Version (MSRV) 22 | 23 | The current MSRV is 1.81. 24 | 25 | ## License 26 | 27 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) 28 | or [MIT license](LICENSE-MIT) at your option. 29 | 30 | ## Disclaimer 31 | 32 | This project is not an official Google project. It is not supported by 33 | Google and Google specifically disclaims all warranties as to its quality, 34 | merchantability, or fitness for a particular purpose. 35 | -------------------------------------------------------------------------------- /gpt_disk_io/examples/reader.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #[cfg(feature = "std")] 10 | use { 11 | gpt_disk_io::gpt_disk_types::BlockSize, 12 | gpt_disk_io::{BlockIoAdapter, Disk}, 13 | std::{env, error, fs}, 14 | }; 15 | 16 | // To create a disk to test this you can use truncate and sgdisk. For example: 17 | // 18 | // truncate --size 10MiB disk.bin 19 | // sgdisk disk.bin --new=1:2048:4096 --change-name=1:'hello world!' --print 20 | // cargo run --features=std --example reader disk.bin 21 | 22 | #[cfg(feature = "std")] 23 | fn main() -> Result<(), Box> { 24 | let disk_path = env::args().nth(1).expect("one argument is required"); 25 | println!("opening {} for reading", disk_path); 26 | 27 | let file = fs::File::open(disk_path)?; 28 | 29 | let mut block_buf = vec![0u8; 512]; 30 | 31 | let block_io = BlockIoAdapter::new(file, BlockSize::BS_512); 32 | let mut disk = Disk::new(block_io)?; 33 | 34 | let primary_header = disk.read_primary_gpt_header(&mut block_buf)?; 35 | println!("{}", primary_header); 36 | assert!(primary_header.is_signature_valid()); 37 | 38 | let layout = primary_header.get_partition_entry_array_layout()?; 39 | for entry in disk.gpt_partition_entry_array_iter(layout, &mut block_buf)? { 40 | let entry = entry?; 41 | if entry.is_used() { 42 | println!("{}", entry); 43 | } 44 | } 45 | 46 | Ok(()) 47 | } 48 | 49 | #[cfg(not(feature = "std"))] 50 | fn main() { 51 | panic!("this program must be compiled with the 'std' feature"); 52 | } 53 | -------------------------------------------------------------------------------- /gpt_disk_io/src/block_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | pub(crate) mod slice_block_io; 10 | 11 | #[cfg(feature = "std")] 12 | pub(crate) mod std_block_io; 13 | 14 | use core::fmt::{Debug, Display}; 15 | use gpt_disk_types::{BlockSize, Lba}; 16 | 17 | /// Trait for reading from and writing to a block device. 18 | /// 19 | /// See also [`BlockIoAdapter`]. 20 | pub trait BlockIo { 21 | /// IO error type. 22 | type Error: Debug + Display + Send + Sync + 'static; 23 | 24 | /// Get the [`BlockSize`]. The return value is not allowed to 25 | /// change. 26 | fn block_size(&self) -> BlockSize; 27 | 28 | /// Get the number of logical blocks in the disk. 29 | /// 30 | /// If the underlying storage has a number of bytes that are not 31 | /// evenly divisible by [`block_size`], the implementation should 32 | /// return the number of whole blocks. In that case, the partial 33 | /// block at the end will not be accessible. 34 | /// 35 | /// [`block_size`]: Self::block_size 36 | fn num_blocks(&mut self) -> Result; 37 | 38 | /// Read contiguous blocks from the disk. The `dst` buffer size must 39 | /// be a multiple of [`block_size`]. Implementations are permitted 40 | /// to panic if this precondition is not met, e.g. by calling 41 | /// [`BlockSize::assert_valid_block_buffer`]. 42 | /// 43 | /// [`block_size`]: Self::block_size 44 | fn read_blocks( 45 | &mut self, 46 | start_lba: Lba, 47 | dst: &mut [u8], 48 | ) -> Result<(), Self::Error>; 49 | 50 | /// Write contiguous block to the disk. The `src` buffer size must 51 | /// be a multiple of [`block_size`]. Implementations are permitted 52 | /// to panic if this precondition is not met, e.g. by calling 53 | /// [`BlockSize::assert_valid_block_buffer`]. 54 | /// 55 | /// Writes are not guaranteed to be complete until [`flush`] is 56 | /// called. 57 | /// 58 | /// [`block_size`]: Self::block_size 59 | /// [`flush`]: Self::flush 60 | fn write_blocks( 61 | &mut self, 62 | start_lba: Lba, 63 | src: &[u8], 64 | ) -> Result<(), Self::Error>; 65 | 66 | /// Flush any pending writes to the device. 67 | fn flush(&mut self) -> Result<(), Self::Error>; 68 | } 69 | 70 | /// Adapter for types that can act as storage, but don't have a block 71 | /// size. This is used to provide `BlockIo` impls for byte slices, 72 | /// files, and various other types. 73 | /// 74 | /// Note that `BlockIoAdapter` can be constructed for any sized type 75 | /// `T`, but not all types provide a `BlockIo` impl for 76 | /// `BlockIoAdapter`. 77 | /// 78 | /// # For byte slices 79 | /// 80 | /// ``` 81 | /// use gpt_disk_io::gpt_disk_types::{BlockSize, Lba}; 82 | /// use gpt_disk_io::{BlockIo, BlockIoAdapter, SliceBlockIoError}; 83 | /// 84 | /// let mut one_block = [0; 512]; 85 | /// 86 | /// // Construct `BlockIoAdapter` for an immutable byte slice: 87 | /// let data: &[u8] = &[0; 1024]; 88 | /// let mut bio = BlockIoAdapter::new(data, BlockSize::BS_512); 89 | /// // Demonstrate that reading succeeds: 90 | /// assert!(bio.read_blocks(Lba(0), &mut one_block).is_ok()); 91 | /// // But writing fails since the storage is immutable: 92 | /// assert!(bio.write_blocks(Lba(0), &one_block).is_err()); 93 | /// 94 | /// // Construct `BlockIoAdapter` for a mutable byte slice: 95 | /// let data: &mut [u8] = &mut [0; 512]; 96 | /// let mut bio = BlockIoAdapter::new(data, BlockSize::BS_512); 97 | /// // Now both reading and writing succeed: 98 | /// assert!(bio.read_blocks(Lba(0), &mut one_block).is_ok()); 99 | /// assert!(bio.write_blocks(Lba(0), &one_block).is_ok()); 100 | /// ``` 101 | /// 102 | /// # With the `alloc` feature 103 | /// 104 | /// Construct a `BlockIoAdapter` that owns a `Vec`: 105 | /// 106 | /// ``` 107 | /// use gpt_disk_io::gpt_disk_types::BlockSize; 108 | /// use gpt_disk_io::{BlockIo, BlockIoAdapter, SliceBlockIoError}; 109 | /// 110 | /// #[cfg(feature = "alloc")] 111 | /// fn example_alloc() -> Result<(), SliceBlockIoError> { 112 | /// let data: Vec = vec![0; 512]; 113 | /// let mut bio = BlockIoAdapter::new(data, BlockSize::BS_512); 114 | /// assert_eq!(bio.num_blocks()?, 1); 115 | /// 116 | /// Ok(()) 117 | /// } 118 | /// ``` 119 | /// 120 | /// # With the `std` feature 121 | /// 122 | /// Construct `BlockIoAdapter` from various file-like types: 123 | /// 124 | /// ``` 125 | /// use gpt_disk_io::gpt_disk_types::BlockSize; 126 | /// use gpt_disk_io::{BlockIo, BlockIoAdapter}; 127 | /// use std::fs::{self, File}; 128 | /// use std::io::{self, Cursor}; 129 | /// use std::path::Path; 130 | /// 131 | /// #[cfg(feature = "std")] 132 | /// fn example_std(path: &Path) -> Result<(), io::Error> { 133 | /// // Construct a `BlockIoAdapter` that takes ownership of a file. 134 | /// // This also works for any type that implements the `ReadWriteSeek` trait. 135 | /// let file = File::open(path)?; 136 | /// let mut bio = BlockIoAdapter::new(file, BlockSize::BS_512); 137 | /// assert_eq!(bio.num_blocks()?, 1); 138 | /// 139 | /// // Construct a `BlockIoAdapter` that borrows a file. 140 | /// let file = File::open(path)?; 141 | /// let mut bio = BlockIoAdapter::new(&file, BlockSize::BS_512); 142 | /// assert_eq!(bio.num_blocks()?, 1); 143 | /// 144 | /// // Construct a `BlockIoAdapter` from another type that 145 | /// // implements `Read + Write + Seek`, but does not directly implement 146 | /// // `ReadWriteSeek`. Due to trait constraints, this requires an `&mut` 147 | /// // borrow; transferring ownership of `cursor` would fail to compile. 148 | /// let mut cursor = Cursor::new(vec![0; 512]); 149 | /// let mut bio = BlockIoAdapter::new(&mut cursor, BlockSize::BS_512); 150 | /// assert_eq!(bio.num_blocks()?, 1); 151 | /// 152 | /// Ok(()) 153 | /// } 154 | /// ``` 155 | #[allow(clippy::module_name_repetitions)] 156 | #[derive(Clone, Debug, Eq, PartialEq)] 157 | pub struct BlockIoAdapter { 158 | storage: T, 159 | block_size: BlockSize, 160 | } 161 | 162 | impl BlockIoAdapter { 163 | /// Create a new `BlockIoAdapter`. 164 | #[must_use] 165 | pub fn new(storage: T, block_size: BlockSize) -> Self { 166 | Self { 167 | storage, 168 | block_size, 169 | } 170 | } 171 | 172 | /// Get the [`BlockSize`]. 173 | #[must_use] 174 | pub fn block_size(&self) -> BlockSize { 175 | self.block_size 176 | } 177 | 178 | /// Get a reference to the underlying storage. 179 | #[must_use] 180 | pub fn storage(&self) -> &T { 181 | &self.storage 182 | } 183 | 184 | /// Get a mutable reference to the underlying storage. 185 | #[must_use] 186 | pub fn storage_mut(&mut self) -> &mut T { 187 | &mut self.storage 188 | } 189 | 190 | /// Consume the adapter and return the underlying storage. 191 | #[must_use] 192 | pub fn take_storage(self) -> T { 193 | self.storage 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /gpt_disk_io/src/block_io/slice_block_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::{BlockIo, BlockIoAdapter}; 10 | use core::fmt::{self, Debug, Display, Formatter}; 11 | use core::ops::Range; 12 | use gpt_disk_types::{BlockSize, Lba}; 13 | 14 | #[cfg(feature = "alloc")] 15 | use alloc::vec::Vec; 16 | 17 | /// Error type used for `&[u8]` and `&mut [u8]` versions of [`BlockIoAdapter`]. 18 | #[allow(clippy::module_name_repetitions)] 19 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 20 | pub enum SliceBlockIoError { 21 | /// Numeric overflow occurred. 22 | #[default] 23 | Overflow, 24 | 25 | /// Attempted to write to a read-only byte slice. 26 | ReadOnly, 27 | 28 | /// A read or write is out of bounds. 29 | OutOfBounds { 30 | /// Start LBA. 31 | start_lba: Lba, 32 | 33 | /// Length in bytes. 34 | length_in_bytes: usize, 35 | }, 36 | } 37 | 38 | impl Display for SliceBlockIoError { 39 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 40 | match self { 41 | Self::Overflow => f.write_str("numeric overflow occurred"), 42 | Self::ReadOnly => { 43 | f.write_str("attempted to write to a read-only byte slice") 44 | } 45 | Self::OutOfBounds { 46 | start_lba, 47 | length_in_bytes, 48 | } => { 49 | write!( 50 | f, 51 | "out of bounds: start_lba={start_lba}, length_in_bytes={length_in_bytes}" 52 | ) 53 | } 54 | } 55 | } 56 | } 57 | 58 | impl core::error::Error for SliceBlockIoError {} 59 | 60 | #[track_caller] 61 | fn buffer_byte_range_opt( 62 | block_size: BlockSize, 63 | start_lba: Lba, 64 | buf: &[u8], 65 | ) -> Option> { 66 | let start_lba = usize::try_from(start_lba).ok()?; 67 | let start_byte = start_lba.checked_mul(block_size.to_usize()?)?; 68 | let end_byte = start_byte.checked_add(buf.len())?; 69 | Some(start_byte..end_byte) 70 | } 71 | 72 | #[track_caller] 73 | fn buffer_byte_range( 74 | block_size: BlockSize, 75 | start_lba: Lba, 76 | buf: &[u8], 77 | ) -> Result, SliceBlockIoError> { 78 | buffer_byte_range_opt(block_size, start_lba, buf) 79 | .ok_or(SliceBlockIoError::Overflow) 80 | } 81 | 82 | #[track_caller] 83 | fn num_blocks( 84 | storage: &[u8], 85 | block_size: BlockSize, 86 | ) -> Result { 87 | let storage_len = u64::try_from(storage.len()) 88 | .map_err(|_| SliceBlockIoError::Overflow)?; 89 | 90 | Ok(storage_len / block_size.to_u64()) 91 | } 92 | 93 | #[track_caller] 94 | fn read_blocks( 95 | storage: &[u8], 96 | block_size: BlockSize, 97 | start_lba: Lba, 98 | dst: &mut [u8], 99 | ) -> Result<(), SliceBlockIoError> { 100 | block_size.assert_valid_block_buffer(dst); 101 | 102 | let src = storage 103 | .get(buffer_byte_range(block_size, start_lba, dst)?) 104 | .ok_or(SliceBlockIoError::OutOfBounds { 105 | start_lba, 106 | length_in_bytes: dst.len(), 107 | })?; 108 | dst.copy_from_slice(src); 109 | Ok(()) 110 | } 111 | 112 | fn write_blocks( 113 | storage: &mut [u8], 114 | block_size: BlockSize, 115 | start_lba: Lba, 116 | src: &[u8], 117 | ) -> Result<(), SliceBlockIoError> { 118 | block_size.assert_valid_block_buffer(src); 119 | 120 | let dst = storage 121 | .get_mut(buffer_byte_range(block_size, start_lba, src)?) 122 | .ok_or(SliceBlockIoError::OutOfBounds { 123 | start_lba, 124 | length_in_bytes: src.len(), 125 | })?; 126 | dst.copy_from_slice(src); 127 | Ok(()) 128 | } 129 | 130 | impl BlockIo for BlockIoAdapter<&[u8]> { 131 | type Error = SliceBlockIoError; 132 | 133 | fn block_size(&self) -> BlockSize { 134 | self.block_size 135 | } 136 | 137 | fn num_blocks(&mut self) -> Result { 138 | num_blocks(self.storage, self.block_size) 139 | } 140 | 141 | fn read_blocks( 142 | &mut self, 143 | start_lba: Lba, 144 | dst: &mut [u8], 145 | ) -> Result<(), Self::Error> { 146 | read_blocks(self.storage, self.block_size, start_lba, dst) 147 | } 148 | 149 | fn write_blocks( 150 | &mut self, 151 | _start_lba: Lba, 152 | _src: &[u8], 153 | ) -> Result<(), Self::Error> { 154 | Err(Self::Error::ReadOnly) 155 | } 156 | 157 | fn flush(&mut self) -> Result<(), Self::Error> { 158 | Ok(()) 159 | } 160 | } 161 | 162 | impl BlockIo for BlockIoAdapter<&mut [u8]> { 163 | type Error = SliceBlockIoError; 164 | 165 | fn block_size(&self) -> BlockSize { 166 | self.block_size 167 | } 168 | 169 | fn num_blocks(&mut self) -> Result { 170 | num_blocks(self.storage, self.block_size) 171 | } 172 | 173 | fn read_blocks( 174 | &mut self, 175 | start_lba: Lba, 176 | dst: &mut [u8], 177 | ) -> Result<(), Self::Error> { 178 | read_blocks(self.storage, self.block_size, start_lba, dst) 179 | } 180 | 181 | fn write_blocks( 182 | &mut self, 183 | start_lba: Lba, 184 | src: &[u8], 185 | ) -> Result<(), Self::Error> { 186 | write_blocks(self.storage, self.block_size, start_lba, src) 187 | } 188 | 189 | fn flush(&mut self) -> Result<(), Self::Error> { 190 | Ok(()) 191 | } 192 | } 193 | 194 | #[cfg(feature = "alloc")] 195 | impl BlockIo for BlockIoAdapter> { 196 | type Error = SliceBlockIoError; 197 | 198 | fn block_size(&self) -> BlockSize { 199 | self.block_size 200 | } 201 | 202 | fn num_blocks(&mut self) -> Result { 203 | num_blocks(&self.storage, self.block_size) 204 | } 205 | 206 | fn read_blocks( 207 | &mut self, 208 | start_lba: Lba, 209 | dst: &mut [u8], 210 | ) -> Result<(), Self::Error> { 211 | read_blocks(&self.storage, self.block_size, start_lba, dst) 212 | } 213 | 214 | fn write_blocks( 215 | &mut self, 216 | start_lba: Lba, 217 | src: &[u8], 218 | ) -> Result<(), Self::Error> { 219 | write_blocks(&mut self.storage, self.block_size, start_lba, src) 220 | } 221 | 222 | fn flush(&mut self) -> Result<(), Self::Error> { 223 | Ok(()) 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /gpt_disk_io/src/block_io/std_block_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::{BlockIo, BlockIoAdapter}; 10 | use gpt_disk_types::{BlockSize, Lba}; 11 | use std::fs::File; 12 | use std::io::{self, Read, Seek, SeekFrom, Write}; 13 | 14 | /// Combination trait for types that impl [`Read`], [`Write`], and [`Seek`]. 15 | pub trait ReadWriteSeek: Read + Write + Seek { 16 | /// Get the number of blocks for the given `block_size`. 17 | /// 18 | /// The default implementation seeks to the end to get the number of 19 | /// bytes. 20 | fn num_blocks(&mut self, block_size: BlockSize) -> Result { 21 | let block_size = block_size.to_u64(); 22 | let num_bytes = self.seek(SeekFrom::End(0))?; 23 | Ok(num_bytes / block_size) 24 | } 25 | 26 | /// Read contiguous blocks. 27 | fn read_blocks( 28 | &mut self, 29 | block_size: BlockSize, 30 | start_lba: Lba, 31 | dst: &mut [u8], 32 | ) -> Result<(), io::Error> { 33 | block_size.assert_valid_block_buffer(dst); 34 | 35 | self.seek(SeekFrom::Start(start_lba.to_u64() * block_size.to_u64()))?; 36 | self.read_exact(dst)?; 37 | Ok(()) 38 | } 39 | 40 | /// Write contiguous blocks. 41 | fn write_blocks( 42 | &mut self, 43 | block_size: BlockSize, 44 | start_lba: Lba, 45 | src: &[u8], 46 | ) -> Result<(), io::Error> { 47 | block_size.assert_valid_block_buffer(src); 48 | 49 | self.seek(SeekFrom::Start(start_lba.to_u64() * block_size.to_u64()))?; 50 | self.write_all(src)?; 51 | Ok(()) 52 | } 53 | } 54 | 55 | impl ReadWriteSeek for File {} 56 | impl ReadWriteSeek for &File {} 57 | impl ReadWriteSeek for &mut T where T: Read + Write + Seek {} 58 | 59 | impl BlockIo for BlockIoAdapter 60 | where 61 | T: ReadWriteSeek, 62 | { 63 | type Error = io::Error; 64 | 65 | fn block_size(&self) -> BlockSize { 66 | self.block_size 67 | } 68 | 69 | fn num_blocks(&mut self) -> Result { 70 | self.storage.num_blocks(self.block_size) 71 | } 72 | 73 | fn read_blocks( 74 | &mut self, 75 | start_lba: Lba, 76 | dst: &mut [u8], 77 | ) -> Result<(), Self::Error> { 78 | self.storage.read_blocks(self.block_size, start_lba, dst) 79 | } 80 | 81 | fn write_blocks( 82 | &mut self, 83 | start_lba: Lba, 84 | src: &[u8], 85 | ) -> Result<(), Self::Error> { 86 | self.storage.write_blocks(self.block_size, start_lba, src) 87 | } 88 | 89 | fn flush(&mut self) -> Result<(), Self::Error> { 90 | self.storage.flush() 91 | } 92 | } 93 | 94 | impl BlockIo for BlockIoAdapter<&mut dyn ReadWriteSeek> { 95 | type Error = io::Error; 96 | 97 | fn block_size(&self) -> BlockSize { 98 | self.block_size 99 | } 100 | 101 | fn num_blocks(&mut self) -> Result { 102 | self.storage.num_blocks(self.block_size) 103 | } 104 | 105 | fn read_blocks( 106 | &mut self, 107 | start_lba: Lba, 108 | dst: &mut [u8], 109 | ) -> Result<(), Self::Error> { 110 | self.storage.read_blocks(self.block_size, start_lba, dst) 111 | } 112 | 113 | fn write_blocks( 114 | &mut self, 115 | start_lba: Lba, 116 | src: &[u8], 117 | ) -> Result<(), Self::Error> { 118 | self.storage.write_blocks(self.block_size, start_lba, src) 119 | } 120 | 121 | fn flush(&mut self) -> Result<(), Self::Error> { 122 | self.storage.flush() 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /gpt_disk_io/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Library for reading and writing GPT disk data structures through a 10 | //! block IO interface. 11 | //! 12 | //! This crate adds a convenient interface for reading and writing the 13 | //! GPT types defined in the [`gpt_disk_types`] crate to a [`Disk`]. The 14 | //! [`Disk`] is represented by the [`BlockIo`] trait, which allows this 15 | //! library to be `no_std`. The [`BlockIo`] trait is designed to be 16 | //! compatible with very simple block APIs, such as [`EFI_BLOCK_IO_PROTOCOL`]. 17 | //! 18 | //! The [`BlockIoAdapter`] type allows the disk to be backed by simple 19 | //! byte-oriented storage backends, such as `&mut [u8]` and `File` (the 20 | //! latter requires the `std` feature). 21 | //! 22 | //! # Features 23 | //! 24 | //! * `alloc`: Enables [`Vec`] implementation of [`BlockIoAdapter`]. 25 | //! * `std`: Enables [`std::io`] implementations of [`BlockIoAdapter`]. 26 | //! 27 | //! # Examples 28 | //! 29 | //! Construct a GPT disk in-memory backed by a `Vec`: 30 | //! 31 | //! ``` 32 | //! use gpt_disk_io::{BlockIoAdapter, BlockIo, Disk, DiskError}; 33 | //! use gpt_disk_types::{ 34 | //! guid, BlockSize, Crc32, GptHeader, GptPartitionEntry, 35 | //! GptPartitionEntryArray, GptPartitionType, LbaLe, U32Le, 36 | //! }; 37 | //! 38 | //! // Space for a 4MiB disk. 39 | //! let mut disk_storage = vec![0; 4 * 1024 * 1024]; 40 | //! 41 | //! // Standard 512-byte block size. 42 | //! let bs = BlockSize::BS_512; 43 | //! 44 | //! // `BlockIoAdapter` implements the `BlockIo` trait which is used by 45 | //! // the `Disk` type for reading and writing. 46 | //! let block_io = BlockIoAdapter::new(disk_storage.as_mut_slice(), bs); 47 | //! 48 | //! let mut disk = Disk::new(block_io)?; 49 | //! 50 | //! // Manually construct the header and partition entries. 51 | //! let primary_header = GptHeader { 52 | //! header_crc32: Crc32(U32Le::from_u32(0xa4877843)), 53 | //! my_lba: LbaLe::from_u64(1), 54 | //! alternate_lba: LbaLe::from_u64(8191), 55 | //! first_usable_lba: LbaLe::from_u64(34), 56 | //! last_usable_lba: LbaLe::from_u64(8158), 57 | //! disk_guid: guid!("57a7feb6-8cd5-4922-b7bd-c78b0914e870"), 58 | //! partition_entry_lba: LbaLe::from_u64(2), 59 | //! number_of_partition_entries: U32Le::from_u32(128), 60 | //! partition_entry_array_crc32: Crc32(U32Le::from_u32(0x9206adff)), 61 | //! ..Default::default() 62 | //! }; 63 | //! let secondary_header = GptHeader { 64 | //! header_crc32: Crc32(U32Le::from_u32(0xdbeb4c13)), 65 | //! my_lba: LbaLe::from_u64(8191), 66 | //! alternate_lba: LbaLe::from_u64(1), 67 | //! partition_entry_lba: LbaLe::from_u64(8159), 68 | //! ..primary_header 69 | //! }; 70 | //! let partition_entry = GptPartitionEntry { 71 | //! partition_type_guid: GptPartitionType(guid!( 72 | //! "ccf0994f-f7e0-4e26-a011-843e38aa2eac" 73 | //! )), 74 | //! unique_partition_guid: guid!("37c75ffd-8932-467a-9c56-8cf1f0456b12"), 75 | //! starting_lba: LbaLe::from_u64(2048), 76 | //! ending_lba: LbaLe::from_u64(4096), 77 | //! attributes: Default::default(), 78 | //! name: "hello world!".parse().unwrap(), 79 | //! }; 80 | //! 81 | //! // Create a buffer the length of one block. A `Vec` is used here, 82 | //! // but any mutable byte slice with the right length will do. 83 | //! let mut block_buf = vec![0u8; bs.to_usize().unwrap()]; 84 | //! 85 | //! // Write out the protective MBR and GPT headers. Note that without 86 | //! // the protective MBR, some tools won't recognize the disk as GPT. 87 | //! disk.write_protective_mbr(&mut block_buf)?; 88 | //! disk.write_primary_gpt_header(&primary_header, &mut block_buf)?; 89 | //! disk.write_secondary_gpt_header(&secondary_header, &mut block_buf)?; 90 | //! 91 | //! // Construct the partition entry array. 92 | //! let layout = primary_header.get_partition_entry_array_layout().unwrap(); 93 | //! let mut bytes = 94 | //! vec![0; layout.num_bytes_rounded_to_block_as_usize(bs).unwrap()]; 95 | //! let mut entry_array = 96 | //! GptPartitionEntryArray::new(layout, bs, &mut bytes).unwrap(); 97 | //! *entry_array.get_partition_entry_mut(0).unwrap() = partition_entry; 98 | //! 99 | //! // Write the primary partition entry array. 100 | //! disk.write_gpt_partition_entry_array(&entry_array)?; 101 | //! 102 | //! // Write the secondary partition entry array. 103 | //! entry_array.set_start_lba(secondary_header.partition_entry_lba.into()); 104 | //! disk.write_gpt_partition_entry_array(&entry_array)?; 105 | //! 106 | //! // Ensure all writes are flushed. This is not needed with the slice 107 | //! // backend, but is good practice for "real" IO. (The disk will also 108 | //! // flush when dropped, but any errors at that point are ignored.) 109 | //! disk.flush()?; 110 | //! 111 | //! # Ok::<(), gpt_disk_io::DiskError>(()) 112 | //! ``` 113 | //! 114 | //! [`File`]: std::fs::File 115 | //! [`Read`]: std::io::Read 116 | //! [`Seek`]: std::io::Seek 117 | //! [`Write`]: std::io::Write 118 | //! [`EFI_BLOCK_IO_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/13_Protocols_Media_Access.html#block-i-o-protocol 119 | 120 | #![cfg_attr(not(feature = "std"), no_std)] 121 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 122 | #![warn(missing_docs)] 123 | #![warn(trivial_casts)] 124 | #![warn(trivial_numeric_casts)] 125 | #![warn(unreachable_pub)] 126 | #![warn(unsafe_code)] 127 | #![warn(clippy::pedantic)] 128 | #![warn(clippy::as_conversions)] 129 | #![allow(clippy::missing_errors_doc)] 130 | #![allow(clippy::missing_panics_doc)] 131 | 132 | #[cfg(feature = "alloc")] 133 | extern crate alloc; 134 | 135 | mod block_io; 136 | mod disk; 137 | 138 | // Re-export dependencies. 139 | pub use gpt_disk_types; 140 | 141 | pub use block_io::slice_block_io::SliceBlockIoError; 142 | pub use block_io::{BlockIo, BlockIoAdapter}; 143 | pub use disk::{Disk, DiskError}; 144 | 145 | #[cfg(feature = "std")] 146 | pub use block_io::std_block_io::ReadWriteSeek; 147 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use core::fmt::{Debug, Display}; 10 | use core::hash::Hash; 11 | use gpt_disk_types::{ 12 | guid, Crc32, GptHeader, GptPartitionEntry, GptPartitionType, LbaLe, U32Le, 13 | }; 14 | use std::collections::hash_map::DefaultHasher; 15 | 16 | #[allow(dead_code)] 17 | pub fn check_derives() 18 | where 19 | T: Clone 20 | + Copy 21 | + Debug 22 | + Default 23 | + Display 24 | + Eq 25 | + PartialEq 26 | + Hash 27 | + Ord 28 | + PartialOrd, 29 | { 30 | let a = T::default(); 31 | 32 | // PartialEq 33 | assert_eq!(a, a); 34 | 35 | // Clone / Copy 36 | assert_eq!(a, a.clone()); 37 | let c: T = a; 38 | assert_eq!(a, c); 39 | 40 | // PartialOrd 41 | assert!(a >= a); 42 | 43 | // Debug/Display 44 | assert!(!format!("{a:?}").is_empty()); 45 | let _ = format!("{a}"); 46 | 47 | // Hash 48 | let mut hasher = DefaultHasher::new(); 49 | a.hash(&mut hasher); 50 | } 51 | 52 | pub fn create_primary_header() -> GptHeader { 53 | GptHeader { 54 | header_crc32: Crc32(U32Le::from_u32(0xa4877843)), 55 | my_lba: LbaLe::from_u64(1), 56 | alternate_lba: LbaLe::from_u64(8191), 57 | first_usable_lba: LbaLe::from_u64(34), 58 | last_usable_lba: LbaLe::from_u64(8158), 59 | disk_guid: guid!("57a7feb6-8cd5-4922-b7bd-c78b0914e870"), 60 | partition_entry_lba: LbaLe::from_u64(2), 61 | number_of_partition_entries: U32Le::from_u32(128), 62 | partition_entry_array_crc32: Crc32(U32Le::from_u32(0x9206adff)), 63 | ..Default::default() 64 | } 65 | } 66 | 67 | #[allow(dead_code)] 68 | pub fn create_secondary_header() -> GptHeader { 69 | GptHeader { 70 | header_crc32: Crc32(U32Le::from_u32(0xdbeb4c13)), 71 | my_lba: LbaLe::from_u64(8191), 72 | alternate_lba: LbaLe::from_u64(1), 73 | partition_entry_lba: LbaLe::from_u64(8159), 74 | ..create_primary_header() 75 | } 76 | } 77 | 78 | #[allow(dead_code)] 79 | pub fn create_partition_entry() -> GptPartitionEntry { 80 | GptPartitionEntry { 81 | partition_type_guid: GptPartitionType(guid!( 82 | "ccf0994f-f7e0-4e26-a011-843e38aa2eac" 83 | )), 84 | unique_partition_guid: guid!("37c75ffd-8932-467a-9c56-8cf1f0456b12"), 85 | starting_lba: LbaLe::from_u64(2048), 86 | ending_lba: LbaLe::from_u64(4096), 87 | attributes: Default::default(), 88 | name: "hello world!".parse().unwrap(), 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_block.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{BlockSize, Lba, LbaLe, LbaRangeInclusive, U64Le}; 13 | 14 | #[test] 15 | fn test_lba() { 16 | check_derives::(); 17 | } 18 | 19 | #[test] 20 | fn test_lba_le() { 21 | check_derives::(); 22 | 23 | assert_eq!(LbaLe::from(Lba(123)), LbaLe(U64Le::from_u64(123))); 24 | } 25 | 26 | #[test] 27 | fn test_lba_range_inclusive() { 28 | check_derives::(); 29 | 30 | let block_size = BlockSize::new(512).unwrap(); 31 | 32 | // Invalid range. 33 | assert!(LbaRangeInclusive::new(Lba(2), Lba(1)).is_none()); 34 | 35 | // Valid range. 36 | let range = LbaRangeInclusive::new(Lba(1), Lba(2)).unwrap(); 37 | assert_eq!(range.start(), 1); 38 | assert_eq!(range.end(), 2); 39 | assert_eq!( 40 | range.to_byte_range(block_size).unwrap(), 41 | 512..=512 + 512 + (512 - 1) 42 | ); 43 | assert_eq!(range.to_string(), "1..=2"); 44 | 45 | // Test conversion from byte range. 46 | 47 | // Valid. 48 | assert_eq!( 49 | range, 50 | LbaRangeInclusive::from_byte_range( 51 | 512..=512 + 512 + (512 - 1), 52 | block_size 53 | ) 54 | .unwrap() 55 | ); 56 | // Start is not on a block boundary. 57 | assert!(LbaRangeInclusive::from_byte_range( 58 | 514..=512 + 512 + (512 - 1), 59 | block_size 60 | ) 61 | .is_none()); 62 | // End is not on a block boundary. 63 | assert!(LbaRangeInclusive::from_byte_range( 64 | 512..=512 + 512 + (512 - 2), 65 | block_size 66 | ) 67 | .is_none()); 68 | } 69 | 70 | #[test] 71 | fn test_block_size() { 72 | check_derives::(); 73 | 74 | assert_eq!(BlockSize::new(512).unwrap().to_u64(), 512); 75 | assert!(BlockSize::new(0).is_none()); 76 | assert!(BlockSize::new(511).is_none()); 77 | 78 | assert_eq!(BlockSize::from_usize(512).unwrap().to_u64(), 512); 79 | assert!(BlockSize::from_usize(0).is_none()); 80 | 81 | assert_eq!(BlockSize::default().to_u32(), 512); 82 | assert_eq!(BlockSize::default().to_u64(), 512); 83 | } 84 | 85 | #[test] 86 | fn test_block_size_is_multiple() { 87 | assert!(BlockSize::BS_512.is_multiple_of_block_size(0)); 88 | assert!(BlockSize::BS_512.is_multiple_of_block_size(512)); 89 | assert!(BlockSize::BS_512.is_multiple_of_block_size(1024)); 90 | 91 | assert!(!BlockSize::BS_512.is_multiple_of_block_size(1023)); 92 | assert!(!BlockSize::BS_512.is_multiple_of_block_size(1025)); 93 | } 94 | 95 | #[test] 96 | #[should_panic] 97 | fn test_block_size_is_multiple_panic() { 98 | let _ = BlockSize::BS_512.is_multiple_of_block_size(u128::MAX); 99 | } 100 | 101 | #[test] 102 | fn test_block_size_assert_valid_block_buffer() { 103 | BlockSize::BS_512.assert_valid_block_buffer(&[0; 512]); 104 | } 105 | 106 | #[test] 107 | #[should_panic] 108 | fn test_block_size_assert_valid_block_buffer_painc() { 109 | BlockSize::BS_512.assert_valid_block_buffer(&[0; 513]); 110 | } 111 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_block_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_io::{BlockIo, BlockIoAdapter, SliceBlockIoError}; 13 | use gpt_disk_types::{BlockSize, Lba}; 14 | 15 | #[cfg(feature = "std")] 16 | use { 17 | gpt_disk_io::ReadWriteSeek, 18 | std::fs::{self, OpenOptions}, 19 | std::io::Write, 20 | }; 21 | 22 | #[test] 23 | fn test_block_io_adapter() { 24 | let mut bio = BlockIoAdapter::new(123, BlockSize::BS_512); 25 | assert_eq!(bio.block_size(), BlockSize::BS_512); 26 | assert_eq!(bio.storage(), &123); 27 | assert_eq!(bio.storage_mut(), &mut 123); 28 | let data: u32 = bio.take_storage(); 29 | assert_eq!(data, 123); 30 | } 31 | 32 | #[test] 33 | fn test_slice_block_io_error() { 34 | check_derives::(); 35 | 36 | assert_eq!( 37 | SliceBlockIoError::Overflow.to_string(), 38 | "numeric overflow occurred" 39 | ); 40 | assert_eq!( 41 | SliceBlockIoError::ReadOnly.to_string(), 42 | "attempted to write to a read-only byte slice" 43 | ); 44 | assert_eq!( 45 | SliceBlockIoError::OutOfBounds { 46 | start_lba: Lba(1), 47 | length_in_bytes: 2 48 | } 49 | .to_string(), 50 | "out of bounds: start_lba=1, length_in_bytes=2", 51 | ); 52 | } 53 | 54 | fn get_read_data() -> Vec { 55 | let mut data = vec![0; 512 * 3]; 56 | 57 | // Write data to the beginning and end of the first two blocks. 58 | data[0] = 1; 59 | data[511] = 2; 60 | data[512] = 3; 61 | data[1023] = 4; 62 | 63 | data 64 | } 65 | 66 | fn check_read(storage: S) -> S 67 | where 68 | BlockIoAdapter: BlockIo, 69 | { 70 | let mut bio = BlockIoAdapter::new(storage, BlockSize::BS_512); 71 | assert_eq!(bio.num_blocks().unwrap(), 3); 72 | assert_eq!(BlockIo::block_size(&bio), BlockSize::BS_512); 73 | 74 | // Read first block. 75 | let mut buf = vec![0; 512]; 76 | bio.read_blocks(Lba(0), &mut buf) 77 | .expect("read_blocks failed"); 78 | assert_eq!(buf[0], 1); 79 | assert_eq!(buf[511], 2); 80 | 81 | // Read second block. 82 | bio.read_blocks(Lba(1), &mut buf) 83 | .expect("read_blocks failed"); 84 | assert_eq!(buf[0], 3); 85 | assert_eq!(buf[511], 4); 86 | 87 | // Only three blocks. 88 | assert!(bio.read_blocks(Lba(3), &mut buf).is_err()); 89 | 90 | // Read two blocks at once. 91 | let mut buf = vec![0; 1024]; 92 | bio.read_blocks(Lba(0), &mut buf) 93 | .expect("read_blocks failed"); 94 | assert_eq!(buf[0], 1); 95 | assert_eq!(buf[511], 2); 96 | assert_eq!(buf[512], 3); 97 | assert_eq!(buf[1023], 4); 98 | 99 | bio.take_storage() 100 | } 101 | 102 | fn check_write(storage: S, get_bytes: G) 103 | where 104 | BlockIoAdapter: BlockIo, 105 | G: Fn(&BlockIoAdapter) -> Vec, 106 | { 107 | let mut bio = BlockIoAdapter::new(storage, BlockSize::BS_512); 108 | assert_eq!(bio.num_blocks().unwrap(), 3); 109 | 110 | let mut buf = vec![0; 512]; 111 | 112 | // Write first block. 113 | buf[0] = 5; 114 | buf[511] = 6; 115 | bio.write_blocks(Lba(0), &buf).unwrap(); 116 | 117 | // Write second block. 118 | buf[0] = 7; 119 | buf[511] = 8; 120 | bio.write_blocks(Lba(1), &buf).unwrap(); 121 | bio.flush().unwrap(); 122 | 123 | // Check write output. 124 | let mut expected = vec![0; 512 * 3]; 125 | expected[0] = 5; 126 | expected[511] = 6; 127 | expected[512] = 7; 128 | expected[1023] = 8; 129 | assert_eq!(get_bytes(&bio), expected); 130 | 131 | // Write two blocks at once, at an offset of one block. 132 | let mut buf = vec![0; 1024]; 133 | buf[0] = 9; 134 | buf[511] = 10; 135 | buf[512] = 11; 136 | buf[1023] = 12; 137 | bio.write_blocks(Lba(1), &buf).unwrap(); 138 | bio.flush().unwrap(); 139 | 140 | // Check write output. 141 | expected[512] = 9; 142 | expected[1023] = 10; 143 | expected[1024] = 11; 144 | expected[1535] = 12; 145 | assert_eq!(get_bytes(&bio), expected); 146 | } 147 | 148 | fn check_read_and_write(storage: S, get_bytes: G) 149 | where 150 | BlockIoAdapter: BlockIo, 151 | G: Fn(&BlockIoAdapter) -> Vec, 152 | { 153 | let storage = check_read(storage); 154 | check_write(storage, get_bytes); 155 | } 156 | 157 | #[test] 158 | fn test_block_io_slice_read() { 159 | let data = get_read_data(); 160 | let storage: &[u8] = &data; 161 | 162 | check_read(storage); 163 | } 164 | 165 | #[test] 166 | #[should_panic] 167 | fn test_block_io_slice_write() { 168 | let data = get_read_data(); 169 | let storage: &[u8] = &data; 170 | 171 | check_write(storage, |bio| bio.storage().to_vec()); 172 | } 173 | 174 | #[test] 175 | fn test_block_io_mut_slice() { 176 | let mut data = get_read_data(); 177 | let storage: &mut [u8] = &mut data; 178 | 179 | check_read_and_write(storage, |bio| bio.storage().to_vec()); 180 | } 181 | 182 | #[cfg(feature = "alloc")] 183 | #[test] 184 | fn test_block_io_vec() { 185 | let storage: Vec = get_read_data(); 186 | check_read_and_write(storage, |bio| bio.storage().to_vec()); 187 | } 188 | 189 | #[cfg(feature = "std")] 190 | #[test] 191 | fn test_block_io_file() { 192 | let path = "/tmp/test_block_io_std_1.bin"; 193 | let mut file = OpenOptions::new() 194 | .read(true) 195 | .write(true) 196 | .create(true) 197 | .open(path) 198 | .unwrap(); 199 | file.write_all(&get_read_data()).unwrap(); 200 | 201 | check_read_and_write(file, |_| fs::read(path).unwrap()); 202 | 203 | fs::remove_file(path).unwrap(); 204 | } 205 | 206 | #[cfg(feature = "std")] 207 | #[test] 208 | fn test_block_io_dyn_readwriteseek() { 209 | let path = "/tmp/test_block_io_std_2.bin"; 210 | let mut file = OpenOptions::new() 211 | .read(true) 212 | .write(true) 213 | .create(true) 214 | .open(path) 215 | .unwrap(); 216 | file.write_all(&get_read_data()).unwrap(); 217 | 218 | let storage: &mut dyn ReadWriteSeek = &mut file; 219 | check_read_and_write(storage, |_| fs::read(path).unwrap()); 220 | 221 | fs::remove_file(path).unwrap(); 222 | } 223 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_crc32.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{Crc32, U32Le}; 13 | 14 | #[test] 15 | fn test_crc32_display() { 16 | check_derives::(); 17 | 18 | let crc = Crc32(U32Le([0x12, 0x34, 0x56, 0x78])); 19 | assert_eq!(format!("{crc:#x}"), "0x78563412"); 20 | assert_eq!(format!("{crc}"), "0x78563412"); 21 | } 22 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_disk.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::{ 12 | create_partition_entry, create_primary_header, create_secondary_header, 13 | }; 14 | use gpt_disk_io::{BlockIo, BlockIoAdapter, Disk}; 15 | use gpt_disk_types::{BlockSize, GptPartitionEntryArray}; 16 | 17 | #[cfg(feature = "std")] 18 | use std::fs::{self, File, OpenOptions}; 19 | 20 | struct SparseChunk { 21 | offset: usize, 22 | data: [u8; 16], 23 | } 24 | 25 | impl SparseChunk { 26 | const fn new(offset: usize, data: [u8; 16]) -> Self { 27 | Self { offset, data } 28 | } 29 | } 30 | 31 | #[rustfmt::skip] 32 | const SPARSE_DISK: &'static [SparseChunk] = &[ 33 | // Test data generated as follows: 34 | // 35 | // truncate --size 4MiB disk.bin 36 | // sgdisk disk.bin \ 37 | // --disk-guid=57a7feb6-8cd5-4922-b7bd-c78b0914e870 \ 38 | // --new=1:2048:4096 \ 39 | // --change-name='1:hello world!' \ 40 | // --partition-guid=1:37c75ffd-8932-467a-9c56-8cf1f0456b12 \ 41 | // --typecode=1:ccf0994f-f7e0-4e26-a011-843e38aa2eac 42 | // hexdump -ve '"SparseChunk::new(0x%_ax, [" 16/1 "%u," "]),\n"' disk.bin \ 43 | // | grep -v '\[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\]' 44 | SparseChunk::new(0x1c0, [2,0,238,130,2,0,1,0,0,0,255,31,0,0,0,0,]), 45 | SparseChunk::new(0x1f0, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,170,]), 46 | SparseChunk::new(0x200, [69,70,73,32,80,65,82,84,0,0,1,0,92,0,0,0,]), 47 | SparseChunk::new(0x210, [67,120,135,164,0,0,0,0,1,0,0,0,0,0,0,0,]), 48 | SparseChunk::new(0x220, [255,31,0,0,0,0,0,0,34,0,0,0,0,0,0,0,]), 49 | SparseChunk::new(0x230, [222,31,0,0,0,0,0,0,182,254,167,87,213,140,34,73,]), 50 | SparseChunk::new(0x240, [183,189,199,139,9,20,232,112,2,0,0,0,0,0,0,0,]), 51 | SparseChunk::new(0x250, [128,0,0,0,128,0,0,0,255,173,6,146,0,0,0,0,]), 52 | SparseChunk::new(0x400, [79,153,240,204,224,247,38,78,160,17,132,62,56,170,46,172,]), 53 | SparseChunk::new(0x410, [253,95,199,55,50,137,122,70,156,86,140,241,240,69,107,18,]), 54 | SparseChunk::new(0x420, [0,8,0,0,0,0,0,0,0,16,0,0,0,0,0,0,]), 55 | SparseChunk::new(0x430, [0,0,0,0,0,0,0,0,104,0,101,0,108,0,108,0,]), 56 | SparseChunk::new(0x440, [111,0,32,0,119,0,111,0,114,0,108,0,100,0,33,0,]), 57 | SparseChunk::new(0x3fbe00, [79,153,240,204,224,247,38,78,160,17,132,62,56,170,46,172,]), 58 | SparseChunk::new(0x3fbe10, [253,95,199,55,50,137,122,70,156,86,140,241,240,69,107,18,]), 59 | SparseChunk::new(0x3fbe20, [0,8,0,0,0,0,0,0,0,16,0,0,0,0,0,0,]), 60 | SparseChunk::new(0x3fbe30, [0,0,0,0,0,0,0,0,104,0,101,0,108,0,108,0,]), 61 | SparseChunk::new(0x3fbe40, [111,0,32,0,119,0,111,0,114,0,108,0,100,0,33,0,]), 62 | SparseChunk::new(0x3ffe00, [69,70,73,32,80,65,82,84,0,0,1,0,92,0,0,0,]), 63 | SparseChunk::new(0x3ffe10, [19,76,235,219,0,0,0,0,255,31,0,0,0,0,0,0,]), 64 | SparseChunk::new(0x3ffe20, [1,0,0,0,0,0,0,0,34,0,0,0,0,0,0,0,]), 65 | SparseChunk::new(0x3ffe30, [222,31,0,0,0,0,0,0,182,254,167,87,213,140,34,73,]), 66 | SparseChunk::new(0x3ffe40, [183,189,199,139,9,20,232,112,223,31,0,0,0,0,0,0,]), 67 | SparseChunk::new(0x3ffe50, [128,0,0,0,128,0,0,0,255,173,6,146,0,0,0,0,]), 68 | ]; 69 | 70 | fn load_test_disk() -> Vec { 71 | let mut disk = vec![0; 4 * 1024 * 1024]; 72 | for chunk in SPARSE_DISK { 73 | let end = chunk.offset + chunk.data.len(); 74 | disk[chunk.offset..end].copy_from_slice(&chunk.data); 75 | } 76 | disk 77 | } 78 | 79 | fn test_disk_read(block_io: Io) 80 | where 81 | Io: BlockIo, 82 | { 83 | let bs = BlockSize::BS_512; 84 | let mut block_buf = vec![0u8; bs.to_usize().unwrap()]; 85 | let mut disk = Disk::new(block_io).unwrap(); 86 | 87 | let primary_header = disk.read_primary_gpt_header(&mut block_buf).unwrap(); 88 | assert_eq!(primary_header, create_primary_header()); 89 | 90 | let secondary_header = 91 | disk.read_secondary_gpt_header(&mut block_buf).unwrap(); 92 | assert_eq!(secondary_header, create_secondary_header()); 93 | 94 | let expected_partition_entry = create_partition_entry(); 95 | 96 | let check_partition_entry_array = |disk: &mut Disk, layout| { 97 | // First use the iter interface. 98 | { 99 | let mut block_buf = vec![0u8; bs.to_usize().unwrap()]; 100 | let mut iter = disk 101 | .gpt_partition_entry_array_iter(layout, &mut block_buf) 102 | .unwrap(); 103 | let entry = iter.next().unwrap().unwrap(); 104 | assert_eq!(entry, expected_partition_entry); 105 | assert!(entry.is_used()); 106 | 107 | let entry = iter.next().unwrap().unwrap(); 108 | assert!(!entry.is_used()); 109 | } 110 | 111 | // Then check the whole array. 112 | let mut array_buf = vec![0u8; bs.to_usize().unwrap() * 34]; 113 | let array = disk 114 | .read_gpt_partition_entry_array(layout, &mut array_buf) 115 | .unwrap(); 116 | let entry = *array.get_partition_entry(0).unwrap(); 117 | assert_eq!(entry, expected_partition_entry); 118 | assert!(entry.is_used()); 119 | 120 | let entry = *array.get_partition_entry(1).unwrap(); 121 | assert!(!entry.is_used()); 122 | }; 123 | 124 | // Check the primary partition entry array. 125 | check_partition_entry_array( 126 | &mut disk, 127 | primary_header.get_partition_entry_array_layout().unwrap(), 128 | ); 129 | 130 | // Check the secondary partition entry array. 131 | check_partition_entry_array( 132 | &mut disk, 133 | secondary_header.get_partition_entry_array_layout().unwrap(), 134 | ); 135 | } 136 | 137 | fn test_disk_write(block_io: Io) 138 | where 139 | Io: BlockIo, 140 | { 141 | let bs = BlockSize::BS_512; 142 | let mut block_buf = vec![0u8; bs.to_usize().unwrap()]; 143 | let mut disk = Disk::new(block_io).unwrap(); 144 | 145 | let primary_header = create_primary_header(); 146 | let secondary_header = create_secondary_header(); 147 | let partition_entry = create_partition_entry(); 148 | 149 | disk.write_protective_mbr(&mut block_buf).unwrap(); 150 | disk.write_primary_gpt_header(&primary_header, &mut block_buf) 151 | .unwrap(); 152 | disk.write_secondary_gpt_header(&secondary_header, &mut block_buf) 153 | .unwrap(); 154 | 155 | let layout = primary_header.get_partition_entry_array_layout().unwrap(); 156 | let mut bytes = 157 | vec![0; layout.num_bytes_rounded_to_block_as_usize(bs).unwrap()]; 158 | let mut entry_array = 159 | GptPartitionEntryArray::new(layout, bs, &mut bytes).unwrap(); 160 | *entry_array.get_partition_entry_mut(0).unwrap() = partition_entry; 161 | disk.write_gpt_partition_entry_array(&entry_array).unwrap(); 162 | 163 | entry_array.set_start_lba(secondary_header.partition_entry_lba.into()); 164 | disk.write_gpt_partition_entry_array(&entry_array).unwrap(); 165 | 166 | disk.flush().unwrap(); 167 | } 168 | 169 | fn test_with_slice(test_disk: &[u8]) { 170 | test_disk_read(BlockIoAdapter::new(test_disk, BlockSize::BS_512)); 171 | } 172 | 173 | fn test_with_mut_slice(test_disk: &[u8]) { 174 | let mut contents = test_disk.to_vec(); 175 | 176 | // Test read. 177 | test_disk_read(BlockIoAdapter::new( 178 | contents.as_mut_slice(), 179 | BlockSize::BS_512, 180 | )); 181 | 182 | // Test write. 183 | let mut new_contents = vec![0; contents.len()]; 184 | test_disk_write(BlockIoAdapter::new( 185 | new_contents.as_mut_slice(), 186 | BlockSize::BS_512, 187 | )); 188 | assert_eq!(contents, new_contents); 189 | } 190 | 191 | #[cfg(feature = "std")] 192 | fn test_with_file(test_disk: &[u8]) { 193 | let path = "tmp_test_disk_file.bin"; 194 | fs::write(path, test_disk).unwrap(); 195 | 196 | let test_disk_file = File::open(path).unwrap(); 197 | 198 | // Test read. 199 | test_disk_read(BlockIoAdapter::new(test_disk_file, BlockSize::BS_512)); 200 | fs::remove_file(path).unwrap(); 201 | 202 | // Test write. 203 | fs::write(path, vec![0; 4 * 1024 * 1024]).unwrap(); 204 | let new_disk_file = OpenOptions::new() 205 | .read(true) 206 | .write(true) 207 | .open(path) 208 | .unwrap(); 209 | test_disk_write(BlockIoAdapter::new(new_disk_file, BlockSize::BS_512)); 210 | assert_eq!(fs::read(path).unwrap(), test_disk); 211 | fs::remove_file(path).unwrap(); 212 | } 213 | 214 | #[test] 215 | #[cfg_attr(miri, ignore)] 216 | fn test_disk() { 217 | let test_disk = load_test_disk(); 218 | 219 | test_with_slice(&test_disk); 220 | test_with_mut_slice(&test_disk); 221 | 222 | #[cfg(feature = "std")] 223 | test_with_file(&test_disk); 224 | } 225 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_guid.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{guid, Guid, GuidFromStrError}; 13 | 14 | #[test] 15 | fn test_guid() { 16 | check_derives::(); 17 | 18 | let guid = Guid::new( 19 | 0x01234567_u32.to_le_bytes(), 20 | 0x89ab_u16.to_le_bytes(), 21 | 0xcdef_u16.to_le_bytes(), 22 | 0x01, 23 | 0x23, 24 | [0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], 25 | ); 26 | 27 | // To byte array. 28 | assert_eq!( 29 | guid.to_bytes(), 30 | [ 31 | 0x67, 0x45, 0x23, 0x01, 0xab, 0x89, 0xef, 0xcd, 0x01, 0x23, 0x45, 32 | 0x67, 0x89, 0xab, 0xcd, 0xef 33 | ] 34 | ); 35 | 36 | // Formatting. 37 | assert_eq!( 38 | guid.to_ascii_hex_lower(), 39 | *b"01234567-89ab-cdef-0123-456789abcdef" 40 | ); 41 | assert_eq!(guid.to_string(), "01234567-89ab-cdef-0123-456789abcdef"); 42 | 43 | // Parsing. 44 | assert_eq!( 45 | "01234567-89ab-cdef-0123-456789abcdef" 46 | .parse::() 47 | .unwrap(), 48 | guid 49 | ); 50 | assert_eq!( 51 | Guid::try_parse("01234567-89ab-cdef-0123-456789abcdef").unwrap(), 52 | guid 53 | ); 54 | 55 | // Macro. 56 | assert_eq!(guid!("01234567-89ab-cdef-0123-456789abcdef"), guid); 57 | } 58 | 59 | #[test] 60 | fn test_guid_error() { 61 | check_derives::(); 62 | } 63 | 64 | /// Inner module that only imports the `guid!` macro. 65 | mod inner { 66 | use gpt_disk_types::guid; 67 | 68 | /// Test that the `guid!` macro works without importing anything 69 | /// else. 70 | #[test] 71 | fn test_guid_macro_paths() { 72 | guid!("01234567-89ab-cdef-0123-456789abcdef"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_header.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::{check_derives, create_primary_header}; 12 | use gpt_disk_types::{ 13 | Crc32, GptHeader, GptHeaderRevision, GptHeaderSignature, 14 | GptPartitionEntryArrayLayout, GptPartitionEntrySize, 15 | GptPartitionEntrySizeError, Lba, U32Le, 16 | }; 17 | 18 | #[test] 19 | fn test_signature() { 20 | check_derives::(); 21 | 22 | assert_eq!( 23 | GptHeaderSignature::EFI_COMPATIBLE_PARTITION_TABLE_HEADER.to_u64(), 24 | 0x5452415020494645 25 | ); 26 | } 27 | 28 | #[test] 29 | fn test_revision() { 30 | check_derives::(); 31 | assert_eq!(GptHeaderRevision::VERSION_1_0.0.to_u32(), 0x00010000); 32 | assert_eq!(GptHeaderRevision::VERSION_1_0.major(), 1); 33 | assert_eq!(GptHeaderRevision::VERSION_1_0.minor(), 0); 34 | 35 | let rev = GptHeaderRevision(U32Le::from_u32(0x1234_5678)); 36 | assert_eq!(rev.major(), 0x1234); 37 | assert_eq!(rev.minor(), 0x5678); 38 | } 39 | 40 | #[test] 41 | fn test_header_signature() { 42 | let header = create_primary_header(); 43 | assert!(header.is_signature_valid()); 44 | } 45 | 46 | #[test] 47 | fn test_header_crc32() { 48 | let mut header = create_primary_header(); 49 | assert_eq!( 50 | header.calculate_header_crc32(), 51 | Crc32(U32Le::from_u32(0xa4877843)) 52 | ); 53 | 54 | header.update_header_crc32(); 55 | assert_eq!(header.header_crc32, Crc32(U32Le::from_u32(0xa4877843))); 56 | } 57 | 58 | #[test] 59 | fn test_header_impls() { 60 | check_derives::(); 61 | 62 | let mut header = create_primary_header(); 63 | 64 | assert_eq!(header.to_string(), "GptHeader { signature: Signature(\"EFI PART\"), revision: 0x00010000, header_size: 92, header_crc32: 0xa4877843, my_lba: 1, alternate_lba: 8191, first_usable_lba: 34, last_usable_lba: 8158, disk_guid: 57a7feb6-8cd5-4922-b7bd-c78b0914e870, partition_entry_lba: 2, number_of_partition_entries: 128, size_of_partition_entry: 128, partition_entry_array_crc32: 0x9206adff }"); 65 | 66 | // Test invalid signature. 67 | header.signature.0 .0[0] = 0xef; 68 | assert!(header.to_string().starts_with( 69 | "GptHeader { signature: Signature(Invalid: 0x54524150204946ef)," 70 | )); 71 | } 72 | 73 | #[test] 74 | fn test_partition_entry_size() { 75 | check_derives::(); 76 | check_derives::(); 77 | 78 | assert_eq!(GptPartitionEntrySize::new(128).unwrap().to_u32(), 128); 79 | assert_eq!(GptPartitionEntrySize::default().to_u32(), 128); 80 | assert!(GptPartitionEntrySize::new(0).is_err()); 81 | assert!(GptPartitionEntrySize::new(64).is_err()); 82 | assert!(GptPartitionEntrySize::new(130).is_err()); 83 | } 84 | 85 | #[test] 86 | fn test_header_partition_layout() { 87 | let mut header = create_primary_header(); 88 | 89 | header.size_of_partition_entry = U32Le::from_u32(256); 90 | assert_eq!( 91 | header.get_partition_entry_array_layout().unwrap(), 92 | GptPartitionEntryArrayLayout { 93 | start_lba: Lba(2), 94 | entry_size: GptPartitionEntrySize::new(256).unwrap(), 95 | num_entries: 128 96 | } 97 | ); 98 | 99 | header.size_of_partition_entry = U32Le::from_u32(64); 100 | assert!(header.get_partition_entry_array_layout().is_err()); 101 | } 102 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_layout.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use gpt_disk_types::{GptHeader, GptPartitionEntry, Guid, MasterBootRecord}; 10 | use std::mem; 11 | 12 | #[test] 13 | fn test_layouts() { 14 | assert_eq!(mem::size_of::(), 16); 15 | assert_eq!(mem::align_of::(), 4); 16 | 17 | assert_eq!(mem::size_of::(), 92); 18 | assert_eq!(mem::align_of::(), 1); 19 | 20 | assert_eq!(mem::size_of::(), 128); 21 | assert_eq!(mem::align_of::(), 1); 22 | 23 | assert_eq!(mem::size_of::(), 512); 24 | assert_eq!(mem::align_of::(), 1); 25 | } 26 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_mbr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{ 13 | Chs, DiskGeometry, Lba, MasterBootRecord, MbrPartitionRecord, U32Le, 14 | }; 15 | 16 | #[test] 17 | fn test_chs() { 18 | check_derives::(); 19 | 20 | assert_eq!( 21 | Chs::from_lba(Lba(8191), DiskGeometry::UNKNOWN) 22 | .unwrap() 23 | .as_tuple(), 24 | (0, 130, 2) 25 | ); 26 | 27 | // Out of range errors. 28 | assert!(Chs::new(0xf000, 1, 1).is_none()); 29 | assert!(Chs::new(1, 1, 0xf0).is_none()); 30 | } 31 | 32 | #[test] 33 | fn test_disk_geometry() { 34 | check_derives::(); 35 | } 36 | 37 | #[test] 38 | fn test_mbr() { 39 | check_derives::(); 40 | 41 | let mut mbr = MasterBootRecord { 42 | boot_strap_code: [0; 440], 43 | unique_mbr_disk_signature: [0x12, 0x34, 0x56, 0x78], 44 | unknown: [0x12, 0x34], 45 | partitions: [ 46 | MbrPartitionRecord { 47 | boot_indicator: 0x12, 48 | start_chs: Chs::new(1, 2, 3).unwrap(), 49 | os_indicator: 0xab, 50 | end_chs: Chs::new(4, 5, 6).unwrap(), 51 | starting_lba: U32Le::from_u32(123), 52 | size_in_lba: U32Le::from_u32(456), 53 | }, 54 | MbrPartitionRecord::default(), 55 | MbrPartitionRecord::default(), 56 | MbrPartitionRecord::default(), 57 | ], 58 | signature: [0x12, 0x34], 59 | }; 60 | let expected = "MasterBootRecord { 61 | boot_strap_code: [0; 440], 62 | unique_mbr_disk_signature: 0x78563412, 63 | unknown: 3412, 64 | partitions: [MbrPartitionRecord { 65 | boot_indicator: 0x12, 66 | start_chs: CHS=1/2/3, 67 | os_indicator: 0xab, 68 | end_chs: CHS=4/5/6, 69 | starting_lba: 123, 70 | size_in_lba: 456 }, 71 | MbrPartitionRecord { 72 | boot_indicator: 0x0, 73 | start_chs: CHS=0/0/0, 74 | os_indicator: 0x0, 75 | end_chs: CHS=0/0/0, 76 | starting_lba: 0, 77 | size_in_lba: 0 }, 78 | MbrPartitionRecord { 79 | boot_indicator: 0x0, 80 | start_chs: CHS=0/0/0, 81 | os_indicator: 0x0, 82 | end_chs: CHS=0/0/0, 83 | starting_lba: 0, 84 | size_in_lba: 0 }, 85 | MbrPartitionRecord { 86 | boot_indicator: 0x0, 87 | start_chs: CHS=0/0/0, 88 | os_indicator: 0x0, 89 | end_chs: CHS=0/0/0, 90 | starting_lba: 0, 91 | size_in_lba: 0 }], 92 | signature: 0x3412 93 | }"; 94 | assert_eq!(mbr.to_string(), expected.replace('\n', " ")); 95 | 96 | mbr.boot_strap_code[0] = 1; 97 | assert!(mbr 98 | .to_string() 99 | .starts_with("MasterBootRecord { boot_strap_code: ,")); 100 | } 101 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_num.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use gpt_disk_types::{U16Le, U32Le, U64Le}; 10 | 11 | #[test] 12 | fn test_u16le() { 13 | let mut v = U16Le::from_u16(123); 14 | assert_eq!(v.to_u16(), 123); 15 | v.set(0xabc); 16 | assert_eq!(v.to_u16(), 0xabc); 17 | assert_eq!(format!("{v:x?}"), "abc"); 18 | assert_eq!(format!("{v}"), "2748"); 19 | } 20 | 21 | #[test] 22 | fn test_u32le() { 23 | let mut v = U32Le::from_u32(123); 24 | assert_eq!(v.to_u32(), 123); 25 | v.set(0xabc); 26 | assert_eq!(v.to_u32(), 0xabc); 27 | assert_eq!(format!("{v:x?}"), "abc"); 28 | assert_eq!(format!("{v}"), "2748"); 29 | } 30 | 31 | #[test] 32 | fn test_u64le() { 33 | let mut v = U64Le::from_u64(123); 34 | assert_eq!(v.to_u64(), 123); 35 | v.set(0xabc); 36 | assert_eq!(v.to_u64(), 0xabc); 37 | assert_eq!(format!("{v:x?}"), "abc"); 38 | assert_eq!(format!("{v}"), "2748"); 39 | } 40 | 41 | #[test] 42 | fn test_num_display() { 43 | let n = U16Le::from_u16(0x1234); 44 | assert_eq!(format!("{n} {n:x} {n:#x}"), "4660 1234 0x1234"); 45 | 46 | let n = U32Le::from_u32(0x1234_5678); 47 | assert_eq!(format!("{n} {n:x} {n:#x}"), "305419896 12345678 0x12345678"); 48 | 49 | let n = U64Le::from_u64(0x1234_5678_9abc_def0); 50 | assert_eq!( 51 | format!("{n} {n:x} {n:#x}"), 52 | "1311768467463790320 123456789abcdef0 0x123456789abcdef0" 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_partition_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{ 13 | BlockSize, GptPartitionEntryArrayLayout, GptPartitionEntrySize, Lba, 14 | }; 15 | 16 | #[test] 17 | fn test_partition_entry_array_layout() { 18 | check_derives::(); 19 | 20 | let layout = GptPartitionEntryArrayLayout { 21 | start_lba: Lba(2), 22 | entry_size: GptPartitionEntrySize::new(256).unwrap(), 23 | num_entries: 128, 24 | }; 25 | assert_eq!(layout.num_blocks(BlockSize::BS_512).unwrap(), 64); 26 | assert_eq!( 27 | layout 28 | .num_bytes_rounded_to_block(BlockSize::BS_512) 29 | .unwrap(), 30 | 64 * 512 31 | ); 32 | assert_eq!(layout.num_bytes_exact().unwrap(), 256 * 128); 33 | 34 | let bs767 = BlockSize::new(512 + 256 - 1).unwrap(); 35 | assert_eq!(layout.num_blocks(bs767).unwrap(), 43); 36 | assert_eq!(layout.num_bytes_rounded_to_block(bs767).unwrap(), 43 * 767); 37 | assert_eq!(layout.num_bytes_exact().unwrap(), 256 * 128); 38 | 39 | assert_eq!(layout.num_blocks_as_usize(bs767).unwrap(), 43); 40 | assert_eq!( 41 | layout.num_bytes_rounded_to_block_as_usize(bs767).unwrap(), 42 | 43 * 767 43 | ); 44 | assert_eq!(layout.num_bytes_exact_as_usize().unwrap(), 256 * 128); 45 | } 46 | -------------------------------------------------------------------------------- /gpt_disk_io/tests/test_partition_entry.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod common; 10 | 11 | use common::check_derives; 12 | use gpt_disk_types::{ 13 | GptPartitionAttributes, GptPartitionEntry, GptPartitionName, 14 | GptPartitionType, Guid, U16Le, U64Le, 15 | }; 16 | 17 | #[test] 18 | fn test_partition_type() { 19 | check_derives::(); 20 | 21 | assert_eq!(GptPartitionType::UNUSED.to_string(), "UNUSED"); 22 | 23 | let guid = Guid::new( 24 | 0x01234567_u32.to_le_bytes(), 25 | 0x89ab_u16.to_le_bytes(), 26 | 0xcdef_u16.to_le_bytes(), 27 | 0x01, 28 | 0x23, 29 | [0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], 30 | ); 31 | assert_eq!( 32 | GptPartitionType(guid).to_string(), 33 | "01234567-89ab-cdef-0123-456789abcdef" 34 | ); 35 | } 36 | 37 | #[test] 38 | fn test_required_partition_attribute() { 39 | check_derives::(); 40 | 41 | let bits = 0x0000_0000_0000_0001u64; 42 | let mut attr = GptPartitionAttributes(U64Le::from_u64(bits)); 43 | 44 | assert!(attr.required_partition()); 45 | 46 | attr.update_required_partition(false); 47 | assert!(!attr.required_partition()); 48 | attr.update_required_partition(true); 49 | assert!(attr.required_partition()); 50 | } 51 | 52 | #[test] 53 | fn test_no_block_io_protocol_attribute() { 54 | let bits = 0x0000_0000_0000_0002u64; 55 | let mut attr = GptPartitionAttributes(U64Le::from_u64(bits)); 56 | 57 | assert!(attr.no_block_io_protocol()); 58 | 59 | attr.update_no_block_io_protocol(false); 60 | assert!(!attr.no_block_io_protocol()); 61 | attr.update_no_block_io_protocol(true); 62 | assert!(attr.no_block_io_protocol()); 63 | } 64 | 65 | #[test] 66 | fn test_legacy_bios_bootable_attribute() { 67 | let bits = 0x0000_0000_0000_0004u64; 68 | let mut attr = GptPartitionAttributes(U64Le::from_u64(bits)); 69 | 70 | assert!(attr.legacy_bios_bootable()); 71 | 72 | attr.update_legacy_bios_bootable(false); 73 | assert!(!attr.legacy_bios_bootable()); 74 | attr.update_legacy_bios_bootable(true); 75 | assert!(attr.legacy_bios_bootable()); 76 | } 77 | 78 | #[test] 79 | fn test_type_specific_attributes() { 80 | let bits = 0x1234_0000_0000_0000u64; 81 | let mut attr = GptPartitionAttributes(U64Le::from_u64(bits)); 82 | 83 | assert_eq!(attr.type_specific_attributes().to_u16(), 0x1234); 84 | 85 | attr.update_type_specific_attributes(U16Le::from_u16(0xabcd)); 86 | assert_eq!(attr.type_specific_attributes().to_u16(), 0xabcd); 87 | } 88 | 89 | #[test] 90 | fn test_partition_attribute_display() { 91 | let mut attr = GptPartitionAttributes(U64Le::from_u64(0)); 92 | assert_eq!(attr.to_string(), "(empty)"); 93 | 94 | attr.update_required_partition(true); 95 | assert_eq!(attr.to_string(), "required_partition (1)"); 96 | 97 | attr.update_required_partition(false); 98 | attr.update_no_block_io_protocol(true); 99 | assert_eq!(attr.to_string(), "no_block_io_protocol (2)"); 100 | 101 | attr.update_no_block_io_protocol(false); 102 | attr.update_legacy_bios_bootable(true); 103 | assert_eq!(attr.to_string(), "legacy_bios_bootable (4)"); 104 | 105 | attr.update_required_partition(true); 106 | assert_eq!( 107 | attr.to_string(), 108 | "required_partition (1), legacy_bios_bootable (4)" 109 | ); 110 | 111 | attr.update_type_specific_attributes(U16Le::from_u16(0x1234)); 112 | assert_eq!( 113 | attr.to_string(), 114 | "required_partition (1), legacy_bios_bootable (4), type_specific(0x1234)" 115 | ); 116 | } 117 | 118 | #[test] 119 | fn test_partition_name() { 120 | check_derives::(); 121 | 122 | let mut name = GptPartitionName::default(); 123 | 124 | assert!(name.is_empty()); 125 | 126 | // "abc" 127 | name.set_char(0, 'a').unwrap(); 128 | name.set_char(1, 'b').unwrap(); 129 | name.set_char(2, 'c').unwrap(); 130 | 131 | // 0xd800 is an invalid character. 132 | name.0[6] = 0x00; 133 | name.0[7] = 0xd8; 134 | 135 | assert_eq!(name.to_string(), "abc�"); 136 | assert!(!name.is_empty()); 137 | 138 | // Test with no trailing null. 139 | for i in 0..name.0.len() { 140 | name.0[i] = if (i % 2) == 0 { b'a' } else { 0 }; 141 | } 142 | assert_eq!(name.to_string(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 143 | } 144 | 145 | #[test] 146 | fn test_partition_entry() { 147 | check_derives::(); 148 | } 149 | -------------------------------------------------------------------------------- /gpt_disk_types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.16.1 2 | 3 | * MSRV increased to 1.81. 4 | * The `Error` trait is now unconditionally implemented for all error types. 5 | 6 | # 0.16.0 7 | 8 | * Bump MSRV to 1.68. 9 | * Add `BlockSize::is_multiple_of_block_size`. 10 | * Add `BlockSize::assert_valid_block_buffer`. 11 | * Add `LbaRangeInclusive::num_blocks`. 12 | * Documentation improvements. 13 | 14 | # 0.15.0 15 | 16 | * Updated to latest `uguid`. 17 | * The `Guid` type's alignment has changed from 1 to 4. 18 | * The `GptHeader` and `GptPartitionEntry` structs are now `repr(C, packed)` 19 | instead of just `repr(C)`. This is due to the alignment of `Guid` changing 20 | from 1 to 4. 21 | * Copied the license files into each package so that the archives on 22 | crates.io include them. 23 | 24 | # 0.14.0 25 | 26 | * Relax version requirement for `bytemuck`. 27 | * When the `std` feature is enabled, also enable it in `uguid`. 28 | * Added a `bytemuck` feature (disabled by default). All of the code that 29 | depends on the `bytemuck` crate is now gated behind this feature. 30 | * Enable `doc_auto_cfg` on docs.rs. 31 | 32 | # 0.13.0 33 | 34 | * Allow the MIT license to be used in addition to Apache-2.0. 35 | 36 | # 0.12.0 37 | 38 | * Updated to latest `uguid`. 39 | 40 | # 0.11.0 41 | 42 | * Updated to latest `uguid`. 43 | * Remove re-export of `bytemuck` dependency. 44 | 45 | # 0.10.0 46 | 47 | * Updated to latest `uguid`. 48 | 49 | # 0.9.0 50 | 51 | * Add dependency on `uguid`. The `Guid` and `GuidFromStrError` types, as 52 | well as the `guid!` macro, are now provided by `uguid` and re-exported 53 | by `gpt_disk_types`. 54 | 55 | # 0.8.0 56 | 57 | * Added `Guid::to_bytes` 58 | * Added `LbaRangeInclusive::num_bytes`. 59 | 60 | # 0.7.0 61 | 62 | * Added `Guid::try_parse`. This is a `const` method that is functionally 63 | equivalent to `Guid::from_str`. 64 | * Added `guid!` macro for creating a `Guid` from a string at compile time. 65 | * Added several `GptPartitionType` constants: `EFI_SYSTEM`, 66 | `LEGACY_MBR`, `BASIC_DATA`, `CHROME_OS_KERNEL`, and 67 | `CHROME_OS_ROOT_FS`. 68 | 69 | # 0.6.0 70 | 71 | * Renamed the `BlockSize` constants: `B512`→`BS_512` and 72 | `B4096`→`BS_4096`. The previous names were a little hard to read. 73 | * Updated documentation. 74 | -------------------------------------------------------------------------------- /gpt_disk_types/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [package] 10 | name = "gpt_disk_types" 11 | version = "0.16.1" 12 | categories = ["data-structures", "embedded", "no-std"] 13 | description = "GPT (GUID Partition Table) disk data types no_std library" 14 | keywords = ["disk", "gpt", "no_std", "partition", "uefi"] 15 | 16 | edition.workspace = true 17 | rust-version.workspace = true 18 | license.workspace = true 19 | repository.workspace = true 20 | 21 | [dependencies] 22 | bytemuck = { workspace = true, features = ["derive"], optional = true } 23 | crc = "3.0.0" 24 | uguid = { version = "2.1.0", path = "../uguid" } 25 | ucs2 = "0.3.2" 26 | 27 | [features] 28 | # See module docstring in src/lib.rs for details of what these features do. 29 | bytemuck = ["dep:bytemuck", "uguid/bytemuck"] 30 | std = ["uguid/std"] 31 | 32 | [package.metadata.docs.rs] 33 | all-features = true 34 | rustdoc-args = ["--cfg", "docsrs"] 35 | -------------------------------------------------------------------------------- /gpt_disk_types/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2022 Google LLC 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /gpt_disk_types/README.md: -------------------------------------------------------------------------------- 1 | # `gpt_disk_types` 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/gpt_disk_types)](https://crates.io/crates/gpt_disk_types) 4 | [![Docs.rs](https://docs.rs/gpt_disk_types/badge.svg)](https://docs.rs/gpt_disk_types) 5 | 6 | `no_std` library providing [GPT] (GUID Partition Table) data 7 | structures. The types are designed to ensure correct endianness 8 | regardless of host platform, and can be used even with corrupted input 9 | data. 10 | 11 | See also the [`gpt_disk_io`] package. 12 | 13 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 14 | [`gpt_disk_io`]: https://crates.io/crates/gpt_disk_io 15 | 16 | ## Features 17 | 18 | No features are enabled by default. 19 | 20 | * `bytemuck`: Implements bytemuck's `Pod` and `Zeroable` traits for many 21 | of the types in this crate. Also enables some methods that rely on 22 | byte access. 23 | * `std`: Currently has no effect. 24 | 25 | ## Minimum Supported Rust Version (MSRV) 26 | 27 | The current MSRV is 1.81. 28 | 29 | ## License 30 | 31 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) 32 | or [MIT license](LICENSE-MIT) at your option. 33 | 34 | ## Disclaimer 35 | 36 | This project is not an official Google project. It is not supported by 37 | Google and Google specifically disclaims all warranties as to its quality, 38 | merchantability, or fitness for a particular purpose. 39 | -------------------------------------------------------------------------------- /gpt_disk_types/src/block.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::U64Le; 10 | use core::fmt::{self, Display, Formatter}; 11 | use core::num::{NonZeroU32, TryFromIntError}; 12 | use core::ops::RangeInclusive; 13 | 14 | #[cfg(feature = "bytemuck")] 15 | use bytemuck::{Pod, Zeroable}; 16 | 17 | /// Logical block address. 18 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 19 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 20 | #[repr(transparent)] 21 | pub struct Lba(pub u64); 22 | 23 | impl Lba { 24 | /// Convert to a plain [`u64`]. 25 | #[must_use] 26 | pub fn to_u64(self) -> u64 { 27 | self.0 28 | } 29 | } 30 | 31 | impl PartialEq for Lba { 32 | fn eq(&self, other: &u64) -> bool { 33 | self.0 == *other 34 | } 35 | } 36 | 37 | impl Display for Lba { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 39 | self.0.fmt(f) 40 | } 41 | } 42 | 43 | impl TryFrom for usize { 44 | type Error = TryFromIntError; 45 | 46 | fn try_from(lba: Lba) -> Result { 47 | lba.0.try_into() 48 | } 49 | } 50 | 51 | impl From for Lba { 52 | fn from(lba: LbaLe) -> Self { 53 | Self(lba.to_u64()) 54 | } 55 | } 56 | 57 | /// Logical block address stored as a [`U64Le`]. 58 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 59 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 60 | #[repr(transparent)] 61 | pub struct LbaLe(pub U64Le); 62 | 63 | impl LbaLe { 64 | /// Create a logical block address from a [`u64`]. 65 | #[must_use] 66 | pub const fn from_u64(v: u64) -> Self { 67 | Self(U64Le::from_u64(v)) 68 | } 69 | 70 | /// Get the logical block address as a [`u64`]. 71 | #[must_use] 72 | pub const fn to_u64(self) -> u64 { 73 | self.0.to_u64() 74 | } 75 | } 76 | 77 | impl Display for LbaLe { 78 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 79 | self.to_u64().fmt(f) 80 | } 81 | } 82 | 83 | impl From for LbaLe { 84 | fn from(lba: Lba) -> Self { 85 | Self::from_u64(lba.0) 86 | } 87 | } 88 | 89 | /// Inclusive range of logical block addresses. 90 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 91 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 92 | #[repr(C)] 93 | pub struct LbaRangeInclusive { 94 | start: Lba, 95 | end: Lba, 96 | } 97 | 98 | impl LbaRangeInclusive { 99 | /// Create an LBA range. The end address must be greater than or 100 | /// equal to the start address. 101 | #[must_use] 102 | pub const fn new(start: Lba, end: Lba) -> Option { 103 | if end.0 >= start.0 { 104 | Some(LbaRangeInclusive { start, end }) 105 | } else { 106 | None 107 | } 108 | } 109 | 110 | /// Starting LBA (inclusive). 111 | #[must_use] 112 | pub const fn start(self) -> Lba { 113 | self.start 114 | } 115 | 116 | /// Ending LBA (inclusive). 117 | #[must_use] 118 | pub const fn end(self) -> Lba { 119 | self.end 120 | } 121 | 122 | /// Create an LBA range from the corresponding byte range for the 123 | /// given block size. 124 | /// 125 | /// The byte range must correspond precisely to block bounds, with 126 | /// the start byte at the beginning of a block and the end byte at 127 | /// the end of a block 128 | /// 129 | /// # Examples 130 | /// 131 | /// ``` 132 | /// use gpt_disk_types::{BlockSize, LbaRangeInclusive}; 133 | /// 134 | /// let bs = BlockSize::BS_512; 135 | /// let r = LbaRangeInclusive::from_byte_range(512..=1535, bs).unwrap(); 136 | /// assert_eq!(r.start().0, 1); 137 | /// assert_eq!(r.end().0, 2); 138 | #[must_use] 139 | pub fn from_byte_range( 140 | byte_range: RangeInclusive, 141 | block_size: BlockSize, 142 | ) -> Option { 143 | let start_byte = byte_range.start(); 144 | let end_byte_plus_1 = byte_range.end().checked_add(1)?; 145 | let block_size = block_size.to_u64(); 146 | 147 | if (start_byte % block_size) != 0 { 148 | return None; 149 | } 150 | if (end_byte_plus_1 % block_size) != 0 { 151 | return None; 152 | } 153 | 154 | let end_lba = (end_byte_plus_1 / block_size).checked_sub(1)?; 155 | 156 | LbaRangeInclusive::new(Lba(start_byte / block_size), Lba(end_lba)) 157 | } 158 | 159 | /// Convert the LBA range to the corresponding byte range for the 160 | /// given block size. 161 | /// 162 | /// # Examples 163 | /// 164 | /// ``` 165 | /// use gpt_disk_types::{BlockSize, Lba, LbaRangeInclusive}; 166 | /// 167 | /// let r = LbaRangeInclusive::new(Lba(1), Lba(2)).unwrap(); 168 | /// let bs = BlockSize::BS_512; 169 | /// assert_eq!(r.to_byte_range(bs).unwrap(), 512..=1535); 170 | /// ``` 171 | #[must_use] 172 | pub fn to_byte_range( 173 | self, 174 | block_size: BlockSize, 175 | ) -> Option> { 176 | let block_size = block_size.to_u64(); 177 | let start_byte = self.start.0.checked_mul(block_size)?; 178 | let end_byte = self 179 | .end 180 | .0 181 | .checked_mul(block_size)? 182 | .checked_add(block_size - 1)?; 183 | Some(start_byte..=end_byte) 184 | } 185 | 186 | /// Get the number of bytes in the LBA range for the given block 187 | /// size. 188 | /// 189 | /// # Examples 190 | /// 191 | /// ``` 192 | /// use gpt_disk_types::{BlockSize, Lba, LbaRangeInclusive}; 193 | /// 194 | /// let r = LbaRangeInclusive::new(Lba(1), Lba(2)).unwrap(); 195 | /// let bs = BlockSize::BS_512; 196 | /// assert_eq!(r.num_bytes(bs).unwrap(), 1024); 197 | /// ``` 198 | #[must_use] 199 | pub fn num_bytes(self, block_size: BlockSize) -> Option { 200 | let r = self.to_byte_range(block_size)?; 201 | r.end().checked_sub(*r.start())?.checked_add(1) 202 | } 203 | 204 | /// Get the number of blocks in the LBA range. 205 | /// 206 | /// # Examples 207 | /// 208 | /// ``` 209 | /// use gpt_disk_types::{Lba, LbaRangeInclusive}; 210 | /// 211 | /// let r = LbaRangeInclusive::new(Lba(1), Lba(2)).unwrap(); 212 | /// assert_eq!(r.num_blocks(), 2); 213 | /// ``` 214 | #[must_use] 215 | pub fn num_blocks(self) -> u64 { 216 | // Add one here since the range is inclusive. 217 | self.end().to_u64() - self.start.to_u64() + 1 218 | } 219 | } 220 | 221 | impl Display for LbaRangeInclusive { 222 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 223 | write!(f, "{}..={}", self.start, self.end) 224 | } 225 | } 226 | 227 | /// Size of a block in bytes. 228 | /// 229 | /// This type enforces some restrictions on the block size: it must be 230 | /// at least 512 bytes and fit within a [`u32`]. 231 | /// 232 | /// # Minimum size 233 | /// 234 | /// The [`MasterBootRecord`] size is 512 bytes and must fit within a 235 | /// block, so the block size must be at least that large. 236 | /// 237 | /// [`MasterBootRecord`]: crate::MasterBootRecord 238 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 239 | #[repr(transparent)] 240 | pub struct BlockSize(NonZeroU32); 241 | 242 | impl BlockSize { 243 | /// 512-byte block size. 244 | pub const BS_512: Self = Self(if let Some(nz) = NonZeroU32::new(512) { 245 | nz 246 | } else { 247 | unreachable!() 248 | }); 249 | 250 | /// 4096-byte block size. 251 | pub const BS_4096: Self = Self(if let Some(nz) = NonZeroU32::new(4096) { 252 | nz 253 | } else { 254 | unreachable!() 255 | }); 256 | 257 | /// Create a `BlockSize`. 258 | #[must_use] 259 | pub const fn new(num_bytes: u32) -> Option { 260 | if let Some(nz) = NonZeroU32::new(num_bytes) { 261 | if num_bytes >= 512 { 262 | Some(Self(nz)) 263 | } else { 264 | None 265 | } 266 | } else { 267 | None 268 | } 269 | } 270 | 271 | /// Create a `BlockSize`. 272 | #[must_use] 273 | pub fn from_usize(num_bytes: usize) -> Option { 274 | Self::new(u32::try_from(num_bytes).ok()?) 275 | } 276 | 277 | /// Get the size in bytes as a [`u32`]. 278 | #[must_use] 279 | pub const fn to_u32(self) -> u32 { 280 | self.0.get() 281 | } 282 | 283 | /// Get the size in bytes as a [`u64`]. 284 | #[allow(clippy::as_conversions)] 285 | #[must_use] 286 | pub const fn to_u64(self) -> u64 { 287 | self.0.get() as u64 288 | } 289 | 290 | /// Get the size in bytes as a [`usize`]. 291 | #[must_use] 292 | pub fn to_usize(self) -> Option { 293 | self.0.get().try_into().ok() 294 | } 295 | 296 | /// Check if `value` is an even multiple of the block size. 297 | /// 298 | /// # Panics 299 | /// 300 | /// Panics if `value` does not fit in a [`u64`]. 301 | #[must_use] 302 | pub fn is_multiple_of_block_size(&self, value: T) -> bool 303 | where 304 | T: TryInto, 305 | { 306 | if let Ok(value) = value.try_into() { 307 | let block_size = self.to_u64(); 308 | (value % block_size) == 0 309 | } else { 310 | panic!("value does not fit in a u64"); 311 | } 312 | } 313 | 314 | /// Assert that the `buffer` size is an even multiple of the block size. 315 | /// 316 | /// # Panics 317 | /// 318 | /// Panics if `buffer.len()` is not an even multiple of the block size. 319 | #[track_caller] 320 | pub fn assert_valid_block_buffer(&self, buffer: &[u8]) { 321 | assert!(self.is_multiple_of_block_size(buffer.len())); 322 | } 323 | } 324 | 325 | impl Default for BlockSize { 326 | fn default() -> Self { 327 | BlockSize::BS_512 328 | } 329 | } 330 | 331 | impl Display for BlockSize { 332 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 333 | write!(f, "{}", self.0) 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /gpt_disk_types/src/crc32.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::U32Le; 10 | use core::fmt::{self, Debug, Display, Formatter, LowerHex}; 11 | 12 | #[cfg(feature = "bytemuck")] 13 | use bytemuck::{Pod, Zeroable}; 14 | 15 | /// 32-bit CRC (cyclic redundancy check). 16 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 17 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 18 | #[repr(transparent)] 19 | pub struct Crc32(pub U32Le); 20 | 21 | // It would be good to replace the long-winded notes above 22 | // `Crc32::ALGORITHM` with something simpler and/or more authoritative 23 | // if possible. 24 | 25 | impl Crc32 { 26 | /// CRC32 algorithm used for GPT: [`crc::CRC_32_ISO_HDLC`] 27 | /// 28 | /// # Notes 29 | /// 30 | /// The UEFI Specification is somewhat vague about the CRC algorithm 31 | /// used. Section 4.2 EFI Table Header says: "Unless otherwise 32 | /// specified, UEFI uses a standard CCITT32 CRC algorithm with a 33 | /// seed polynomial value of 0x04c11db7 for its CRC calculations." 34 | /// There are no further mentions of either "CCITT32" or "04c11db7" 35 | /// in the spec. It's not clear what if any specification CCITT32 36 | /// refers to. 37 | /// 38 | /// The [Catalogue of parametrised CRC algorithms], which is the 39 | /// source of truth for the catalog used by the `crc` crate, has no 40 | /// references to CCITT32, but does have several entries that use 41 | /// the `0x04c11db7` polynomial. Of these, CRC-32/ISO-HDLC appears 42 | /// to be widely used and recommended by ITU-T, which is the 43 | /// successor of CCITT. 44 | /// 45 | /// [Catalogue of parametrised CRC algorithms]: https://reveng.sourceforge.io/crc-catalogue/17plus.htm 46 | pub const ALGORITHM: crc::Algorithm = crc::CRC_32_ISO_HDLC; 47 | } 48 | 49 | impl Display for Crc32 { 50 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 51 | write!(f, "{self:#x}") 52 | } 53 | } 54 | 55 | impl LowerHex for Crc32 { 56 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 57 | LowerHex::fmt(&self.0, f) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /gpt_disk_types/src/header.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::{ 10 | Crc32, GptPartitionEntry, GptPartitionEntryArrayLayout, 11 | GptPartitionEntrySize, GptPartitionEntrySizeError, Guid, LbaLe, U32Le, 12 | U64Le, 13 | }; 14 | use core::fmt::{self, Display, Formatter}; 15 | use core::mem; 16 | 17 | #[cfg(feature = "bytemuck")] 18 | use bytemuck::{bytes_of, Pod, Zeroable}; 19 | 20 | /// GPT header signature. 21 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 22 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 23 | #[repr(transparent)] 24 | pub struct GptHeaderSignature(pub U64Le); 25 | 26 | impl Display for GptHeaderSignature { 27 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 28 | f.write_str("Signature(")?; 29 | if *self == Self::EFI_COMPATIBLE_PARTITION_TABLE_HEADER { 30 | f.write_str("\"EFI PART\"")?; 31 | } else { 32 | write!(f, "Invalid: {:#016x}", self.0)?; 33 | } 34 | f.write_str(")") 35 | } 36 | } 37 | 38 | impl GptHeaderSignature { 39 | /// EFI-compatible partition table header. This is the only valid 40 | /// signature. 41 | pub const EFI_COMPATIBLE_PARTITION_TABLE_HEADER: Self = 42 | Self(U64Le(*b"EFI PART")); 43 | 44 | /// Convert to [`u64`] with the host's endianness. 45 | #[must_use] 46 | pub const fn to_u64(self) -> u64 { 47 | self.0.to_u64() 48 | } 49 | } 50 | 51 | impl Default for GptHeaderSignature { 52 | fn default() -> Self { 53 | Self::EFI_COMPATIBLE_PARTITION_TABLE_HEADER 54 | } 55 | } 56 | 57 | /// GPT header revision. 58 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 59 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 60 | #[repr(transparent)] 61 | pub struct GptHeaderRevision(pub U32Le); 62 | 63 | impl GptHeaderRevision { 64 | /// Version 1.0. This is the only valid revision. 65 | pub const VERSION_1_0: Self = Self(U32Le::from_u32(0x0001_0000)); 66 | 67 | /// Get the major part of the version. 68 | #[allow(clippy::missing_panics_doc)] 69 | #[must_use] 70 | pub fn major(self) -> u16 { 71 | u16::from_le_bytes(self.0 .0[2..4].try_into().unwrap()) 72 | } 73 | 74 | /// Get the minor part of the version. 75 | #[allow(clippy::missing_panics_doc)] 76 | #[must_use] 77 | pub fn minor(self) -> u16 { 78 | u16::from_le_bytes(self.0 .0[0..2].try_into().unwrap()) 79 | } 80 | } 81 | 82 | impl Default for GptHeaderRevision { 83 | fn default() -> Self { 84 | Self::VERSION_1_0 85 | } 86 | } 87 | 88 | impl Display for GptHeaderRevision { 89 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 90 | write!(f, "{:#08x}", self.0) 91 | } 92 | } 93 | 94 | /// GPT header that appears near the start and end of a GPT-formatted disk. 95 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 96 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 97 | #[repr(C, packed)] 98 | pub struct GptHeader { 99 | /// Magic signature for the header. In a valid header this must be 100 | /// [`GptHeaderSignature::EFI_COMPATIBLE_PARTITION_TABLE_HEADER`]. 101 | pub signature: GptHeaderSignature, 102 | 103 | /// Revision number for the header. In a valid header this must be 104 | /// [`GptHeaderRevision::VERSION_1_0`]. 105 | pub revision: GptHeaderRevision, 106 | 107 | /// Size of the header in bytes. In a valid header this must be 108 | /// greater than or equal to 92 bytes, and less than or equal to the 109 | /// block size. 110 | pub header_size: U32Le, 111 | 112 | /// CRC32 checksum of the entire header. When calculating the 113 | /// checksum, this field is included in the checksum as four zero 114 | /// bytes. 115 | pub header_crc32: Crc32, 116 | 117 | /// Reserved bytes. In a valid header these must be all zero. 118 | pub reserved: U32Le, 119 | 120 | /// The LBA that contains this header. 121 | pub my_lba: LbaLe, 122 | 123 | /// The LBA that contains the alternate header. 124 | pub alternate_lba: LbaLe, 125 | 126 | /// First LBA that can be used for the data of a partition in the 127 | /// partition entry array. 128 | pub first_usable_lba: LbaLe, 129 | 130 | /// Last LBA that can be used for the data of a partition in the 131 | /// partition entry array. 132 | pub last_usable_lba: LbaLe, 133 | 134 | /// Unique ID for the disk. 135 | pub disk_guid: Guid, 136 | 137 | /// First LBA of the partition entry array. 138 | pub partition_entry_lba: LbaLe, 139 | 140 | /// Number of partitions in the partition entry array. 141 | pub number_of_partition_entries: U32Le, 142 | 143 | /// Size in bytes of each entry in the partition entry array. 144 | pub size_of_partition_entry: U32Le, 145 | 146 | /// CRC32 checksum of the partition entry array. 147 | pub partition_entry_array_crc32: Crc32, 148 | } 149 | 150 | impl GptHeader { 151 | /// Check if the header's signature matches 152 | /// [`GptHeaderSignature::EFI_COMPATIBLE_PARTITION_TABLE_HEADER`]. 153 | #[must_use] 154 | pub fn is_signature_valid(&self) -> bool { 155 | self.signature 156 | == GptHeaderSignature::EFI_COMPATIBLE_PARTITION_TABLE_HEADER 157 | } 158 | 159 | /// Calculate the header's CRC32 checksum. This returns the checksum 160 | /// but does not update the checksum field in the header. 161 | #[cfg(feature = "bytemuck")] 162 | #[must_use] 163 | pub fn calculate_header_crc32(&self) -> Crc32 { 164 | let crc = crc::Crc::::new(&Crc32::ALGORITHM); 165 | let mut digest = crc.digest(); 166 | digest.update(bytes_of(&self.signature)); 167 | digest.update(bytes_of(&self.revision)); 168 | digest.update(bytes_of(&self.header_size)); 169 | digest.update(&[0u8; 4]); // Zeroes for the `header_crc32` field. 170 | digest.update(bytes_of(&self.reserved)); 171 | digest.update(bytes_of(&self.my_lba)); 172 | digest.update(bytes_of(&self.alternate_lba)); 173 | digest.update(bytes_of(&self.first_usable_lba)); 174 | digest.update(bytes_of(&self.last_usable_lba)); 175 | digest.update(bytes_of(&{ self.disk_guid })); 176 | digest.update(bytes_of(&self.partition_entry_lba)); 177 | digest.update(bytes_of(&self.number_of_partition_entries)); 178 | digest.update(bytes_of(&self.size_of_partition_entry)); 179 | digest.update(bytes_of(&self.partition_entry_array_crc32)); 180 | Crc32(U32Le(digest.finalize().to_le_bytes())) 181 | } 182 | 183 | /// Update the header's CRC32 checksum. 184 | #[cfg(feature = "bytemuck")] 185 | pub fn update_header_crc32(&mut self) { 186 | self.header_crc32 = self.calculate_header_crc32(); 187 | } 188 | 189 | /// Get the [`GptPartitionEntryArrayLayout`] for this header. 190 | pub fn get_partition_entry_array_layout( 191 | &self, 192 | ) -> Result { 193 | Ok(GptPartitionEntryArrayLayout { 194 | start_lba: self.partition_entry_lba.into(), 195 | entry_size: GptPartitionEntrySize::new( 196 | self.size_of_partition_entry.to_u32(), 197 | )?, 198 | num_entries: self.number_of_partition_entries.to_u32(), 199 | }) 200 | } 201 | } 202 | 203 | impl Default for GptHeader { 204 | fn default() -> Self { 205 | Self { 206 | signature: GptHeaderSignature::default(), 207 | revision: GptHeaderRevision::default(), 208 | header_size: U32Le::from_u32( 209 | u32::try_from(mem::size_of::()).unwrap(), 210 | ), 211 | header_crc32: Crc32::default(), 212 | reserved: U32Le::default(), 213 | my_lba: LbaLe::default(), 214 | alternate_lba: LbaLe::default(), 215 | first_usable_lba: LbaLe::default(), 216 | last_usable_lba: LbaLe::default(), 217 | disk_guid: Guid::default(), 218 | partition_entry_lba: LbaLe::default(), 219 | number_of_partition_entries: U32Le::default(), 220 | size_of_partition_entry: U32Le::from_u32( 221 | u32::try_from(mem::size_of::()).unwrap(), 222 | ), 223 | partition_entry_array_crc32: Crc32::default(), 224 | } 225 | } 226 | } 227 | 228 | impl Display for GptHeader { 229 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 230 | write!(f, "GptHeader {{ signature: {}", self.signature)?; 231 | write!(f, ", revision: {:#x}", self.revision.0)?; 232 | write!(f, ", header_size: {}", self.header_size.to_u32())?; 233 | write!(f, ", header_crc32: {:#x}", self.header_crc32)?; 234 | write!(f, ", my_lba: {}", self.my_lba)?; 235 | write!(f, ", alternate_lba: {}", self.alternate_lba)?; 236 | write!(f, ", first_usable_lba: {}", self.first_usable_lba)?; 237 | write!(f, ", last_usable_lba: {}", self.last_usable_lba)?; 238 | write!(f, ", disk_guid: {}", &{ self.disk_guid })?; 239 | write!(f, ", partition_entry_lba: {}", self.partition_entry_lba)?; 240 | write!( 241 | f, 242 | ", number_of_partition_entries: {}", 243 | self.number_of_partition_entries 244 | )?; 245 | write!( 246 | f, 247 | ", size_of_partition_entry: {}", 248 | self.size_of_partition_entry 249 | )?; 250 | write!( 251 | f, 252 | ", partition_entry_array_crc32: {:#x}", 253 | self.partition_entry_array_crc32 254 | )?; 255 | f.write_str(" }") 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /gpt_disk_types/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Library of GPT disk data types. 10 | //! 11 | //! # GPT disk components 12 | //! 13 | //! ```text 14 | //! ┌───┬───────┬─────────────────┬─────────┬───────────────────┬─────────┐ 15 | //! │MBR│Primary│Primary partition│Partition│Secondary partition│Secondary│ 16 | //! │ │header │entry array │data │entry array │header │ 17 | //! └───┴───────┴─────────────────┴─────────┴───────────────────┴─────────┘ 18 | //! ``` 19 | //! 20 | //! 1. The first block of the disk contains a protective MBR. See 21 | //! [`MasterBootRecord::protective_mbr`]. 22 | //! 2. The second block of the disk contains the primary GPT header. See 23 | //! [`GptHeader`]. 24 | //! 3. Additional blocks after the header contain the partition entry 25 | //! array. See [`GptPartitionEntry`] and [`GptPartitionEntryArray`]. 26 | //! 4. At the end of the disk is a secondary GPT header and partition 27 | //! entry array. 28 | //! 29 | //! # Endianness 30 | //! 31 | //! The UEFI Specification specifies that data structures are little 32 | //! endian (section 1.8.1 "Data Structure Descriptions"). Unless 33 | //! otherwise noted, all fields in this library are little endian. This 34 | //! is true even when running the code on a big-endian architecture; the 35 | //! [`U16Le`], [`U32Le`], [`U64Le`], and [`LbaLe`] types help enforce 36 | //! this. The little-endian convention is also used for [`Display`] 37 | //! implementations. This means bytes within each field will appear 38 | //! reversed when compared with a flat hex dump of GPT data. 39 | //! 40 | //! [`Display`]: core::fmt::Display 41 | //! 42 | //! # Features 43 | //! 44 | //! * `bytemuck`: Implements bytemuck's `Pod` and `Zeroable` traits for 45 | //! many of the types in this crate. Also enables some methods that 46 | //! rely on byte access. 47 | //! * `std`: Currently has no effect. 48 | //! 49 | //! # Examples 50 | //! 51 | //! Construct a GPT header: 52 | //! 53 | //! ``` 54 | //! use gpt_disk_types::{guid, Crc32, GptHeader, LbaLe, U32Le}; 55 | //! 56 | //! let header = GptHeader { 57 | //! header_crc32: Crc32(U32Le::from_u32(0xa4877843)), 58 | //! my_lba: LbaLe::from_u64(1), 59 | //! alternate_lba: LbaLe::from_u64(8191), 60 | //! first_usable_lba: LbaLe::from_u64(34), 61 | //! last_usable_lba: LbaLe::from_u64(8158), 62 | //! disk_guid: guid!("57a7feb6-8cd5-4922-b7bd-c78b0914e870"), 63 | //! partition_entry_lba: LbaLe::from_u64(2), 64 | //! number_of_partition_entries: U32Le::from_u32(128), 65 | //! partition_entry_array_crc32: Crc32(U32Le::from_u32(0x9206adff)), 66 | //! ..Default::default() 67 | //! }; 68 | //! ``` 69 | //! 70 | //! Construct a GPT partition entry: 71 | //! 72 | //! ``` 73 | //! use gpt_disk_types::{guid, GptPartitionEntry, GptPartitionType, LbaLe}; 74 | //! 75 | //! let entry = GptPartitionEntry { 76 | //! partition_type_guid: GptPartitionType(guid!( 77 | //! "ccf0994f-f7e0-4e26-a011-843e38aa2eac" 78 | //! )), 79 | //! unique_partition_guid: guid!("37c75ffd-8932-467a-9c56-8cf1f0456b12"), 80 | //! starting_lba: LbaLe::from_u64(2048), 81 | //! ending_lba: LbaLe::from_u64(4096), 82 | //! attributes: Default::default(), 83 | //! name: "hello world!".parse().unwrap(), 84 | //! }; 85 | //! ``` 86 | 87 | #![no_std] 88 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 89 | #![warn(missing_copy_implementations)] 90 | #![warn(missing_debug_implementations)] 91 | #![warn(missing_docs)] 92 | #![warn(trivial_casts)] 93 | #![warn(trivial_numeric_casts)] 94 | #![warn(unreachable_pub)] 95 | #![warn(unsafe_code)] 96 | #![warn(unused_crate_dependencies)] 97 | #![warn(clippy::pedantic)] 98 | #![warn(clippy::as_conversions)] 99 | #![allow(clippy::missing_errors_doc)] 100 | #![allow(clippy::module_name_repetitions)] 101 | 102 | mod block; 103 | mod crc32; 104 | mod header; 105 | mod mbr; 106 | mod num; 107 | mod partition_array; 108 | mod partition_entry; 109 | 110 | // Re-export dependencies. 111 | pub use crc; 112 | pub use ucs2; 113 | pub use uguid::{guid, Guid, GuidFromStrError}; 114 | 115 | pub use block::{BlockSize, Lba, LbaLe, LbaRangeInclusive}; 116 | pub use crc32::Crc32; 117 | pub use header::{GptHeader, GptHeaderRevision, GptHeaderSignature}; 118 | pub use mbr::{Chs, DiskGeometry, MasterBootRecord, MbrPartitionRecord}; 119 | pub use num::{U16Le, U32Le, U64Le}; 120 | pub use partition_array::{ 121 | GptPartitionEntryArray, GptPartitionEntryArrayError, 122 | GptPartitionEntryArrayLayout, 123 | }; 124 | pub use partition_entry::{ 125 | GptPartitionAttributes, GptPartitionEntry, GptPartitionEntrySize, 126 | GptPartitionEntrySizeError, GptPartitionName, GptPartitionNameFromStrError, 127 | GptPartitionNameSetCharError, GptPartitionType, 128 | }; 129 | -------------------------------------------------------------------------------- /gpt_disk_types/src/mbr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::num::format_u8_slice_lower_hex_le; 10 | use crate::{Lba, U32Le}; 11 | use core::fmt::{self, Display, Formatter}; 12 | 13 | #[cfg(feature = "bytemuck")] 14 | use bytemuck::{Pod, Zeroable}; 15 | 16 | /// Legacy disk geometry used for converting between [`Lba`] and [`Chs`]. 17 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 18 | pub struct DiskGeometry { 19 | /// Heads per cylinder. 20 | pub heads_per_cylinder: u32, 21 | 22 | /// Sectors per track. 23 | pub sectors_per_track: u32, 24 | } 25 | 26 | impl DiskGeometry { 27 | /// These are the same fallback values that gdisk uses when the disk 28 | /// geometry isn't known. 29 | pub const UNKNOWN: Self = Self { 30 | heads_per_cylinder: 255, 31 | sectors_per_track: 63, 32 | }; 33 | } 34 | 35 | impl Default for DiskGeometry { 36 | fn default() -> Self { 37 | Self::UNKNOWN 38 | } 39 | } 40 | 41 | impl Display for DiskGeometry { 42 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 43 | write!( 44 | f, 45 | "HPC={}/SPT={}", 46 | self.heads_per_cylinder, self.sectors_per_track 47 | ) 48 | } 49 | } 50 | 51 | /// Legacy MBR cylinder/head/sector. 52 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 53 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 54 | #[repr(C)] 55 | pub struct Chs(pub [u8; 3]); 56 | 57 | impl Chs { 58 | /// Get the 10 cylinder bits as a [`u16`]. 59 | #[must_use] 60 | pub fn cylinder(self) -> u16 { 61 | let h = self.0[1] & 0b1100_0000; 62 | let l = self.0[2]; 63 | (u16::from(h) << 2) | u16::from(l) 64 | } 65 | 66 | /// Get the 8 head bits as a [`u8`]. 67 | #[must_use] 68 | pub fn head(self) -> u8 { 69 | self.0[0] 70 | } 71 | 72 | /// Get the 6 sector bits as a [`u8`]. 73 | #[must_use] 74 | pub fn sector(self) -> u8 { 75 | self.0[1] & 0b0011_1111 76 | } 77 | 78 | /// Get a tuple of `(cylinder, head, sector)`. 79 | #[must_use] 80 | pub fn as_tuple(self) -> (u16, u8, u8) { 81 | (self.cylinder(), self.head(), self.sector()) 82 | } 83 | 84 | /// Create a new `Chs`. Returns `None` if `cylinder` can't fit in 10 85 | /// bits, or if `sector` can't fit in 6 bits. 86 | #[allow(clippy::missing_panics_doc)] 87 | #[must_use] 88 | pub fn new(cylinder: u16, head: u8, sector: u8) -> Option { 89 | if (cylinder & 0b1111_1100_0000_0000) != 0 { 90 | return None; 91 | } 92 | if (sector & 0b1100_0000) != 0 { 93 | return None; 94 | } 95 | Some(Chs([ 96 | head, 97 | u8::try_from((cylinder & 0b11_0000_0000) >> 2).unwrap() 98 | | (sector & 0b0011_1111), 99 | u8::try_from(cylinder & 0xff).unwrap(), 100 | ])) 101 | } 102 | 103 | /// Convert LBA to CHS address. Returns `None` if the LBA value 104 | /// cannot fit in the CHS format. 105 | #[must_use] 106 | pub fn from_lba(lba: Lba, geom: DiskGeometry) -> Option { 107 | let lba = u32::try_from(lba.0).ok()?; 108 | 109 | // https://en.wikipedia.org/wiki/Logical_block_addressing 110 | let cylinder = lba / (geom.heads_per_cylinder * geom.sectors_per_track); 111 | let head = (lba / geom.sectors_per_track) % geom.heads_per_cylinder; 112 | let sector = (lba % geom.sectors_per_track) + 1; 113 | 114 | Self::new( 115 | cylinder.try_into().ok()?, 116 | head.try_into().ok()?, 117 | sector.try_into().ok()?, 118 | ) 119 | } 120 | } 121 | 122 | impl Display for Chs { 123 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 124 | write!( 125 | f, 126 | "CHS={}/{}/{}", 127 | self.cylinder(), 128 | self.head(), 129 | self.sector() 130 | ) 131 | } 132 | } 133 | 134 | /// Legacy MBR partition record. 135 | /// 136 | /// See Table 5-2 "Legacy MBR Partition Record" in the UEFI Specification. 137 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 138 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 139 | #[repr(C)] 140 | pub struct MbrPartitionRecord { 141 | /// A value of `0x80` indicates this is a legacy bootable 142 | /// partition. Any other value indicates it is not bootable. UEFI 143 | /// firmware does not use this field's value. 144 | pub boot_indicator: u8, 145 | 146 | /// Start of the partition. UEFI firmware does not use this field's 147 | /// value. 148 | pub start_chs: Chs, 149 | 150 | /// Type of partition. A value of `0xef` defines a system 151 | /// partition. A value of `0xee` is used in a protective MBR to 152 | /// define a fake partition covering the entire disk. Other values 153 | /// are possible but not defined by the UEFI Specification. 154 | /// 155 | /// See section 5.2.2 "OS Types" in the UEFI Specification. 156 | pub os_indicator: u8, 157 | 158 | /// End of the partition. UEFI firmware does not use this field's 159 | /// value. 160 | pub end_chs: Chs, 161 | 162 | /// Starting LBA of the partition. UEFI firmware uses this field to 163 | /// determine the start of the partition. 164 | pub starting_lba: U32Le, 165 | 166 | /// Size of the partition in logical blocks. UEFI firmware uses this 167 | /// field to determine the size of the partition. 168 | pub size_in_lba: U32Le, 169 | } 170 | 171 | impl Display for MbrPartitionRecord { 172 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 173 | f.write_str("MbrPartitionRecord { ")?; 174 | write!(f, "boot_indicator: {:#x}", self.boot_indicator)?; 175 | write!(f, ", start_chs: {}", self.start_chs)?; 176 | write!(f, ", os_indicator: {:#x}", self.os_indicator)?; 177 | write!(f, ", end_chs: {}", self.end_chs)?; 178 | write!(f, ", starting_lba: {}", self.starting_lba)?; 179 | write!(f, ", size_in_lba: {}", self.size_in_lba)?; 180 | f.write_str(" }") 181 | } 182 | } 183 | 184 | /// Legacy master boot record. 185 | /// 186 | /// See Table 5-1 "Legacy MBR" in the UEFI Specification. 187 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 188 | #[repr(C)] 189 | pub struct MasterBootRecord { 190 | /// Executable code used on non-UEFI systems select a partition and 191 | /// load the first logical block of that partition. 192 | pub boot_strap_code: [u8; 440], 193 | 194 | /// Unique identifier for the disk. This value is not used by UEFI 195 | /// firmware. 196 | pub unique_mbr_disk_signature: [u8; 4], 197 | 198 | /// Reserved field that is not used by UEFI firmware. 199 | pub unknown: [u8; 2], 200 | 201 | /// Four legacy MBR partitions. 202 | pub partitions: [MbrPartitionRecord; 4], 203 | 204 | /// MBR signature, set to `0xaa55`. 205 | pub signature: [u8; 2], 206 | } 207 | 208 | // Manual implementation needed because of the large boot_strap_code 209 | // array field. 210 | impl Default for MasterBootRecord { 211 | fn default() -> Self { 212 | Self { 213 | boot_strap_code: [0; 440], 214 | unique_mbr_disk_signature: [0; 4], 215 | unknown: [0, 2], 216 | partitions: [MbrPartitionRecord::default(); 4], 217 | signature: [0; 2], 218 | } 219 | } 220 | } 221 | 222 | // Manual implementation needed because of the large boot_strap_code 223 | // array field. 224 | #[cfg(feature = "bytemuck")] 225 | #[allow(unsafe_code)] 226 | unsafe impl Pod for MasterBootRecord {} 227 | #[cfg(feature = "bytemuck")] 228 | #[allow(unsafe_code)] 229 | unsafe impl Zeroable for MasterBootRecord {} 230 | 231 | impl MasterBootRecord { 232 | /// Return whether the [`boot_strap_code`] field is all zeros or not. 233 | /// 234 | /// [`boot_strap_code`]: Self::boot_strap_code 235 | #[must_use] 236 | pub fn is_boot_strap_code_zero(&self) -> bool { 237 | self.boot_strap_code.iter().all(|b| *b == 0) 238 | } 239 | 240 | /// Create a protective MBR for the given disk size. 241 | /// 242 | /// See section 5.2.3 "Protective MBR" of the UEFI Specification. 243 | #[must_use] 244 | pub fn protective_mbr(num_blocks: u64) -> Self { 245 | let size_in_lba = u32::try_from(num_blocks).unwrap_or(0xffff_ffff); 246 | 247 | Self { 248 | boot_strap_code: [0; 440], 249 | unique_mbr_disk_signature: [0; 4], 250 | unknown: [0; 2], 251 | partitions: [ 252 | MbrPartitionRecord { 253 | boot_indicator: 0, 254 | // CHS=0,0,2 255 | start_chs: Chs([0, 2, 0]), 256 | os_indicator: 0xee, 257 | end_chs: Chs::from_lba( 258 | Lba(num_blocks - 1), 259 | DiskGeometry::UNKNOWN, 260 | ) 261 | .unwrap_or(Chs([0xff, 0xff, 0xff])), 262 | starting_lba: U32Le::from_u32(1), 263 | size_in_lba: U32Le::from_u32(size_in_lba - 1), 264 | }, 265 | MbrPartitionRecord::default(), 266 | MbrPartitionRecord::default(), 267 | MbrPartitionRecord::default(), 268 | ], 269 | signature: [0x55, 0xaa], 270 | } 271 | } 272 | } 273 | 274 | impl Display for MasterBootRecord { 275 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 276 | f.write_str("MasterBootRecord { boot_strap_code: ")?; 277 | 278 | if self.is_boot_strap_code_zero() { 279 | write!(f, "[0; {}]", self.boot_strap_code.len())?; 280 | } else { 281 | f.write_str("")?; 282 | } 283 | 284 | f.write_str(", unique_mbr_disk_signature: 0x")?; 285 | format_u8_slice_lower_hex_le(f, &self.unique_mbr_disk_signature)?; 286 | 287 | f.write_str(", unknown: ")?; 288 | format_u8_slice_lower_hex_le(f, &self.unknown)?; 289 | 290 | f.write_str(", partitions: [")?; 291 | for (i, partition) in self.partitions.iter().enumerate() { 292 | if i != 0 { 293 | f.write_str(", ")?; 294 | } 295 | partition.fmt(f)?; 296 | } 297 | 298 | f.write_str("], signature: 0x")?; 299 | format_u8_slice_lower_hex_le(f, &self.signature)?; 300 | 301 | f.write_str(" }") 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /gpt_disk_types/src/num.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use core::fmt::{self, Debug, Display, Formatter, LowerHex}; 10 | 11 | #[cfg(feature = "bytemuck")] 12 | use bytemuck::{Pod, Zeroable}; 13 | 14 | /// 16-bit unsigned integer stored as a little-endian. 15 | #[derive(Clone, Copy, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 16 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 17 | #[repr(transparent)] 18 | pub struct U16Le(pub [u8; 2]); 19 | 20 | impl U16Le { 21 | /// Convert to [`u16`] with the host's endianness. 22 | #[must_use] 23 | pub const fn to_u16(self) -> u16 { 24 | u16::from_le_bytes(self.0) 25 | } 26 | 27 | /// Create a `U16Le` from a [`u16`] with the host's endianness. 28 | #[must_use] 29 | pub const fn from_u16(v: u16) -> Self { 30 | Self(v.to_le_bytes()) 31 | } 32 | 33 | /// Update the value to a [`u16`] with the host's endianness. 34 | pub fn set(&mut self, v: u16) { 35 | *self = Self::from_u16(v); 36 | } 37 | } 38 | 39 | impl Debug for U16Le { 40 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 41 | Debug::fmt(&self.to_u16(), f) 42 | } 43 | } 44 | 45 | impl Display for U16Le { 46 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 47 | Display::fmt(&self.to_u16(), f) 48 | } 49 | } 50 | 51 | impl LowerHex for U16Le { 52 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 53 | format_u8_slice_lower_hex_le(f, &self.0) 54 | } 55 | } 56 | 57 | /// 32-bit unsigned integer stored as a little-endian. 58 | #[derive(Clone, Copy, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 59 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 60 | #[repr(transparent)] 61 | pub struct U32Le(pub [u8; 4]); 62 | 63 | impl U32Le { 64 | /// Convert to [`u32`] with the host's endianness. 65 | #[must_use] 66 | pub const fn to_u32(self) -> u32 { 67 | u32::from_le_bytes(self.0) 68 | } 69 | 70 | /// Create a `U32Le` from a [`u32`] with the host's endianness. 71 | #[must_use] 72 | pub const fn from_u32(v: u32) -> Self { 73 | Self(v.to_le_bytes()) 74 | } 75 | 76 | /// Update the value to a [`u32`] with the host's endianness. 77 | pub fn set(&mut self, v: u32) { 78 | *self = Self::from_u32(v); 79 | } 80 | } 81 | 82 | impl Debug for U32Le { 83 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 84 | Debug::fmt(&self.to_u32(), f) 85 | } 86 | } 87 | 88 | impl Display for U32Le { 89 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 90 | Display::fmt(&self.to_u32(), f) 91 | } 92 | } 93 | 94 | impl LowerHex for U32Le { 95 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 96 | format_u8_slice_lower_hex_le(f, &self.0) 97 | } 98 | } 99 | 100 | /// 64-bit unsigned integer stored as a little-endian. 101 | #[derive(Clone, Copy, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 102 | #[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))] 103 | #[repr(transparent)] 104 | pub struct U64Le(pub [u8; 8]); 105 | 106 | impl U64Le { 107 | /// Convert to [`u64`] with the host's endianness. 108 | #[must_use] 109 | pub const fn to_u64(self) -> u64 { 110 | u64::from_le_bytes(self.0) 111 | } 112 | 113 | /// Create a `U64Le` from a [`u64`] with the host's endianness. 114 | #[must_use] 115 | pub const fn from_u64(v: u64) -> Self { 116 | Self(v.to_le_bytes()) 117 | } 118 | 119 | /// Update the value to a [`u64`] with the host's endianness. 120 | pub fn set(&mut self, v: u64) { 121 | *self = Self::from_u64(v); 122 | } 123 | } 124 | 125 | impl Debug for U64Le { 126 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 127 | Debug::fmt(&self.to_u64(), f) 128 | } 129 | } 130 | 131 | impl Display for U64Le { 132 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 133 | Display::fmt(&self.to_u64(), f) 134 | } 135 | } 136 | 137 | impl LowerHex for U64Le { 138 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 139 | format_u8_slice_lower_hex_le(f, &self.0) 140 | } 141 | } 142 | 143 | pub(crate) fn format_u8_slice_lower_hex_le( 144 | f: &mut Formatter<'_>, 145 | s: &[u8], 146 | ) -> fmt::Result { 147 | if f.alternate() { 148 | f.write_str("0x")?; 149 | } 150 | for byte in s.iter().rev() { 151 | write!(f, "{byte:02x}")?; 152 | } 153 | Ok(()) 154 | } 155 | -------------------------------------------------------------------------------- /gpt_disk_types/src/partition_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::{BlockSize, Crc32, GptPartitionEntrySize, Lba, U32Le}; 10 | use core::fmt::{self, Display, Formatter}; 11 | 12 | #[cfg(feature = "bytemuck")] 13 | use { 14 | crate::GptPartitionEntry, 15 | bytemuck::{from_bytes, from_bytes_mut}, 16 | core::mem, 17 | core::ops::Range, 18 | }; 19 | 20 | /// Disk layout of a GPT partition entry array. 21 | #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] 22 | pub struct GptPartitionEntryArrayLayout { 23 | /// First block of the array. 24 | pub start_lba: Lba, 25 | 26 | /// Size in bytes of each entry. 27 | pub entry_size: GptPartitionEntrySize, 28 | 29 | /// Number of entries in the array. 30 | pub num_entries: u32, 31 | } 32 | 33 | impl GptPartitionEntryArrayLayout { 34 | /// Get the number of blocks needed for this layout. Returns `None` 35 | /// if overflow occurs. 36 | #[must_use] 37 | pub fn num_blocks(&self, block_size: BlockSize) -> Option { 38 | let block_size = block_size.to_u64(); 39 | let num_bytes_exact = self.num_bytes_exact()?; 40 | 41 | let mut num_blocks = num_bytes_exact / block_size; 42 | if num_bytes_exact % block_size != 0 { 43 | num_blocks = num_blocks.checked_add(1)?; 44 | } 45 | 46 | Some(num_blocks) 47 | } 48 | 49 | /// Get the number of blocks needed for this layout. Returns `None` 50 | /// if overflow occurs. 51 | #[must_use] 52 | pub fn num_blocks_as_usize(&self, block_size: BlockSize) -> Option { 53 | self.num_blocks(block_size)?.try_into().ok() 54 | } 55 | 56 | /// Get the number of bytes needed for the entries in this layout, 57 | /// ignoring any padding needed at the end to match the block 58 | /// size. This corresponds to the number of bytes that are covered 59 | /// by the [`partition_entry_array_crc32`]. 60 | /// 61 | /// Returns `None` if overflow occurs. 62 | /// 63 | /// [`partition_entry_array_crc32`]: crate::GptHeader::partition_entry_array_crc32 64 | #[must_use] 65 | pub fn num_bytes_exact(&self) -> Option { 66 | let entry_size = self.entry_size.to_u64(); 67 | let num_entries = u64::from(self.num_entries); 68 | entry_size.checked_mul(num_entries) 69 | } 70 | 71 | /// Get the number of bytes needed for the entries in this layout, 72 | /// ignoring any padding needed at the end to match the block 73 | /// size. This corresponds to the number of bytes that are covered 74 | /// by the [`partition_entry_array_crc32`]. 75 | /// 76 | /// Returns `None` if overflow occurs. 77 | /// 78 | /// [`partition_entry_array_crc32`]: crate::GptHeader::partition_entry_array_crc32 79 | #[must_use] 80 | pub fn num_bytes_exact_as_usize(&self) -> Option { 81 | self.num_bytes_exact()?.try_into().ok() 82 | } 83 | 84 | /// Get the number of bytes needed for this layout, rounded up to 85 | /// the nearest block. This is equivalent to [`num_blocks`] * 86 | /// `block_size`. 87 | /// 88 | /// Returns `None` if overflow occurs. 89 | /// 90 | /// [`num_blocks`]: Self::num_blocks 91 | #[must_use] 92 | pub fn num_bytes_rounded_to_block( 93 | &self, 94 | block_size: BlockSize, 95 | ) -> Option { 96 | let num_blocks = self.num_blocks(block_size)?; 97 | num_blocks.checked_mul(block_size.to_u64()) 98 | } 99 | 100 | /// Get the number of bytes needed for this layout, rounded up to 101 | /// the nearest block. This is equivalent to [`num_blocks`] * 102 | /// `block_size`. 103 | /// 104 | /// Returns `None` if overflow occurs. 105 | /// 106 | /// [`num_blocks`]: Self::num_blocks 107 | #[must_use] 108 | pub fn num_bytes_rounded_to_block_as_usize( 109 | &self, 110 | block_size: BlockSize, 111 | ) -> Option { 112 | self.num_bytes_rounded_to_block(block_size)?.try_into().ok() 113 | } 114 | } 115 | 116 | impl Display for GptPartitionEntryArrayLayout { 117 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 118 | write!( 119 | f, 120 | "start_lba={}/entry_size={}/num_entries={}", 121 | self.start_lba, self.entry_size, self.num_entries 122 | ) 123 | } 124 | } 125 | 126 | /// Errors used by [`GptPartitionEntryArray`]. 127 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 128 | pub enum GptPartitionEntryArrayError { 129 | /// The storage buffer is not large enough. It must be at least 130 | /// [`layout.num_bytes_rounded_to_block`] in size. 131 | /// 132 | /// [`layout.num_bytes_rounded_to_block`]: GptPartitionEntryArrayLayout::num_bytes_rounded_to_block 133 | BufferTooSmall, 134 | 135 | /// Numeric overflow occurred. 136 | Overflow, 137 | } 138 | 139 | impl Display for GptPartitionEntryArrayError { 140 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 141 | match self { 142 | Self::BufferTooSmall => f.write_str("storage buffer is too small"), 143 | Self::Overflow => f.write_str("numeric overflow occurred"), 144 | } 145 | } 146 | } 147 | 148 | impl core::error::Error for GptPartitionEntryArrayError {} 149 | 150 | /// Storage for a GPT partition entry array. 151 | #[allow(missing_debug_implementations)] 152 | pub struct GptPartitionEntryArray<'a> { 153 | layout: GptPartitionEntryArrayLayout, 154 | num_bytes_exact: usize, 155 | storage: &'a mut [u8], 156 | } 157 | 158 | impl<'a> GptPartitionEntryArray<'a> { 159 | /// Create a new `GptPartitionEntryArray` with the given 160 | /// `layout`. The length of `storage` must be at least 161 | /// [`layout.num_bytes_rounded_to_block`]. 162 | /// 163 | /// [`layout.num_bytes_rounded_to_block`]: GptPartitionEntryArrayLayout::num_bytes_rounded_to_block 164 | pub fn new( 165 | layout: GptPartitionEntryArrayLayout, 166 | block_size: BlockSize, 167 | storage: &'a mut [u8], 168 | ) -> Result { 169 | let num_bytes_required = layout 170 | .num_bytes_rounded_to_block_as_usize(block_size) 171 | .ok_or(GptPartitionEntryArrayError::Overflow)?; 172 | 173 | let num_bytes_exact = layout 174 | .num_bytes_exact_as_usize() 175 | .ok_or(GptPartitionEntryArrayError::Overflow)?; 176 | 177 | let storage = storage 178 | .get_mut(..num_bytes_required) 179 | .ok_or(GptPartitionEntryArrayError::BufferTooSmall)?; 180 | 181 | Ok(Self { 182 | layout, 183 | num_bytes_exact, 184 | storage, 185 | }) 186 | } 187 | 188 | /// Get a reference to the storage buffer. 189 | #[must_use] 190 | pub fn storage(&self) -> &[u8] { 191 | self.storage 192 | } 193 | 194 | /// Get a mutable reference to the storage buffer. 195 | #[must_use] 196 | pub fn storage_mut(&mut self) -> &mut [u8] { 197 | self.storage 198 | } 199 | 200 | /// Get the partition entry array layout. 201 | #[must_use] 202 | pub fn layout(&self) -> &GptPartitionEntryArrayLayout { 203 | &self.layout 204 | } 205 | 206 | /// Change the partition entry array's start [`Lba`]. 207 | pub fn set_start_lba(&mut self, start_lba: Lba) { 208 | self.layout.start_lba = start_lba; 209 | } 210 | 211 | #[cfg(feature = "bytemuck")] 212 | fn get_entry_byte_range(&self, index: u32) -> Option> { 213 | if index >= self.layout.num_entries { 214 | return None; 215 | } 216 | 217 | let start = usize::try_from( 218 | u64::from(index) * u64::from(self.layout.entry_size.to_u32()), 219 | ) 220 | .ok()?; 221 | Some(start..start + mem::size_of::()) 222 | } 223 | 224 | /// Get a partition entry reference. The `index` is zero-based. 225 | #[cfg(feature = "bytemuck")] 226 | #[must_use] 227 | pub fn get_partition_entry( 228 | &self, 229 | index: u32, 230 | ) -> Option<&GptPartitionEntry> { 231 | Some(from_bytes(&self.storage[self.get_entry_byte_range(index)?])) 232 | } 233 | 234 | /// Get a mutable partition entry reference. The `index` is zero-based. 235 | #[cfg(feature = "bytemuck")] 236 | #[must_use] 237 | pub fn get_partition_entry_mut( 238 | &mut self, 239 | index: u32, 240 | ) -> Option<&mut GptPartitionEntry> { 241 | let range = self.get_entry_byte_range(index)?; 242 | Some(from_bytes_mut(&mut self.storage[range])) 243 | } 244 | 245 | /// Calculate the CRC32 checksum for the partition entry array. The 246 | /// return value can then be set in the 247 | /// [`GptHeader::partition_entry_array_crc32`] field. 248 | /// 249 | /// [`GptHeader::partition_entry_array_crc32`]: crate::GptHeader::partition_entry_array_crc32 250 | #[must_use] 251 | pub fn calculate_crc32(&self) -> Crc32 { 252 | let crc = crc::Crc::::new(&Crc32::ALGORITHM); 253 | let mut digest = crc.digest(); 254 | digest.update(&self.storage[..self.num_bytes_exact]); 255 | Crc32(U32Le(digest.finalize().to_le_bytes())) 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /uguid/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.2.1 2 | 3 | * MSRV increased to 1.81. 4 | * The `Error` trait is now unconditionally implemented for `GuidFromStrError`. 5 | 6 | # 2.2.0 7 | 8 | * Added `Variant` enum and `Guid::variant` method. 9 | * Added `Guid::from_random_bytes` constructor. 10 | * Added `Guid::is_zero` method. 11 | * Added `Guid::version` method. 12 | * Conversions of the `time_low` field to/from bytes now treat that field 13 | as native endian rather than little endian. 14 | * Fix non-upper-case-globals linter warning. 15 | 16 | # 2.1.0 17 | 18 | * Bump MSRV to 1.68. 19 | * Add docstring for `Guid::from_str`. 20 | 21 | # 2.0.1 22 | 23 | * Fix typo in readme. 24 | 25 | # 2.0.0 26 | 27 | * Error messages from `guid!` and `aligned_guid!` have been improved by 28 | marking the `parse_or_panic` method `track_caller`. 29 | * `AlignedGuid` has been removed. 30 | * `Guid` is now 4-byte aligned. 31 | * The fields of `Guid` are now private. It is no longer possible to 32 | directly construct `Guid`; one of the constructors such as `guid!`, 33 | `Guid::new`, or `Guid::from_bytes` must be used instead. New accessor 34 | methods have been added for each of the internal fields. 35 | 36 | # 1.2.1 37 | 38 | * Copied the license files into each package so that the archives on 39 | crates.io include them. 40 | 41 | # 1.2.0 42 | 43 | * Add `Guid::parse_or_panic` and `AlignedGuid::parse_or_panic`. These 44 | have the same functionality as the corresponding `try_parse` methods, 45 | except they will panic on failure. This is useful in `const` contexts 46 | where the panic is used as a compilation error. 47 | * The `guid!` and `aligned_guid!` macros now force const evaluation of 48 | the input. This was the intended behavior before, but it was not 49 | implemented correctly. Any new compilation failures caused by this 50 | change indicate a bug in the calling code. 51 | 52 | # 1.1.1 53 | 54 | * Change `Guid` back to `repr(C)` instead of `repr(C, align(1))`. Even 55 | though the alignment of the struct is 1-byte either way, structs with 56 | any alignment set are not allowed in packed structures so this was a 57 | breaking change. 58 | 59 | # 1.1.0 (yanked) 60 | 61 | * Add `AlignedGuid`, which is identical to `Guid` except the struct is 62 | 8-byte aligned instead of 1-byte aligned. 63 | * The `Guid` and `AlignedGuid` types implement `From` for each other to 64 | convert between them. 65 | * Add `aligned_guid!` macro, which is identical to the `guid!` macro 66 | except it creates an `AlignedGuid` instead of a `Guid`. 67 | 68 | This release was yanked due to accidentally changing the repr of `Guid`. 69 | 70 | # 1.0.4 71 | 72 | * Relax version requirements for `bytemuck` and `serde`. 73 | * Enable `doc_auto_cfg` on docs.rs. 74 | 75 | # 1.0.3 76 | 77 | * Fix license links in README, take two. 78 | 79 | # 1.0.2 80 | 81 | * Fix license links in README. 82 | 83 | # 1.0.1 84 | 85 | * Allow the MIT license to be used in addition to Apache-2.0. 86 | 87 | # 1.0.0 88 | 89 | * Make `GuidFromStrError` into an enum with three variants to allow for 90 | better error messages. 91 | 92 | # 0.7.0 93 | 94 | * Add a const `Guid::from_bytes` constructor. 95 | * Make `Guid::to_bytes` const. 96 | * Remove re-export of `bytemuck` dependency. 97 | * Make the `bytemuck` dependency optional with the new `bytemuck` feature. 98 | 99 | # 0.6.0 100 | 101 | * Add `Guid::to_ascii_hex_lower` method. This is a const function that 102 | creates a `[u8; 36]` array containing the GUID in standard 103 | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` format. 104 | * Add `serde` feature (disabled by default) that implements serde's 105 | `Serialize` and `Deserialize` traits for the `Guid` type. 106 | * Remove unused `From` impl for `GuidFromStrError`. 107 | -------------------------------------------------------------------------------- /uguid/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [package] 10 | name = "uguid" 11 | version = "2.2.1" 12 | categories = ["data-structures", "embedded", "no-std"] 13 | description = "GUID (Globally Unique Identifier) no_std library" 14 | keywords = ["gpt", "guid", "no_std", "uefi"] 15 | 16 | edition.workspace = true 17 | rust-version.workspace = true 18 | license.workspace = true 19 | repository.workspace = true 20 | 21 | [dependencies] 22 | bytemuck = { workspace = true, features = ["derive"], optional = true } 23 | serde = { version = "1.0.0", default-features = false, features = ["derive"], optional = true } 24 | 25 | [dev-dependencies] 26 | serde_test = "1.0.0" 27 | trybuild = "1.0.80" 28 | 29 | [features] 30 | # See module docstring in src/lib.rs for details of what these features do. 31 | bytemuck = ["dep:bytemuck"] 32 | serde = ["dep:serde"] 33 | std = [] 34 | 35 | [package.metadata.docs.rs] 36 | all-features = true 37 | rustdoc-args = ["--cfg", "docsrs"] 38 | -------------------------------------------------------------------------------- /uguid/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /uguid/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2022 Google LLC 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /uguid/README.md: -------------------------------------------------------------------------------- 1 | # `uguid` 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/uguid)](https://crates.io/crates/uguid) 4 | [![Docs.rs](https://docs.rs/uguid/badge.svg)](https://docs.rs/uguid) 5 | 6 | `no_std` library providing a GUID (Globally Unique Identifier) type, as 7 | used in GPT disks, UEFI, and Microsoft Windows. 8 | 9 | [GPT]: https://en.wikipedia.org/wiki/GUID_Partition_Table 10 | 11 | ## Features 12 | 13 | No features are enabled by default. 14 | 15 | * `bytemuck`: Implements bytemuck's `Pod` and `Zeroable` traits for `Guid`. 16 | * `serde`: Implements serde's `Serialize` and `Deserialize` traits for `Guid`. 17 | * `std`: Currently has no effect. 18 | 19 | ## Minimum Supported Rust Version (MSRV) 20 | 21 | The current MSRV is 1.81. 22 | 23 | ## License 24 | 25 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) 26 | or [MIT license](LICENSE-MIT) at your option. 27 | 28 | ## Disclaimer 29 | 30 | This project is not an official Google project. It is not supported by 31 | Google and Google specifically disclaims all warranties as to its quality, 32 | merchantability, or fitness for a particular purpose. 33 | -------------------------------------------------------------------------------- /uguid/examples/guid_info.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023> Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::{env, process}; 10 | use uguid::Guid; 11 | 12 | const USAGE: &str = r#" 13 | usage: guid_info 14 | the format is "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 15 | where each `x` is a hex digit (any of `0-9`, `a-f`, or `A-F`). 16 | "#; 17 | 18 | fn format_bytes(bytes: &[u8]) -> String { 19 | let mut s = String::new(); 20 | for (i, byte) in bytes.iter().enumerate() { 21 | if i != 0 && (i % 2) == 0 { 22 | s.push(' '); 23 | } 24 | s += &format!("{byte:02x}"); 25 | } 26 | s 27 | } 28 | 29 | fn format_guid(guid: Guid) -> String { 30 | format!( 31 | "guid: {guid} 32 | version: {version} 33 | variant: {variant:?} 34 | time_low: {time_low} 35 | time_mid: {time_mid} 36 | time_high_and_version: {time_high_and_version} 37 | clock_seq_high_and_reserved: {clock_seq_high_and_reserved} 38 | clock_seq_low: {clock_seq_low} 39 | node: {node}", 40 | version = guid.version(), 41 | variant = guid.variant(), 42 | time_low = format_bytes(&guid.time_low()), 43 | time_mid = format_bytes(&guid.time_mid()), 44 | time_high_and_version = format_bytes(&guid.time_high_and_version()), 45 | clock_seq_high_and_reserved = 46 | format_bytes(&[guid.clock_seq_high_and_reserved()]), 47 | clock_seq_low = format_bytes(&[guid.clock_seq_low()]), 48 | node = format_bytes(&guid.node()) 49 | ) 50 | } 51 | 52 | fn main() { 53 | let args: Vec<_> = env::args().collect(); 54 | if args.len() != 2 { 55 | println!("{}", USAGE.trim()); 56 | process::exit(1); 57 | } 58 | 59 | let arg = &args[1]; 60 | match arg.parse::() { 61 | Ok(guid) => { 62 | println!("{}", format_guid(guid)); 63 | } 64 | Err(err) => { 65 | println!("invalid input: {err}"); 66 | process::exit(1); 67 | } 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | 75 | #[test] 76 | fn test_format_bytes() { 77 | assert_eq!(format_bytes(&[]), ""); 78 | assert_eq!(format_bytes(&[0]), "00"); 79 | assert_eq!(format_bytes(&[0x12, 0x34]), "1234"); 80 | assert_eq!(format_bytes(&[0x12, 0x34, 0x56, 0x78]), "1234 5678"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /uguid/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use core::fmt::{self, Display, Formatter}; 10 | 11 | /// Error type for [`Guid::try_parse`] and [`Guid::from_str`]. 12 | /// 13 | /// [`Guid::from_str`]: core::str::FromStr::from_str 14 | /// [`Guid::try_parse`]: crate::Guid::try_parse 15 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 16 | pub enum GuidFromStrError { 17 | /// Input has the wrong length, expected 36 bytes. 18 | Length, 19 | 20 | /// Input is missing a separator (`-`) at this byte index. 21 | Separator(u8), 22 | 23 | /// Input contains invalid ASCII hex at this byte index. 24 | Hex(u8), 25 | } 26 | 27 | impl Default for GuidFromStrError { 28 | fn default() -> Self { 29 | Self::Length 30 | } 31 | } 32 | 33 | impl Display for GuidFromStrError { 34 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 35 | match self { 36 | Self::Length => { 37 | f.write_str("GUID string has wrong length (expected 36 bytes)") 38 | } 39 | Self::Separator(index) => write!( 40 | f, 41 | "GUID string is missing a separator (`-`) at index {index}", 42 | ), 43 | Self::Hex(index) => { 44 | write!( 45 | f, 46 | "GUID string contains invalid ASCII hex at index {index}", 47 | ) 48 | } 49 | } 50 | } 51 | } 52 | 53 | impl core::error::Error for GuidFromStrError {} 54 | -------------------------------------------------------------------------------- /uguid/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Library providing a GUID (Globally Unique Identifier) type. The 10 | //! format is defined in [RFC 4122]. However, unlike "normal" UUIDs 11 | //! (such as those provided by the [`uuid`] crate), the first three 12 | //! fields are little-endian. See [Appendix A] of the UEFI 13 | //! Specification. This format of GUID is also used in Microsoft 14 | //! Windows. 15 | //! 16 | //! [Appendix A]: https://uefi.org/specs/UEFI/2.10/Apx_A_GUID_and_Time_Formats.html 17 | //! [RFC 4122]: https://datatracker.ietf.org/doc/html/rfc4122 18 | //! [`uuid`]: https://docs.rs/uuid/latest/uuid 19 | //! 20 | //! # Features 21 | //! 22 | //! No features are enabled by default. 23 | //! 24 | //! * `bytemuck`: Implements bytemuck's `Pod` and `Zeroable` traits for `Guid`. 25 | //! * `serde`: Implements serde's `Serialize` and `Deserialize` traits for `Guid`. 26 | //! * `std`: Currently has no effect. 27 | //! 28 | //! # Examples 29 | //! 30 | //! Construct a GUID at compile time with the `guid!` macro: 31 | //! 32 | //! ``` 33 | //! use uguid::guid; 34 | //! 35 | //! let guid = guid!("01234567-89ab-cdef-0123-456789abcdef"); 36 | //! ``` 37 | //! 38 | //! Parse a GUID at runtime from a string: 39 | //! 40 | //! ``` 41 | //! use uguid::Guid; 42 | //! 43 | //! let guid: Guid = "01234567-89ab-cdef-0123-456789abcdef".parse().unwrap(); 44 | //! ``` 45 | //! 46 | //! Construct a GUID from its components or a byte array: 47 | //! 48 | //! ``` 49 | //! use uguid::Guid; 50 | //! 51 | //! ##[rustfmt::skip] 52 | //! let guid1 = Guid::from_bytes([ 53 | //! 0x01, 0x02, 0x03, 0x04, 54 | //! 0x05, 0x06, 0x07, 0x08, 55 | //! 0x09, 0x10, 0x11, 0x12, 56 | //! 0x13, 0x14, 0x15, 0x16, 57 | //! ]); 58 | //! let guid2 = Guid::new( 59 | //! [0x01, 0x02, 0x03, 0x04], 60 | //! [0x05, 0x06], 61 | //! [0x07, 0x08], 62 | //! 0x09, 63 | //! 0x10, 64 | //! [0x11, 0x12, 0x13, 0x14, 0x15, 0x16], 65 | //! ); 66 | //! assert_eq!(guid1, guid2); 67 | //! ``` 68 | //! 69 | //! Convert to a string or a byte array: 70 | //! 71 | //! ``` 72 | //! use uguid::guid; 73 | //! 74 | //! let guid = guid!("01234567-89ab-cdef-0123-456789abcdef"); 75 | //! assert_eq!(guid.to_string(), "01234567-89ab-cdef-0123-456789abcdef"); 76 | //! assert_eq!( 77 | //! guid.to_bytes(), 78 | //! [ 79 | //! 0x67, 0x45, 0x23, 0x01, 0xab, 0x89, 0xef, 0xcd, 0x01, 0x23, 0x45, 80 | //! 0x67, 0x89, 0xab, 0xcd, 0xef 81 | //! ] 82 | //! ); 83 | //! ``` 84 | 85 | #![no_std] 86 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 87 | #![warn(missing_copy_implementations)] 88 | #![warn(missing_debug_implementations)] 89 | #![warn(missing_docs)] 90 | #![warn(trivial_casts)] 91 | #![warn(trivial_numeric_casts)] 92 | #![warn(unreachable_pub)] 93 | #![warn(unsafe_code)] 94 | #![warn(clippy::pedantic)] 95 | #![warn(clippy::as_conversions)] 96 | #![allow(clippy::missing_errors_doc)] 97 | #![allow(clippy::module_name_repetitions)] 98 | 99 | /// Macro replacement for the `?` operator, which cannot be used in 100 | /// const functions. 101 | macro_rules! mtry { 102 | ($expr:expr $(,)?) => { 103 | match $expr { 104 | Ok(val) => val, 105 | Err(err) => { 106 | return Err(err); 107 | } 108 | } 109 | }; 110 | } 111 | 112 | mod error; 113 | mod guid; 114 | mod util; 115 | 116 | pub use error::GuidFromStrError; 117 | pub use guid::{Guid, Variant}; 118 | 119 | /// Create a [`Guid`] from a string at compile time. 120 | /// 121 | /// # Examples 122 | /// 123 | /// ``` 124 | /// use uguid::{guid, Guid}; 125 | /// assert_eq!( 126 | /// guid!("01234567-89ab-cdef-0123-456789abcdef"), 127 | /// Guid::new( 128 | /// [0x67, 0x45, 0x23, 0x01], 129 | /// [0xab, 0x89], 130 | /// [0xef, 0xcd], 131 | /// 0x01, 132 | /// 0x23, 133 | /// [0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], 134 | /// ) 135 | /// ); 136 | /// ``` 137 | #[macro_export] 138 | macro_rules! guid { 139 | ($s:literal) => {{ 140 | // Create a temporary const value to force an error in the input 141 | // to fail at compile time. 142 | const G: $crate::Guid = $crate::Guid::parse_or_panic($s); 143 | G 144 | }}; 145 | } 146 | -------------------------------------------------------------------------------- /uguid/src/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use crate::GuidFromStrError; 10 | 11 | pub(crate) const fn byte_to_ascii_hex_lower(byte: u8) -> (u8, u8) { 12 | let mut l = byte & 0xf; 13 | let mut h = byte >> 4; 14 | if l <= 9 { 15 | l += b'0'; 16 | } else { 17 | l += b'a' - 10; 18 | } 19 | if h <= 9 { 20 | h += b'0'; 21 | } else { 22 | h += b'a' - 10; 23 | } 24 | (h, l) 25 | } 26 | 27 | /// Parse a hexadecimal ASCII character as a `u8`. 28 | const fn parse_byte_from_ascii_char(c: u8) -> Option { 29 | match c { 30 | b'0' => Some(0x0), 31 | b'1' => Some(0x1), 32 | b'2' => Some(0x2), 33 | b'3' => Some(0x3), 34 | b'4' => Some(0x4), 35 | b'5' => Some(0x5), 36 | b'6' => Some(0x6), 37 | b'7' => Some(0x7), 38 | b'8' => Some(0x8), 39 | b'9' => Some(0x9), 40 | b'a' | b'A' => Some(0xa), 41 | b'b' | b'B' => Some(0xb), 42 | b'c' | b'C' => Some(0xc), 43 | b'd' | b'D' => Some(0xd), 44 | b'e' | b'E' => Some(0xe), 45 | b'f' | b'F' => Some(0xf), 46 | _ => None, 47 | } 48 | } 49 | 50 | /// Parse a pair of hexadecimal ASCII characters as a `u8`. For example, 51 | /// `(b'1', b'a')` is parsed as `0x1a`. 52 | const fn parse_byte_from_ascii_char_pair(a: u8, b: u8) -> Option { 53 | let Some(a) = parse_byte_from_ascii_char(a) else { 54 | return None; 55 | }; 56 | 57 | let Some(b) = parse_byte_from_ascii_char(b) else { 58 | return None; 59 | }; 60 | 61 | Some((a << 4) | b) 62 | } 63 | 64 | /// Parse a pair of hexadecimal ASCII characters at position `start` as 65 | /// a `u8`. 66 | pub(crate) const fn parse_byte_from_ascii_str_at( 67 | s: &[u8], 68 | start: u8, 69 | ) -> Result { 70 | // This `as` conversion is needed because this is a const 71 | // function. It is always valid since `usize` is always bigger than 72 | // a u8. 73 | #![allow(clippy::as_conversions)] 74 | let start_usize = start as usize; 75 | 76 | if let Some(byte) = 77 | parse_byte_from_ascii_char_pair(s[start_usize], s[start_usize + 1]) 78 | { 79 | Ok(byte) 80 | } else { 81 | Err(GuidFromStrError::Hex(start)) 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | mod tests { 87 | use super::*; 88 | 89 | #[test] 90 | fn test_to_ascii() { 91 | assert_eq!(byte_to_ascii_hex_lower(0x1f), (b'1', b'f')); 92 | assert_eq!(byte_to_ascii_hex_lower(0xf1), (b'f', b'1')); 93 | } 94 | 95 | #[test] 96 | fn test_parse() { 97 | assert_eq!(parse_byte_from_ascii_char_pair(b'1', b'a'), Some(0x1a)); 98 | assert_eq!(parse_byte_from_ascii_char_pair(b'8', b'f'), Some(0x8f)); 99 | 100 | assert_eq!(parse_byte_from_ascii_char_pair(b'g', b'a'), None); 101 | assert_eq!(parse_byte_from_ascii_char_pair(b'a', b'g'), None); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /uguid/tests/test_guid.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use core::mem; 10 | use uguid::{guid, Guid, GuidFromStrError, Variant}; 11 | 12 | #[test] 13 | fn test_guid() { 14 | assert_eq!(mem::size_of::(), 16); 15 | assert_eq!(mem::align_of::(), 4); 16 | 17 | // Constructors. 18 | let guid = Guid::new( 19 | 0x01234567_u32.to_le_bytes(), 20 | 0x89ab_u16.to_le_bytes(), 21 | 0xcdef_u16.to_le_bytes(), 22 | 0x01, 23 | 0x23, 24 | [0x45, 0x67, 0x89, 0xab, 0xcd, 0xef], 25 | ); 26 | let guid2 = Guid::from_bytes([ 27 | 0x67, 0x45, 0x23, 0x01, 0xab, 0x89, 0xef, 0xcd, 0x01, 0x23, 0x45, 0x67, 28 | 0x89, 0xab, 0xcd, 0xef, 29 | ]); 30 | assert_eq!(guid, guid2); 31 | 32 | // Accessors. 33 | assert_eq!(guid.time_low(), [0x67, 0x45, 0x23, 0x01]); 34 | assert_eq!(guid.time_mid(), [0xab, 0x89]); 35 | assert_eq!(guid.time_high_and_version(), [0xef, 0xcd]); 36 | assert_eq!(guid.clock_seq_high_and_reserved(), 0x01); 37 | assert_eq!(guid.clock_seq_low(), 0x23); 38 | assert_eq!(guid.node(), [0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); 39 | 40 | // To byte array. 41 | assert_eq!( 42 | guid.to_bytes(), 43 | [ 44 | 0x67, 0x45, 0x23, 0x01, 0xab, 0x89, 0xef, 0xcd, 0x01, 0x23, 0x45, 45 | 0x67, 0x89, 0xab, 0xcd, 0xef 46 | ] 47 | ); 48 | 49 | // Formatting. 50 | assert_eq!( 51 | guid.to_ascii_hex_lower(), 52 | *b"01234567-89ab-cdef-0123-456789abcdef" 53 | ); 54 | assert_eq!(guid.to_string(), "01234567-89ab-cdef-0123-456789abcdef"); 55 | 56 | // Parsing. 57 | assert_eq!( 58 | "01234567-89ab-cdef-0123-456789abcdef" 59 | .parse::() 60 | .unwrap(), 61 | guid 62 | ); 63 | assert_eq!( 64 | Guid::try_parse("01234567-89ab-cdef-0123-456789abcdef").unwrap(), 65 | guid 66 | ); 67 | 68 | // Macro. 69 | assert_eq!(guid!("01234567-89ab-cdef-0123-456789abcdef"), guid); 70 | } 71 | 72 | #[test] 73 | fn test_from_random_bytes() { 74 | let random_bytes = [ 75 | 0x68, 0xc0, 0x5f, 0xd7, 0x78, 0x21, 0xf9, 0x01, 0x66, 0x15, 0xab, 0x54, 76 | 0xe9, 0xcc, 0x44, 0xb0, 77 | ]; 78 | let expected_bytes = [ 79 | 0x68, 0xc0, 0x5f, 0xd7, 0x78, 0x21, 0xf9, 0x4f, 0xa6, 0x15, 0xab, 0x54, 80 | 0xe9, 0xcc, 0x44, 0xb0, 81 | ]; 82 | 83 | let guid = Guid::from_random_bytes(random_bytes); 84 | assert_eq!(guid.to_bytes(), expected_bytes); 85 | assert_eq!(guid.variant(), Variant::Rfc4122); 86 | assert_eq!(guid.version(), 4); 87 | } 88 | 89 | #[test] 90 | fn test_parse_or_panic_success() { 91 | let _g = Guid::parse_or_panic("01234567-89ab-cdef-0123-456789abcdef"); 92 | } 93 | 94 | #[test] 95 | #[should_panic] 96 | fn test_parse_or_panic_len() { 97 | let _g = Guid::parse_or_panic("01234567-89ab-cdef-0123-456789abcdef0"); 98 | } 99 | 100 | #[test] 101 | #[should_panic] 102 | fn test_parse_or_panic_sep() { 103 | let _g = Guid::parse_or_panic("01234567089ab-cdef-0123-456789abcdef"); 104 | } 105 | 106 | #[test] 107 | #[should_panic] 108 | fn test_parse_or_panic_hex() { 109 | let _g = Guid::parse_or_panic("g1234567-89ab-cdef-0123-456789abcdef"); 110 | } 111 | 112 | #[test] 113 | fn test_guid_error() { 114 | // Wrong length. 115 | let s = "01234567-89ab-cdef-0123-456789abcdef0"; 116 | assert_eq!(s.len(), 37); 117 | assert_eq!(s.parse::(), Err(GuidFromStrError::Length)); 118 | 119 | // Wrong separator. 120 | let s = "01234567089ab-cdef-0123-456789abcdef"; 121 | assert_eq!(s.parse::(), Err(GuidFromStrError::Separator(8))); 122 | let s = "01234567-89ab0cdef-0123-456789abcdef"; 123 | assert_eq!(s.parse::(), Err(GuidFromStrError::Separator(13))); 124 | let s = "01234567-89ab-cdef00123-456789abcdef"; 125 | assert_eq!(s.parse::(), Err(GuidFromStrError::Separator(18))); 126 | let s = "01234567-89ab-cdef-01230456789abcdef"; 127 | assert_eq!(s.parse::(), Err(GuidFromStrError::Separator(23))); 128 | 129 | // Invalid hex. 130 | let s = "g1234567-89ab-cdef-0123-456789abcdef"; 131 | assert_eq!(s.parse::(), Err(GuidFromStrError::Hex(0))); 132 | 133 | assert_eq!( 134 | GuidFromStrError::Length.to_string(), 135 | "GUID string has wrong length (expected 36 bytes)" 136 | ); 137 | assert_eq!( 138 | GuidFromStrError::Separator(8).to_string(), 139 | "GUID string is missing a separator (`-`) at index 8" 140 | ); 141 | assert_eq!( 142 | GuidFromStrError::Hex(10).to_string(), 143 | "GUID string contains invalid ASCII hex at index 10" 144 | ); 145 | } 146 | 147 | #[test] 148 | fn test_guid_variant() { 149 | assert_eq!( 150 | guid!("00000000-0000-0000-0000-000000000000").variant(), 151 | Variant::ReservedNcs 152 | ); 153 | assert_eq!( 154 | guid!("00000000-0000-0000-8000-000000000000").variant(), 155 | Variant::Rfc4122 156 | ); 157 | assert_eq!( 158 | guid!("00000000-0000-0000-c000-000000000000").variant(), 159 | Variant::ReservedMicrosoft 160 | ); 161 | assert_eq!( 162 | guid!("00000000-0000-0000-e000-000000000000").variant(), 163 | Variant::ReservedFuture 164 | ); 165 | } 166 | 167 | #[test] 168 | fn test_guid_version() { 169 | assert_eq!(guid!("00000000-0000-0000-8000-000000000000").version(), 0); 170 | assert_eq!(guid!("00000000-0000-1000-8000-000000000000").version(), 1); 171 | assert_eq!(guid!("00000000-0000-2000-8000-000000000000").version(), 2); 172 | assert_eq!(guid!("00000000-0000-4000-8000-000000000000").version(), 4); 173 | } 174 | 175 | #[test] 176 | fn test_guid_is_zero() { 177 | assert!(guid!("00000000-0000-0000-0000-000000000000").is_zero()); 178 | assert!(!guid!("308bbc16-a308-47e8-8977-5e5646c5291f").is_zero()); 179 | } 180 | 181 | /// Inner module that only imports the `guid!` macro. 182 | mod inner { 183 | use uguid::guid; 184 | 185 | /// Test that the `guid!` macro works without importing anything 186 | /// else. 187 | #[test] 188 | fn test_guid_macro_paths() { 189 | let _g = guid!("01234567-89ab-cdef-0123-456789abcdef"); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /uguid/tests/test_macro_error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Tests errors from the guid macros. 10 | #[test] 11 | fn test_compilation_errors() { 12 | let t = trybuild::TestCases::new(); 13 | t.compile_fail("tests/ui/*.rs"); 14 | } 15 | -------------------------------------------------------------------------------- /uguid/tests/test_serde.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | #![cfg(feature = "serde")] 10 | 11 | use serde_test::Token; 12 | use uguid::{guid, Guid}; 13 | 14 | #[test] 15 | fn test_serde() { 16 | let guid = guid!("01234567-89ab-cdef-0123-456789abcdef"); 17 | 18 | serde_test::assert_tokens( 19 | &guid, 20 | &[Token::Str("01234567-89ab-cdef-0123-456789abcdef")], 21 | ); 22 | 23 | serde_test::assert_de_tokens_error::( 24 | &[Token::Str("1234")], 25 | "GUID string has wrong length (expected 36 bytes)", 26 | ); 27 | 28 | serde_test::assert_de_tokens_error::( 29 | &[Token::U64(1234)], 30 | "invalid type: integer `1234`, expected a string in the format \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\""); 31 | } 32 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_hex.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use uguid::guid; 10 | 11 | fn main() { 12 | let _g = guid!("g1234567-89ab-cdef-0123-456789abcdef"); 13 | } 14 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_hex.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/ui/guid_hex.rs:12:14 3 | | 4 | 12 | let _g = guid!("g1234567-89ab-cdef-0123-456789abcdef"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'GUID string contains one or more invalid characters', $DIR/tests/ui/guid_hex.rs:12:14 6 | | 7 | = note: this error originates in the macro `guid` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_len.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use uguid::guid; 10 | 11 | fn main() { 12 | let _g = guid!("1234"); 13 | } 14 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_len.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/ui/guid_len.rs:12:14 3 | | 4 | 12 | let _g = guid!("1234"); 5 | | ^^^^^^^^^^^^^ the evaluated program panicked at 'GUID string has wrong length (expected 36 bytes)', $DIR/tests/ui/guid_len.rs:12:14 6 | | 7 | = note: this error originates in the macro `guid` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_sep.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use uguid::guid; 10 | 11 | fn main() { 12 | let _g = guid!("01234567089ab-cdef-0123-456789abcdef"); 13 | } 14 | -------------------------------------------------------------------------------- /uguid/tests/ui/guid_sep.stderr: -------------------------------------------------------------------------------- 1 | error[E0080]: evaluation of constant value failed 2 | --> tests/ui/guid_sep.rs:12:14 3 | | 4 | 12 | let _g = guid!("01234567089ab-cdef-0123-456789abcdef"); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'GUID string is missing one or more separators (`-`)', $DIR/tests/ui/guid_sep.rs:12:14 6 | | 7 | = note: this error originates in the macro `guid` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 or the MIT license 5 | # , at your 6 | # option. This file may not be copied, modified, or distributed 7 | # except according to those terms. 8 | 9 | [package] 10 | name = "xtask" 11 | version = "0.0.0" 12 | edition = "2021" 13 | publish = false 14 | license = "MIT OR Apache-2.0" 15 | 16 | [dependencies] 17 | anyhow = "1.0.75" 18 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | mod package; 10 | mod util; 11 | 12 | use package::Package; 13 | use std::env; 14 | use std::process::{exit, Command}; 15 | use util::run_cmd; 16 | 17 | const FEAT_OPTIONS: [bool; 2] = [false, true]; 18 | const FEAT_BYTEMUCK: &str = "bytemuck"; 19 | const FEAT_SERDE: &str = "serde"; 20 | const FEAT_STD: &str = "std"; 21 | 22 | #[derive(Clone, Copy)] 23 | enum CargoAction { 24 | Test, 25 | Lint, 26 | } 27 | 28 | impl CargoAction { 29 | fn as_str(self) -> &'static str { 30 | match self { 31 | Self::Lint => "clippy", 32 | Self::Test => "test", 33 | } 34 | } 35 | } 36 | 37 | fn get_cargo_cmd( 38 | action: CargoAction, 39 | package: Package, 40 | features: &[&str], 41 | ) -> Command { 42 | let mut cmd = Command::new("cargo"); 43 | cmd.args([action.as_str(), "--package", package.name()]); 44 | if !features.is_empty() { 45 | cmd.args(["--features", &features.join(",")]); 46 | } 47 | match action { 48 | CargoAction::Test => {} 49 | CargoAction::Lint => { 50 | cmd.args(["--", "-D", "warnings"]); 51 | } 52 | } 53 | cmd 54 | } 55 | 56 | fn test_package(package: Package, features: &[&str]) { 57 | run_cmd(get_cargo_cmd(CargoAction::Lint, package, features)).unwrap(); 58 | run_cmd(get_cargo_cmd(CargoAction::Test, package, features)).unwrap(); 59 | } 60 | 61 | fn test_uguid() { 62 | for feat_bytemuck in FEAT_OPTIONS { 63 | for feat_serde in FEAT_OPTIONS { 64 | for feat_std in FEAT_OPTIONS { 65 | let mut features = Vec::new(); 66 | if feat_bytemuck { 67 | features.push(FEAT_BYTEMUCK); 68 | } 69 | if feat_serde { 70 | features.push(FEAT_SERDE); 71 | } 72 | if feat_std { 73 | features.push(FEAT_STD); 74 | } 75 | 76 | test_package(Package::Uguid, &features); 77 | } 78 | } 79 | } 80 | } 81 | 82 | fn test_gpt_disk_types() { 83 | for feat_bytemuck in FEAT_OPTIONS { 84 | for feat_std in FEAT_OPTIONS { 85 | let mut features = Vec::new(); 86 | if feat_bytemuck { 87 | features.push(FEAT_BYTEMUCK); 88 | } 89 | if feat_std { 90 | features.push(FEAT_STD); 91 | } 92 | 93 | test_package(Package::GptDiskTypes, &features); 94 | } 95 | } 96 | } 97 | 98 | fn test_gpt_disk_io() { 99 | let feature_lists = [ 100 | vec![], 101 | vec!["alloc"], 102 | // std implicitly enabled alloc, so no need for a separate alloc+std. 103 | vec!["std"], 104 | ]; 105 | 106 | for features in feature_lists { 107 | test_package(Package::GptDiskIo, &features); 108 | } 109 | } 110 | 111 | fn main() { 112 | let args: Vec<_> = env::args().collect(); 113 | let arg_test_all = "test_all"; 114 | let arg_test_uguid = "test_uguid"; 115 | let arg_test_gpt_disk_types = "test_gpt_disk_types"; 116 | let arg_test_gpt_disk_io = "test_gpt_disk_io"; 117 | let actions = &[ 118 | arg_test_all, 119 | arg_test_uguid, 120 | arg_test_gpt_disk_types, 121 | arg_test_gpt_disk_io, 122 | ]; 123 | if args.len() != 2 || !actions.contains(&args[1].as_ref()) { 124 | println!("usage: cargo xtask [{}]", actions.join("|")); 125 | exit(1); 126 | } 127 | 128 | let action = &args[1]; 129 | if action == arg_test_all || action == arg_test_uguid { 130 | test_uguid(); 131 | } 132 | if action == arg_test_all || action == arg_test_gpt_disk_types { 133 | test_gpt_disk_types(); 134 | } 135 | if action == arg_test_all || action == arg_test_gpt_disk_io { 136 | test_gpt_disk_io(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /xtask/src/package.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | /// Public packages in the workspace. 10 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 11 | pub enum Package { 12 | Uguid, 13 | GptDiskTypes, 14 | GptDiskIo, 15 | } 16 | 17 | impl Package { 18 | /// Package name. 19 | pub fn name(self) -> &'static str { 20 | match self { 21 | Self::Uguid => "uguid", 22 | Self::GptDiskTypes => "gpt_disk_types", 23 | Self::GptDiskIo => "gpt_disk_io", 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /xtask/src/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use anyhow::{bail, Result}; 10 | use std::process::Command; 11 | 12 | fn print_cmd(cmd: &Command) { 13 | println!("Running: {}", format!("{cmd:?}").replace('"', "")); 14 | } 15 | 16 | pub fn run_cmd(mut cmd: Command) -> Result<()> { 17 | print_cmd(&cmd); 18 | let status = cmd.status().expect("failed to launch"); 19 | if status.success() { 20 | Ok(()) 21 | } else { 22 | bail!("command failed: {status}"); 23 | } 24 | } 25 | --------------------------------------------------------------------------------