Documentation
¶
Overview ¶
Package softmagic is a pure-Go port of file(1)'s rule-driven matcher.
It compiles libmagic's Magdir source files unchanged and reproduces the reference's entry ordering exactly. The ordering, strength and tie-break code is a translation of apprentice.c from file 5.48, Copyright (c) Ian F. Darwin 1986-1995, maintained by Christos Zoulas; that notice is reproduced in COPYING.
This module is a library only. The command-line tool lives in the separate module code.eitanity.com/softmagic-cli.
Index ¶
- Constants
- func DumpSources(srcs []Source, o CompileOptions) (string, error)
- type Checks
- type CompileError
- type CompileOptions
- type Continued
- type Database
- func (db *Database) Entries() int
- func (db *Database) Hash() string
- func (db *Database) Identify(data []byte) Result
- func (db *Database) IdentifyAt(ctx context.Context, r io.ReaderAt, size int64, o Options) Result
- func (db *Database) IdentifyPrefix(ctx context.Context, prefix []byte, o Options) Result
- func (db *Database) IdentifyWith(ctx context.Context, data []byte, o Options) Result
- func (db *Database) Join(extra *Database, hash string) (*Database, error)
- func (db *Database) Lines() int
- func (db *Database) List() string
- func (db *Database) Marshal() ([]byte, error)
- type Examined
- type Failure
- type Failures
- type Limits
- type LoadError
- type Options
- type Result
- type RuleID
- type Source
Constants ¶
const ( // PhaseBuiltin is a built-in detector (tar, JSON, CSV, SIMH, CDF). PhaseBuiltin = "builtin" // PhaseMagic is a binary rule match, with any ELF text appended. PhaseMagic = "magic" // PhaseText is the text phase: text rules and the encoding // description. PhaseText = "text" // PhaseDefault is "data", "empty" or "very short file (no magic)". PhaseDefault = "default" )
Phases: which pass of file_buffer produced the description.
const ( TruncRecursion = "recursion" TruncSearch = "search" TruncRegex = "regex" TruncTime = "time" TruncOutput = "output" TruncInvariant = "invariant" // TruncCDF is a Composite Document File sector chain larger than the // library reads (only a header with a nonstandard sector size can // declare one); the document is described as one whose chain could // not be read. TruncCDF = "cdf" // TruncMemory is Options.MaxMemory: a read past the window that would // have held more than the budget failed, and the answer is the one // the reference gives when that read fails. TruncMemory = "memory" // TruncIndirect, TruncName and TruncELF are the hard limits of // Options.Limits: the reference stops with an error, and so does the // output mode that reached one (see Result.Failures). TruncIndirect = "indirect" TruncName = "name" TruncELF = "elf" // TruncRead is a window IdentifyAt could not read; see // Examined.ReadError. TruncRead = "read" )
Truncation reasons.
const ( // DefaultMaxBytes is how much of the input is consulted: the // reference's FILE_BYTES_MAX in file 5.48. DefaultMaxBytes = 7 * 1024 * 1024 // DefaultMaxMemory is how many bytes one call's working buffers may // hold beyond the input window, the default of Options.MaxMemory. DefaultMaxMemory = 64 << 20 )
Limits. The reference's are named beside each. A limit that fires is reported in Examined.Truncated, never hidden.
const ImplementsFile = "5.48"
ImplementsFile is the file(1) release whose grammar, strength function and sort this package reproduces. A compiled database made for another release is refused at load.
Variables ¶
This section is empty.
Functions ¶
func DumpSources ¶
func DumpSources(srcs []Source, o CompileOptions) (string, error)
DumpSources is file -c over rule sources: each rule line in the order read, in libmagic's parsed form, one per line as file_mdump prints it. The text is what was dumped before an error, if there was one; the reference prints a warning for a refused line and goes on, where this returns the CompileError.
Types ¶
type Checks ¶
type Checks uint16
Checks is a set of libmagic's optional checks, for Options.Exclude.
const ( CheckSoft Checks = 1 << iota // the rules: -e soft CheckText // the text phase: -e text, -e ascii CheckEncoding // the classification file_buffer passes on: -e encoding CheckTar // -e tar CheckJSON // -e json CheckCSV // -e csv CheckSIMH // -e simh CheckCDF // -e cdf CheckELF // -e elf CheckCompress // -e compress CheckAppType // -e apptype CheckTokens // -e tokens )
The checks file -e names. CheckCompress, CheckAppType and CheckTokens are accepted and have no effect, as in the reference on this platform: there is no decompression, the application-type check is OS/2's, and tokens is kept upstream only for compatibility.
type CompileError ¶
CompileError is a refused rule file: the file and line are always named so a malformed rule fails here, at compile time; identifying has no error path.
func (*CompileError) Error ¶
func (e *CompileError) Error() string
type CompileOptions ¶
type CompileOptions struct {
// Base, when set, is the database this one will be appended to: a
// "use" may name one of its "name" entries (a local rule
// file extending the embedded database, as file -m a:b does).
Base *Database
// SourceDir is the directory name the reference was given on its command
// line (file -m <dir>). The reference tucks "<dir>/<file>" into every
// empty description, where it takes part in the ordering tie-break
// so the port must know the same name to reproduce the same
// order. Default: "magic/Magdir".
SourceDir string
}
CompileOptions configures Compile. The zero value is the default.
type Continued ¶
type Continued struct {
// Descriptions is file -b -k.
Descriptions []string
// MIMEs is file -b -k --mime-type. file -b -k -i is MIMEs joined,
// then "; charset=" and Result.Charset.
MIMEs []string
// Encodings is file -b -k --mime-encoding: separators from the matches,
// which print nothing in that mode, then the charset in the last
// element.
Encodings []string
// Extensions is file -b -k --extension, each element as printed
// ("jpeg/jpg").
Extensions []string
// Apple is file -b -k --apple.
Apple []string
// Failures are the continue runs that a hard limit stopped; a mode
// that failed has no list.
Failures Failures
}
Continued is a continue-mode answer (libmagic's MAGIC_CONTINUE, file -k). The reference computes each output mode in a run of its own, and an entry can answer in one mode and not another (a match with no MIME annotation is a description and no MIME type), so element i of one list is not about the same entry as element i of another. Each list is that mode's output split where a separator was printed. Without Options.Raw the elements are escaped as the reference escapes the one buffer it prints them in, so joined with "\\012- " (the separator as that escaping prints it) a list is byte-identical to file -b -k in that mode; with Raw, joined with "\n- ", to file -b -k -r. An element can be empty: the reference prints a separator after a match that printed nothing in that mode. All are nil without Options.Continue.
type Database ¶
type Database struct {
// contains filtered or unexported fields
}
Database is a compiled, immutable rule set. It is safe for concurrent use: nothing writes to it after Compile returns, and all per-call state lives in the pooled scan.
func CompileSources ¶
func CompileSources(hash string, srcs []Source, o CompileOptions) (*Database, error)
CompileSources builds the sorted database from rule sources in the order given (the reference reads a directory in byte order of name) and records hash as the database's identity, which Hash and Examined.DatabaseHash report. The compile package reads a directory and hashes it; call that unless the rules are already in memory.
func Default ¶
Default loads the database compiled into the library from the vendored Magdir of the release named by ImplementsFile. Load it once and keep it; it is safe for concurrent use. Its regexes compile on first use, which keeps the load near a millisecond; DefaultEager compiles them now. Unlike Load it does not compile them to check them: the embedded bytes are what Compile produced, which TestEmbeddedCurrent enforces.
func DefaultEager ¶
DefaultEager is Default with every regex compiled before it returns: no lazy work and no allocation after this call, at about 1.5 ms more load time. A long-running process wants this; a one-file command does not.
func Load ¶
Load reads a compiled database. It refuses a different format version or a database compiled for a different file(1) release, naming both, and any record Compile could not have produced, including a regex RE2 rejects: every regex is compiled before Load returns (about 1 ms for the whole Magdir), since the bytes may come from anywhere.
func LoadEager ¶
LoadEager is Load, which already compiles every regex before it returns; it is kept beside DefaultEager for callers that name it.
func (*Database) Hash ¶
Hash is the SHA-256 of the Magdir source the database was compiled from, as hex: Examined.DatabaseHash in every Result.
func (*Database) Identify ¶
Identify returns what file(1) would print for data, with the default options and no deadline. It is safe to call from any number of goroutines on one Database.
func (*Database) IdentifyAt ¶
IdentifyAt identifies an input of the given size that r reads: the first MaxBytes are consulted as Identify would, and the ELF built-in may read headers beyond them, as file(1) does through its file descriptor. Examined.Bytes counts only the window.
func (*Database) IdentifyPrefix ¶
IdentifyPrefix identifies from a prefix of the input: Examined.Complete is false, and NeedMore reports whether more bytes could change the answer.
func (*Database) IdentifyWith ¶
IdentifyWith is Identify with a context, whose deadline or cancellation stops the match with Truncated "time", and explicit options.
func (*Database) Join ¶
Join returns a new database that searches db first and then extra, as file(1) searches the directories of `-m a:b` in order: each keeps its own sort, a "use" may name an entry of either, and the first map to match answers. hash identifies the combination; the compile package's Append derives one from both inputs' hashes. Neither input is modified.
func (*Database) List ¶
List prints every entry in matching order in the format of file -l:
Set 0: Binary patterns: Strength = 670@1446: WonderWitch transfer file [] ... Text patterns: ... Set 1: ...
The output is byte-identical to the reference's `file -m <dir> -l` for the same Magdir; the tests compare the two in full, which is what proves the matching order.
type Examined ¶
type Examined struct {
// Truncated is "" or what stopped a match, one of the Trunc constants:
// "recursion", "search", "regex", "time", "output", "invariant" (an
// internal consistency check failed and the entry under evaluation was
// skipped), "cdf", "memory", "indirect", "name", "elf" or "read".
Truncated string
// ReadError is the error IdentifyAt's reader returned for the window,
// when it read fewer bytes than asked and the error is not io.EOF.
// Nothing was identified: Truncated is TruncRead and the answer is
// the default, "data". file reports such an input as unreadable.
ReadError error
// DatabaseHash is the hash of the Magdir source the database was
// compiled from.
DatabaseHash string
// ImplementsFile is the file(1) release whose grammar and ordering this
// library reproduces.
ImplementsFile string
// Bytes is the number of input bytes actually consulted.
Bytes int
// Complete is true when the whole input was available; false for a
// bounded prefix (IdentifyPrefix).
Complete bool
// NeedMore is true when a longer prefix could change Description or
// MIME; always false when Complete.
NeedMore bool
}
Examined reports the scope and the limits of one identification.
type Failure ¶
type Failure struct {
// Message is libmagic's own: "indirect count (50) exceeded".
Message string
// Buffer is what the output buffer held: what the mode had printed, or,
// when Pushed, the output of the indirect match or ELF reader the error
// came inside, which the reference reads into a buffer of its own.
Buffer string
Pushed bool
}
Failure is libmagic's error for one output mode; the zero value is none. libmagic appends Message to its current output buffer, after a blank when that is not empty, and magic_error returns the result: see Text.
type Failures ¶
type Failures struct {
Description Failure
MIME Failure // also -i, which is the MIME run with the charset after it
Encoding Failure
Extension Failure
Apple Failure
}
Failures holds, per output mode, the error libmagic reports when a hard limit of Options.Limits stopped that mode's run. Each mode is a run of its own in the reference, and a run that returns at its first annotation can finish before the line that stops another, so one mode can fail while another answers.
type Limits ¶
type Limits struct {
Indirect int // -P indir: indirect evaluations per pass over the rules (50)
Name int // -P name: nested name/use evaluations (150)
Regex int // -P regex: bytes a regex test scans (8192)
Encoding int // -P encoding: bytes classified as text or not (65536)
ELFNotes int // -P elf_notes: ELF notes read (256)
ELFPhnum int // -P elf_phnum: ELF program headers read (2048)
ELFShnum int // -P elf_shnum: ELF section headers read (32768)
ELFShsize int // -P elf_shsize: largest ELF note section read (128 MiB)
}
Limits are libmagic's MAGIC_PARAM_* parameters (file -P) other than the byte count, which is Options.MaxBytes. Zero means file's default; a negative value stands for file's 0; a value above what file -P accepts is clamped to it. Reaching Indirect, Name or ELFShsize stops the identification as the reference's error does: see Result.Failures.
type Options ¶
type Options struct {
// MaxBytes caps the input consulted (file -P bytes); 0 means
// DefaultMaxBytes, a negative value means none at all, as bytes=0
// does for the reference, and values above 0x7fffffff, the most file
// -P accepts, are clamped to it. IdentifyAt allocates the window, up
// to the smaller of MaxBytes and the input's size, before it reads.
MaxBytes int
// MaxDepth caps use/indirect nesting; 0 means maxFrames, and larger
// values are clamped to it.
MaxDepth int
// Executable is the input file's executable permission bit, which the
// reference consults through ${x?a:b} in rule descriptions (file(1)
// sets it from stat; an ELF dynamic section overrides it).
Executable bool
// Continue is libmagic's MAGIC_CONTINUE (file -k): Result.Continued
// lists every match in each output mode. The other fields of the
// Result are what they are without it.
Continue bool
// Raw is libmagic's MAGIC_RAW (file -r): the answer is returned as it
// was printed. Without it, strings copied from the input have their
// non-printable bytes as \ooo escapes, and the whole answer then goes
// through the reference's output escaping, which writes as \ooo every
// character glibc's iswprint rejects in a UTF-8 locale (a control byte
// a %c rule copied, say) and, when the answer is not valid UTF-8,
// every byte outside printable ASCII.
Raw bool
// SafeText is not in the reference: an answer safe to place in HTML, a
// log line or a quoted value as it is. Every byte copied from the
// input is written as \xHH unless it is printable ASCII other than
// \ < > & " ' and `, so text from the file can neither open markup
// nor end a quoted value, and each backslash from the file starts an
// escape, which makes the text decode exactly. The answer as a whole,
// failure buffers included, is then printable ASCII: any other byte,
// such as the UTF-8 of a rule's own text, is \xHH too. Rule text is
// otherwise unchanged and the classification is the same. It
// overrides Raw. The answer is no longer the reference's.
SafeText bool
// Exclude switches checks off, as libmagic's MAGIC_NO_CHECK_* flags
// (file -e) do.
Exclude Checks
// Limits are libmagic's other MAGIC_PARAM_* parameters (file -P).
Limits Limits
// MaxMemory caps the bytes one call's working buffers may hold beyond
// the input window: the tables and streams of a Composite Document
// File, ELF data read past the window, and the file's tail. 0 means
// DefaultMaxMemory, values above 1 GiB are clamped to it, and a
// negative value allows none. A read that would need more fails, with
// Truncated "memory". The reference has no such limit.
MaxMemory int
}
Options configures one identification. The zero value means defaults.
type Result ¶
type Result struct {
// Description is exactly what `file -b` prints.
Description string
// MIME is exactly what `file -b -i` prints before "; charset=".
MIME string
// Charset is the charset `file -i` reports; "binary" for binary input.
Charset string
// Extensions is the !:ext list of the first annotated line on the
// winning path, split on '/'; nil when there is none.
Extensions []string
// Apple is the !:apple creator/type of the first annotated line on the
// winning path (what `file --apple` prints); "" when there is none.
Apple string
// Rules lists every rule line that contributed text, in emission order.
Rules []RuleID
// Phase names the pass that produced Description: PhaseBuiltin,
// PhaseMagic, PhaseText or PhaseDefault. file(1)'s driver needs it:
// the reference joins its stat-layer prefix to a text-phase
// description differently from the others.
Phase string
// Continued is set with Options.Continue: what `file -b -k` prints in
// each output mode, as lists.
Continued Continued
// Failures is set when a hard limit of Options.Limits stopped an output
// mode, where the reference reports an error; the fields above are then
// what was established before it.
Failures Failures
// Examined says what the call looked at and what stopped it.
Examined Examined
// Strength is the winning entry's computed strength (as file -l prints it), 0 when
// no entry matched.
Strength int
}
Result is what Identify returns. A blank is never an answer: an input that matches no rule has Description "data" and MIME "application/octet-stream", as file(1) prints.
Source Files
¶
- ascmagic.go
- bits.go
- builtin.go
- cdf.go
- cdf2.go
- check.go
- compile.go
- compiled.go
- continue.go
- convert.go
- database.go
- der.go
- doc.go
- dump.go
- elf.go
- elfnote.go
- encoding.go
- errors.go
- escape.go
- format.go
- getstr.go
- identify.go
- input.go
- json.go
- match.go
- memo.go
- mget.go
- number.go
- parse.go
- prefilter.go
- print.go
- printf.go
- record.go
- regex.go
- result.go
- scan.go
- strength.go
- types.go
- utf8.go
- value.go
- wctype.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package compile builds a softmagic database from rule source on a file system, as file(1) reads a magic directory, and identifies the result by a SHA-256 of what it read.
|
Package compile builds a softmagic database from rule source on a file system, as file(1) reads a magic directory, and identifies the result by a SHA-256 of what it read. |
|
internal
|
|
|
invariant
Package invariant is the module's one assertion primitive (Power of Ten rule 5: assertions stay in production and have a recovery action).
|
Package invariant is the module's one assertion primitive (Power of Ten rule 5: assertions stay in production and have a recovery action). |
|
rules
Package rules holds the test that checks this module's own code against the parts of Holzmann's Power of Ten ("The Power of 10: Rules for Developing Safety-Critical Code", IEEE Computer 39(6), 2006) that no linter checks: recursion, loop forms, assertion density, conversions to int that a 32-bit host would wrap, pointer and func types, package-level state, build tags and suppression comments.
|
Package rules holds the test that checks this module's own code against the parts of Holzmann's Power of Ten ("The Power of 10: Rules for Developing Safety-Critical Code", IEEE Computer 39(6), 2006) that no linter checks: recursion, loop forms, assertion density, conversions to int that a 32-bit host would wrap, pointer and func types, package-level state, build tags and suppression comments. |