Skip to content

Event export fails silently — per-event export subdirectory created without execute permission (missing chmod) #5074

Description

@techrockedge

Describe the bug
When exporting events (Console → select events → Export), the export can fail to include images, video, and the generated HTML detail/frame pages for an event, with no clear error shown in the UI. The underlying PHP log shows repeated cp: cannot stat ... Permission denied errors for files that do exist and are readable.
Root cause
In web/skins/classic/includes/export_functions.php, inside exportEvents(), the top-level export directory is created and explicitly locked down with a chmod:
$export_dir = ZM_DIR_EXPORTS.'/'.$export_root.($connkey?'_'.$connkey:'');
...
chmod($export_dir, 0700);
But the per-event subdirectory created just below it is not:
$event_dir = $export_dir.'/'.$event->Id();
if (!(@mkdir($event_dir) or file_exists($event_dir))) {
ZM\Error("Can't mkdir $event_dir");
}
// no chmod() call here
Since no explicit mode is passed to mkdir(), the resulting permissions on $event_dir depend entirely on the umask in effect for the PHP process at that moment. On systems where that umask is restrictive enough to remove all execute bits (e.g. resulting in 0660/drw-rw----), the directory becomes untraversable — even for root. This is a documented Linux kernel behavior: CAP_DAC_OVERRIDE does not bypass the directory search/execute check when a directory has zero execute bits set for anyone, so root-owned PHP processes (e.g. running under php-cgi as root) are blocked exactly the same as any other user.
The result is every subsequent file operation into $event_dir (copying JPEGs, writing zmEventDetail.html, zmEventFrames.html, etc.) fails with Permission denied, and the export silently produces an incomplete or empty archive.
To Reproduce

  1. Have a PHP-FPM/php-cgi environment with a umask that removes execute bits from newly created directories with default 0777 mode (e.g. a strict umask such as 0117). 2. Export any event from the ZoneMinder web UI (Console → Export, or the Download button). 3. Check /var/tmp/zm/zmExport_/<event_id> — its permissions will lack execute bits entirely. 4. Check the web server / PHP error log for repeated cp: cannot stat ...: Permission denied lines referencing files inside that directory.
    Expected behavior
    The per-event export subdirectory should have the same explicit, predictable permissions as the top-level export directory, regardless of the PHP process's umask.
    Suggested fix
    Add an explicit chmod() call after creating $event_dir, mirroring the existing handling of $export_dir:
    $event_dir = $export_dir.'/'.$event->Id();
    if (!(@mkdir($event_dir) or file_exists($event_dir))) {
    ZM\Error("Can't mkdir $event_dir");
    }
    chmod($event_dir, 0700);
    Patch attached (zm-export-event-dir-permissions.patch).
    Environment
    • ZoneMinder version: 1.38.4
    • OS: Void Linux
    • Web server / PHP: php-cgi (running as root)
    • Storage: local (/var/lib/zoneminder/events), not using symlinked deep storage in this case

zm-export-event-dir-permissions.patch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions