List:Commits« Previous MessageNext Message »
From:Chuck Bell Date:March 30 2009 9:07pm
Subject:Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2803)
Bug#43747
View as plain text  
Hi Ingo,

I have made an alternative patch that solves the problem for Windows. I fear it 
is a bit hackish (or whackish for Windows) and probably not a good solution. I 
have not tested it on Linux either.

Unfortunately, I have also found more unusual behavior on Windows. Not only does 
the my_stat() on Windows not accept the ending '/' in the path passed as a 
parameter, you can no longer set @@global.backupdir = 'some/valid/path' without 
the ending '/' on Windows (see attached diff for ancillary changes to other 
tests. That is simply weird.

This has led me to the conclusion that we cannot make the code do what the bug 
report has described as a solution. That saddens me because your observations 
and suggested improvements have merit.

Thus, I think unless someone else has another idea other than the one I describe 
below, we will have to mark this one as 'not a bug' and make sure the 
documentation contains the (now) limitations described in the bug report. 
Jorgen: do you concur?

Chuck

> Sigh. If there was only a way to build and test faster on Windows, I'd
> test even trivial patches there.

I feel your pain...everyday. ;)

...

> my_access() checks directories as well as files and other file system
> objects. We use it with the flags F_OK|W_OK. So my_access() checks if
> the filesystem object exists and is writable. But it does not care if it
> is a directory, a file, or something else.

Aha, I see.

> The intended purpose of the test_if_directory() method was to test if a
> file system object was a directory or not a directory. The method was
> designed to return TRUE if the path name referenced an existent file
> system object and that this object was a directory. It returned FALSE,
> if the path referenced a non-existent object, or an existent object,
> which was not a directory. My idea was, that this check would make the
> behavior of the backupdir variable more intuitive. It would accept its
> value only if it is an existent directory and refuse all other cases as
> invalid.

Right. Well, we can make this work -- just not using my_stat(). We could take 
the time to create a Windows specific 'is_a_directory()' method, but that would 
mean some very specific code added to the server.

...

> I respect your decision. Just in case that you are curious, what my idea
> was behind test_if_directory(), I'll try to explain. Somehow I start to
> feel like repeating myself. Why am I not able to make even trivial
> things clear? (Sigh!) Starting from my personal preference, to refuse
> non-directory paths, I thought that it would be a great idea to be able
> to tell, if a given path references a directory or not. Since I didn't
> find any such function in mysys, I planned to create one. The function
> should return TRUE if the path references a directory and FALSE if it
> does not. I remembered the nice system service stat(2), which returns a
> bunch of information about a path. Among other information, it returns
> the type of object. This is encoded in the st_mode element of the
> returned structure. There is a type mask S_IFTYPE and a type value
> S_IFDIR. The convenience macro S_ISDIR combines these values to return
> zero or non-zero, depending on the type of the object. MY_S_ISDIR is
> just a redefine. my_stat() returns a pointer to the information struct
> if the filesystem object exists and the information could be retrieved,
> and NULL otherwise. Using the above information, test_if_directory()
> returns TRUE or FALSE, depending on the fact if the path references an
> existent directory or not. I hope, this is somewhat believable now.

Thanks for the explanation. I think I could create a Windows-specific method to 
test this but it would mean we would be adding to the platform-specific code 
dilemma.

Chuck

=== modified file 'include/my_dir.h'
--- include/my_dir.h	2008-06-20 00:27:48 +0000
+++ include/my_dir.h	2009-03-30 15:40:11 +0000
@@ -100,6 +100,7 @@ extern MY_DIR *my_dir(const char *path,m
 extern void my_dirend(MY_DIR *buffer);
 extern MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags);
 extern int my_fstat(int filenr, MY_STAT *stat_area, myf MyFlags);
+extern my_bool test_if_directory(const char* path);
 
 #endif /* MY_DIR_H */
 

=== modified file 'mysql-test/suite/backup/r/backup_backupdir.result'
--- mysql-test/suite/backup/r/backup_backupdir.result	2009-03-18 22:01:12 +0000
+++ mysql-test/suite/backup/r/backup_backupdir.result	2009-03-30 20:15:13 +0000
@@ -7,7 +7,7 @@ CREATE TABLE bup_backupdir.t1(a INT);
 INSERT INTO bup_backupdir.t1 VALUES (1), (2), (3);
 Create a directory for backup images
 Reset backupdir 
-SET @@global.backupdir = '../../tmp/backup';
+SET @@global.backupdir = '../../tmp/backup/';
 Perform backup
 BACKUP DATABASE bup_backupdir TO 'bup_backupdir1.bak';
 backup_id
@@ -76,6 +76,49 @@ Warnings:
 Warning	####	The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: This_is_really_stupid/not/there/at/all
 Warning	####	The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: This_is_really_stupid/not/there/at/all
 set global max_allowed_packet=DEFAULT;
+#
+# Set backupdir to an existent file.
+#
+# Create file.
+# Set backupdir.
+SET @@global.backupdir = '../../tmp/backup_file.txt';
+Warnings:
+Warning	1739	The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt backup to a pure file name in invalid backupdir.
+BACKUP DATABASE bup_backupdir TO 'bup_backupdir_x.bak';
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt restore from a pure file name in invalid backupdir.
+RESTORE FROM 'bup_backupdir_x.bak' OVERWRITE;
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt backup to a relative path name in invalid backupdir.
+# Try 'test' in case current directory is used instead of backupdir.
+BACKUP DATABASE bup_backupdir TO 'test/bup_backupdir_x.bak';
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt restore from a relative path name in invalid backupdir.
+# Try 'test' in case current directory is used instead of backupdir.
+RESTORE FROM 'test/bup_backupdir_x.bak' OVERWRITE;
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt backup to a backref path in invalid backupdir.
+BACKUP DATABASE bup_backupdir TO '../bup_backupdir_x.bak';
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt restore from a backref path in invalid backupdir.
+RESTORE FROM '../bup_backupdir_x.bak' OVERWRITE;
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Attempt backup to an absolute path.
+# *Actual BACKUP DATABASE command not printed due to
+#  non-deterministic path.
+# *Performing: 
+# *BACKUP DATABASE bup_backupdir TO
+#  '$MYSQLTEST_VARDIR/bup_backupdir_x.bak';
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
+# Ensure backup image file does not exist
+# Attempt restore from an absolute path.
+# *Actual RESTORE command not printed due to
+#  non-deterministic path.
+# *Performing: 
+# *RESTORE FROM
+#  '$MYSQLTEST_VARDIR/bup_backupdir_x.bak';
+ERROR HY000: The path specified for the system variable backupdir cannot be accessed or
is invalid. ref: ../../tmp/backup_file.txt
 Cleanup
 Reset backupdir 
 SET @@global.backupdir = @@global.datadir;

=== modified file 'mysql-test/suite/backup/r/backup_securebackup.result'
--- mysql-test/suite/backup/r/backup_securebackup.result	2009-02-09 18:17:55 +0000
+++ mysql-test/suite/backup/r/backup_securebackup.result	2009-03-30 20:55:33 +0000
@@ -24,7 +24,7 @@ Ensure backup image file went to the cor
 
 Change backupdir to securebackup_path/subpath 
 (MYSQLD_DATADIR/securebackup_path/subpath)
-SET @@global.backupdir = 'securebackup_path/subpath';
+SET @@global.backupdir = 'securebackup_path/subpath/';
 
 Backup to subpath of path specified by --secure-backup-file-priv option, 
 no dir in backup file name
@@ -62,7 +62,7 @@ ERROR HY000: The MySQL server is running
 
 Change backupdir to securebackup_path/subpath 
 (MYSQLD_DATADIR/securefilepriv_path/subpath)
-SET @@global.backupdir = 'securefilepriv_path/subpath';
+SET @@global.backupdir = 'securefilepriv_path/subpath/';
 (MYSQLD_DATADIR/securefilepriv_path)
 BACKUP DATABASE mysqltest TO 'securefilepriv_path/bup_sfp5.bak';
 ERROR HY000: The MySQL server is running with the --secure-backup-file-priv option so it
cannot execute this statement

=== modified file 'mysql-test/suite/backup/t/backup_backupdir.test'
--- mysql-test/suite/backup/t/backup_backupdir.test	2009-02-26 22:19:09 +0000
+++ mysql-test/suite/backup/t/backup_backupdir.test	2009-03-30 20:12:28 +0000
@@ -37,7 +37,7 @@ rmdir $MYSQLTEST_VARDIR/tmp/backup;
 mkdir $MYSQLTEST_VARDIR/tmp/backup;
 
 --echo Reset backupdir 
-SET @@global.backupdir = '../../tmp/backup';
+SET @@global.backupdir = '../../tmp/backup/';
 
 --echo Perform backup
 --replace_column 1 #
@@ -135,6 +135,75 @@ SET @@global.backupdir = 'This_is_really
 
 set global max_allowed_packet=DEFAULT;
 
+--echo #
+--echo # Set backupdir to an existent file.
+--echo #
+
+--echo # Create file.
+--error 0,1
+--remove_file $MYSQLTEST_VARDIR/tmp/backup_file.txt
+--write_file $MYSQLTEST_VARDIR/tmp/backup_file.txt EOF
+blabla
+EOF
+--echo # Set backupdir.
+SET @@global.backupdir = '../../tmp/backup_file.txt';
+
+--echo # Attempt backup to a pure file name in invalid backupdir.
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--error ER_BACKUP_BACKUPDIR
+BACKUP DATABASE bup_backupdir TO 'bup_backupdir_x.bak';
+
+--echo # Attempt restore from a pure file name in invalid backupdir.
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--error ER_BACKUP_BACKUPDIR
+RESTORE FROM 'bup_backupdir_x.bak' OVERWRITE;
+
+--echo # Attempt backup to a relative path name in invalid backupdir.
+--echo # Try 'test' in case current directory is used instead of backupdir.
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--error ER_BACKUP_BACKUPDIR
+BACKUP DATABASE bup_backupdir TO 'test/bup_backupdir_x.bak';
+
+--echo # Attempt restore from a relative path name in invalid backupdir.
+--echo # Try 'test' in case current directory is used instead of backupdir.
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+--error ER_BACKUP_BACKUPDIR
+RESTORE FROM 'test/bup_backupdir_x.bak' OVERWRITE;
+
+--echo # Attempt backup to a backref path in invalid backupdir.
+--error ER_BACKUP_BACKUPDIR
+BACKUP DATABASE bup_backupdir TO '../bup_backupdir_x.bak';
+
+--echo # Attempt restore from a backref path in invalid backupdir.
+--error ER_BACKUP_BACKUPDIR
+RESTORE FROM '../bup_backupdir_x.bak' OVERWRITE;
+
+--echo # Attempt backup to an absolute path.
+--echo # *Actual BACKUP DATABASE command not printed due to
+--echo #  non-deterministic path.
+--echo # *Performing: 
+--echo # *BACKUP DATABASE bup_backupdir TO
+--echo #  '\$MYSQLTEST_VARDIR/bup_backupdir_x.bak';
+--disable_query_log
+--error ER_BACKUP_BACKUPDIR
+eval BACKUP DATABASE bup_backupdir TO '$MYSQLTEST_VARDIR/bup_backupdir_x.bak';
+--enable_query_log
+
+--echo # Ensure backup image file does not exist
+--error 1
+--file_exists $MYSQLTEST_VARDIR/bup_backupdir_x.bak
+
+--echo # Attempt restore from an absolute path.
+--echo # *Actual RESTORE command not printed due to
+--echo #  non-deterministic path.
+--echo # *Performing: 
+--echo # *RESTORE FROM
+--echo #  '\$MYSQLTEST_VARDIR/bup_backupdir_x.bak';
+--disable_query_log
+--error ER_BACKUP_BACKUPDIR
+eval RESTORE FROM '$MYSQLTEST_VARDIR/bup_backupdir_x.bak' OVERWRITE;
+--enable_query_log
+
 --echo Cleanup
 
 --echo Reset backupdir 

=== modified file 'mysql-test/suite/backup/t/backup_securebackup.test'
--- mysql-test/suite/backup/t/backup_securebackup.test	2009-02-09 18:17:55 +0000
+++ mysql-test/suite/backup/t/backup_securebackup.test	2009-03-30 20:55:01 +0000
@@ -71,7 +71,7 @@ BACKUP DATABASE mysqltest TO 'secureback
 --echo  
 --echo Change backupdir to securebackup_path/subpath 
 --echo (MYSQLD_DATADIR/securebackup_path/subpath)
-SET @@global.backupdir = 'securebackup_path/subpath';
+SET @@global.backupdir = 'securebackup_path/subpath/';
 
 --echo  
 --echo Backup to subpath of path specified by --secure-backup-file-priv option, 
@@ -141,7 +141,7 @@ RESTORE FROM 'securefilepriv_path/bup_sf
 --echo  
 --echo Change backupdir to securebackup_path/subpath 
 --echo (MYSQLD_DATADIR/securefilepriv_path/subpath)
-SET @@global.backupdir = 'securefilepriv_path/subpath';
+SET @@global.backupdir = 'securefilepriv_path/subpath/';
 
 #
 # Now check to ensure backup cannot write to the --secure-file-priv location even

=== modified file 'mysys/my_lib.c'
--- mysys/my_lib.c	2008-06-17 23:37:23 +0000
+++ mysys/my_lib.c	2009-03-30 16:24:27 +0000
@@ -508,6 +508,41 @@ error:
 
 #endif /* _WIN32 */
 
+
+/**
+  Test if path is a directory.
+
+  @param[in]    path            path name
+
+  @return       test result
+    @retval     TRUE            path is directory
+    @retval     FALSE           path is not directory
+*/
+
+my_bool test_if_directory(const char* path)
+{
+  MY_STAT statbuf;
+  char dir_path[FN_REFLEN];
+  int i= strlen(path);
+  strnmov(dir_path, path, i);
+
+#if defined(_WIN32)
+  /*
+    If this is a Windows machine, we need to strip the last / or \ off the 
+    path if present so that my_stat() can correct identify the path
+    as containing a directory.
+  */
+  if ((dir_path[i-1] == '/') || (dir_path[i-1] == '\\'))
+    dir_path[i-1] = 0;
+#endif /* _WIN32 */
+
+  if (my_stat(dir_path, &statbuf, MYF(0)) &&
+      MY_S_ISDIR(statbuf.st_mode))
+    return TRUE;
+  return FALSE;
+}
+
+
 /****************************************************************************
 ** File status
 ** Note that MY_STAT is assumed to be same as struct stat

=== modified file 'sql/backup/kernel.cc'
--- sql/backup/kernel.cc	2009-03-16 14:38:05 +0000
+++ sql/backup/kernel.cc	2009-03-30 15:40:12 +0000
@@ -61,6 +61,7 @@
 
 #include "../mysql_priv.h"
 #include "../si_objects.h"
+#include "my_dir.h"
 
 #include "backup_kernel.h"
 #include "backup_info.h"
@@ -160,8 +161,15 @@ execute_backup_command(THD *thd, 
     Check backupdir for validity. This is needed since we cannot trust
     that the path is still valid. Access could have changed or the
     folders in the path could have been moved, deleted, etc.
-  */
-  if (backupdir->length() && my_access(backupdir->c_ptr(), (F_OK|W_OK)))
+    This can of course happen after the check too...
+    It has been decided that this check shall be done even if the
+    BACKUP/RESTORE statement contains an absolute path name.
+    If this requirement is dropped one day, we can easily add
+    && !test_if_hard_path(lex->backup_dir.str). See Bug#43536.
+  */
+  if (backupdir->length() &&
+      (!test_if_directory(backupdir->c_ptr_safe()) ||
+       my_access(backupdir->c_ptr_safe(), (F_OK|W_OK))))
     DBUG_RETURN(send_error(context, ER_BACKUP_BACKUPDIR, backupdir->c_ptr()));
  
   switch (lex->sql_command) {

=== modified file 'sql/set_var.cc'
--- sql/set_var.cc	2009-03-18 21:09:40 +0000
+++ sql/set_var.cc	2009-03-30 15:40:12 +0000
@@ -3032,11 +3032,9 @@ static int sys_check_backupdir(THD *thd,
   if (!path_length)
     return 0;
 
-  /*
-    Check if directory exists and we have permission to create file and
-    write to file.
-  */
-  if (my_access(path, (F_OK|W_OK)))
+  /* Check if backupdir is a directory and we have write permission to it. */
+  if (!test_if_directory(path) ||
+      my_access(path, (F_OK|W_OK)))
     goto err;
 
   return 0;
@@ -3108,7 +3106,9 @@ static bool sys_update_backupdir(THD *th
   logger.unlock();
   pthread_mutex_unlock(&LOCK_global_system_variables);
 
-  if (my_access(sys_var_backupdir.value, (F_OK|W_OK)))
+  /* Check if backupdir is a directory and we have write permission to it. */
+  if (!test_if_directory(sys_var_backupdir.value) ||
+      my_access(sys_var_backupdir.value, (F_OK|W_OK)))
     goto err;
 
   return result;


Thread
bzr commit into mysql-6.0-backup branch (ingo.struewing:2803) Bug#43747Ingo Struewing20 Mar
  • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2803)Bug#43747Jørgen Løland23 Mar
  • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2803)Bug#43747Chuck Bell23 Mar
    • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2803)Bug#43747Ingo Strüwing24 Mar
      • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2803)Bug#43747Chuck Bell30 Mar
        • HLS for Bug#43747 [Re: bzr commit into mysql-6.0-backup branch(ingo.struewing:2803) Bug#43747]Ingo Strüwing1 Apr