diff --git a/include/my_sys.h b/include/my_sys.h index 472c2ba5ca0..9983ee3319f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2017, 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 @@ -49,6 +49,7 @@ typedef struct my_aio_result { #ifdef _WIN32 #include /*for alloca*/ #endif +#include #define MY_INIT(name) { my_progname= name; my_init(); } @@ -491,6 +492,16 @@ typedef struct st_io_cache /* Used when cacheing files */ typedef int (*qsort2_cmp)(const void *, const void *, const void *); +/* + Subset of struct stat fields filled by stat/lstat/fstat that uniquely + identify a file +*/ +typedef struct st_file_id +{ + dev_t st_dev; + ino_t st_ino; +} ST_FILE_ID; + /* defines for mf_iocache */ /* Test if buffer is inited */ @@ -569,8 +580,9 @@ extern File my_create(const char *FileName,int CreateFlags, extern int my_close(File Filedes,myf MyFlags); extern int my_mkdir(const char *dir, int Flags, myf MyFlags); extern int my_readlink(char *to, const char *filename, myf MyFlags); -extern int my_is_symlink(const char *filename); +extern int my_is_symlink(const char *filename, ST_FILE_ID *file_id); extern int my_realpath(char *to, const char *filename, myf MyFlags); +extern int my_is_same_file(File file, const ST_FILE_ID *file_id); extern File my_create_with_symlink(const char *linkname, const char *filename, int createflags, int access_flags, myf MyFlags); diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index cdb16a7422b..4569fcf7fbf 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2001, 2017, 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 @@ -99,11 +99,18 @@ int my_symlink(const char *content, const char *linkname, myf MyFlags) #endif -int my_is_symlink(const char *filename __attribute__((unused))) +int my_is_symlink(const char *filename __attribute__((unused)), + ST_FILE_ID *file_id) { #if defined (HAVE_LSTAT) && defined (S_ISLNK) struct stat stat_buff; - return !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode); + int result= !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode); + if (file_id && !result) + { + file_id->st_dev= stat_buff.st_dev; + file_id->st_ino= stat_buff.st_ino; + } + return result; #elif defined (_WIN32) DWORD dwAttr = GetFileAttributes(filename); return (dwAttr != INVALID_FILE_ATTRIBUTES) && @@ -164,3 +171,20 @@ int my_realpath(char *to, const char *filename, myf MyFlags) #endif return 0; } + + +/** + Return non-zero if the file descriptor and a previously lstat-ed file + identified by file_id point to the same file +*/ +int my_is_same_file(File file, const ST_FILE_ID *file_id) +{ + MY_STAT stat_buf; + if (my_fstat(file, &stat_buf, MYF(0)) == -1) + { + my_errno= errno; + return 0; + } + return (stat_buf.st_dev == file_id->st_dev) + && (stat_buf.st_ino == file_id->st_ino); +} diff --git a/storage/myisam/mi_delete_table.c b/storage/myisam/mi_delete_table.c index b6530334dd2..768b4618928 100644 --- a/storage/myisam/mi_delete_table.c +++ b/storage/myisam/mi_delete_table.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2017, 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 @@ -29,7 +29,7 @@ int mi_delete_table(const char *name) #endif fn_format(from,name,"",MI_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from)) + if (my_is_symlink(from, NULL) && (*myisam_test_invalid_symlink)(from)) { /* Symlink is pointing to file in data directory. @@ -44,7 +44,7 @@ int mi_delete_table(const char *name) DBUG_RETURN(my_errno); } fn_format(from,name,"",MI_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from)) + if (my_is_symlink(from, NULL) && (*myisam_test_invalid_symlink)(from)) { /* Symlink is pointing to file in data directory. diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index 93f70a5d340..1f0cc8e2169 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2017, 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 @@ -78,6 +78,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) ulong rec_per_key_part[HA_MAX_POSSIBLE_KEY*MI_MAX_KEY_SEG]; my_off_t key_root[HA_MAX_POSSIBLE_KEY],key_del[MI_MAX_KEY_BLOCK_SIZE]; ulonglong max_key_file_length, max_data_file_length; + ST_FILE_ID file_id= {0, 0}; DBUG_ENTER("mi_open"); LINT_INIT(m_info); @@ -89,11 +90,15 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) realpath_err= my_realpath(name_buff, fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0)); - if (my_is_symlink(org_name) && - (realpath_err || (*myisam_test_invalid_symlink)(name_buff))) + if (my_is_symlink(name_buff, &file_id)) { - my_errno= HA_WRONG_CREATE_OPTION; - DBUG_RETURN (NULL); + if (realpath_err || + (*myisam_test_invalid_symlink)(name_buff) || + my_is_symlink(name_buff, &file_id)) + { + my_errno= HA_WRONG_CREATE_OPTION; + DBUG_RETURN (NULL); + } } mysql_mutex_lock(&THR_LOCK_myisam); @@ -113,17 +118,28 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) my_errno= HA_ERR_CRASHED; goto err; }); + DEBUG_SYNC_C("before_opening_indexfile"); if ((kfile= mysql_file_open(mi_key_file_kfile, name_buff, - (open_mode= O_RDWR) | O_SHARE, MYF(0))) < 0) + (open_mode= O_RDWR) | O_SHARE | O_NOFOLLOW, + MYF(0))) < 0) { if ((errno != EROFS && errno != EACCES) || mode != O_RDONLY || (kfile= mysql_file_open(mi_key_file_kfile, name_buff, - (open_mode= O_RDONLY) | O_SHARE, MYF(0))) < 0) + (open_mode= O_RDONLY) | O_SHARE | O_NOFOLLOW, + MYF(0))) < 0) goto err; } + + if (!my_is_same_file(kfile, &file_id)) + { + mysql_file_close(kfile, MYF(0)); + my_errno= HA_WRONG_CREATE_OPTION; + goto err; + } + share->mode=open_mode; errpos=1; if (mysql_file_read(kfile, share->state.header.file_version, head_length, @@ -1206,14 +1222,16 @@ int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name, { char *data_name= share->data_file_name; char real_data_name[FN_REFLEN]; + ST_FILE_ID file_id= {0, 0}; if (org_name) { fn_format(real_data_name,org_name,"",MI_NAME_DEXT,4); - if (my_is_symlink(real_data_name)) + if (my_is_symlink(real_data_name, &file_id)) { if (my_realpath(real_data_name, real_data_name, MYF(0)) || - (*myisam_test_invalid_symlink)(real_data_name)) + (*myisam_test_invalid_symlink)(real_data_name) || + my_is_symlink(real_data_name, &file_id)) { my_errno= HA_WRONG_CREATE_OPTION; return 1; @@ -1221,9 +1239,19 @@ int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name, data_name= real_data_name; } } + DEBUG_SYNC_C("before_opening_datafile"); info->dfile= mysql_file_open(mi_key_file_dfile, - data_name, share->mode | O_SHARE, MYF(MY_WME)); - return info->dfile >= 0 ? 0 : 1; + data_name, share->mode | O_SHARE | O_NOFOLLOW, + MYF(MY_WME)); + if (info->dfile < 0) + return 1; + if (org_name && !my_is_same_file(info->dfile, &file_id)) + { + mysql_file_close(info->dfile, MYF(0)); + my_errno= HA_WRONG_CREATE_OPTION; + return 1; + } + return 0; }