Skip to main content

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