mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Fix #80329: Add option to specify LOAD DATA LOCAL white list folder
* allow the user to specify a folder where files that can be sent via LOAD DATA LOCAL can exist * add mysqli.local_infile_directory for mysqli (ignored if mysqli.allow_local_infile is enabled) * add PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY for pdo_mysql (ignored if PDO::MYSQL_ATTR_LOCAL_INFILE is enabled) * add related tests * fixes for building with libmysql 8.x * small improvement in existing tests * update php.ini-[development|production] files Closes GH-6448. Co-authored-by: Nikita Popov <nikic@php.net>
This commit is contained in:
parent
7f8ea83ef4
commit
da011a312a
40 changed files with 743 additions and 26 deletions
2
NEWS
2
NEWS
|
@ -24,6 +24,8 @@ PHP NEWS
|
|||
. Fixed bug #70372 (Emulate mysqli_fetch_all() for libmysqlclient). (Nikita)
|
||||
. Fixed bug #80330 (Replace language in APIs and source code/docs).
|
||||
(Darek Ślusarczyk)
|
||||
. Fixed bug #80329 (Add option to specify LOAD DATA LOCAL white list folder
|
||||
(including libmysql)). (Darek Ślusarczyk)
|
||||
|
||||
- Opcache:
|
||||
. Added inheritance cache. (Dmitry)
|
||||
|
|
12
UPGRADING
12
UPGRADING
|
@ -176,6 +176,18 @@ PHP 8.1 UPGRADE NOTES
|
|||
Note, that the quality of the custom secret is crucial for the quality of the resulting hash. It is
|
||||
highly recommended for the secret to use the best possible entropy.
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli.local_infile_directory ini setting has been added, which can be
|
||||
used to specify a directory from which files are allowed to be loaded. It
|
||||
is only meaningful if mysqli.allow_local_infile is not enabled, as all
|
||||
directories are allowed in that case.
|
||||
|
||||
- PDO MySQL:
|
||||
. The PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY attribute has been added, which
|
||||
can be used to specify a directory from which files are allowed to be
|
||||
loaded. It is only meaningful if PDO::MYSQL_ATTR_LOCAL_INFILE is not
|
||||
enabled, as all directories are allowed in that case.
|
||||
|
||||
- PDO SQLite:
|
||||
. SQLite's "file:" DSN syntax is now supported, which allows specifying
|
||||
additional flags. This feature is not available if open_basedir is set.
|
||||
|
|
|
@ -18,6 +18,8 @@ jobs:
|
|||
set -o
|
||||
sudo service mysql start
|
||||
mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
|
||||
# Ensure local_infile tests can run.
|
||||
mysql -uroot -proot -e "SET GLOBAL local_infile = true"
|
||||
displayName: 'Setup MySQL server'
|
||||
# Does not support caching_sha2_auth :(
|
||||
#- template: libmysqlclient_test.yml
|
||||
|
|
|
@ -5,6 +5,8 @@ steps:
|
|||
sudo service postgresql start
|
||||
sudo service slapd start
|
||||
mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS test"
|
||||
# Ensure local_infile tests can run.
|
||||
mysql -uroot -proot -e "SET GLOBAL local_infile = true"
|
||||
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
|
||||
sudo -u postgres psql -c "CREATE DATABASE test;"
|
||||
docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P "<YourStrong@Passw0rd>" -Q "create login pdo_test with password='password', check_policy=off; create user pdo_test for login pdo_test; grant alter, control to pdo_test;"
|
||||
|
|
|
@ -499,6 +499,7 @@ PHP_INI_BEGIN()
|
|||
#endif
|
||||
STD_PHP_INI_BOOLEAN("mysqli.reconnect", "0", PHP_INI_SYSTEM, OnUpdateLong, reconnect, zend_mysqli_globals, mysqli_globals)
|
||||
STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "0", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals)
|
||||
STD_PHP_INI_ENTRY("mysqli.local_infile_directory", NULL, PHP_INI_SYSTEM, OnUpdateString, local_infile_directory, zend_mysqli_globals, mysqli_globals)
|
||||
PHP_INI_END()
|
||||
/* }}} */
|
||||
|
||||
|
@ -523,6 +524,7 @@ static PHP_GINIT_FUNCTION(mysqli)
|
|||
mysqli_globals->report_mode = 0;
|
||||
mysqli_globals->report_ht = 0;
|
||||
mysqli_globals->allow_local_infile = 0;
|
||||
mysqli_globals->local_infile_directory = NULL;
|
||||
mysqli_globals->rollback_on_cached_plink = FALSE;
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -600,6 +602,9 @@ PHP_MINIT_FUNCTION(mysqli)
|
|||
REGISTER_LONG_CONSTANT("MYSQLI_READ_DEFAULT_FILE", MYSQL_READ_DEFAULT_FILE, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("MYSQLI_OPT_CONNECT_TIMEOUT", MYSQL_OPT_CONNECT_TIMEOUT, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("MYSQLI_OPT_LOCAL_INFILE", MYSQL_OPT_LOCAL_INFILE, CONST_CS | CONST_PERSISTENT);
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(MYSQLI_USE_MYSQLND)
|
||||
REGISTER_LONG_CONSTANT("MYSQLI_OPT_LOAD_DATA_LOCAL_DIR", MYSQL_OPT_LOAD_DATA_LOCAL_DIR, CONST_CS | CONST_PERSISTENT);
|
||||
#endif
|
||||
REGISTER_LONG_CONSTANT("MYSQLI_INIT_COMMAND", MYSQL_INIT_COMMAND, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("MYSQLI_OPT_READ_TIMEOUT", MYSQL_OPT_READ_TIMEOUT, CONST_CS | CONST_PERSISTENT);
|
||||
#ifdef MYSQLI_USE_MYSQLND
|
||||
|
@ -1021,7 +1026,7 @@ void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend
|
|||
MYSQL_ROW row;
|
||||
unsigned int i, num_fields;
|
||||
MYSQL_FIELD *fields;
|
||||
zend_ulong *field_len;
|
||||
unsigned long *field_len;
|
||||
|
||||
if (!(row = mysql_fetch_row(result))) {
|
||||
RETURN_NULL();
|
||||
|
|
|
@ -1210,7 +1210,7 @@ PHP_FUNCTION(mysqli_fetch_lengths)
|
|||
#ifdef MYSQLI_USE_MYSQLND
|
||||
const size_t *ret;
|
||||
#else
|
||||
const zend_ulong *ret;
|
||||
const unsigned long *ret;
|
||||
#endif
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
|
||||
|
@ -1673,6 +1673,9 @@ static int mysqli_options_get_option_zval_type(int option)
|
|||
case MYSQL_SET_CHARSET_DIR:
|
||||
#if MYSQL_VERSION_ID > 50605 || defined(MYSQLI_USE_MYSQLND)
|
||||
case MYSQL_SERVER_PUBLIC_KEY:
|
||||
#endif
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(MYSQLI_USE_MYSQLND)
|
||||
case MYSQL_OPT_LOAD_DATA_LOCAL_DIR:
|
||||
#endif
|
||||
return IS_STRING;
|
||||
|
||||
|
|
|
@ -332,6 +332,12 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, bool is_real_connect, b
|
|||
unsigned int allow_local_infile = MyG(allow_local_infile);
|
||||
mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&allow_local_infile);
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(MYSQLI_USE_MYSQLND)
|
||||
if (MyG(local_infile_directory) && !php_check_open_basedir(MyG(local_infile_directory))) {
|
||||
mysql_options(mysql->mysql, MYSQL_OPT_LOAD_DATA_LOCAL_DIR, MyG(local_infile_directory));
|
||||
}
|
||||
#endif
|
||||
|
||||
end:
|
||||
if (!mysqli_resource) {
|
||||
mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
|
||||
|
|
|
@ -267,7 +267,7 @@ static int result_lengths_read(mysqli_object *obj, zval *retval, bool quiet)
|
|||
#ifdef MYSQLI_USE_MYSQLND
|
||||
const size_t *ret;
|
||||
#else
|
||||
const zend_ulong *ret;
|
||||
const unsigned long *ret;
|
||||
#endif
|
||||
uint32_t field_count;
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ typedef _Bool my_bool;
|
|||
#include <errmsg.h>
|
||||
#include <mysqld_error.h>
|
||||
#include "mysqli_libmysql.h"
|
||||
|
||||
#endif /* MYSQLI_USE_MYSQLND */
|
||||
|
||||
|
||||
|
@ -276,6 +277,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqli)
|
|||
char *default_pw;
|
||||
zend_long reconnect;
|
||||
zend_long allow_local_infile;
|
||||
char *local_infile_directory;
|
||||
zend_long strict;
|
||||
zend_long error_no;
|
||||
char *error_msg;
|
||||
|
|
|
@ -55,6 +55,5 @@ $link->close();
|
|||
unlink('bug77956.data');
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: mysqli::query(): LOAD DATA LOCAL INFILE forbidden in %s on line %d
|
||||
[006] [2000] LOAD DATA LOCAL INFILE is forbidden, check mysqli.allow_local_infile
|
||||
done
|
||||
[006] [2000] LOAD DATA LOCAL INFILE is forbidden, check related settings like mysqli.allow_local_infile|mysqli.local_infile_directory or PDO::MYSQL_ATTR_LOCAL_INFILE|PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY
|
||||
done
|
3
ext/mysqli/tests/foo/bar/bar.data
Normal file
3
ext/mysqli/tests/foo/bar/bar.data
Normal file
|
@ -0,0 +1,3 @@
|
|||
97
|
||||
98
|
||||
99
|
3
ext/mysqli/tests/foo/foo.data
Normal file
3
ext/mysqli/tests/foo/foo.data
Normal file
|
@ -0,0 +1,3 @@
|
|||
1
|
||||
2
|
||||
3
|
|
@ -6,8 +6,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
function check_local_infile_support($link, $engine, $table_name = 'test') {
|
||||
|
||||
function check_local_infile_allowed_by_server($link) {
|
||||
if (!$res = mysqli_query($link, 'SHOW VARIABLES LIKE "local_infile"'))
|
||||
return "Cannot check if Server variable 'local_infile' is set to 'ON'";
|
||||
|
||||
|
@ -16,6 +15,15 @@
|
|||
if ('ON' != $row['Value'])
|
||||
return sprintf("Server variable 'local_infile' seems not set to 'ON', found '%s'", $row['Value']);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function check_local_infile_support($link, $engine, $table_name = 'test') {
|
||||
$res = check_local_infile_allowed_by_server($link);
|
||||
if ($res) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
if (!mysqli_query($link, sprintf('DROP TABLE IF EXISTS %s', $table_name))) {
|
||||
return "Failed to drop old test table";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
--TEST--
|
||||
mysqli.allow_local_infile overrides mysqli.local_infile_directory
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
require_once('skipifconnectfailure.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
|
||||
die("skip Cannot connect to MySQL");
|
||||
|
||||
include_once("local_infile_tools.inc");
|
||||
if ($msg = check_local_infile_allowed_by_server($link))
|
||||
die(sprintf("skip %s, [%d] %s", $msg, $link->errno, $link->error));
|
||||
|
||||
mysqli_close($link);
|
||||
|
||||
?>
|
||||
--INI--
|
||||
open_basedir={PWD}
|
||||
mysqli.allow_local_infile=1
|
||||
mysqli.local_infile_directory={PWD}/foo/bar
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("connect.inc");
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
|
||||
}
|
||||
|
||||
if (!$link->query("DROP TABLE IF EXISTS test")) {
|
||||
printf("[002] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if (!$link->query("CREATE TABLE test (id INT UNSIGNED NOT NULL PRIMARY KEY) ENGINE=" . $engine)) {
|
||||
printf("[003] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
if (!$link->query("LOAD DATA LOCAL INFILE '".$filepath."' INTO TABLE test")) {
|
||||
printf("[004] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if ($res = mysqli_query($link, 'SELECT COUNT(id) AS num FROM test')) {
|
||||
$row = mysqli_fetch_assoc($res);
|
||||
mysqli_free_result($res);
|
||||
|
||||
$row_count = $row['num'];
|
||||
$expected_row_count = 3;
|
||||
if ($row_count != $expected_row_count) {
|
||||
printf("[005] %d != %d\n", $row_count, $expected_row_count);
|
||||
}
|
||||
} else {
|
||||
printf("[006] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$link->close();
|
||||
echo "done";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once('connect.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[clean] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
}
|
||||
|
||||
if (!$link->query($link, 'DROP TABLE IF EXISTS test')) {
|
||||
printf("[clean] Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
|
||||
}
|
||||
|
||||
$link->close();
|
||||
?>
|
||||
--EXPECT--
|
||||
done
|
|
@ -202,6 +202,10 @@ mysqli.allow_local_infile=1
|
|||
$expected_constants["MYSQLI_TYPE_JSON"] = true;
|
||||
}
|
||||
|
||||
if ($version > 80210 || $IS_MYSQLND) {
|
||||
$expected_constants['MYSQLI_OPT_LOAD_DATA_LOCAL_DIR'] = true;
|
||||
}
|
||||
|
||||
$unexpected_constants = array();
|
||||
|
||||
foreach ($constants as $group => $consts) {
|
||||
|
|
|
@ -16,11 +16,11 @@ echo "server: ", $row['Value'], "\n";
|
|||
mysqli_free_result($res);
|
||||
mysqli_close($link);
|
||||
|
||||
echo "connector: ", ini_get("mysqli.allow_local_infile"), "\n";
|
||||
echo 'connector: ', ini_get('mysqli.allow_local_infile'), ' ', var_export(ini_get('mysqli.local_infile_directory')), "\n";
|
||||
|
||||
print "done!\n";
|
||||
?>
|
||||
--EXPECTF--
|
||||
server: %s
|
||||
connector: 0
|
||||
connector: 0 ''
|
||||
done!
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
--TEST--
|
||||
mysqli.local_infile_directory vs access allowed
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
require_once('skipifconnectfailure.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
|
||||
die("skip Cannot connect to MySQL");
|
||||
|
||||
include_once("local_infile_tools.inc");
|
||||
if ($msg = check_local_infile_allowed_by_server($link))
|
||||
die(sprintf("skip %s, [%d] %s", $msg, $link->errno, $link->error));
|
||||
|
||||
mysqli_close($link);
|
||||
|
||||
?>
|
||||
--INI--
|
||||
open_basedir={PWD}
|
||||
mysqli.allow_local_infile=0
|
||||
mysqli.local_infile_directory={PWD}/foo
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("connect.inc");
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
|
||||
}
|
||||
|
||||
if (!$link->query("DROP TABLE IF EXISTS test")) {
|
||||
printf("[002] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if (!$link->query("CREATE TABLE test (id INT UNSIGNED NOT NULL PRIMARY KEY) ENGINE=" . $engine)) {
|
||||
printf("[003] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
if (!$link->query("LOAD DATA LOCAL INFILE '".$filepath."' INTO TABLE test")) {
|
||||
printf("[004] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/bar/bar.data');
|
||||
if (!$link->query("LOAD DATA LOCAL INFILE '".$filepath."' INTO TABLE test")) {
|
||||
printf("[005] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if ($res = mysqli_query($link, 'SELECT COUNT(id) AS num FROM test')) {
|
||||
$row = mysqli_fetch_assoc($res);
|
||||
mysqli_free_result($res);
|
||||
|
||||
$row_count = $row['num'];
|
||||
$expected_row_count = 6;
|
||||
if ($row_count != $expected_row_count) {
|
||||
printf("[006] %d != %d\n", $row_count, $expected_row_count);
|
||||
}
|
||||
} else {
|
||||
printf("[007] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$link->close();
|
||||
echo "done";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once('connect.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[clean] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
}
|
||||
|
||||
if (!$link->query($link, 'DROP TABLE IF EXISTS test')) {
|
||||
printf("[clean] Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
|
||||
}
|
||||
|
||||
$link->close();
|
||||
?>
|
||||
--EXPECT--
|
||||
done
|
|
@ -0,0 +1,65 @@
|
|||
--TEST--
|
||||
mysqli.local_infile_directory access denied
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
require_once('skipifconnectfailure.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
|
||||
die("skip Cannot connect to MySQL");
|
||||
|
||||
include_once("local_infile_tools.inc");
|
||||
if ($msg = check_local_infile_allowed_by_server($link))
|
||||
die(sprintf("skip %s, [%d] %s", $msg, $link->errno, $link->error));
|
||||
|
||||
mysqli_close($link);
|
||||
|
||||
?>
|
||||
--INI--
|
||||
open_basedir={PWD}
|
||||
mysqli.allow_local_infile=0
|
||||
mysqli.local_infile_directory={PWD}/foo/bar
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("connect.inc");
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
|
||||
}
|
||||
|
||||
if (!$link->query("DROP TABLE IF EXISTS test")) {
|
||||
printf("[002] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if (!$link->query("CREATE TABLE test (id INT UNSIGNED NOT NULL PRIMARY KEY) ENGINE=" . $engine)) {
|
||||
printf("[003] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
if (!$link->query("LOAD DATA LOCAL INFILE '".$filepath."' INTO TABLE test")) {
|
||||
printf("[004] [%d] %s\n", $link->errno, $link->error);
|
||||
} else {
|
||||
printf("[005] bug! should not happen - access denied expected\n");
|
||||
}
|
||||
|
||||
$link->close();
|
||||
echo "done";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once('connect.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[clean] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
}
|
||||
|
||||
if (!$link->query($link, 'DROP TABLE IF EXISTS test')) {
|
||||
printf("[clean] Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
|
||||
}
|
||||
|
||||
$link->close();
|
||||
?>
|
||||
--EXPECTF--
|
||||
[004] [2068] LOAD DATA LOCAL INFILE %s
|
||||
done
|
|
@ -0,0 +1,65 @@
|
|||
--TEST--
|
||||
mysqli.local_infile_directory vs open_basedir
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
require_once('skipifconnectfailure.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
|
||||
die("skip Cannot connect to MySQL");
|
||||
|
||||
include_once("local_infile_tools.inc");
|
||||
if ($msg = check_local_infile_allowed_by_server($link))
|
||||
die(sprintf("skip %s, [%d] %s", $msg, $link->errno, $link->error));
|
||||
|
||||
mysqli_close($link);
|
||||
|
||||
?>
|
||||
--INI--
|
||||
open_basedir={PWD}
|
||||
mysqli.allow_local_infile=0
|
||||
mysqli.local_infile_directory={PWD}/../
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("connect.inc");
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
|
||||
}
|
||||
|
||||
if (!$link->query("DROP TABLE IF EXISTS test")) {
|
||||
printf("[002] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
if (!$link->query("CREATE TABLE test (id INT UNSIGNED NOT NULL PRIMARY KEY) ENGINE=" . $engine)) {
|
||||
printf("[003] [%d] %s\n", $link->errno, $link->error);
|
||||
}
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
if (!$link->query("LOAD DATA LOCAL INFILE '".$filepath."' INTO TABLE test")) {
|
||||
printf("[004] [%d] %s\n", $link->errno, $link->error);
|
||||
} else {
|
||||
printf("[005] bug! should not happen - operation not permitted expected\n");
|
||||
}
|
||||
|
||||
echo "done";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require_once('connect.inc');
|
||||
|
||||
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("[clean] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
}
|
||||
|
||||
if (!$link->query($link, 'DROP TABLE IF EXISTS test')) {
|
||||
printf("[clean] Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
|
||||
}
|
||||
|
||||
$link->close();
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: mysqli_connect(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line %d
|
||||
[004] [2068] LOAD DATA LOCAL INFILE %s
|
||||
done
|
|
@ -46,7 +46,7 @@ require_once('skipifconnectfailure.inc');
|
|||
if ($IS_MYSQLND) {
|
||||
$expected = array(
|
||||
'size',
|
||||
'mysqli.allow_local_infile',
|
||||
'mysqli.allow_local_infile', 'mysqli.local_infile_directory',
|
||||
'mysqli.allow_persistent', 'mysqli.max_persistent'
|
||||
);
|
||||
foreach ($expected as $k => $entry)
|
||||
|
|
|
@ -251,6 +251,10 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn)
|
|||
mnd_pefree(conn->options->connect_attr, pers);
|
||||
conn->options->connect_attr = NULL;
|
||||
}
|
||||
if (conn->options->local_infile_directory) {
|
||||
mnd_pefree(conn->options->local_infile_directory, pers);
|
||||
conn->options->local_infile_directory = NULL;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -1648,6 +1652,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
|
|||
conn->options->flags &= ~CLIENT_LOCAL_FILES;
|
||||
}
|
||||
break;
|
||||
case MYSQL_OPT_LOAD_DATA_LOCAL_DIR:
|
||||
{
|
||||
if (conn->options->local_infile_directory) {
|
||||
mnd_pefree(conn->options->local_infile_directory, conn->persistent);
|
||||
}
|
||||
|
||||
if (!value || (*value == '\0')) {
|
||||
conn->options->local_infile_directory = NULL;
|
||||
} else {
|
||||
conn->options->local_infile_directory = mnd_pestrdup(value, conn->persistent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MYSQL_INIT_COMMAND:
|
||||
{
|
||||
char ** new_init_commands;
|
||||
|
|
|
@ -129,6 +129,7 @@
|
|||
#define CR_PARAMS_NOT_BOUND 2031
|
||||
#define CR_INVALID_PARAMETER_NO 2034
|
||||
#define CR_INVALID_BUFFER_USE 2035
|
||||
#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
|
||||
|
||||
#define MYSQLND_EE_FILENOTFOUND 7890
|
||||
|
||||
|
@ -247,6 +248,7 @@ typedef enum mysqlnd_client_option
|
|||
MYSQL_OPT_NET_BUFFER_LENGTH,
|
||||
MYSQL_OPT_TLS_VERSION,
|
||||
MYSQL_OPT_SSL_MODE,
|
||||
MYSQL_OPT_LOAD_DATA_LOCAL_DIR,
|
||||
MYSQLND_DEPRECATED_ENUM1 = 200,
|
||||
MYSQLND_OPT_INT_AND_FLOAT_NATIVE = 201,
|
||||
MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202,
|
||||
|
|
|
@ -149,12 +149,51 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filenam
|
|||
MYSQLND_INFILE infile;
|
||||
MYSQLND_PFC * net = conn->protocol_frame_codec;
|
||||
MYSQLND_VIO * vio = conn->vio;
|
||||
bool is_local_infile_enabled = (conn->options->flags & CLIENT_LOCAL_FILES) == CLIENT_LOCAL_FILES;
|
||||
const char* local_infile_directory = conn->options->local_infile_directory;
|
||||
bool is_local_infile_dir_set = local_infile_directory != NULL;
|
||||
bool prerequisities_ok = TRUE;
|
||||
|
||||
DBG_ENTER("mysqlnd_handle_local_infile");
|
||||
|
||||
if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
|
||||
SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
|
||||
"LOAD DATA LOCAL INFILE is forbidden, check mysqli.allow_local_infile");
|
||||
/*
|
||||
if local_infile is disabled, and local_infile_dir is not set, then operation is forbidden
|
||||
*/
|
||||
if (!is_local_infile_enabled && !is_local_infile_dir_set) {
|
||||
SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE,
|
||||
"LOAD DATA LOCAL INFILE is forbidden, check related settings like "
|
||||
"mysqli.allow_local_infile|mysqli.local_infile_directory or "
|
||||
"PDO::MYSQL_ATTR_LOCAL_INFILE|PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY");
|
||||
prerequisities_ok = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
if local_infile_dir is set, then check whether it actually exists, and is accessible
|
||||
*/
|
||||
if (is_local_infile_dir_set) {
|
||||
php_stream *stream = php_stream_opendir(local_infile_directory, REPORT_ERRORS, NULL);
|
||||
if (stream) {
|
||||
php_stream_closedir(stream);
|
||||
} else {
|
||||
SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE, "cannot open local_infile_directory");
|
||||
prerequisities_ok = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if local_infile is disabled and local_infile_dir is set, then we have to check whether
|
||||
filename is located inside its subtree
|
||||
but only in such a case, because when local_infile is enabled, then local_infile_dir is ignored
|
||||
*/
|
||||
if (prerequisities_ok && !is_local_infile_enabled && is_local_infile_dir_set) {
|
||||
if (php_check_specific_open_basedir(local_infile_directory, filename) == -1) {
|
||||
SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE,
|
||||
"LOAD DATA LOCAL INFILE DIRECTORY restriction in effect. Unable to open file");
|
||||
prerequisities_ok = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prerequisities_ok) {
|
||||
/* write empty packet to server */
|
||||
ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
|
||||
*is_warning = TRUE;
|
||||
|
|
|
@ -231,6 +231,8 @@ typedef struct st_mysqlnd_session_options
|
|||
unsigned int max_allowed_packet;
|
||||
|
||||
bool int_and_float_native;
|
||||
|
||||
char *local_infile_directory;
|
||||
} MYSQLND_SESSION_OPTIONS;
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,10 @@ if (PHP_PDO_MYSQL != "no") {
|
|||
ADD_EXTENSION_DEP('pdo_mysql', 'pdo');
|
||||
} else {
|
||||
if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) &&
|
||||
CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL", PHP_PHP_BUILD + "\\include\\mysql;" + PHP_PDO_MYSQL)) {
|
||||
CHECK_HEADER_ADD_INCLUDE("mysql.h", "CFLAGS_PDO_MYSQL",
|
||||
PHP_PDO_MYSQL + "\\include;" +
|
||||
PHP_PHP_BUILD + "\\include\\mysql;" +
|
||||
PHP_PDO_MYSQL)) {
|
||||
EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
} else {
|
||||
WARNING("pdo_mysql not enabled; libraries and headers not found");
|
||||
|
|
|
@ -521,6 +521,24 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_
|
|||
ZVAL_BOOL(return_value, H->local_infile);
|
||||
break;
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND)
|
||||
case PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY:
|
||||
{
|
||||
const char* local_infile_directory = NULL;
|
||||
#ifdef PDO_USE_MYSQLND
|
||||
local_infile_directory = H->server->data->options->local_infile_directory;
|
||||
#else
|
||||
mysql_get_option(H->server, MYSQL_OPT_LOAD_DATA_LOCAL_DIR, &local_infile_directory);
|
||||
#endif
|
||||
if (local_infile_directory) {
|
||||
ZVAL_STRING(return_value, local_infile_directory);
|
||||
} else {
|
||||
ZVAL_NULL(return_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
PDO_DBG_RETURN(0);
|
||||
}
|
||||
|
@ -724,6 +742,17 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND)
|
||||
zend_string *local_infile_directory = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY, NULL);
|
||||
if (local_infile_directory && !php_check_open_basedir(ZSTR_VAL(local_infile_directory))) {
|
||||
if (mysql_options(H->server, MYSQL_OPT_LOAD_DATA_LOCAL_DIR, (const char *)ZSTR_VAL(local_infile_directory))) {
|
||||
zend_string_release(local_infile_directory);
|
||||
pdo_mysql_error(dbh);
|
||||
goto cleanup;
|
||||
}
|
||||
zend_string_release(local_infile_directory);
|
||||
}
|
||||
#endif
|
||||
#ifdef MYSQL_OPT_RECONNECT
|
||||
/* since 5.0.3, the default for this option is 0 if not specified.
|
||||
* we want the old behaviour
|
||||
|
|
|
@ -124,6 +124,9 @@ static PHP_MINIT_FUNCTION(pdo_mysql)
|
|||
#ifdef PDO_USE_MYSQLND
|
||||
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_VERIFY_SERVER_CERT", (zend_long)PDO_MYSQL_ATTR_SSL_VERIFY_SERVER_CERT);
|
||||
#endif
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND)
|
||||
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_LOCAL_INFILE_DIRECTORY", (zend_long)PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY);
|
||||
#endif
|
||||
|
||||
#ifdef PDO_USE_MYSQLND
|
||||
mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api);
|
||||
|
|
|
@ -178,6 +178,9 @@ enum {
|
|||
#ifdef PDO_USE_MYSQLND
|
||||
PDO_MYSQL_ATTR_SSL_VERIFY_SERVER_CERT,
|
||||
#endif
|
||||
#if MYSQL_VERSION_ID >= 80021 || defined(PDO_USE_MYSQLND)
|
||||
PDO_MYSQL_ATTR_LOCAL_INFILE_DIRECTORY,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,8 +26,8 @@ var_dump($flags);
|
|||
array(3) {
|
||||
[%d]=>
|
||||
bool(true)
|
||||
[1001]=>
|
||||
[%d]=>
|
||||
bool(true)
|
||||
[12]=>
|
||||
[%d]=>
|
||||
bool(true)
|
||||
}
|
||||
|
|
3
ext/pdo_mysql/tests/foo/bar/bar.data
Normal file
3
ext/pdo_mysql/tests/foo/bar/bar.data
Normal file
|
@ -0,0 +1,3 @@
|
|||
97;first
|
||||
98;second
|
||||
99;third
|
3
ext/pdo_mysql/tests/foo/foo.data
Normal file
3
ext/pdo_mysql/tests/foo/foo.data
Normal file
|
@ -0,0 +1,3 @@
|
|||
1;one
|
||||
2;two
|
||||
3;three
|
|
@ -156,6 +156,11 @@ MySQLPDOTest::skip();
|
|||
set_option_and_check(33, PDO::MYSQL_ATTR_DIRECT_QUERY, 1, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
|
||||
set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, 0, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
|
||||
|
||||
if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
|
||||
set_option_and_check(35, PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY, null, 'PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY');
|
||||
// libmysqlclient returns the directory with a trailing slash.
|
||||
// set_option_and_check(36, PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY, __DIR__, 'PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY');
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[001] %s, [%s] %s Line: %s\n",
|
||||
$e->getMessage(),
|
||||
|
|
|
@ -13,6 +13,16 @@ if (!extension_loaded('mysqli') && !extension_loaded('mysqlnd')) {
|
|||
<?php
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
|
||||
function get_client_version() {
|
||||
if (extension_loaded('mysqli')) {
|
||||
return mysqli_get_client_version();
|
||||
}
|
||||
/* XXX the MySQL client library version isn't exposed with any
|
||||
constants, the single possibility is to use the PDO::getAttribute().
|
||||
This however will fail with no connection. */
|
||||
return MySQLPDOTest::getClientVersion(MySQLPDOTest::factory());
|
||||
}
|
||||
|
||||
$expected = array(
|
||||
'MYSQL_ATTR_USE_BUFFERED_QUERY' => true,
|
||||
'MYSQL_ATTR_LOCAL_INFILE' => true,
|
||||
|
@ -38,15 +48,12 @@ if (!extension_loaded('mysqli') && !extension_loaded('mysqlnd')) {
|
|||
if (extension_loaded('mysqlnd')) {
|
||||
$expected['MYSQL_ATTR_SSL_VERIFY_SERVER_CERT'] = true;
|
||||
$expected['MYSQL_ATTR_SERVER_PUBLIC_KEY'] = true;
|
||||
} else if (extension_loaded('mysqli')) {
|
||||
if (mysqli_get_client_version() > 50605) {
|
||||
$expected['MYSQL_ATTR_SERVER_PUBLIC_KEY'] = true;
|
||||
}
|
||||
} else if (MySQLPDOTest::getClientVersion(MySQLPDOTest::factory()) > 50605) {
|
||||
/* XXX the MySQL client library version isn't exposed with any
|
||||
constants, the single possibility is to use the PDO::getAttribute().
|
||||
This however will fail with no connection. */
|
||||
$expected['MYSQL_ATTR_SERVER_PUBLIC_KEY'] = true;
|
||||
} else if (get_client_version() > 50605) {
|
||||
$expected['MYSQL_ATTR_SERVER_PUBLIC_KEY'] = true;
|
||||
}
|
||||
|
||||
if (MySQLPDOTest::isPDOMySQLnd() || get_client_version() >= 80021) {
|
||||
$expected['MYSQL_ATTR_LOCAL_INFILE_DIRECTORY'] = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -5,6 +5,9 @@ ensure default for local infile is off
|
|||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
require_once(__DIR__ . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
MySQLPDOTest::skip();
|
||||
if (!defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
|
||||
die("skip No MYSQL_ATTR_LOCAL_INFILE_DIRECTORY support");
|
||||
}
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
|
@ -17,8 +20,10 @@ $pass = PDO_MYSQL_TEST_PASS;
|
|||
|
||||
$db = new PDO($dsn, $user, $pass);
|
||||
echo var_export($db->getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE)), "\n";
|
||||
echo var_export($db->getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY)), "\n";
|
||||
echo "done!\n";
|
||||
?>
|
||||
--EXPECT--
|
||||
false
|
||||
NULL
|
||||
done!
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
--TEST--
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY vs access allowed
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
MySQLPDOTest::skip();
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipifinfilenotallowed.inc');
|
||||
if (!defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
|
||||
die("skip No MYSQL_ATTR_LOCAL_INFILE_DIRECTORY support");
|
||||
}
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
function exec_and_count($offset, &$db, $sql, $exp) {
|
||||
try {
|
||||
$ret = $db->exec($sql);
|
||||
if ($ret !== $exp) {
|
||||
printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n",
|
||||
$offset, $exp, gettype($exp), $ret, gettype($ret), $sql,
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[%03d] '%s' has failed, [%s] %s\n",
|
||||
$offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
putenv('PDOTEST_ATTR='.serialize([
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE=>false,
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY=>__DIR__."/foo"
|
||||
]));
|
||||
$db = MySQLPDOTest::factory();
|
||||
MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
|
||||
|
||||
try {
|
||||
exec_and_count(1, $db, 'DROP TABLE IF EXISTS test', 0);
|
||||
exec_and_count(2, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0);
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/bar/bar.data');
|
||||
|
||||
$sql = sprintf("LOAD DATA LOCAL INFILE %s INTO TABLE test FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'", $db->quote($filepath));
|
||||
if (exec_and_count(3, $db, $sql, 3)) {
|
||||
$stmt = $db->query('SELECT id, col1 FROM test ORDER BY id ASC');
|
||||
$expected = array(
|
||||
array("id" => 97, "col1" => "first"),
|
||||
array("id" => 98, "col1" => "second"),
|
||||
array("id" => 99, "col1" => "third"),
|
||||
);
|
||||
$ret = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($expected as $offset => $exp) {
|
||||
foreach ($exp as $key => $value) {
|
||||
$actual_value = trim(strval($ret[$offset][$key]));
|
||||
if ($actual_value != $value) {
|
||||
printf("Results seem wrong, check manually\n");
|
||||
echo "------ EXPECTED OUTPUT ------\n";
|
||||
var_dump($expected);
|
||||
echo "------ ACTUAL OUTPUT ------\n";
|
||||
var_dump($ret);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[001] %s, [%s] %s\n",
|
||||
$e->getMessage(),
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
}
|
||||
|
||||
print "done!";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require dirname(__FILE__) . '/mysql_pdo_test.inc';
|
||||
$db = MySQLPDOTest::factory();
|
||||
$db->exec('DROP TABLE IF EXISTS test');
|
||||
?>
|
||||
--EXPECT--
|
||||
done!
|
|
@ -0,0 +1,76 @@
|
|||
--TEST--
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY vs access denied
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
MySQLPDOTest::skip();
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipifinfilenotallowed.inc');
|
||||
if (!defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
|
||||
die("skip No MYSQL_ATTR_LOCAL_INFILE_DIRECTORY support");
|
||||
}
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
function exec_and_count($offset, &$db, $sql, $exp) {
|
||||
try {
|
||||
$ret = $db->exec($sql);
|
||||
if ($ret !== $exp) {
|
||||
printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n",
|
||||
$offset, $exp, gettype($exp), $ret, gettype($ret), $sql,
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[%03d] '%s' has failed, [%s] %s\n",
|
||||
$offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
putenv('PDOTEST_ATTR='.serialize([
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE=>false,
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY=>__DIR__."/foo/bar"
|
||||
]));
|
||||
$db = MySQLPDOTest::factory();
|
||||
MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
|
||||
|
||||
try {
|
||||
exec_and_count(1, $db, 'DROP TABLE IF EXISTS test', 0);
|
||||
exec_and_count(2, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0);
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
|
||||
$sql = sprintf("LOAD DATA LOCAL INFILE %s INTO TABLE test FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'", $db->quote($filepath));
|
||||
if (exec_and_count(3, $db, $sql, false)) {
|
||||
$stmt = $db->query('SELECT id, col1 FROM test ORDER BY id ASC');
|
||||
$expected = array();
|
||||
$ret = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($ret != $expected) {
|
||||
printf("Results seem wrong, check manually\n");
|
||||
echo "------ EXPECTED OUTPUT ------\n";
|
||||
var_dump($expected);
|
||||
echo "------ ACTUAL OUTPUT ------\n";
|
||||
var_dump($ret);
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[001] %s, [%s] %s\n",
|
||||
$e->getMessage(),
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
}
|
||||
|
||||
print "done!";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require dirname(__FILE__) . '/mysql_pdo_test.inc';
|
||||
$db = MySQLPDOTest::factory();
|
||||
$db->exec('DROP TABLE IF EXISTS test');
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: PDO::exec(): SQLSTATE[HY000]: General error: 2068 LOAD DATA LOCAL INFILE %s in %s on line %d
|
||||
done!
|
|
@ -0,0 +1,85 @@
|
|||
--TEST--
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE overrides PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc');
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
MySQLPDOTest::skip();
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipifinfilenotallowed.inc');
|
||||
if (!defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
|
||||
die("skip No MYSQL_ATTR_LOCAL_INFILE_DIRECTORY support");
|
||||
}
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
function exec_and_count($offset, &$db, $sql, $exp) {
|
||||
try {
|
||||
$ret = $db->exec($sql);
|
||||
if ($ret !== $exp) {
|
||||
printf("[%03d] Expecting '%s'/%s got '%s'/%s when running '%s', [%s] %s\n",
|
||||
$offset, $exp, gettype($exp), $ret, gettype($ret), $sql,
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[%03d] '%s' has failed, [%s] %s\n",
|
||||
$offset, $sql, $db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc');
|
||||
putenv('PDOTEST_ATTR='.serialize([
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE=>true,
|
||||
PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY=>__DIR__."/foo/bar"
|
||||
]));
|
||||
$db = MySQLPDOTest::factory();
|
||||
MySQLPDOTest::createTestTable($db, MySQLPDOTest::detect_transactional_mysql_engine($db));
|
||||
|
||||
try {
|
||||
exec_and_count(1, $db, 'DROP TABLE IF EXISTS test', 0);
|
||||
exec_and_count(2, $db, sprintf('CREATE TABLE test(id INT NOT NULL PRIMARY KEY, col1 CHAR(10)) ENGINE=%s', PDO_MYSQL_TEST_ENGINE), 0);
|
||||
|
||||
$filepath = str_replace('\\', '/', __DIR__.'/foo/foo.data');
|
||||
|
||||
$sql = sprintf("LOAD DATA LOCAL INFILE %s INTO TABLE test FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'", $db->quote($filepath));
|
||||
if (exec_and_count(3, $db, $sql, 3)) {
|
||||
$stmt = $db->query('SELECT id, col1 FROM test ORDER BY id ASC');
|
||||
$expected = array(
|
||||
array("id" => 1, "col1" => "one"),
|
||||
array("id" => 2, "col1" => "two"),
|
||||
array("id" => 3, "col1" => "three"),
|
||||
);
|
||||
$ret = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($expected as $offset => $exp) {
|
||||
foreach ($exp as $key => $value) {
|
||||
$actual_value = trim(strval($ret[$offset][$key]));
|
||||
if ($actual_value != $value) {
|
||||
printf("Results seem wrong, check manually\n");
|
||||
echo "------ EXPECTED OUTPUT ------\n";
|
||||
var_dump($expected);
|
||||
echo "------ ACTUAL OUTPUT ------\n";
|
||||
var_dump($ret);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
printf("[001] %s, [%s] %s\n",
|
||||
$e->getMessage(),
|
||||
$db->errorCode(), implode(' ', $db->errorInfo()));
|
||||
}
|
||||
|
||||
print "done!";
|
||||
?>
|
||||
--CLEAN--
|
||||
<?php
|
||||
require dirname(__FILE__) . '/mysql_pdo_test.inc';
|
||||
$db = MySQLPDOTest::factory();
|
||||
$db->exec('DROP TABLE IF EXISTS test');
|
||||
?>
|
||||
--EXPECT--
|
||||
done!
|
6
ext/pdo_mysql/tests/skipifinfilenotallowed.inc
Normal file
6
ext/pdo_mysql/tests/skipifinfilenotallowed.inc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
$db = MySQLPDOTest::factory();
|
||||
$stmt = $db->query("SHOW VARIABLES LIKE 'local_infile'");
|
||||
if (($row = $stmt->fetch(PDO::FETCH_ASSOC)) && ($row['value'] != 'ON'))
|
||||
die("skip Server variable 'local_infile' seems not set to 'ON', found '". $row['value'] ."'");
|
||||
?>
|
|
@ -1151,6 +1151,10 @@ mysqli.max_persistent = -1
|
|||
; https://php.net/mysqli.allow_local_infile
|
||||
;mysqli.allow_local_infile = On
|
||||
|
||||
; It allows the user to specify a folder where files that can be sent via LOAD DATA
|
||||
; LOCAL can exist. It is ignored if mysqli.allow_local_infile is enabled.
|
||||
;mysqli.local_infile_directory =
|
||||
|
||||
; Allow or prevent persistent links.
|
||||
; https://php.net/mysqli.allow-persistent
|
||||
mysqli.allow_persistent = On
|
||||
|
|
|
@ -1153,6 +1153,10 @@ mysqli.max_persistent = -1
|
|||
; https://php.net/mysqli.allow_local_infile
|
||||
;mysqli.allow_local_infile = On
|
||||
|
||||
; It allows the user to specify a folder where files that can be sent via LOAD DATA
|
||||
; LOCAL can exist. It is ignored if mysqli.allow_local_infile is enabled.
|
||||
;mysqli.local_infile_directory =
|
||||
|
||||
; Allow or prevent persistent links.
|
||||
; https://php.net/mysqli.allow-persistent
|
||||
mysqli.allow_persistent = On
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue