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
35int 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*/
60static 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*/
85static 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*/
104static 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
143int 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*/
162static 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
191my_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
237int 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