Bug #43767 Wrong path if backupdir is symlink and backref path used
Submitted: 20 Mar 2009 11:47 Modified: 29 Jun 2009 14:40
Reporter: Ingo Strüwing Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Backup Severity:S2 (Serious)
Version:6.0 OS:Any
Assigned to: Ingo Strüwing CPU Architecture:Any

[20 Mar 2009 11:47] Ingo Strüwing
Description:
If the server variable "backupdir" is set to a symbolic link, which references an existent directory, BACKUP and RESTORE use the wrong path, if their backup image file paramter is given as a path that references backwards (i.e. starts with ../).

Instead of going backwards from the symlink's target directory, it goes backwards from the symlink itself.

How to repeat:
--mkdir $MYSQLTEST_VARDIR/tmp/backup
--mkdir $MYSQLTEST_VARDIR/tmp/backup/backup_sym

--exec ln -s backup/backup_sym $MYSQLTEST_VARDIR/tmp/backup_symlink

BACKUP DATABASE bup_backupdir TO '../bup_backupdir_1.bak';

The file is created as $MYSQLTEST_VARDIR/tmp/bup_backupdir_1.bak
while it should be $MYSQLTEST_VARDIR/tmp/backup/bup_backupdir_1.bak

Compare normal file operations:

mkdir -p mysql-test/var/tmp/backup/backup_sym
ln -s backup/backup_sym mysql-test/var/tmp/backup_symlink
touch mysql-test/var/tmp/backup_symlink/../backup.txt
find mysql-test/var/tmp/backup -type f
mysql-test/var/tmp/backup/backup.txt
[20 Mar 2009 11:51] Ingo Strüwing
Suggested triage values:
Defect: Medium. Annoying, but not really serious.
Workaround: None.
Impact: Substantial, assuming that use of symbolic links is common, but the use of ../ is less common.
[27 Mar 2009 16:34] Ingo Strüwing
If this will be fixed, please note that some tests need to have their workarounds removed. The tests are not pushed yet. They are developed for WL#4518 (Online Backup: Test backupdir variable). The affected test cases are: backup_backupdir, backup_backupdir_not_windows, and backup_backupdir_sync.
[24 Apr 2009 14:55] Ingo Strüwing
This is fixed together with Bug#43747 (BACKUP with backref path succeeds on invalid backupdir). See there for more information.
[24 Apr 2009 14:58] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/72789

2797 Ingo Struewing	2009-04-24
      Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
      Bug#43767 - Wrong path if backupdir is symlink and backref path used
      
      A backupdir variable value like 'existent_file.txt' and a backup image
      file name like '../backup_file.bak' allowed for a successful BACKUP and
      RESTORE (Bug #43747). It had been decided that we do not want to allow
      BACKUP or RESTORE to succeed, if backupdir is set to an invalid path
      (compare Bug 43536 - BACKUP with absolute path fails on invalid
      backupdir).
      
      A backupdir variable value like 'symlink_to_dir1_dir2' and a backup image
      file name like '../backup_file.bak' located the file in the wrong
      directory. If "symlink_to_dir1_dir2 -> dir1/dir2", the used file was
      just "backup_file.bak". Cleanup of "../" was done based on the symlink
      itself, not based on its target (dir2). Normal file system operations
      interpret a path of "symlink_to_dir1_dir2/../backup_file.bak" as
      "dir1/backup_file.bak".
      
      The main problem was that MySQL did not have a path handling function,
      which cleans a path while respecting symbolic links, works even if the
      path name references a non-existent file system object, and works on
      Windows too. For more details see the explanation in the bug report
      #43747.
      
      Another problem was that the backupdir variable was checked for an
      existent and writable file system object only. It was not required that
      this object needs to be a directory.
      
      The main part of the patch is the new function safe_cleanup_cat_path()
      in mf_pack.c.
      
      Another important new function is test_if_directory() in my_lib.c. It
      allows to verify that backupdir is not just a writable path, but also a
      directory.
      
      Yet another new function is cut_print_path() in mf_format.c. It supports
      the creation of good error messages. Many error message templates have
      limited space for path names, e.g. %.64s. Often the last part of a path
      name is more informative for error messages than its begin. This
      function returns a cut path with a prepended "... ", if the path is
      longer than the length limit.
      
      Yet another new function is is_usable_directory() in set_var.cc. It
      supports checking of variable values when they are set.
      
      To avoid frequent use of strlen() I added length variables to the
      seldom or never changed variables home_dir, mysql_real_data_home, and
      opt_secure_backup_file_priv.
      
      Finally I used these functions in the backup kernel to build an
      absolute, clean path for the backupdir to check it for being a writable
      directory and to prepend it to a relative backup image file path.
      
      Additionally I used the functions on opt_secure_backup_file_priv before
      comparing it against the resulting backup image file path.
      
      To have a good test coverage for the new non-trivial function
      safe_cleanup_cat_path(), I made a unittest for it.
     @ include/my_dir.h
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added declaration for test_if_directory().
     @ include/my_sys.h
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added declarations for home_dir_len, cut_print_path(), and
        cleanup_cat_path().
     @ mysql-test/suite/backup/r/backup_backupdir.result
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Updated test result.
     @ mysql-test/suite/backup/r/backup_backupdir_not_windows.result
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test result.
     @ mysql-test/suite/backup/r/backup_securebackup.result
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Updated test result.
     @ mysql-test/suite/backup/t/backup_backupdir.test
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added proper cleanup for backupdir.
        Added SELECTs for backupdir to show its changes.
        Removed tests for limited length of the variable.
        Made the test for a "really long string" better checkable.
        Added new tests for the reported bug.
        Added tests for special variable values (NULL and DEFAULT).
     @ mysql-test/suite/backup/t/backup_backupdir_not_windows.test
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test case for Bug#43767.
     @ mysql-test/suite/backup/t/backup_securebackup-master.opt
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added a --backupdir option for coverage testing.
     @ mysql-test/suite/backup/t/backup_securebackup.test
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added proper cleanup for backupdir.
        Fixed wrong tests (bup_sfp5.bak, bup_sfp1.bak).
        Added a printout for the backupdir option.
     @ mysys/mf_format.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added definition of new function cut_print_path().
     @ mysys/mf_pack.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added definition of new function safe_cleanup_cat_path().
     @ mysys/my_init.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added setup for home_dir_len.
     @ mysys/my_lib.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added definition of new function test_if_directory().
     @ mysys/my_static.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added declaration of home_dir_len.
     @ sql/backup/kernel.cc
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Moved check of backupdir from execute_backup_command() to
        Backup_restore_ctx::prepare_path().
        Changed the latter to use safe_cleanup_cat_path() and
        check_backupdir().
     @ sql/backup/stream.cc
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Changed Stream::test_secure_file_priv_access() to use
        safe_cleanup_cat_path() on opt_secure_backup_file_priv.
     @ sql/mysql_priv.h
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added declarations of mysql_real_data_home_len and
        opt_secure_backup_file_priv_len.
     @ sql/mysqld.cc
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added definitions and setup of mysql_real_data_home_len and
        opt_secure_backup_file_priv_len.
        Moved check of backupdir value from mysqld_get_one_option() to
        fix_paths().
        Used is_usable_directory() on backupdir and
        opt_secure_backup_file_priv.
     @ sql/set_var.cc
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Removed double declarations.
        Removed sys_check_backupdir() altogether. All checks are done in
        sys_update_backupdir() now.
        Changed make_default_backupdir() to default_backupdir(), which
        allocates the value.
        Added definition of new function check_backupdir().
        Added definition of new function is_usable_directory().
        Changed sys_update_backupdir() to use is_usable_directory().
     @ sql/set_var.h
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added declarations for is_usable_directory(), check_backupdir(),
        and default_backupdir().
     @ sql/share/errmsg.txt
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Changed ER_BACKUP_BACKUPDIR to the more genaral usable ER_NOT_RW_DIR.
     @ unittest/mysys/CMakeLists.txt
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added safe_cleanup_cat_path-t.
     @ unittest/mysys/Makefile.am
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added safe_cleanup_cat_path-t.
     @ unittest/mysys/safe_cleanup_cat_path-t.c
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New unit test for safe_cleanup_cat_path().
[28 Apr 2009 17:04] Chuck Bell
Patch approved. Well done.
[29 Apr 2009 9:11] Jørgen Løland
Good to push.
[1 May 2009 12:15] Ingo Strüwing
Queued to 6.0-backup.
[6 May 2009 16:56] Ingo Strüwing
I'm going to modify the solution in that path cleanup for ../ will not only be suppressed if the preceding path element is a symbolic link, but also if it is not a directory. The current solution will do the following wrong:

  dir/non-existent-file.txt/../existent-file.txt

This will result in dir/existent-file.txt, which is not what standard file operations would do.
[8 May 2009 15:56] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/73665

2808 Ingo Struewing	2009-05-08
      Bug#43767 - Wrong path if backupdir is symlink and backref path used
      
      Addendum fix to Bug#43747 (BACKUP with backref path succeeds
      on invalid backupdir) and Bug#43767.
      
      The former fix checked every path element for being a symlink.
      Only then it was prevented to let ../ remove this path element.
      
      But the correct solution is to prevent ../ from removing all
      path elements except of real (non-symlinked) directories.
      
      A path like dir/non-existent-file.txt/../existent-file.txt must not
      be cleaned up to dir/existent-file.txt. Normal (operating system)
      file operations don't do that either.
      
      ".." is a valid path element with its back-referencing semantics
      in an existent directory only.
     @ include/my_sys.h
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added define for MY_RETURN_SYMLINK.
     @ mysys/mf_pack.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Changed safe_cleanup_cat_path() to call test_if_real_directory()
        instead of my_is_symlink() on each path element.
        Fixed two issues with removal of ./ and duplicate / behind
        non-real-dirctory path elements.
     @ mysys/my_lib.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added function test_if_real_directory().
     @ unittest/mysys/safe_cleanup_cat_path-t.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added directory creation/removal.
        Reworked tests to show when directories and non-directories are used.
        Added tests so that we use a directory and a non-directory
        everywhere it makes sense.
[8 May 2009 15:58] Ingo Strüwing
Please review the small addendum patch.
[19 May 2009 9:49] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/74470

2808 Ingo Struewing	2009-05-19
      Bug#43767 - Wrong path if backupdir is symlink and backref path used
      
      Addendum fix to Bug#43747 (BACKUP with backref path succeeds
      on invalid backupdir) and Bug#43767.
      
      The former fix checked every path element for being a symlink.
      Only then it was prevented to let ../ remove this path element.
      
      But the correct solution is to prevent ../ from removing all
      path elements except of real (non-symlinked) directories.
      
      A path like dir/non-existent-file.txt/../existent-file.txt must not
      be cleaned up to dir/existent-file.txt. Normal (operating system)
      file operations don't do that either.
      
      ".." is a valid path element with its back-referencing semantics
      in an existent directory only.
      
      However, on Windows, we need to do the clean-ups irrespectively
      of the file system object. Windows system services do the clean-up
      even after path elements that refer to non-existent objects.
      In the above example, dir/existent-file.txt is used.
      
      safe_cleanup_cat_path(), its unittest, and the regression tests
      do now respect the platform difference.
     @ include/my_sys.h
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added MY_RETURN_SYMLINK.
     @ mysql-test/suite/backup/include/backup_image_path.inc
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New body for new test cases.
     @ mysql-test/suite/backup/r/backup_image_path_nonwin.result
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test result.
     @ mysql-test/suite/backup/r/backup_image_path_win.result
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test result.
     @ mysql-test/suite/backup/t/backup_image_path_nonwin.test
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test case.
     @ mysql-test/suite/backup/t/backup_image_path_win.test
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        New test case.
     @ mysys/mf_pack.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Fixed comments.
        Changed check for symlink to check for real directory.
     @ mysys/my_lib.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Added test_if_real_directory().
     @ unittest/mysys/safe_cleanup_cat_path-t.c
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        Changed unittest to work with real directories.
        Added tests to be more systematic.
        Grouped platform specific tests.
[20 May 2009 17:22] Chuck Bell
Approved
[21 May 2009 8:18] Jørgen Løland
Good to push
[22 May 2009 14:53] Ingo Strüwing
Queued to 6.0-backup.
[29 Jun 2009 14:40] Paul DuBois
No changelog entry needed. Not in any released version.
[6 Jan 2010 17:55] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/96163

3035 Ingo Struewing	2010-01-06
      WL#5101 - MySQL Backup back port
      Merged revid:ingo.struewing@sun.com-20090424145553-xmtiynisc12yf3wl
      Merged revid:ingo.struewing@sun.com-20090519094845-d1a0tsqervmfwp57
        Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        
        A backupdir variable value like 'existent_file.txt' and a backup image
        file name like '../backup_file.bak' allowed for a successful BACKUP and
        RESTORE (Bug #43747). It had been decided that we do not want to allow
        BACKUP or RESTORE to succeed, if backupdir is set to an invalid path
        (compare Bug 43536 - BACKUP with absolute path fails on invalid
        backupdir).
        
        A backupdir variable value like 'symlink_to_dir1_dir2' and a backup image
        file name like '../backup_file.bak' located the file in the wrong
        directory. If "symlink_to_dir1_dir2 -> dir1/dir2", the used file was
        just "backup_file.bak". Cleanup of "../" was done based on the symlink
        itself, not based on its target (dir2). Normal file system operations
        interpret a path of "symlink_to_dir1_dir2/../backup_file.bak" as
        "dir1/backup_file.bak".
        
        The main problem was that MySQL did not have a path handling function,
        which cleans a path while respecting symbolic links, works even if the
        path name references a non-existent file system object, and works on
        Windows too. For more details see the explanation in the bug report
        #43747.
        
        Another problem was that the backupdir variable was checked for an
        existent and writable file system object only. It was not required that
        this object needs to be a directory.
        
        The main part of the patch is the new function safe_cleanup_cat_path()
        in mf_pack.c.
        
        Another important new function is test_if_directory() in my_lib.c. It
        allows to verify that backupdir is not just a writable path, but also a
        directory.
        
        Yet another new function is cut_print_path() in mf_format.c. It supports
        the creation of good error messages. Many error message templates have
        limited space for path names, e.g. %.64s. Often the last part of a path
        name is more informative for error messages than its begin. This
        function returns a cut path with a prepended "... ", if the path is
        longer than the length limit.
        
        Yet another new function is is_usable_directory() in set_var.cc. It
        supports checking of variable values when they are set.
        
        To avoid frequent use of strlen() I added length variables to the
        seldom or never changed variables home_dir, mysql_real_data_home, and
        opt_secure_backup_file_priv.
        
        Finally I used these functions in the backup kernel to build an
        absolute, clean path for the backupdir to check it for being a writable
        directory and to prepend it to a relative backup image file path.
        
        Additionally I used the functions on opt_secure_backup_file_priv before
        comparing it against the resulting backup image file path.
        
        To have a good test coverage for the new non-trivial function
        safe_cleanup_cat_path(), I made a unittest for it.
      ---
        Bug#43767 - Wrong path if backupdir is symlink and backref path used
        
        Addendum fix to Bug#43747 (BACKUP with backref path succeeds
        on invalid backupdir) and Bug#43767.
        
        The former fix checked every path element for being a symlink.
        Only then it was prevented to let ../ remove this path element.
        
        But the correct solution is to prevent ../ from removing all
        path elements except of real (non-symlinked) directories.
        
        A path like dir/non-existent-file.txt/../existent-file.txt must not
        be cleaned up to dir/existent-file.txt. Normal (operating system)
        file operations don't do that either.
        
        ".." is a valid path element with its back-referencing semantics
        in an existent directory only.
        
        However, on Windows, we need to do the clean-ups irrespectively
        of the file system object. Windows system services do the clean-up
        even after path elements that refer to non-existent objects.
        In the above example, dir/existent-file.txt is used.
        
        safe_cleanup_cat_path(), its unittest, and the regression tests
        do now respect the platform difference.
      
      original changeset: 2599.146.1
      original changeset: 2599.148.1
     @ include/my_sys.h
        WL#5101 - MySQL Backup back port
        Removed a MY_FLAG value clash.
     @ libmysql/CMakeLists.txt
        WL#5101 - MySQL Backup back port
        Added mysys as include directory. Required for my_rnd.c.
     @ unittest/mysys/CMakeLists.txt
        WL#5101 - MySQL Backup back port
            Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
            Bug#43767 - Wrong path if backupdir is symlink and backref path used
            Added safe_cleanup_cat_path-t.
     @ unittest/mysys/Makefile.am
        WL#5101 - MySQL Backup back port
            Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
            Bug#43767 - Wrong path if backupdir is symlink and backref path used
            Added safe_cleanup_cat_path-t.
     @ unittest/mysys/safe_cleanup_cat_path-t.c
        WL#5101 - MySQL Backup back port
            Bug#43747 - BACKUP with backref path succeeds on invalid backupdir
            Bug#43767 - Wrong path if backupdir is symlink and backref path used
            New unit test for safe_cleanup_cat_path().