Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,075 of 9,075 pages
About the help in other languages
All help pages

The .ezpkg file

What an .ezpkg archive is, what is inside it and where, the names it is given, the checks an archive passes before anything of it is written, and how to open, list and check one by hand.

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/help/site-packages/basics/the-ezpkg-file.md

On this page

The .ezpkg file

This page is for administrators and developers who download, upload, unpack or build package archives. It answers: what format an .ezpkg file is, what is inside, which checks it must pass on import, and how to look into one without installing it.

Contents

  1. The format
  2. What is inside
  3. File names
  4. How Exponential reads an archive
  5. The checks before anything is written
  6. Opening an .ezpkg by hand
  7. How the 7x archives are built
  8. Related pages

The format

An .ezpkg file is a gzip compressed tar archive of one package directory. Nothing else: no header, no signature, no manifest besides the package's own definition file. The suffix comes from eZPackage::suffix() (ezpkg). When the kernel exports a package itself, eZPackage::exportToArchive() writes a tar archive and compresses it with zlib; it can also write tar.bz2 and zip, but tar.gz and ezpkg are the same thing and the default.

The JSON format of a package is an .ezpkg too, with the same layout; only the definition file and the documents differ. See Classic and JSON packages.

What is inside

The archive holds the contents of the package directory, not the directory: package.xml is at the top of the archive, not inside a folder named after the package. A typical layout:

Path in the archiveWhat it isWritten by
package.xml (or package.json)The definition: name, type, version, dependencies, install itemseZPackage::storePackageFile()
documents/Documents named in <documents>, for example LICENCEeZPackage::documentDirectory()
files/<collection>/<file type>/Files named in <files>: thumbnails, images, design files (files/default/thumbnail/thumbnail.png)eZPackage::appendFile()
simplefiles/Files kept by key, such as images of content objects (simplefiles/24ec46f7.jpg)eZPackage::appendSimpleFile()
settings/The install scripts of a site packagethe package author
ezcontentclass/One XML document per content class (class-article.xml)the ezcontentclass handler
ezcontentobject/contentobjects.xml and one object-<remote id>.xml per objectthe ezcontentobject handler
ezextension/extension-<name>.xml and the extension's filesthe ezextension handler
sql/[<database type>/]SQL files of ezsql itemsthe ezdb handler

exportToArchive() copies documents, files, simplefiles, settings and the sub-directory of every install item, so a file outside those directories does not survive an export. The .cache/ directory that the local repository keeps next to package.xml is never part of a package.

Listing two real archives from the package server:

curl -s https://exponential.packages.exponential.earth/exponential/6.0/6.0.15/sevenx_multisite.ezpkg | tar -tzf -
curl -s https://exponential.packages.exponential.earth/exponential/6.0/6.0.15/sevenx_classes.ezpkg | tar -tzf - | head
documents/
documents/LICENCE
ezcontentclass/
ezcontentclass/class-real_video.xml
ezcontentclass/class-comment.xml
...

File names

WhereNameExample
Package server<name>.ezpkgsevenx_multisite.ezpkg
Export from Setup > Packages or ezpm export (classic)<name>-<version>-<release>.ezpkgmypackage-1.0.0-1.ezpkg
Export in the JSON format<name>-<version>-<release>.json.ezpkgmypackage-1.0.0-1.json.ezpkg

The name of the file does not matter on import: the package's name comes from <name> in its definition (eZPackage::importUnaudited()), and the format from which definition file is at the top. The setup wizard's upload strips a -<digit>.<digits>-<digit>.ezpkg ending from the file name when it reports what it imported (eZStepSiteTypes::uploadPackage()).

How Exponential reads an archive

eZPackage::import() (and importUnaudited() behind it) works in this order:

  1. Scan the tar headers (expPackageArchive::index(), next section), then open the archive through compress.zlib:// (a gzip compressed archive, or a plain tar archive as it is). An archive the archive library reads as it is (the ustar magic in every header, only files, directories and GNU long names, as Exponential's export writes) is opened directly. Any other is first rewritten into that form in a temporary file below the cache directory, which is removed as soon as it is open: the V7 archives eZ Publish 3.x wrote, PAX archives (bsdtar on macOS and the BSDs, GNU tar --format=posix, Python's tarfile), archives repacked with GNU tar (./ in front of every name, hard and symbolic links), and any with a tar type the library cannot read.
  2. Extract only package.xml and package.json (plain files only, never a link) into a temporary directory below the cache directory (packages/import/), read the definition, and delete the temporary copy.
  3. Refuse the package when its name is not valid (STATUS_INVALID_NAME), or when a package of that name already exists in the repository (STATUS_ALREADY_EXISTS).
  4. Choose the repository: the one passed in, else the directory made from <vendor>.
  5. Check every entry of the archive (next section).
  6. Extract the whole archive into var/storage/packages/<repository>/<name>/ and read the package back. Every extracted file gets mode 0644 and every directory 0755 (within the installation's file mode limits), whatever modes the archive gives (expPackageArchiveModes).

The import of an archive is recorded in the audit trail as system.package.import, with the archive's SHA-256 and never its content (see doc/eng-us/bc/6.0/audit.md).

The checks before anything is written

eZPackageUploadInspector holds the rules. eZPackage::importUnaudited() calls its entriesProblem() for every archive it extracts, so the setup wizard, ezpm import and the upload page are all covered:

CheckRefusal codeLimit
Every tar header readable, its checksum right, no data cut off, every PAX header well formedunreadable
A long name (GNU L/K, PAX path/linkpath) at most 4096 bytes, a PAX header at most 64 KiBtar_long_name
No file or directory name in a path longer than 255 bytes (no file system stores it)tar_name_segment
At most MaxEntries entriestoo_many_entries50000
Every path relative, without .., a back slash, NUL or a control characterunsafe_path
Unpacked size at most MaxUnpackedSize (copies of links count)too_large_unpacked2147483648 (2 GiB)
The vendor gives a usable repository directory name(import refused)

What a package cannot hold does not refuse the package. It is passed over with a notice, and the rest of the package is imported; the notices are written to the debug log and listed among the validation warnings of the upload page:

EntryWhat happensNotice
A hard link or a symbolic link to a file of the packageImported as a copy of that file
A symbolic link with an absolute target, or one whose .. leaves the packagePassed overlink_outside
A symbolic link to a directoryPassed overlink_directory
A link to a path the package does not hold (or links going round in a circle)Passed overlink_missing
A character or block device, a fifo (tar types 3, 4, 6)Passed overspecial_file
A sparse file (S, PAX GNU.sparse) or a tar type no package tool writesPassed overunsupported_entry

Read without a notice: regular files typed 0, \0 (V7, eZ Publish 3.x) and 7; directories 5, the GNU dump directory D and a file entry whose name ends in /; GNU long names L and K (also of a multiple of 512 bytes); PAX extended headers (path, linkpath, size, mtime; extended attributes and the other records are ignored); the PAX global header g and the GNU volume label V, which name no entry; the entry ./ itself.

The administration's upload page (package/upload) runs the full inspect() first, which adds:

CheckRefusal codeSetting
The uploaded name ends in an allowed suffixsuffixAllowedSuffixes[]: ezpkg, tar.gz, tgz
The file is at most MaxArchiveSize bytestoo_large268435456 (256 MiB)
The file starts with the gzip signature, or is a plain tar archivenot_gzip
A definition file at the top, well formed, at most 16 MiB, with a valid nameno_definition, definition_malformed, ...

The tar header checks run before the archive library reads a single entry, header by header, with the data blocks skipped by their size, and the archive library is only ever given an archive in the form it reads: its GNU reader looped until max_execution_time on a header type it does not know (a PAX header, a regular file typed \0 with the ustar magic), read a long name into memory in whatever size the header gave, and read one block too many after a long name whose size is a multiple of 512 bytes. The packages of every generation import: the V7 archives of eZ Publish 3.x, the ustar archives of the 4.x and 5.x exports, and packages repacked with GNU tar (in any of its formats), bsdtar or Python's tarfile. The archives the kernel's tests check this with are in tests/tests/kernel/classes/packages/fixtures/legacy/, built by build-legacy-package-fixtures.php next to them.

The limits are in settings/package.ini [UploadSettings]; change them in settings/override/package.ini.append.php.

[UploadSettings]
MaxArchiveSize=268435456
MaxUnpackedSize=2147483648
MaxEntries=50000
AllowedSuffixes[]
AllowedSuffixes[]=ezpkg
AllowedSuffixes[]=tar.gz
AllowedSuffixes[]=tgz

Note

eZPackage::parseDOMTree() also refuses a definition whose <version><number>, <version><release> or <type value> is not a plain token of up to 64 letters, digits and ._+- (eZPackage::isValidDefinitionToken()). Such a package is not read at all. See package.xml reference.

Opening an .ezpkg by hand

You need nothing but tar. Work in a scratch directory of your own, never in var/storage/packages/.

# list the entries
tar -tzf sevenx_multisite.ezpkg

# print the definition without unpacking anything else
tar -xzOf sevenx_multisite.ezpkg package.xml

# unpack into a new directory
mkdir sevenx_multisite && tar -xzf sevenx_multisite.ezpkg -C sevenx_multisite

# check a package without importing it (from the installation root)
php ezpm.php validate sevenx_multisite.ezpkg

ezpm validate checks the definition, every document the install items name, and, for a JSON package, the schema and the checksums; it prints PASS or FAIL and exits with 1 on failure (see the JSON package format). To bring an archive into an installation, see Downloading a package by hand and Import versus install.

How the 7x archives are built

The 7x packages are kept as plain folders in Git (se7enxweb/sevenx-ezpackage, packages/<name>/). packages/build.sh turns each folder into an archive from inside the folder, which is why there is no top folder:

cd "${folder%/}"
tar -zcf ../${folder}.ezpkg "${EXCLUDES[@]}" *

EXCLUDES leaves out version control files (.git, .gitignore, .gitattributes, .gitmodules, .svn, .hg), editor and system leftovers (.DS_Store, .idea, .vscode, *~, *.swp) and node_modules. How a built archive reaches the server is described in How a release reaches the package server.

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x