magic_sys/
lib.rs

1//! # Features
2//!
3//! ## Build features
4//! - `pkg-config` (_enabled by default_) — Enable build using [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) with the [`pkg-config` crate](https://docs.rs/pkg-config)  
5//!   Check the [crate README for `pkg-config` configuration details](https://crates.io/crates/magic#pkg-config)
6//! - `vcpkg` (_enabled by default_) — Enable build using [`vcpkg`](https://vcpkg.io/) with the [`vcpkg` crate](https://docs.rs/vcpkg)  
7//!   Check the [crate README for `vcpkg` configuration details](https://crates.io/crates/magic#vcpkg)
8//!
9//! ## `libmagic` API features
10//! - `v5-40` — Enable [`libmagic` v5.40 API](#libmagic-v540)
11//! - `v5-44` — Enable [`libmagic` v5.44 API](#libmagic-v544)
12//! - `v5-45` — Enable [`libmagic` v5.45 API](#libmagic-v545)
13//!
14//!
15//! # `libmagic` changelog
16//!
17//! The following is a subset of `libmagic` changes that are relevant for this `magic-sys` crate.
18//!
19//! `magic-sys` implements `libmagic` API v5.38 ..= v5.46.  
20//! `magic-sys` requires `libmagic` v5.39 or any newer version to build.  
21//!
22//! ## `libmagic` v5.38
23//!
24//! API baseline.  
25//!
26//! ## `libmagic` v5.39
27//!
28//! No API changes.  
29//! Add `libmagic.pc` to build (statically) with `pkg-config`.  
30//!
31//! ## `libmagic` v5.40
32//!
33//! Add [`MAGIC_PARAM_ENCODING_MAX`].  
34//!
35//! ## `libmagic` v5.41
36//!
37//! No API changes.
38//!
39//! ## `libmagic` v5.42
40//!
41//! No API changes.
42//!
43//! ## `libmagic` v5.43
44//!
45//! No API changes.
46//!
47//! ## `libmagic` v5.44
48//!
49//! Add [`MAGIC_NO_COMPRESS_FORK`].
50//!
51//! ## `libmagic` v5.45
52//!
53//! Add [`MAGIC_NO_CHECK_SIMH`].  
54//! Add [`MAGIC_PARAM_ELF_SHSIZE_MAX`].  
55//! Change [`MAGIC_NO_CHECK_BUILTIN`].  
56//!
57//! ## `libmagic` v5.46
58//!
59//! No API changes.
60//!
61
62#![cfg_attr(docsrs, feature(doc_cfg))]
63#![cfg_attr(docsrs, feature(doc_cfg_hide))]
64#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
65// Technically this crate doesn't need Rust `std`
66// but you'll still have to get the `libmagic` C library working for your target
67#![cfg_attr(not(test), no_std)]
68
69use core::ffi::c_void;
70use core::ffi::{c_char, c_int};
71use usize as size_t; // core::ffi::c_size_t is unstable, but "is currently always usize"
72
73// `libmagic` API as in "magic.h"
74
75#[allow(non_camel_case_types)]
76// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
77#[repr(C)]
78pub struct magic_set {
79    _data: [u8; 0],
80    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
81}
82
83#[allow(non_camel_case_types)]
84pub type magic_t = *mut magic_set;
85
86pub const MAGIC_NONE: c_int = 0x000_0000;
87pub const MAGIC_DEBUG: c_int = 0x000_0001;
88pub const MAGIC_SYMLINK: c_int = 0x000_0002;
89pub const MAGIC_COMPRESS: c_int = 0x000_0004;
90pub const MAGIC_DEVICES: c_int = 0x000_0008;
91pub const MAGIC_MIME_TYPE: c_int = 0x000_0010;
92pub const MAGIC_CONTINUE: c_int = 0x000_0020;
93pub const MAGIC_CHECK: c_int = 0x000_0040;
94pub const MAGIC_PRESERVE_ATIME: c_int = 0x000_0080;
95pub const MAGIC_RAW: c_int = 0x000_0100;
96pub const MAGIC_ERROR: c_int = 0x000_0200;
97pub const MAGIC_MIME_ENCODING: c_int = 0x000_0400;
98pub const MAGIC_MIME: c_int = MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING;
99pub const MAGIC_APPLE: c_int = 0x00_0800;
100pub const MAGIC_EXTENSION: c_int = 0x100_0000;
101pub const MAGIC_COMPRESS_TRANSP: c_int = 0x200_0000;
102#[cfg_attr(docsrs, doc(cfg(feature = "v5-44")))]
103#[cfg(feature = "v5-44")]
104pub const MAGIC_NO_COMPRESS_FORK: c_int = 0x400_0000;
105pub const MAGIC_NODESC: c_int = MAGIC_EXTENSION | MAGIC_MIME | MAGIC_APPLE;
106
107pub const MAGIC_NO_CHECK_COMPRESS: c_int = 0x000_1000;
108pub const MAGIC_NO_CHECK_TAR: c_int = 0x000_2000;
109pub const MAGIC_NO_CHECK_SOFT: c_int = 0x000_4000;
110pub const MAGIC_NO_CHECK_APPTYPE: c_int = 0x000_8000;
111pub const MAGIC_NO_CHECK_ELF: c_int = 0x001_0000;
112pub const MAGIC_NO_CHECK_TEXT: c_int = 0x002_0000;
113pub const MAGIC_NO_CHECK_CDF: c_int = 0x004_0000;
114pub const MAGIC_NO_CHECK_CSV: c_int = 0x008_0000;
115pub const MAGIC_NO_CHECK_TOKENS: c_int = 0x010_0000;
116pub const MAGIC_NO_CHECK_ENCODING: c_int = 0x020_0000;
117pub const MAGIC_NO_CHECK_JSON: c_int = 0x040_0000;
118#[cfg_attr(docsrs, doc(cfg(feature = "v5-45")))]
119#[cfg(feature = "v5-45")]
120pub const MAGIC_NO_CHECK_SIMH: c_int = 0x080_0000;
121
122#[cfg(not(feature = "v5-45"))]
123pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
124MAGIC_NO_CHECK_TAR      |
125// MAGIC_NO_CHECK_SOFT  |
126MAGIC_NO_CHECK_APPTYPE  |
127MAGIC_NO_CHECK_ELF      |
128MAGIC_NO_CHECK_TEXT     |
129MAGIC_NO_CHECK_CSV      |
130MAGIC_NO_CHECK_CDF      |
131MAGIC_NO_CHECK_TOKENS   |
132MAGIC_NO_CHECK_ENCODING |
133MAGIC_NO_CHECK_JSON;
134
135#[cfg(feature = "v5-45")]
136pub const MAGIC_NO_CHECK_BUILTIN: c_int = MAGIC_NO_CHECK_COMPRESS |
137MAGIC_NO_CHECK_TAR      |
138// MAGIC_NO_CHECK_SOFT  |
139MAGIC_NO_CHECK_APPTYPE  |
140MAGIC_NO_CHECK_ELF      |
141MAGIC_NO_CHECK_TEXT     |
142MAGIC_NO_CHECK_CSV      |
143MAGIC_NO_CHECK_CDF      |
144MAGIC_NO_CHECK_TOKENS   |
145MAGIC_NO_CHECK_ENCODING |
146MAGIC_NO_CHECK_JSON     |
147MAGIC_NO_CHECK_SIMH;
148
149#[deprecated]
150pub const MAGIC_NO_CHECK_ASCII: c_int = MAGIC_NO_CHECK_TEXT;
151
152#[deprecated]
153pub const MAGIC_NO_CHECK_FORTRAN: c_int = 0x00_0000;
154#[deprecated]
155pub const MAGIC_NO_CHECK_TROFF: c_int = 0x00_0000;
156
157// TODO: MAGIC_VERSION string
158
159// TODO: MAGIC_SNPRINTB bytes
160
161pub const MAGIC_PARAM_INDIR_MAX: c_int = 0;
162pub const MAGIC_PARAM_NAME_MAX: c_int = 1;
163pub const MAGIC_PARAM_ELF_PHNUM_MAX: c_int = 2;
164pub const MAGIC_PARAM_ELF_SHNUM_MAX: c_int = 3;
165pub const MAGIC_PARAM_ELF_NOTES_MAX: c_int = 4;
166pub const MAGIC_PARAM_REGEX_MAX: c_int = 5;
167pub const MAGIC_PARAM_BYTES_MAX: c_int = 6;
168#[cfg_attr(docsrs, doc(cfg(feature = "v5-40")))]
169#[cfg(feature = "v5-40")]
170pub const MAGIC_PARAM_ENCODING_MAX: c_int = 7;
171#[cfg_attr(docsrs, doc(cfg(feature = "v5-45")))]
172#[cfg(feature = "v5-45")]
173pub const MAGIC_PARAM_ELF_SHSIZE_MAX: c_int = 8;
174
175// NOTE: the following are from `file.h`, but part of `magic.h` API
176pub const FILE_LOAD: c_int = 0;
177pub const FILE_CHECK: c_int = 1;
178pub const FILE_COMPILE: c_int = 2;
179pub const FILE_LIST: c_int = 3;
180
181extern "C" {
182    pub fn magic_open(flags: c_int) -> magic_t;
183    pub fn magic_close(cookie: magic_t);
184
185    pub fn magic_getpath(magicfile: *const c_char, action: c_int) -> *const c_char;
186    pub fn magic_file(cookie: magic_t, filename: *const c_char) -> *const c_char;
187    pub fn magic_descriptor(cookie: magic_t, fd: c_int) -> *const c_char;
188    pub fn magic_buffer(cookie: magic_t, buffer: *const c_void, length: size_t) -> *const c_char;
189
190    pub fn magic_error(cookie: magic_t) -> *const c_char;
191    pub fn magic_getflags(cookie: magic_t) -> c_int;
192    #[must_use]
193    pub fn magic_setflags(cookie: magic_t, flags: c_int) -> c_int;
194
195    pub fn magic_version() -> c_int;
196    #[must_use]
197    pub fn magic_load(cookie: magic_t, filename: *const c_char) -> c_int;
198    #[must_use]
199    pub fn magic_load_buffers(
200        cookie: magic_t,
201        buffers: *mut *mut c_void,
202        sizes: *mut size_t,
203        nbuffers: size_t,
204    ) -> c_int;
205
206    #[must_use]
207    pub fn magic_compile(cookie: magic_t, filename: *const c_char) -> c_int;
208    #[must_use]
209    pub fn magic_check(cookie: magic_t, filename: *const c_char) -> c_int;
210    #[must_use]
211    pub fn magic_list(cookie: magic_t, filename: *const c_char) -> c_int;
212    pub fn magic_errno(cookie: magic_t) -> c_int;
213
214    #[must_use]
215    pub fn magic_setparam(cookie: magic_t, param: c_int, value: *const c_void) -> c_int;
216    #[must_use]
217    pub fn magic_getparam(cookie: magic_t, param: c_int, value: *mut c_void) -> c_int;
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    // Those tests are mostly just sanity checks to see if linkage works,
225    // they are NOT testing `libmagic` API/implementation
226
227    #[test]
228    fn test_magic_open() {
229        unsafe {
230            magic_open(MAGIC_NONE);
231        }
232    }
233
234    #[test]
235    fn test_magic_close() {
236        unsafe {
237            magic_close(std::ptr::null_mut());
238        }
239    }
240
241    #[test]
242    fn test_magic_getpath() {
243        unsafe {
244            magic_getpath(std::ptr::null(), FILE_CHECK);
245        }
246    }
247
248    #[test]
249    fn test_magic_file() {
250        unsafe {
251            magic_file(std::ptr::null_mut(), std::ptr::null());
252        }
253    }
254
255    #[test]
256    fn test_magic_descriptor() {
257        unsafe {
258            magic_descriptor(std::ptr::null_mut(), -1);
259        }
260    }
261
262    #[test]
263    fn test_magic_buffer() {
264        unsafe {
265            magic_buffer(std::ptr::null_mut(), std::ptr::null(), 0);
266        }
267    }
268
269    #[test]
270    fn test_magic_error() {
271        unsafe {
272            magic_error(std::ptr::null_mut());
273        }
274    }
275
276    #[test]
277    fn test_magic_getflags() {
278        unsafe {
279            magic_getflags(std::ptr::null_mut());
280        }
281    }
282
283    #[test]
284    fn test_magic_setflags() {
285        unsafe {
286            let _ = magic_setflags(std::ptr::null_mut(), MAGIC_NONE);
287        }
288
289        // libmagic accepts basically any invalid flag except MAGIC_PRESERVE_ATIME
290    }
291
292    #[test]
293    fn test_magic_version() {
294        unsafe {
295            magic_version();
296        }
297    }
298
299    #[test]
300    fn test_magic_load() {
301        unsafe {
302            let _ = magic_load(std::ptr::null_mut(), std::ptr::null());
303        }
304    }
305
306    #[test]
307    fn test_magic_load_buffers() {
308        unsafe {
309            let _ = magic_load_buffers(
310                std::ptr::null_mut(),
311                std::ptr::null_mut(),
312                std::ptr::null_mut(),
313                0,
314            );
315        }
316    }
317
318    #[test]
319    fn test_magic_compile() {
320        unsafe {
321            let _ = magic_compile(std::ptr::null_mut(), std::ptr::null());
322        }
323    }
324
325    #[test]
326    fn test_magic_check() {
327        unsafe {
328            let _ = magic_check(std::ptr::null_mut(), std::ptr::null());
329        }
330    }
331
332    #[test]
333    fn test_magic_list() {
334        unsafe {
335            let _ = magic_list(std::ptr::null_mut(), std::ptr::null());
336        }
337    }
338
339    #[test]
340    fn test_magic_errno() {
341        unsafe {
342            magic_errno(std::ptr::null_mut());
343        }
344    }
345
346    #[test]
347    fn test_magic_setparam() {
348        unsafe {
349            let _ = magic_setparam(
350                std::ptr::null_mut(),
351                MAGIC_PARAM_INDIR_MAX,
352                std::ptr::null(),
353            );
354        }
355    }
356
357    #[test]
358    fn test_magic_getparam() {
359        unsafe {
360            let _ = magic_getparam(
361                std::ptr::null_mut(),
362                MAGIC_PARAM_INDIR_MAX,
363                std::ptr::null_mut(),
364            );
365        }
366
367        #[cfg(feature = "v5-40")]
368        {
369            let mgc = unsafe { magic_open(MAGIC_NONE) };
370            assert_ne!(mgc, std::ptr::null_mut());
371
372            let mut val: size_t = 0;
373
374            let rv = unsafe {
375                magic_getparam(
376                    mgc,
377                    MAGIC_PARAM_ENCODING_MAX,
378                    &mut val as *mut size_t as *mut _,
379                )
380            };
381            assert_ne!(rv, -1);
382
383            unsafe {
384                magic_close(mgc);
385            }
386        }
387
388        #[cfg(feature = "v5-45")]
389        {
390            let mgc = unsafe { magic_open(MAGIC_NONE) };
391            assert_ne!(mgc, std::ptr::null_mut());
392
393            let mut val: size_t = 0;
394
395            let rv = unsafe {
396                magic_getparam(
397                    mgc,
398                    MAGIC_PARAM_ELF_SHSIZE_MAX,
399                    &mut val as *mut size_t as *mut _,
400                )
401            };
402            assert_ne!(rv, -1);
403
404            unsafe {
405                magic_close(mgc);
406            }
407        }
408    }
409}