Coverage Report

Created: 2024-06-18 07:03

/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 */