Skip to content

Add MariaDB data directory initialization functionality#181

Merged
jwaisner merged 2 commits intomainfrom
mariadb
Jan 16, 2026
Merged

Add MariaDB data directory initialization functionality#181
jwaisner merged 2 commits intomainfrom
mariadb

Conversation

@N6REJ
Copy link
Contributor

@N6REJ N6REJ commented Jan 12, 2026

PR Type

Enhancement, Bug fix


Description

  • Add MariaDB data directory initialization with initData() method

  • Support both init.bat and mariadb-install-db.exe initialization paths

  • Add getDataDir() method to retrieve MariaDB data directory path

  • Integrate data initialization into service installation and startup flows

  • Fix port error logging to include actual port and application name

  • Clean up code formatting and whitespace inconsistencies


Diagram Walkthrough

flowchart LR
  A["Service Install/Start"] --> B["Check initData Method"]
  B --> C["MariaDB initData"]
  C --> D["Check init.bat"]
  D -->|Exists| E["Execute init.bat"]
  D -->|Not Found| F["Use mariadb-install-db.exe"]
  E --> G["Verify mysql Directory"]
  F --> G
  G -->|Success| H["Return True"]
  G -->|Failure| I["Return False"]
Loading

File Walkthrough

Relevant files
Enhancement
class.bin.mariadb.php
MariaDB data initialization and getter methods                     

core/classes/bins/class.bin.mariadb.php

  • Add getDataDir() method returning MariaDB data directory path
  • Add comprehensive initData() method with dual initialization
    strategies
  • Support both init.bat and mariadb-install-db.exe for data
    initialization
  • Include verification checks and detailed logging throughout
    initialization process
  • Fix code formatting in getCliExe() and getAdmin() method braces
+93/-3   
class.batch.php
Add MariaDB batch initialization method                                   

core/classes/class.batch.php

  • Add initializeMariadb() static method for MariaDB initialization via
    init.bat
  • Method follows same pattern as existing initializePostgresql() with
    60-second timeout
  • Fix trailing whitespace in removeSymlink() method for code consistency
+18/-4   
class.win32service.php
Add MariaDB initialization to service start                           

core/classes/class.win32service.php

  • Add MariaDB data initialization in start() method alongside MySQL
  • Call initData() when starting MariaDB service
  • Maintain consistency with existing MySQL initialization pattern
+3/-0     
Enhancement, bug fix
class.util.php
Integrate data initialization into service operations       

core/classes/class.util.php

  • Add initData() call in installService() before service installation
  • Add initData() call in startService() before service startup
  • Fix port error logging to use correct variables $port and $isPortInUse
  • Ensure data directory is initialized before service operations
+11/-1   

@N6REJ N6REJ added bug 🐛 For known bugs Blocker 🚩 Show Stopper labels Jan 12, 2026
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 12, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Command injection

Description: The new initData() constructs and executes OS commands (via
Batch::initializeMariadb($path) / Batch::exec(...)) using $path (and derived $dataDir)
without clear validation/escaping guarantees, creating a potential command-injection/RCE
risk if an attacker can influence the MariaDB install path (e.g., a path containing
metacharacters or crafted quoting that breaks out of the intended command).
class.bin.mariadb.php [701-755]

Referred Code
public function initData($path = null, $version = null)
{
    Util::logTrace( 'Starting MariaDB data initialization' );
    $startTime = microtime( true );

    $path    = $path != null ? $path : $this->getCurrentPath();
    $version = $version != null ? $version : $this->getVersion();
    $dataDir = $path . '/data';

    if ( is_dir( $dataDir . '/mysql' ) ) {
        Util::logTrace( 'MariaDB data directory already initialized' );

        return true;
    }

    if ( !is_dir( $dataDir ) ) {
        @mkdir( $dataDir, 0777, true );
        Util::logTrace( 'Created MariaDB data directory' );
    }

    // Check for init.bat first


 ... (clipped 34 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Suppressed mkdir errors: The new data directory creation suppresses errors with @mkdir and does not validate the
result before continuing, which can lead to silent failures and misleading
"Created" logs.

Referred Code
if ( !is_dir( $dataDir ) ) {
    @mkdir( $dataDir, 0777, true );
    Util::logTrace( 'Created MariaDB data directory' );
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Command injection risk: The new command execution CMD /C " . $path . /init.bat" uses $path without
robust escaping/validation, which can allow command-line injection if $path is externally
influenced or contains special characters/quotes.

Referred Code
public static function initializeMariadb($path)
{
    if (!file_exists($path . '/init.bat')) {
        Util::logWarning($path . '/init.bat does not exist');
        return;
    }
    self::exec('initializeMariadb', 'CMD /C "' . $path . '/init.bat"', 60);
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing user context: The new MariaDB initialization logging records actions and outcomes but does not include a
user identity/actor context, which may be required for audit trails depending on how
service install/start is triggered.

Referred Code
Util::logTrace( 'Starting MariaDB data initialization' );
$startTime = microtime( true );

$path    = $path != null ? $path : $this->getCurrentPath();
$version = $version != null ? $version : $this->getVersion();
$dataDir = $path . '/data';

if ( is_dir( $dataDir . '/mysql' ) ) {
    Util::logTrace( 'MariaDB data directory already initialized' );

    return true;
}

if ( !is_dir( $dataDir ) ) {
    @mkdir( $dataDir, 0777, true );
    Util::logTrace( 'Created MariaDB data directory' );
}

// Check for init.bat first
if ( file_exists( $path . '/init.bat' ) ) {
    Util::logTrace( 'Initializing MariaDB via init.bat' );


 ... (clipped 43 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Exception message exposed: The new initialization flow logs raw exception messages via Util::logTrace(...), which may
expose internal details (paths/command errors) to log consumers depending on how trace
logs are surfaced.

Referred Code
    try {
        Batch::initializeMariadb( $path );
    } catch ( \Throwable $e ) {
        Util::logTrace( 'Error during MariaDB initialization via Batch: ' . $e->getMessage() );

        return false;
    }
} else {
    // Use mariadb-install-db.exe
    Util::logTrace( 'Initializing MariaDB via mariadb-install-db.exe' );
    $installDbExe = $path . '/bin/mariadb-install-db.exe';
    if ( !file_exists( $installDbExe ) ) {
        $installDbExe = $path . '/bin/mysql_install_db.exe';
    }

    if ( file_exists( $installDbExe ) ) {
        $cmd = '"' . Util::formatWindowsPath( $installDbExe ) . '"';
        $cmd .= ' --datadir="' . Util::formatWindowsPath( $dataDir ) . '"';

        try {
            Batch::exec( 'initializeMariadb', $cmd, 60 );


 ... (clipped 4 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Unstructured path logging: The new/updated logging writes unstructured strings and includes full filesystem paths
(e.g., $path . '/init.bat' and $link), which may be considered sensitive
depending on the deployment and log exposure controls.

Referred Code
public static function initializeMariadb($path)
{
    if (!file_exists($path . '/init.bat')) {
        Util::logWarning($path . '/init.bat does not exist');
        return;
    }
    self::exec('initializeMariadb', 'CMD /C "' . $path . '/init.bat"', 60);
}

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 12, 2026

PR Code Suggestions ✨

Latest suggestions up to fd39c7d

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix quoting for init script

In initializeMariadb, normalize the path to init.bat using
Util::formatWindowsPath() and use the CMD /C ""..."" quoting pattern to prevent
errors with paths containing spaces.

core/classes/class.batch.php [239-246]

 public static function initializeMariadb($path)
 {
-    if (!file_exists($path . '/init.bat')) {
-        Util::logWarning($path . '/init.bat does not exist');
+    $initBat = $path . '/init.bat';
+    if (!file_exists($initBat)) {
+        Util::logWarning($initBat . ' does not exist');
         return;
     }
-    self::exec('initializeMariadb', 'CMD /C "' . $path . '/init.bat"', 60);
+
+    $initBat = Util::formatWindowsPath($initBat);
+    self::exec('initializeMariadb', 'CMD /C ""' . $initBat . '""', 60);
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion addresses a common and critical issue with command execution on Windows involving paths with spaces, making the new initializeMariadb function significantly more robust.

Medium
Provide basedir for initialization

Add the --basedir argument to the mariadb-install-db.exe command to ensure it
can correctly locate its support files during data directory initialization.

core/classes/bins/class.bin.mariadb.php [739-754]

 if ( file_exists( $installDbExe ) ) {
     $cmd = '"' . Util::formatWindowsPath( $installDbExe ) . '"';
+    $cmd .= ' --basedir="' . Util::formatWindowsPath( $path ) . '"';
     $cmd .= ' --datadir="' . Util::formatWindowsPath( $dataDir ) . '"';
 
     try {
         Batch::exec( 'initializeMariadb', $cmd, 60 );
     } catch ( \Throwable $e ) {
         Util::logTrace( 'Error during MariaDB initialization via mariadb-install-db: ' . $e->getMessage() );
 
         return false;
     }
 } else {
     Util::logError( 'MariaDB initialization failed: No init.bat or mariadb-install-db.exe found' );
 
     return false;
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that mariadb-install-db.exe often requires the --basedir argument for reliable execution, preventing potential silent initialization failures.

Medium
Stop on failed initialization

Check the return value of $bin->initData() and abort the service installation or
start if it returns false, logging an error and notifying the user.

core/classes/class.util.php [1657-1659]

 if (method_exists($bin, 'initData')) {
-    $bin->initData();
+    $ok = $bin->initData();
+    if (!$ok) {
+        self::logError('Data directory initialization failed for ' . $bin->getName());
+        if ($showWindow) {
+            $bearsamppWinbinder->messageBoxError(
+                'Data directory initialization failed for ' . $bin->getName(),
+                $boxTitle
+            );
+        }
+        return false;
+    }
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the return value of initData() is ignored, which could lead to a broken service state, and proposes robust error handling that improves the new feature.

Medium
  • More

Previous suggestions

Suggestions up to commit 45925bf
CategorySuggestion                                                                                                                                    Impact
High-level
Initialization logic ignores custom data directories

The new MariaDB data initialization logic incorrectly uses a hardcoded data
directory path. It should instead read the datadir setting from the MariaDB
configuration file to ensure it uses the correct, potentially custom, data
directory.

Examples:

core/classes/bins/class.bin.mariadb.php [701-768]
public function initData($path = null, $version = null)
{
    Util::logTrace( 'Starting MariaDB data initialization' );
    $startTime = microtime( true );

    $path    = $path != null ? $path : $this->getCurrentPath();
    $version = $version != null ? $version : $this->getVersion();
    $dataDir = $path . '/data';

    if ( is_dir( $dataDir . '/mysql' ) ) {

 ... (clipped 58 lines)

Solution Walkthrough:

Before:

class BinMariadb extends Module {
    public function initData($path = null, $version = null) {
        $path = $path ?? $this->getCurrentPath();
        $dataDir = $path . '/data'; // Hardcoded path

        if (is_dir($dataDir . '/mysql')) {
            return true;
        }

        // ... create directory ...

        // ... run initialization commands using the hardcoded $dataDir ...
        $cmd = '"' . $installDbExe . '"';
        $cmd .= ' --datadir="' . Util::formatWindowsPath($dataDir) . '"';
        Batch::exec('initializeMariadb', $cmd, 60);
        // ...
    }
}

After:

class BinMariadb extends Module {
    private function getDatadirFromConfig() {
        // Logic to read this->conf file and parse the 'datadir' value
        // Fallback to default if not found
        $configFile = $this->getConf();
        // ... parse content for [mysqld] section and datadir key ...
        // return parsed_datadir or default_datadir
    }

    public function initData($path = null, $version = null) {
        $dataDir = $this->getDatadirFromConfig(); // Get path from config

        if (is_dir($dataDir . '/mysql')) {
            return true;
        }

        // ... create directory ...

        // ... run initialization commands using the configured $dataDir ...
        $cmd = '"' . $installDbExe . '"';
        $cmd .= ' --datadir="' . Util::formatWindowsPath($dataDir) . '"';
        Batch::exec('initializeMariadb', $cmd, 60);
        // ...
    }
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical flaw where the data initialization logic hardcodes the datadir path, ignoring the actual configuration file setting, which can lead to incorrect behavior and data management issues.

High
Possible issue
Handle the return value of initData

Check the boolean return value of $bin->initData() in installService and
startService. If it returns false, abort the operation by returning false to
prevent further execution with a failed data directory initialization.

core/classes/class.util.php [1657-1659]

 if (method_exists($bin, 'initData')) {
-    $bin->initData();
+    if (!$bin->initData()) {
+        return false;
+    }
 }
Suggestion importance[1-10]: 8

__

Why: This is a critical suggestion that fixes a major flaw in the PR. The initData method is designed to report success or failure, and ignoring its return value would cause services to attempt to start or install even after data initialization has failed, leading to subsequent errors.

Medium
Add explicit error handling for directory creation

Replace the error-suppressed mkdir call with explicit error handling. Check the
return value of mkdir, log an error, and return false if directory creation
fails.

core/classes/bins/class.bin.mariadb.php [716-719]

 if ( !is_dir( $dataDir ) ) {
-    @mkdir( $dataDir, 0777, true );
+    if ( !mkdir( $dataDir, 0777, true ) && !is_dir( $dataDir ) ) {
+        Util::logError( 'Failed to create MariaDB data directory at ' . $dataDir );
+        return false;
+    }
     Util::logTrace( 'Created MariaDB data directory' );
 }
Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies that suppressing errors with @mkdir is risky and can hide underlying filesystem issues. The proposed change adds proper error handling, making the data initialization process more robust and easier to debug.

Medium

@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 Security concerns

Command execution:
The PR adds new command execution paths (init.bat and mariadb-install-db.exe) constructed from filesystem paths. If the installation path can be influenced by untrusted input, this could enable command injection or execution of an unexpected binary/script. Ensure $path originates from trusted configuration and that Batch::exec does not invoke a shell in a way that allows injection beyond the quoted executable path.

⚡ Recommended focus areas for review

Error Handling

initData() suppresses directory creation errors (uses @mkdir) and does not validate creation success before proceeding. Also, failures from initialization commands are mostly handled, but the method may continue with a missing/invalid data directory, leading to harder-to-diagnose startup failures. Consider checking mkdir return value / is_dir after creation and logging a clear error.

public function initData($path = null, $version = null)
{
    Util::logTrace( 'Starting MariaDB data initialization' );
    $startTime = microtime( true );

    $path    = $path != null ? $path : $this->getCurrentPath();
    $version = $version != null ? $version : $this->getVersion();
    $dataDir = $path . '/data';

    if ( is_dir( $dataDir . '/mysql' ) ) {
        Util::logTrace( 'MariaDB data directory already initialized' );

        return true;
    }

    if ( !is_dir( $dataDir ) ) {
        @mkdir( $dataDir, 0777, true );
        Util::logTrace( 'Created MariaDB data directory' );
    }

    // Check for init.bat first
    if ( file_exists( $path . '/init.bat' ) ) {
        Util::logTrace( 'Initializing MariaDB via init.bat' );
        try {
            Batch::initializeMariadb( $path );
        } catch ( \Throwable $e ) {
            Util::logTrace( 'Error during MariaDB initialization via Batch: ' . $e->getMessage() );

            return false;
        }
    } else {
        // Use mariadb-install-db.exe
        Util::logTrace( 'Initializing MariaDB via mariadb-install-db.exe' );
        $installDbExe = $path . '/bin/mariadb-install-db.exe';
        if ( !file_exists( $installDbExe ) ) {
            $installDbExe = $path . '/bin/mysql_install_db.exe';
        }

        if ( file_exists( $installDbExe ) ) {
            $cmd = '"' . Util::formatWindowsPath( $installDbExe ) . '"';
            $cmd .= ' --datadir="' . Util::formatWindowsPath( $dataDir ) . '"';

            try {
                Batch::exec( 'initializeMariadb', $cmd, 60 );
            } catch ( \Throwable $e ) {
                Util::logTrace( 'Error during MariaDB initialization via mariadb-install-db: ' . $e->getMessage() );

                return false;
            }
        } else {
            Util::logError( 'MariaDB initialization failed: No init.bat or mariadb-install-db.exe found' );

            return false;
        }
    }

    // Verify initialization
    if ( !is_dir( $dataDir . '/mysql' ) ) {
        Util::logTrace( 'MariaDB initialization appears to have failed: mysql directory still missing' );

        return false;
    }

    $totalTime = round( microtime( true ) - $startTime, 2 );
    Util::logTrace( "MariaDB initialization completed in {$totalTime}s" );

    return true;
}
Ignored Result

Service install/start calls initData() but ignores its boolean result. If initialization fails, service installation/startup will proceed and may fail later with less actionable errors. Consider short-circuiting when initData() returns false and surfacing a user-facing error/log entry.

if (method_exists($bin, 'initData')) {
    $bin->initData();
}
Command Robustness

The initialization command for mariadb-install-db.exe uses only --datadir=... and relies on defaults for other parameters. Depending on MariaDB packaging, it may require additional flags (e.g., --basedir, defaults file, or explicit working dir). Also ensure quoting/escaping is sufficient for Windows paths with special characters.

Util::logTrace( 'Initializing MariaDB via mariadb-install-db.exe' );
$installDbExe = $path . '/bin/mariadb-install-db.exe';
if ( !file_exists( $installDbExe ) ) {
    $installDbExe = $path . '/bin/mysql_install_db.exe';
}

if ( file_exists( $installDbExe ) ) {
    $cmd = '"' . Util::formatWindowsPath( $installDbExe ) . '"';
    $cmd .= ' --datadir="' . Util::formatWindowsPath( $dataDir ) . '"';

    try {
        Batch::exec( 'initializeMariadb', $cmd, 60 );
    } catch ( \Throwable $e ) {
        Util::logTrace( 'Error during MariaDB initialization via mariadb-install-db: ' . $e->getMessage() );

        return false;
    }
} else {
    Util::logError( 'MariaDB initialization failed: No init.bat or mariadb-install-db.exe found' );

    return false;
}

@jwaisner jwaisner merged commit e9eef63 into main Jan 16, 2026
3 checks passed
@jwaisner jwaisner deleted the mariadb branch January 16, 2026 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Blocker 🚩 Show Stopper bug 🐛 For known bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants