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