1 | /* Copyright (c) 2000, 2012, Oracle and/or its affiliates |
2 | Copyright (c) 2012, 2014, SkySQL Ab |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
16 | |
17 | #include "mysys_priv.h" |
18 | #include <m_string.h> |
19 | |
20 | #ifdef __WIN__ |
21 | |
22 | /* |
23 | Check a file or path for accessability. |
24 | |
25 | SYNOPSIS |
26 | file_access() |
27 | path Path to file |
28 | amode Access method |
29 | |
30 | RETURN VALUES |
31 | 0 ok |
32 | -1 error (We use -1 as my_access is mapped to access on other platforms) |
33 | */ |
34 | |
35 | int my_access(const char *path, int amode) |
36 | { |
37 | DWORD attributes; |
38 | |
39 | attributes = GetFileAttributes(path); |
40 | if (attributes == INVALID_FILE_ATTRIBUTES || |
41 | ((attributes & FILE_ATTRIBUTE_READONLY) && (amode & W_OK))) |
42 | { |
43 | my_errno= errno= EACCES; |
44 | return -1; |
45 | } |
46 | return 0; |
47 | } |
48 | |
49 | #endif /* __WIN__ */ |
50 | |
51 | |
52 | /* |
53 | List of file names that causes problem on windows |
54 | |
55 | NOTE that one can also not have file names of type CON.TXT |
56 | |
57 | NOTE: it is important to keep "CLOCK$" on the first place, |
58 | we skip it in check_if_legal_tablename. |
59 | */ |
60 | static const char *reserved_names[]= |
61 | { |
62 | "CLOCK$" , |
63 | "CON" , "PRN" , "AUX" , "NUL" , |
64 | "COM1" , "COM2" , "COM3" , "COM4" , "COM5" , "COM6" , "COM7" , "COM8" , "COM9" , |
65 | "LPT1" , "LPT2" , "LPT3" , "LPT4" , "LPT5" , "LPT6" , "LPT7" , "LPT8" , "LPT9" , |
66 | NullS |
67 | }; |
68 | |
69 | #define MAX_RESERVED_NAME_LENGTH 6 |
70 | |
71 | |
72 | /* |
73 | Looks up a null-terminated string in a list, |
74 | case insensitively. |
75 | |
76 | SYNOPSIS |
77 | str_list_find() |
78 | list list of items |
79 | str item to find |
80 | |
81 | RETURN |
82 | 0 ok |
83 | 1 reserved file name |
84 | */ |
85 | static int str_list_find(const char **list, const char *str) |
86 | { |
87 | const char **name; |
88 | for (name= list; *name; name++) |
89 | { |
90 | if (!my_strcasecmp(&my_charset_latin1, *name, str)) |
91 | return 1; |
92 | } |
93 | return 0; |
94 | } |
95 | |
96 | |
97 | /* |
98 | A map for faster reserved_names lookup, |
99 | helps to avoid loops in many cases. |
100 | 1 - can be the first letter |
101 | 2 - can be the second letter |
102 | 4 - can be the third letter |
103 | */ |
104 | static char reserved_map[256]= |
105 | { |
106 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
107 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
108 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* !"#$%&'()*+,-./ */ |
109 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0123456789:;<=>? */ |
110 | 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* @ABCDEFGHIJKLMNO */ |
111 | 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* PQRSTUVWXYZ[\]^_ */ |
112 | 0,1,0,1,0,0,0,0,0,0,0,0,7,4,5,2, /* bcdefghijklmno */ |
113 | 3,0,2,0,4,2,0,0,4,0,0,0,0,0,0,0, /* pqrstuvwxyz{|}~. */ |
114 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
115 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
116 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
117 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
118 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
119 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
120 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ................ */ |
121 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* ................ */ |
122 | }; |
123 | |
124 | |
125 | /* |
126 | Check if a table name may cause problems |
127 | |
128 | SYNOPSIS |
129 | check_if_legal_tablename |
130 | name Table name (without any extensions) |
131 | |
132 | DESCRIPTION |
133 | We don't check 'CLOCK$' because dollar sign is encoded as @0024, |
134 | making table file name 'CLOCK@0024', which is safe. |
135 | This is why we start lookup from the second element |
136 | (i.e. &reserver_name[1]) |
137 | |
138 | RETURN |
139 | 0 ok |
140 | 1 reserved file name |
141 | */ |
142 | |
143 | int check_if_legal_tablename(const char *name) |
144 | { |
145 | DBUG_ENTER("check_if_legal_tablename" ); |
146 | DBUG_RETURN((reserved_map[(uchar) name[0]] & 1) && |
147 | (reserved_map[(uchar) name[1]] & 2) && |
148 | (reserved_map[(uchar) name[2]] & 4) && |
149 | str_list_find(&reserved_names[1], name)); |
150 | } |
151 | |
152 | |
153 | #ifdef __WIN__ |
154 | /** |
155 | Checks if the drive letter supplied is valid or not. Valid drive |
156 | letters are A to Z, both lower case and upper case. |
157 | |
158 | @param drive_letter : The drive letter to validate. |
159 | |
160 | @return TRUE if the drive exists, FALSE otherwise. |
161 | */ |
162 | static my_bool does_drive_exists(char drive_letter) |
163 | { |
164 | DWORD drive_mask= GetLogicalDrives(); |
165 | drive_letter= toupper(drive_letter); |
166 | |
167 | return (drive_letter >= 'A' && drive_letter <= 'Z') && |
168 | (drive_mask & (0x1 << (drive_letter - 'A'))); |
169 | } |
170 | |
171 | /** |
172 | Verifies if the file name supplied is allowed or not. On Windows |
173 | file names with a colon (:) are not allowed because such file names |
174 | store data in Alternate Data Streams which can be used to hide |
175 | the data. |
176 | Apart from colon, other characters that are not allowed in filenames |
177 | on Windows are greater/less sign, double quotes, forward slash, backslash, |
178 | pipe and star characters. |
179 | |
180 | See MSDN documentation on filename restrictions. |
181 | |
182 | @param name contains the file name with or without path |
183 | @param length contains the length of file name |
184 | @param allow_current_dir TRUE if paths like C:foobar are allowed, |
185 | FALSE otherwise |
186 | |
187 | @return TRUE if the file name is allowed, FALSE otherwise. |
188 | */ |
189 | #define ILLEGAL_FILENAME_CHARS "<>:\"/\\|?*" |
190 | |
191 | my_bool is_filename_allowed(const char *name __attribute__((unused)), |
192 | size_t length __attribute__((unused)), |
193 | my_bool allow_current_dir __attribute__((unused))) |
194 | { |
195 | /* |
196 | For Windows, check if the file name contains : character. |
197 | Start from end of path and search if the file name contains : |
198 | */ |
199 | const char* ch = NULL; |
200 | for (ch= name + length - 1; ch >= name; --ch) |
201 | { |
202 | if (FN_LIBCHAR == *ch || '/' == *ch) |
203 | break; |
204 | else if (':' == *ch) |
205 | { |
206 | /* |
207 | File names like C:foobar.txt are allowed since the syntax means |
208 | file foobar.txt in current directory of C drive. However file |
209 | names likes CC:foobar are not allowed since this syntax means ADS |
210 | foobar in file CC. |
211 | */ |
212 | return (allow_current_dir && (ch - name == 1) && |
213 | does_drive_exists(*name)); |
214 | } |
215 | else if (strchr(ILLEGAL_FILENAME_CHARS, *ch)) |
216 | return FALSE; |
217 | } |
218 | return TRUE; |
219 | } /* is_filename_allowed */ |
220 | #endif /* __WIN__ */ |
221 | |
222 | #if defined(__WIN__) || defined(__EMX__) |
223 | |
224 | |
225 | /* |
226 | Check if a path will access a reserved file name that may cause problems |
227 | |
228 | SYNOPSIS |
229 | check_if_legal_filename |
230 | path Path to file |
231 | |
232 | RETURN |
233 | 0 ok |
234 | 1 reserved file name |
235 | */ |
236 | |
237 | int check_if_legal_filename(const char *path) |
238 | { |
239 | const char *end; |
240 | const char **reserved_name; |
241 | DBUG_ENTER("check_if_legal_filename" ); |
242 | |
243 | if (!is_filename_allowed(path, strlen(path), TRUE)) |
244 | DBUG_RETURN(1); |
245 | |
246 | path+= dirname_length(path); /* To start of filename */ |
247 | if (!(end= strchr(path, FN_EXTCHAR))) |
248 | end= strend(path); |
249 | if (path == end || (uint) (end - path) > MAX_RESERVED_NAME_LENGTH) |
250 | DBUG_RETURN(0); /* Simplify inner loop */ |
251 | |
252 | for (reserved_name= reserved_names; *reserved_name; reserved_name++) |
253 | { |
254 | const char *reserved= *reserved_name; /* never empty */ |
255 | const char *name= path; |
256 | |
257 | do |
258 | { |
259 | if (*reserved != my_toupper(&my_charset_latin1, *name)) |
260 | break; |
261 | if (++name == end && !reserved[1]) |
262 | DBUG_RETURN(1); /* Found wrong path */ |
263 | } while (*++reserved); |
264 | } |
265 | DBUG_RETURN(0); |
266 | } |
267 | |
268 | #endif /* defined(__WIN__) || defined(__EMX__) */ |
269 | |