/src/server/mysys/mf_pack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. |
2 | | Copyright (c) 2012, 2020, MariaDB Corporation. |
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-1335 USA. |
16 | | */ |
17 | | |
18 | | #include "mysys_priv.h" |
19 | | #include <m_string.h> |
20 | | #ifdef HAVE_PWD_H |
21 | | #include <pwd.h> |
22 | | #endif |
23 | | |
24 | | static char * expand_tilde(char **path); |
25 | | |
26 | | /* Pack a dirname ; Changes HOME to ~/ and current dev to ./ */ |
27 | | /* from is a dirname (from dirname() ?) ending with FN_LIBCHAR */ |
28 | | /* to may be == from */ |
29 | | |
30 | | void pack_dirname(char * to, const char *from) |
31 | 0 | { |
32 | 0 | int cwd_err; |
33 | 0 | size_t d_length,length,UNINIT_VAR(buff_length); |
34 | 0 | char * start; |
35 | 0 | char buff[FN_REFLEN + 1]; |
36 | 0 | DBUG_ENTER("pack_dirname"); |
37 | |
|
38 | 0 | (void) intern_filename(to,from); /* Change to intern name */ |
39 | |
|
40 | | #ifdef FN_DEVCHAR |
41 | | if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skip device part */ |
42 | | start++; |
43 | | else |
44 | | #endif |
45 | 0 | start=to; |
46 | |
|
47 | 0 | if (!(cwd_err= my_getwd(buff,FN_REFLEN,MYF(0)))) |
48 | 0 | { |
49 | 0 | buff_length= strlen(buff); |
50 | 0 | d_length= (size_t) (start-to); |
51 | 0 | if ((start == to || |
52 | 0 | (buff_length == d_length && !memcmp(buff,start,d_length))) && |
53 | 0 | *start != FN_LIBCHAR && *start) |
54 | 0 | { /* Put current dir before */ |
55 | 0 | bchange((uchar*) to, d_length, (uchar*) buff, buff_length, strlen(to)+1); |
56 | 0 | } |
57 | 0 | } |
58 | |
|
59 | 0 | if ((d_length= cleanup_dirname(to,to)) != 0) |
60 | 0 | { |
61 | 0 | length=0; |
62 | 0 | if (home_dir) |
63 | 0 | { |
64 | 0 | length= strlen(home_dir); |
65 | 0 | if (home_dir[length-1] == FN_LIBCHAR) |
66 | 0 | length--; /* Don't test last '/' */ |
67 | 0 | } |
68 | 0 | if (length > 1 && length < d_length) |
69 | 0 | { /* test if /xx/yy -> ~/yy */ |
70 | 0 | if (memcmp(to,home_dir,length) == 0 && to[length] == FN_LIBCHAR) |
71 | 0 | { |
72 | 0 | to[0]=FN_HOMELIB; /* Filename begins with ~ */ |
73 | 0 | (void) strmov_overlapp(to+1,to+length); |
74 | 0 | } |
75 | 0 | } |
76 | 0 | if (! cwd_err) |
77 | 0 | { /* Test if cwd is ~/... */ |
78 | 0 | if (length > 1 && length < buff_length) |
79 | 0 | { |
80 | 0 | if (memcmp(buff,home_dir,length) == 0 && buff[length] == FN_LIBCHAR) |
81 | 0 | { |
82 | 0 | buff[0]=FN_HOMELIB; |
83 | 0 | (void) strmov_overlapp(buff+1,buff+length); |
84 | 0 | } |
85 | 0 | } |
86 | 0 | if (is_prefix(to,buff)) |
87 | 0 | { |
88 | 0 | length= strlen(buff); |
89 | 0 | if (to[length]) |
90 | 0 | (void) strmov_overlapp(to,to+length); /* Remove everything before */ |
91 | 0 | else |
92 | 0 | { |
93 | 0 | to[0]= FN_CURLIB; /* Put ./ instead of cwd */ |
94 | 0 | to[1]= FN_LIBCHAR; |
95 | 0 | to[2]= '\0'; |
96 | 0 | } |
97 | 0 | } |
98 | 0 | } |
99 | 0 | } |
100 | 0 | DBUG_PRINT("exit",("to: '%s'",to)); |
101 | 0 | DBUG_VOID_RETURN; |
102 | 0 | } /* pack_dirname */ |
103 | | |
104 | | |
105 | | /* |
106 | | remove unwanted chars from dirname |
107 | | |
108 | | SYNOPSIS |
109 | | cleanup_dirname() |
110 | | to Store result here |
111 | | from Dirname to fix. May be same as to |
112 | | |
113 | | IMPLEMENTATION |
114 | | "/../" removes prev dir |
115 | | "/~/" removes all before ~ |
116 | | //" is same as "/", except on Win32 at start of a file |
117 | | "/./" is removed |
118 | | Unpacks home_dir if "~/.." used |
119 | | Unpacks current dir if if "./.." used |
120 | | |
121 | | RETURN |
122 | | # length of new name |
123 | | */ |
124 | | |
125 | | size_t cleanup_dirname(register char *to, const char *from) |
126 | 0 | { |
127 | 0 | reg5 size_t length; |
128 | 0 | reg2 char * pos; |
129 | 0 | reg3 char * from_ptr; |
130 | 0 | reg4 char * start; |
131 | 0 | char parent[5], /* for "FN_PARENTDIR" */ |
132 | 0 | buff[FN_REFLEN + 1],*end_parentdir; |
133 | | #ifdef BACKSLASH_MBTAIL |
134 | | CHARSET_INFO *fs= fs_character_set(); |
135 | | #endif |
136 | 0 | DBUG_ENTER("cleanup_dirname"); |
137 | 0 | DBUG_PRINT("enter",("from: '%s'",from)); |
138 | |
|
139 | 0 | start=buff; |
140 | 0 | from_ptr=(char *) from; |
141 | | #ifdef FN_DEVCHAR |
142 | | if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0) |
143 | | { /* Skip device part */ |
144 | | length=(size_t) (pos-from_ptr)+1; |
145 | | start=strnmov(buff,from_ptr,length); from_ptr+=length; |
146 | | } |
147 | | #endif |
148 | |
|
149 | 0 | parent[0]=FN_LIBCHAR; |
150 | 0 | length=(size_t) (strmov(parent+1,FN_PARENTDIR)-parent); |
151 | 0 | for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++) |
152 | 0 | { |
153 | | #ifdef BACKSLASH_MBTAIL |
154 | | uint l; |
155 | | if (my_ci_use_mb(fs) && (l= my_ismbchar(fs, from_ptr - 1, from_ptr + 2))) |
156 | | { |
157 | | for (l-- ; l ; *++pos= *from_ptr++, l--); |
158 | | start= pos + 1; /* Don't look inside multi-byte char */ |
159 | | continue; |
160 | | } |
161 | | #endif |
162 | 0 | if (*pos == '/') |
163 | 0 | *pos = FN_LIBCHAR; |
164 | 0 | if (*pos == FN_LIBCHAR) |
165 | 0 | { |
166 | 0 | if ((size_t) (pos-start) > length && memcmp(pos-length,parent,length) == 0) |
167 | 0 | { /* If .../../; skip prev */ |
168 | 0 | pos-=length; |
169 | 0 | if (pos != start) |
170 | 0 | { /* not /../ */ |
171 | 0 | pos--; |
172 | 0 | if (*pos == FN_HOMELIB && (pos == start || pos[-1] == FN_LIBCHAR)) |
173 | 0 | { |
174 | 0 | if (!home_dir) |
175 | 0 | { |
176 | 0 | pos+=length+1; /* Don't unpack ~/.. */ |
177 | 0 | continue; |
178 | 0 | } |
179 | 0 | pos=strmov(buff,home_dir)-1; /* Unpacks ~/.. */ |
180 | 0 | if (*pos == FN_LIBCHAR) |
181 | 0 | pos--; /* home ended with '/' */ |
182 | 0 | } |
183 | 0 | if (*pos == FN_CURLIB && (pos == start || pos[-1] == FN_LIBCHAR)) |
184 | 0 | { |
185 | 0 | if (my_getwd(curr_dir,FN_REFLEN,MYF(0))) |
186 | 0 | { |
187 | 0 | pos+=length+1; /* Don't unpack ./.. */ |
188 | 0 | continue; |
189 | 0 | } |
190 | 0 | pos=strmov(buff,curr_dir)-1; /* Unpacks ./.. */ |
191 | 0 | if (*pos == FN_LIBCHAR) |
192 | 0 | pos--; /* home ended with '/' */ |
193 | 0 | } |
194 | 0 | end_parentdir=pos; |
195 | 0 | while (pos >= start && *pos != FN_LIBCHAR) /* remove prev dir */ |
196 | 0 | pos--; |
197 | 0 | if (pos[1] == FN_HOMELIB || |
198 | 0 | (pos >= start && memcmp(pos, parent, length) == 0)) |
199 | 0 | { /* Don't remove ~user/ */ |
200 | 0 | pos=strmov(end_parentdir+1,parent); |
201 | 0 | *pos=FN_LIBCHAR; |
202 | 0 | continue; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | 0 | else if ((size_t) (pos-start) == length-1 && |
207 | 0 | !memcmp(start,parent+1,length-1)) |
208 | 0 | start=pos; /* Starts with "../" */ |
209 | 0 | else if (pos-start > 0 && pos[-1] == FN_LIBCHAR) |
210 | 0 | { |
211 | | #ifdef FN_NETWORK_DRIVES |
212 | | if (pos-start != 1) |
213 | | #endif |
214 | 0 | pos--; /* Remove dupplicate '/' */ |
215 | 0 | } |
216 | 0 | else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR) |
217 | 0 | pos-=2; /* Skip /./ */ |
218 | 0 | } |
219 | 0 | } |
220 | 0 | (void) strmov(to,buff); |
221 | 0 | DBUG_PRINT("exit",("to: '%s'",to)); |
222 | 0 | DBUG_RETURN((size_t) (pos-buff)); |
223 | 0 | } /* cleanup_dirname */ |
224 | | |
225 | | |
226 | | /* |
227 | | On system where you don't have symbolic links, the following |
228 | | code will allow you to create a file: |
229 | | directory-name.sym that should contain the real path |
230 | | to the directory. This will be used if the directory name |
231 | | doesn't exists |
232 | | */ |
233 | | |
234 | | |
235 | | my_bool my_use_symdir=0; /* Set this if you want to use symdirs */ |
236 | | |
237 | | #ifdef USE_SYMDIR |
238 | | void symdirget(char *dir) |
239 | | { |
240 | | char buff[FN_REFLEN + 1]; |
241 | | char *pos=strend(dir); |
242 | | if (dir[0] && pos[-1] != FN_DEVCHAR && my_access(dir, F_OK)) |
243 | | { |
244 | | File file; |
245 | | size_t length; |
246 | | char temp= *(--pos); /* May be "/" or "\" */ |
247 | | strmov(pos,".sym"); |
248 | | file= my_open(dir, O_RDONLY, MYF(0)); |
249 | | *pos++=temp; *pos=0; /* Restore old filename */ |
250 | | if (file >= 0) |
251 | | { |
252 | | if ((length= my_read(file, buff, sizeof(buff) - 1, MYF(0))) > 0) |
253 | | { |
254 | | for (pos= buff + length ; |
255 | | pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ; |
256 | | pos --); |
257 | | |
258 | | /* Ensure that the symlink ends with the directory symbol */ |
259 | | if (pos == buff || pos[-1] != FN_LIBCHAR) |
260 | | *pos++=FN_LIBCHAR; |
261 | | |
262 | | strmake(dir,buff, (size_t) (pos-buff)); |
263 | | } |
264 | | my_close(file, MYF(0)); |
265 | | } |
266 | | } |
267 | | } |
268 | | #endif /* USE_SYMDIR */ |
269 | | |
270 | | |
271 | | /** |
272 | | Convert a directory name to a format which can be compared as strings |
273 | | |
274 | | @param to result buffer, FN_REFLEN chars in length; may be == from |
275 | | @param from 'packed' directory name, in whatever format |
276 | | @returns size of the normalized name |
277 | | |
278 | | @details |
279 | | - Ensures that last char is FN_LIBCHAR, unless it is FN_DEVCHAR |
280 | | - Uses cleanup_dirname |
281 | | |
282 | | It does *not* expand ~/ (although, see cleanup_dirname). Nor does it do |
283 | | any case folding. All case-insensitive normalization should be done by |
284 | | the caller. |
285 | | */ |
286 | | |
287 | | size_t normalize_dirname(char *to, const char *from) |
288 | 0 | { |
289 | 0 | size_t length; |
290 | 0 | char buff[FN_REFLEN + 1]; |
291 | 0 | DBUG_ENTER("normalize_dirname"); |
292 | | |
293 | | /* |
294 | | Despite the name, this actually converts the name to the system's |
295 | | format (TODO: name this properly). |
296 | | */ |
297 | 0 | (void) intern_filename(buff, from); |
298 | 0 | length= strlen(buff); /* Fix that '/' is last */ |
299 | 0 | if (length && |
300 | | #ifdef FN_DEVCHAR |
301 | | buff[length - 1] != FN_DEVCHAR && |
302 | | #endif |
303 | 0 | buff[length - 1] != FN_LIBCHAR && buff[length - 1] != '/') |
304 | 0 | { |
305 | | /* we need reserve 2 bytes for the trailing slash and the zero */ |
306 | 0 | if (length >= sizeof (buff) - 1) |
307 | 0 | length= sizeof (buff) - 2; |
308 | 0 | buff[length]= FN_LIBCHAR; |
309 | 0 | buff[length + 1]= '\0'; |
310 | 0 | } |
311 | |
|
312 | 0 | length=cleanup_dirname(to, buff); |
313 | |
|
314 | 0 | DBUG_RETURN(length); |
315 | 0 | } |
316 | | |
317 | | |
318 | | /** |
319 | | Fixes a directory name so that can be used by open() |
320 | | |
321 | | @param to Result buffer, FN_REFLEN characters. May be == from |
322 | | @param from 'Packed' directory name (may contain ~) |
323 | | |
324 | | @details |
325 | | - Uses normalize_dirname() |
326 | | - Expands ~/... to home_dir/... |
327 | | - Resolves MySQL's fake "foo.sym" symbolic directory names (if USE_SYMDIR) |
328 | | - Changes a UNIX filename to system filename (replaces / with \ on windows) |
329 | | |
330 | | @returns |
331 | | Length of new directory name (= length of to) |
332 | | */ |
333 | | |
334 | | size_t unpack_dirname(char * to, const char *from) |
335 | 0 | { |
336 | 0 | size_t length, h_length; |
337 | 0 | char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion; |
338 | 0 | DBUG_ENTER("unpack_dirname"); |
339 | |
|
340 | 0 | length= normalize_dirname(buff, from); |
341 | |
|
342 | 0 | if (buff[0] == FN_HOMELIB) |
343 | 0 | { |
344 | 0 | suffix=buff+1; tilde_expansion=expand_tilde(&suffix); |
345 | 0 | if (tilde_expansion) |
346 | 0 | { |
347 | 0 | length-= (size_t) (suffix-buff)-1; |
348 | 0 | if (length+(h_length= strlen(tilde_expansion)) <= FN_REFLEN) |
349 | 0 | { |
350 | 0 | if ((h_length > 0) && (tilde_expansion[h_length-1] == FN_LIBCHAR)) |
351 | 0 | h_length--; |
352 | 0 | if (buff+h_length < suffix) |
353 | 0 | bmove(buff+h_length,suffix,length); |
354 | 0 | else |
355 | 0 | bmove_upp((uchar*) buff+h_length+length, (uchar*) suffix+length, length); |
356 | 0 | bmove(buff,tilde_expansion,h_length); |
357 | 0 | } |
358 | 0 | } |
359 | 0 | } |
360 | | #ifdef USE_SYMDIR |
361 | | if (my_use_symdir) |
362 | | symdirget(buff); |
363 | | #endif |
364 | 0 | DBUG_RETURN(system_filename(to,buff)); /* Fix for open */ |
365 | 0 | } /* unpack_dirname */ |
366 | | |
367 | | |
368 | | /* Expand tilde to home or user-directory */ |
369 | | /* Path is reset to point at FN_LIBCHAR after ~xxx */ |
370 | | |
371 | | static char * expand_tilde(char **path) |
372 | 0 | { |
373 | 0 | if (path[0][0] == FN_LIBCHAR) |
374 | 0 | return home_dir; /* ~/ expanded to home */ |
375 | 0 | #ifdef HAVE_GETPWNAM |
376 | 0 | { |
377 | 0 | char *str,save; |
378 | 0 | struct passwd *user_entry; |
379 | |
|
380 | 0 | if (!(str=strchr(*path,FN_LIBCHAR))) |
381 | 0 | str=strend(*path); |
382 | 0 | save= *str; *str= '\0'; |
383 | 0 | user_entry=getpwnam(*path); |
384 | 0 | *str=save; |
385 | 0 | endpwent(); |
386 | 0 | if (user_entry) |
387 | 0 | { |
388 | 0 | *path=str; |
389 | 0 | return user_entry->pw_dir; |
390 | 0 | } |
391 | 0 | } |
392 | 0 | #endif |
393 | 0 | return (char *) 0; |
394 | 0 | } |
395 | | |
396 | | |
397 | | /* |
398 | | Fix filename so it can be used by open, create |
399 | | |
400 | | SYNOPSIS |
401 | | unpack_filename() |
402 | | to Store result here. Must be at least of size FN_REFLEN. |
403 | | from Filename in unix format (with ~) |
404 | | |
405 | | RETURN |
406 | | # length of to |
407 | | |
408 | | NOTES |
409 | | to may be == from |
410 | | ~ will only be expanded if total length < FN_REFLEN |
411 | | */ |
412 | | |
413 | | |
414 | | size_t unpack_filename(char * to, const char *from) |
415 | 0 | { |
416 | 0 | size_t length, n_length, buff_length; |
417 | 0 | char buff[FN_REFLEN + 1]; |
418 | 0 | DBUG_ENTER("unpack_filename"); |
419 | |
|
420 | 0 | length=dirname_part(buff, from, &buff_length);/* copy & convert dirname */ |
421 | 0 | n_length=unpack_dirname(buff,buff); |
422 | 0 | if (n_length+strlen(from+length) < FN_REFLEN) |
423 | 0 | { |
424 | 0 | (void) strmov(buff+n_length,from+length); |
425 | 0 | length= system_filename(to,buff); /* Fix to usably filename */ |
426 | 0 | } |
427 | 0 | else |
428 | 0 | length= system_filename(to,from); /* Fix to usably filename */ |
429 | 0 | DBUG_RETURN(length); |
430 | 0 | } /* unpack_filename */ |
431 | | |
432 | | |
433 | | /* Convert filename (unix standard) to system standard */ |
434 | | /* Used before system command's like open(), create() .. */ |
435 | | /* Returns used length of to; total length should be FN_REFLEN */ |
436 | | |
437 | | size_t system_filename(char *to, const char *from) |
438 | 0 | { |
439 | 0 | return (size_t) (strmake(to,from,FN_REFLEN-1)-to); |
440 | 0 | } |
441 | | |
442 | | /* Fix a filename to intern (UNIX format) */ |
443 | | |
444 | | char *intern_filename(char *to, const char *from) |
445 | 0 | { |
446 | 0 | size_t length, to_length; |
447 | 0 | char buff[FN_REFLEN + 1]; |
448 | 0 | if (from == to) |
449 | 0 | { /* Dirname may destroy from */ |
450 | 0 | (void) strnmov(buff, from, FN_REFLEN); |
451 | 0 | from=buff; |
452 | 0 | } |
453 | 0 | length= dirname_part(to, from, &to_length); /* Copy dirname & fix chars */ |
454 | 0 | (void) strnmov(to + to_length, from + length, FN_REFLEN - to_length); |
455 | 0 | return (to); |
456 | 0 | } /* intern_filename */ |