From: Kristofer Pettersson Date: September 27 2010 8:08am Subject: bzr commit into mysql-trunk branch (kristofer.pettersson:3197) Bug#29071 List-Archive: http://lists.mysql.com/commits/119143 X-Bug: 29071 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0477396855057754718==" --===============0477396855057754718== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/thek/bzr/mysql-trunk/ based on revid:dlenev@stripped 3197 Kristofer Pettersson 2010-09-27 Bug#29071 invalid Open_files or Slave_open_temp_tables value in show global status If an error occurred during registration of a file handle, the Open_files counter would show very large numbers. Only decrese my_file_opened if the file descriptor was maked as anything but UNOPEN @ mysys/my_fopen.c * The variable "my_stream_opened" needs to be strongly associated with the my_file_info::type attribute. @ mysys/my_open.c * The variable "my_file_opened" needs to be strongly associated with the my_file_info::type attribute. added: unittest/gunit/my_fopen-t.cc unittest/gunit/my_open-t.cc modified: mysys/my_fopen.c mysys/my_open.c unittest/gunit/CMakeLists.txt === modified file 'mysys/my_fopen.c' --- a/mysys/my_fopen.c 2010-07-08 21:20:08 +0000 +++ b/mysys/my_fopen.c 2010-09-27 08:08:35 +0000 @@ -20,18 +20,18 @@ static void make_ftype(char * to,int flag); -/* +/** Open a file as stream - SYNOPSIS - my_fopen() - FileName Path-name of file - Flags Read | write | append | trunc (like for open()) - MyFlags Flags for handling errors - - RETURN - 0 Error - # File handler + @param FileName Path-name of file + @param Flags like for open() + @param MyFlags Flags for handling errors + + @see my_win_fopen() + + @return + @retval 0 Error + @retval # File handler */ FILE *my_fopen(const char *filename, int flags, myf MyFlags) @@ -52,20 +52,29 @@ FILE *my_fopen(const char *filename, int if (fd != 0) { /* - The test works if MY_NFILE < 128. The problem is that fileno() is char + The test works if my_file_limit < 128. The problem is that fileno() is char on some OS (SUNOS). Actually the filename save isn't that important so we can ignore if this doesn't work. */ int filedesc= my_fileno(fd); + mysql_mutex_lock(&THR_LOCK_open); if ((uint)filedesc >= my_file_limit) { - thread_safe_increment(my_stream_opened,&THR_LOCK_open); - DBUG_RETURN(fd); /* safeguard */ + /* + The number of concurrently opened files exceeds the limits or + the file descriptor can't be mapped to the my_file_info array. + */ + my_errno= EMFILE; +#ifndef _WIN32 + fclose(fd); +#else + my_win_fclose(fd); +#endif + mysql_mutex_unlock(&THR_LOCK_open); + goto err; } - mysql_mutex_lock(&THR_LOCK_open); - if ((my_file_info[filedesc].name= (char*) - my_strdup(filename,MyFlags))) + if ((my_file_info[filedesc].name= (char*) my_strdup(filename,MyFlags))) { my_stream_opened++; my_file_total_opened++; @@ -80,6 +89,8 @@ FILE *my_fopen(const char *filename, int } else my_errno=errno; + +err: DBUG_PRINT("error",("Got error %d on open",my_errno)); if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) my_error((flags & O_RDONLY) || (flags == O_RDONLY ) ? EE_FILENOTFOUND : @@ -98,13 +109,25 @@ int my_fclose(FILE *fd, myf MyFlags) DBUG_ENTER("my_fclose"); DBUG_PRINT("my",("stream: 0x%lx MyFlags: %d", (long) fd, MyFlags)); - mysql_mutex_lock(&THR_LOCK_open); + if(fd == 0) + { + /* Function called with wrong parameters */ + DBUG_RETURN(EBADF); + } + file= my_fileno(fd); + if ((uint) file >= my_file_limit) + { + /* Function called with wrong parameters */ + DBUG_RETURN(EBADF); + } + #ifndef _WIN32 err= fclose(fd); #else err= my_win_fclose(fd); #endif + mysql_mutex_lock(&THR_LOCK_open); if(err < 0) { my_errno=errno; @@ -112,12 +135,12 @@ int my_fclose(FILE *fd, myf MyFlags) my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG), my_filename(file),errno); } - else - my_stream_opened--; - if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN) + + if (my_file_info[file].type != UNOPEN) { my_file_info[file].type = UNOPEN; my_free(my_file_info[file].name); + my_stream_opened--; } mysql_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(err); === modified file 'mysys/my_open.c' --- a/mysys/my_open.c 2010-07-08 21:20:08 +0000 +++ b/mysys/my_open.c 2010-09-27 08:08:35 +0000 @@ -19,17 +19,20 @@ #include -/* +/** Open a file - SYNOPSIS - my_open() - FileName Fully qualified file name - Flags Read | write - MyFlags Special flags - - RETURN VALUE - File descriptor + @param FileName Fully qualified file name + @param Flags Same as for unix system call open(2) + @param MyFlags Special flags + + @note This function encapsluates my_win_open and open(3) calls. + + @see my_win_open + + @return + @retval -1 An error occurred; my_errno has the error code. + @retval # On success a file descriptor is returned. */ File my_open(const char *FileName, int Flags, myf MyFlags) @@ -70,6 +73,11 @@ int my_close(File fd, myf MyFlags) DBUG_ENTER("my_close"); DBUG_PRINT("my",("fd: %d MyFlags: %d",fd, MyFlags)); + if (fd < 0 || fd >= my_file_limit) + { + /* Function called with wrong parameters */ + DBUG_RETURN(EBADF); + } mysql_mutex_lock(&THR_LOCK_open); #ifndef _WIN32 do @@ -86,15 +94,15 @@ int my_close(File fd, myf MyFlags) if (MyFlags & (MY_FAE | MY_WME)) my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),my_filename(fd),errno); } - if ((uint) fd < my_file_limit && my_file_info[fd].type != UNOPEN) + if (my_file_info[fd].type != UNOPEN) { my_free(my_file_info[fd].name); #if defined(THREAD) && !defined(HAVE_PREAD) && !defined(_WIN32) mysql_mutex_destroy(&my_file_info[fd].mutex); #endif my_file_info[fd].type = UNOPEN; + my_file_opened--; } - my_file_opened--; mysql_mutex_unlock(&THR_LOCK_open); DBUG_RETURN(err); } /* my_close */ @@ -103,17 +111,15 @@ int my_close(File fd, myf MyFlags) /* Register file in my_file_info[] - SYNOPSIS - my_register_filename() - fd File number opened, -1 if error on open - FileName File name - type_file_type How file was created - error_message_number Error message number if caller got error (fd == -1) - MyFlags Flags for my_close() - - RETURN - -1 error - # Filenumber + @param fd File number opened, -1 if error on open + @param FileName File name + @param type_file_type How file was created + @param error_message_numbe Error message number if caller got error (fd == -1) + @param MyFlags Flags for my_close() + + @return + @retval -1 An error occurred + @retval # On success the file descriptor is returned. */ @@ -121,41 +127,48 @@ File my_register_filename(File fd, const type_of_file, uint error_message_number, myf MyFlags) { DBUG_ENTER("my_register_filename"); - if ((int) fd >= MY_FILE_MIN) + if (fd == -1) { - if ((uint) fd >= my_file_limit) - { -#if defined(THREAD) && !defined(HAVE_PREAD) - my_errno= EMFILE; -#else - thread_safe_increment(my_file_opened,&THR_LOCK_open); - DBUG_RETURN(fd); /* safeguard */ -#endif - } - else - { - mysql_mutex_lock(&THR_LOCK_open); - if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags))) - { - my_file_opened++; - my_file_total_opened++; - my_file_info[fd].type = type_of_file; -#if defined(THREAD) && !defined(HAVE_PREAD) && !defined(_WIN32) - mysql_mutex_init(key_my_file_info_mutex, &my_file_info[fd].mutex, - MY_MUTEX_INIT_FAST); -#endif - mysql_mutex_unlock(&THR_LOCK_open); - DBUG_PRINT("exit",("fd: %d",fd)); - DBUG_RETURN(fd); - } - mysql_mutex_unlock(&THR_LOCK_open); - my_errno= ENOMEM; - } - (void) my_close(fd, MyFlags); - } - else + /* + The file descriptor indicates that the FileName couldn't be opened + properly; - Report the error. + */ my_errno= errno; + goto err; + } + + mysql_mutex_lock(&THR_LOCK_open); + if (fd >= my_file_limit) + { + /* + The number of concurrently opened files exceeds the limits or + the file descriptor can't be mapped to the my_file_info array. + */ + my_errno= EMFILE; + close(fd); + mysql_mutex_unlock(&THR_LOCK_open); + goto err; + } + + if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags))) + { + my_file_opened++; + my_file_total_opened++; + my_file_info[fd].type = type_of_file; +#if defined(THREAD) && !defined(_WIN32) && !defined(HAVE_PREAD) + mysql_mutex_init(key_my_file_info_mutex, &my_file_info[fd].mutex, + MY_MUTEX_INIT_FAST); +#endif + mysql_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("fd: %d",fd)); + DBUG_RETURN(fd); + } +#if defined(THREAD) + mysql_mutex_unlock(&THR_LOCK_open); +#endif + my_errno= ENOMEM; +err: DBUG_PRINT("error",("Got error %d on open", my_errno)); if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) { === modified file 'unittest/gunit/CMakeLists.txt' --- a/unittest/gunit/CMakeLists.txt 2010-07-30 08:34:23 +0000 +++ b/unittest/gunit/CMakeLists.txt 2010-09-27 08:08:35 +0000 @@ -207,7 +207,7 @@ IF (CMAKE_CXX_COMPILER_ID STREQUAL "SunP ENDIF() # Add tests (link them with sql library) -SET(TESTS sql_list mdl mdl_mytap my_regex thread_utils) +SET(TESTS sql_list mdl mdl_mytap my_regex thread_utils my_open my_fopen) FOREACH(test ${TESTS}) ADD_EXECUTABLE(${test}-t ${test}-t.cc) TARGET_LINK_LIBRARIES(${test}-t gunit sqlgunitlib strings dbug regex) === added file 'unittest/gunit/my_fopen-t.cc' --- a/unittest/gunit/my_fopen-t.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/my_fopen-t.cc 2010-09-27 08:08:35 +0000 @@ -0,0 +1,162 @@ +/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#include "my_config.h" +#include +#include "mysqld_error.h" +#include "my_sys.h" + +#define MAX_NUMBER_OF_FILE_DESCRIPTORS (OS_FILE_LIMIT+MY_NFILE+1024) + +struct st_my_file_info my_file_info_default[MY_NFILE]; +class My_open_test : public ::testing::Test +{ +public: + My_open_test() + { + my_thread_basic_global_init(); + } + + virtual void SetUp() + { + // set all global variables. + my_thread_basic_global_reinit(); + my_stream_opened= 0; + my_file_total_opened= 0; + my_set_max_open_files(MAX_NUMBER_OF_FILE_DESCRIPTORS); + my_errno= 0; + } +private: + +}; + +TEST_F(My_open_test, EnvironmentCheck) +{ +} + +TEST_F(My_open_test, FailToOpenUnknownFile) +{ + FILE *fp; + fp= my_fopen("/really/unknown/does-not-exist.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_EQ((FILE *)0, fp); + EXPECT_EQ(0, my_stream_opened); + EXPECT_EQ(0, my_file_total_opened); +} + + +TEST_F(My_open_test, SuccessToOpenKnownBinaryFile) +{ + EXPECT_EQ(0, my_stream_opened); + FILE *fp; + fp= my_fopen("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + /* + The file should exist in the test dir and the call to my_open should not + fail under normal circumstances. + */ + EXPECT_NE((FILE *)0, fp); + + /* + The number of currently opened files should have increased + */ + EXPECT_EQ(1, my_stream_opened); + /* + The number of total files should have increased + */ + EXPECT_EQ(1, my_file_total_opened); + + + /* + When the file is closed the number of currently opened files should have + decreased to 0. + */ + my_fclose(fp, MYF(0)); + EXPECT_EQ(0, my_stream_opened); + + /* + But the number of files which has been opened should of course stay the + same. + */ + EXPECT_EQ(1, my_file_total_opened); + + /* + Lets try it again to see if the number of files ever opened in the system + increases to 2 + */ + fp= my_fopen("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_NE((FILE *)0, fp); + EXPECT_EQ(1, my_stream_opened); + EXPECT_EQ(2, my_file_total_opened); + my_fclose(fp, MYF(0)); + EXPECT_EQ(0, my_stream_opened); + EXPECT_EQ(2, my_file_total_opened); + +} + +TEST_F(My_open_test, TooManyOpenFiles) +{ + FILE *fp[MAX_NUMBER_OF_FILE_DESCRIPTORS+1]; + /* + Attempt to open MAX_NUMBER_OF_FILE_DESCRIPTORS number of files. + NOTE: Couting begins at 0 hence we exclude i==MAX_NUMBER_OF_FILE_DESCRIPTORS + */ + int opened_files= 0; + for(unsigned i= 0; i=0; --i) + { + my_fclose(fp[i],MYF(0)); + } + /* + No more files should be opened. + */ + EXPECT_EQ(0, my_stream_opened); + EXPECT_EQ(opened_files, my_file_total_opened); +} + +TEST_F(My_open_test, MultipleClose) +{ + FILE *fp; + fp= my_fopen("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_NE((FILE *)0, fp); + my_fclose(fp, MYF(0)); + my_fclose(fp, MYF(0)); + my_fclose(fp, MYF(0)); + EXPECT_EQ(0, my_stream_opened); + EXPECT_EQ(1, my_file_total_opened); + fp= my_fopen("/really/dont/exist.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_EQ((FILE *)0, fp); + my_fclose(fp, MYF(0)); + my_fclose(fp, MYF(0)); + my_fclose(fp, MYF(0)); + EXPECT_EQ(0, my_stream_opened); + EXPECT_EQ(1, my_file_total_opened); +} + === added file 'unittest/gunit/my_open-t.cc' --- a/unittest/gunit/my_open-t.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/my_open-t.cc 2010-09-27 08:08:35 +0000 @@ -0,0 +1,161 @@ +/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#include "my_config.h" +#include +#include "mysqld_error.h" +#include "my_sys.h" + +#define MAX_NUMBER_OF_FILE_DESCRIPTORS (OS_FILE_LIMIT+MY_NFILE+1024) + +struct st_my_file_info my_file_info_default[MY_NFILE]; +class My_open_test : public ::testing::Test +{ +public: + My_open_test() + { + my_thread_basic_global_init(); + } + + virtual void SetUp() + { + // set all global variables. + my_thread_basic_global_reinit(); + my_file_opened= 0; + my_file_total_opened= 0; + my_set_max_open_files(MAX_NUMBER_OF_FILE_DESCRIPTORS); + my_errno= 0; + } +private: + +}; + +TEST_F(My_open_test, EnvironmentCheck) +{ +} + +TEST_F(My_open_test, FailToOpenUnknownFile) +{ + File fd; + fd= my_open("/really/unknown/does-not-exist.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_EQ(-1, fd); + EXPECT_EQ(0, my_file_opened); + EXPECT_EQ(0, my_file_total_opened); +} + + +TEST_F(My_open_test, SuccessToOpenKnownBinaryFile) +{ + EXPECT_EQ(0, my_file_opened); + File fd; + fd= my_open("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + /* + The file should exist in the test dir and the call to my_open should not + fail under normal circumstances. + */ + EXPECT_NE(-1, fd); + + /* + The number of currently opened files should have increased + */ + EXPECT_EQ(1, my_file_opened); + /* + The number of total files should have increased + */ + EXPECT_EQ(1, my_file_total_opened); + + my_close(fd, MYF(0)); + + /* + When the file is closed the number of currently opened files should have + decreased to 0. + */ + EXPECT_EQ(0, my_file_opened); + /* + But the number of files which has been opened should of course stay the + same. + */ + EXPECT_EQ(1, my_file_total_opened); + + /* + Lets try it again to see if the number of files ever opened in the system + increases to 2 + */ + fd= my_open("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_NE(-1, fd); + EXPECT_EQ(1, my_file_opened); + EXPECT_EQ(2, my_file_total_opened); + my_close(fd, MYF(0)); + EXPECT_EQ(0, my_file_opened); + EXPECT_EQ(2, my_file_total_opened); + +} + +TEST_F(My_open_test, TooManyOpenFiles) +{ + File fd[MAX_NUMBER_OF_FILE_DESCRIPTORS+1]; + /* + Attempt to open MAX_NUMBER_OF_FILE_DESCRIPTORS number of files. + NOTE: Couting begins at 0 hence we exclude i==MAX_NUMBER_OF_FILE_DESCRIPTORS + */ + int opened_files= 0; + for(unsigned i= 0; i=0; --i) + { + my_close(fd[i],MYF(0)); + } + /* + No more files should be opened. + */ + EXPECT_EQ(0, my_file_opened); + EXPECT_EQ(opened_files, my_file_total_opened); +} + +TEST_F(My_open_test, MultipleClose) +{ + File fd; + fd= my_open("successful_open_rdonly.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_NE(-1, fd); + my_close(fd, MYF(0)); + my_close(fd, MYF(0)); + my_close(fd, MYF(0)); + EXPECT_EQ(0, my_file_opened); + EXPECT_EQ(1, my_file_total_opened); + fd= my_open("/really/dont/exist.bin", O_RDONLY | O_BINARY, MYF(0)); + EXPECT_EQ(-1, fd); + my_close(fd, MYF(0)); + my_close(fd, MYF(0)); + my_close(fd, MYF(0)); + EXPECT_EQ(0, my_file_opened); + EXPECT_EQ(1, my_file_total_opened); +} + --===============0477396855057754718== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/kristofer.pettersson@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kristofer.pettersson@stripped\ # 16m93uoooanijhsu # target_branch: file:///home/thek/bzr/mysql-trunk/ # testament_sha1: 840965f22cf6610e7f76c43d672e16a4bb86aa72 # timestamp: 2010-09-27 10:08:39 +0200 # base_revision_id: dlenev@stripped # # Begin bundle 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 woSGp2xFcA== --===============0477396855057754718==--