~ubuntu-branches/ubuntu/trusty/bash/trusty-security

« back to all changes in this revision

Viewing changes to lib/sh/pathcanon.c

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2014-03-03 22:52:05 UTC
  • mfrom: (1.3.5) (2.2.6 experimental)
  • Revision ID: package-import@ubuntu.com-20140303225205-87ltrt5kspeq0g1b
Tags: 4.3-1ubuntu1
* Merge with Debian; remaining changes:
  - skel.bashrc:
    - Run lesspipe.
    - Enable ls aliases.
    - Set options in ll alias to -alF.
    - Define an alert alias.
    - Enabled colored grep aliases.
  - etc.bash.bashrc:
    - Add sudo hint.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* pathcanon.c -- canonicalize and manipulate pathnames. */
 
2
 
 
3
/* Copyright (C) 2000 Free Software Foundation, Inc.
 
4
 
 
5
   This file is part of GNU Bash, the Bourne Again SHell.
 
6
 
 
7
   Bash is free software: you can redistribute it and/or modify
 
8
   it under the terms of the GNU General Public License as published by
 
9
   the Free Software Foundation, either version 3 of the License, or
 
10
   (at your option) any later version.
 
11
 
 
12
   Bash is distributed in the hope that it will be useful,
 
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
   GNU General Public License for more details.
 
16
 
 
17
   You should have received a copy of the GNU General Public License
 
18
   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 
19
*/
 
20
 
 
21
#include <config.h>
 
22
 
 
23
#include <bashtypes.h>
 
24
#if defined (HAVE_SYS_PARAM_H)
 
25
#  include <sys/param.h>
 
26
#endif
 
27
#include <posixstat.h>
 
28
 
 
29
#if defined (HAVE_UNISTD_H)
 
30
#  include <unistd.h>
 
31
#endif
 
32
 
 
33
#include <filecntl.h>
 
34
#include <bashansi.h>
 
35
#include <stdio.h>
 
36
#include <chartypes.h>
 
37
#include <errno.h>
 
38
 
 
39
#include "shell.h"
 
40
 
 
41
#if !defined (errno)
 
42
extern int errno;
 
43
#endif
 
44
 
 
45
#if defined (__CYGWIN__)
 
46
#include <sys/cygwin.h>
 
47
 
 
48
static int
 
49
_is_cygdrive (path)
 
50
     char *path;
 
51
{
 
52
  static char user[MAXPATHLEN];
 
53
  static char system[MAXPATHLEN];
 
54
  static int first_time = 1;
 
55
 
 
56
  /* If the path is the first part of a network path, treat it as
 
57
     existing. */
 
58
  if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/'))
 
59
    return 1; 
 
60
  /* Otherwise check for /cygdrive prefix. */
 
61
  if (first_time)
 
62
    {
 
63
      char user_flags[MAXPATHLEN];
 
64
      char system_flags[MAXPATHLEN];
 
65
      /* Get the cygdrive info */
 
66
      cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags);
 
67
      first_time = 0;
 
68
    }
 
69
  return !strcasecmp (path, user) || !strcasecmp (path, system);
 
70
}
 
71
#endif /* __CYGWIN__ */ 
 
72
 
 
73
/* Return 1 if PATH corresponds to a directory.  A function for debugging. */
 
74
static int
 
75
_path_isdir (path)
 
76
     char *path;
 
77
{
 
78
  int l;
 
79
  struct stat sb;
 
80
 
 
81
  /* This should leave errno set to the correct value. */
 
82
  errno = 0;
 
83
  l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode);
 
84
#if defined (__CYGWIN__)
 
85
  if (l == 0)
 
86
    l = _is_cygdrive (path);
 
87
#endif
 
88
  return l;
 
89
}
 
90
 
 
91
/* Canonicalize PATH, and return a new path.  The new path differs from PATH
 
92
   in that:
 
93
        Multple `/'s are collapsed to a single `/'.
 
94
        Leading `./'s and trailing `/.'s are removed.
 
95
        Trailing `/'s are removed.
 
96
        Non-leading `../'s and trailing `..'s are handled by removing
 
97
        portions of the path. */
 
98
 
 
99
/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
 
100
 
 
101
#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
 
102
 
 
103
char *
 
104
sh_canonpath (path, flags)
 
105
     char *path;
 
106
     int flags;
 
107
{
 
108
  char stub_char;
 
109
  char *result, *p, *q, *base, *dotdot;
 
110
  int rooted, double_slash_path;
 
111
 
 
112
  /* The result cannot be larger than the input PATH. */
 
113
  result = (flags & PATH_NOALLOC) ? path : savestring (path);
 
114
 
 
115
  /* POSIX.2 says to leave a leading `//' alone.  On cygwin, we skip over any
 
116
     leading `x:' (dos drive name). */
 
117
  if (rooted = ROOTEDPATH(path))
 
118
    {
 
119
      stub_char = DIRSEP;
 
120
#if defined (__CYGWIN__)
 
121
      base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1;
 
122
#else
 
123
      base = result + 1;
 
124
#endif
 
125
      double_slash_path = DOUBLE_SLASH (path);
 
126
      base += double_slash_path;
 
127
    }
 
128
  else
 
129
    {
 
130
      stub_char = '.';
 
131
#if defined (__CYGWIN__)
 
132
      base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result;
 
133
#else
 
134
      base = result;
 
135
#endif
 
136
      double_slash_path = 0;
 
137
    }
 
138
 
 
139
  /*
 
140
   * invariants:
 
141
   *      base points to the portion of the path we want to modify
 
142
   *      p points at beginning of path element we're considering.
 
143
   *      q points just past the last path element we wrote (no slash).
 
144
   *      dotdot points just past the point where .. cannot backtrack
 
145
   *      any further (no slash).
 
146
   */
 
147
  p = q = dotdot = base;
 
148
 
 
149
  while (*p)
 
150
    {
 
151
      if (ISDIRSEP(p[0])) /* null element */
 
152
        p++;
 
153
      else if(p[0] == '.' && PATHSEP(p[1]))     /* . and ./ */
 
154
        p += 1;         /* don't count the separator in case it is nul */
 
155
      else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
 
156
        {
 
157
          p += 2; /* skip `..' */
 
158
          if (q > dotdot)       /* can backtrack */
 
159
            {
 
160
              if (flags & PATH_CHECKDOTDOT)
 
161
                {
 
162
                  char c;
 
163
 
 
164
                  /* Make sure what we have so far corresponds to a valid
 
165
                     path before we chop some of it off. */
 
166
                  c = *q;
 
167
                  *q = '\0';
 
168
                  if (_path_isdir (result) == 0)
 
169
                    {
 
170
                      if ((flags & PATH_NOALLOC) == 0)
 
171
                        free (result);
 
172
                      return ((char *)NULL);
 
173
                    }
 
174
                  *q = c;
 
175
                }
 
176
 
 
177
              while (--q > dotdot && ISDIRSEP(*q) == 0)
 
178
                ;
 
179
            }
 
180
          else if (rooted == 0)
 
181
            {
 
182
              /* /.. is / but ./../ is .. */
 
183
              if (q != base)
 
184
                *q++ = DIRSEP;
 
185
              *q++ = '.';
 
186
              *q++ = '.';
 
187
              dotdot = q;
 
188
            }
 
189
        }
 
190
      else      /* real path element */
 
191
        {
 
192
          /* add separator if not at start of work portion of result */
 
193
          if (q != base)
 
194
            *q++ = DIRSEP;
 
195
          while (*p && (ISDIRSEP(*p) == 0))
 
196
            *q++ = *p++;
 
197
          /* Check here for a valid directory with _path_isdir. */
 
198
          if (flags & PATH_CHECKEXISTS)
 
199
            {
 
200
              char c;
 
201
 
 
202
              /* Make sure what we have so far corresponds to a valid
 
203
                 path before we chop some of it off. */
 
204
              c = *q;
 
205
              *q = '\0';
 
206
              if (_path_isdir (result) == 0)
 
207
                {
 
208
                  if ((flags & PATH_NOALLOC) == 0)
 
209
                    free (result);
 
210
                  return ((char *)NULL);
 
211
                }
 
212
              *q = c;
 
213
            }
 
214
        }
 
215
    }
 
216
 
 
217
  /* Empty string is really ``.'' or `/', depending on what we started with. */
 
218
  if (q == result)
 
219
    *q++ = stub_char;
 
220
  *q = '\0';
 
221
 
 
222
  /* If the result starts with `//', but the original path does not, we
 
223
     can turn the // into /.  Because of how we set `base', this should never
 
224
     be true, but it's a sanity check. */
 
225
  if (DOUBLE_SLASH(result) && double_slash_path == 0)
 
226
    {
 
227
      if (result[2] == '\0')    /* short-circuit for bare `//' */
 
228
        result[1] = '\0';
 
229
      else
 
230
        strcpy (result, result + 1);
 
231
    }
 
232
 
 
233
  return (result);
 
234
}