How Linux Determines File Type Without Extensions
Unlike operating systems that rely on three- or four-letter file
extensions to identify formats, the Linux operating system determines
file types by inspecting the actual contents of the file and its
filesystem metadata. By examining internal signatures known as "magic
numbers," checking inode properties, and applying heuristic text
analysis via libraries like libmagic, Linux can accurately
classify executables, media, scripts, and plain text files regardless of
what the file is named.
Inode Metadata and Fundamental File Classes
At the kernel and filesystem level, Linux categorizes everything as
one of seven fundamental file types. This distinction is stored directly
in the file's metadata structure, called an inode, within the
st_mode field.
The seven fundamental types are:
- Regular files (
-) - Directories (
d) - Symbolic links (
l) - Character device files (
c) - Block device files (
b) - Named pipes / FIFOs (
p) - Sockets (
s)
This mechanism allows the kernel to instantly know how to handle system-level operations—such as directing data to a device driver or reading a directory—without ever inspecting the file's name or inner payload.
Magic Numbers and the Magic Database
For regular files, Linux relies on byte signatures known as "magic numbers" to identify specific file formats (such as JPEG, ELF binary, or PDF).
A magic number is a unique sequence of bytes embedded at specific offsets, usually at the very beginning of the file. For example:
- ELF Binaries (Linux executables): Always begin with
the byte sequence
7F 45 4C 46(.ELF). - PDF Documents: Begin with the ASCII sequence
%PDF(25 50 44 46). - PNG Images: Start with the eight-byte signature
89 50 4E 47 0D 0A 1A 0A. - Gzip Archives: Begin with
1F 8B.
Utilities like the file command read these bytes and
match them against an extensive pattern database typically located in
/usr/share/misc/magic, /etc/magic, or compiled
into the shared library libmagic. The database specifies
the exact byte offset, the expected data type, the value to match, and
the resulting human-readable description.
The Shebang Mechanism for Scripts
For executable text files, the Linux kernel relies on a specific
magic number consisting of the two ASCII characters #!
(known as a "shebang" or "hashbang") at the very start of the file
(0x23 0x21).
When an execution system call (such as execve) is made
on a script, the kernel reads this sequence, parses the interpreter path
that follows (e.g., #!/bin/bash or
#!/usr/bin/env python3), and loads that designated binary
to parse and run the remaining lines of the file.
Heuristic and Language Tests
When a file lacks a recognizable magic number, Linux falls back on algorithmic heuristics. The system inspects the file's byte distribution to differentiate between binary and text:
- Character Encoding Verification: The system scans
the byte stream for non-printable characters, control characters, and
null bytes (
0x00). If none are present, it evaluates whether the byte sequence conforms to valid ASCII, UTF-8, UTF-16, or other character encodings. - Syntactic Scanning: If the file is identified as
plain text, tools like
filescan for common keywords or syntax constructs (such as HTML tags, C source preprocessor directives like#include, or standard JSON structures) to provide an accurate format classification.
Through this multi-tiered approach—inode classification, magic byte matching, and heuristic content scanning—Linux guarantees that file formats are identified by their true structure rather than arbitrary naming conventions.