~ubuntu-branches/ubuntu/vivid/gzip/vivid

« back to all changes in this revision

Viewing changes to lib/getcwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-10-19 11:42:42 UTC
  • mfrom: (4.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20111019114242-d8wiiu8kbvdtgmgj
Tags: 1.4-1ubuntu1
* Merge with Debian testing.  Remaining Ubuntu changes:
  - debian/{control,rules}: Remove the Win32 build and mingw64
    build-dependency, since mingw is in universe, and will remain so for
    the forseeable future.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 1991-1999, 2004-2010 Free Software Foundation, Inc.
 
2
   This file is part of the GNU C Library.
 
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; either version 3 of the License, or
 
7
   (at your option) any later version.
 
8
 
 
9
   This program is distributed in the hope that it will be useful,
 
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
   GNU General Public License for more details.
 
13
 
 
14
   You should have received a copy of the GNU General Public License
 
15
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
16
 
 
17
#if !_LIBC
 
18
# include <config.h>
 
19
# include <unistd.h>
 
20
#endif
 
21
 
 
22
#include <errno.h>
 
23
#include <sys/types.h>
 
24
#include <sys/stat.h>
 
25
#include <stdbool.h>
 
26
#include <stddef.h>
 
27
 
 
28
#include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
 
29
 
 
30
/* If this host provides the openat function, then enable
 
31
   code below to make getcwd more efficient and robust.  */
 
32
#ifdef HAVE_OPENAT
 
33
# define HAVE_OPENAT_SUPPORT 1
 
34
#else
 
35
# define HAVE_OPENAT_SUPPORT 0
 
36
#endif
 
37
 
 
38
#ifndef __set_errno
 
39
# define __set_errno(val) (errno = (val))
 
40
#endif
 
41
 
 
42
#include <dirent.h>
 
43
#ifndef _D_EXACT_NAMLEN
 
44
# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
 
45
#endif
 
46
#ifndef _D_ALLOC_NAMLEN
 
47
# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
 
48
#endif
 
49
 
 
50
#include <unistd.h>
 
51
#include <stdlib.h>
 
52
#include <string.h>
 
53
 
 
54
#if _LIBC
 
55
# ifndef mempcpy
 
56
#  define mempcpy __mempcpy
 
57
# endif
 
58
#endif
 
59
 
 
60
#include <limits.h>
 
61
 
 
62
#ifndef MAX
 
63
# define MAX(a, b) ((a) < (b) ? (b) : (a))
 
64
#endif
 
65
#ifndef MIN
 
66
# define MIN(a, b) ((a) < (b) ? (a) : (b))
 
67
#endif
 
68
 
 
69
#ifndef PATH_MAX
 
70
# ifdef MAXPATHLEN
 
71
#  define PATH_MAX MAXPATHLEN
 
72
# else
 
73
#  define PATH_MAX 1024
 
74
# endif
 
75
#endif
 
76
 
 
77
#if D_INO_IN_DIRENT
 
78
# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
 
79
#else
 
80
# define MATCHING_INO(dp, ino) true
 
81
#endif
 
82
 
 
83
#if !_LIBC
 
84
# define __getcwd rpl_getcwd
 
85
# define __lstat lstat
 
86
# define __closedir closedir
 
87
# define __opendir opendir
 
88
# define __readdir readdir
 
89
#endif
 
90
 
 
91
/* The results of opendir() in this file are not used with dirfd and fchdir,
 
92
   and we do not leak fds to any single-threaded code that could use stdio,
 
93
   therefore save some unnecessary recursion in fchdir.c.
 
94
   FIXME - if the kernel ever adds support for multi-thread safety for
 
95
   avoiding standard fds, then we should use opendir_safer and
 
96
   openat_safer.  */
 
97
#undef opendir
 
98
#undef closedir
 
99
 
 
100
/* Get the name of the current working directory, and put it in SIZE
 
101
   bytes of BUF.  Returns NULL if the directory couldn't be determined or
 
102
   SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
 
103
   NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
 
104
   unless SIZE == 0, in which case it is as big as necessary.  */
 
105
 
 
106
char *
 
107
__getcwd (char *buf, size_t size)
 
108
{
 
109
  /* Lengths of big file name components and entire file names, and a
 
110
     deep level of file name nesting.  These numbers are not upper
 
111
     bounds; they are merely large values suitable for initial
 
112
     allocations, designed to be large enough for most real-world
 
113
     uses.  */
 
114
  enum
 
115
    {
 
116
      BIG_FILE_NAME_COMPONENT_LENGTH = 255,
 
117
      BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
 
118
      DEEP_NESTING = 100
 
119
    };
 
120
 
 
121
#if HAVE_OPENAT_SUPPORT
 
122
  int fd = AT_FDCWD;
 
123
  bool fd_needs_closing = false;
 
124
#else
 
125
  char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
 
126
  char *dotlist = dots;
 
127
  size_t dotsize = sizeof dots;
 
128
  size_t dotlen = 0;
 
129
#endif
 
130
  DIR *dirstream = NULL;
 
131
  dev_t rootdev, thisdev;
 
132
  ino_t rootino, thisino;
 
133
  char *dir;
 
134
  register char *dirp;
 
135
  struct stat st;
 
136
  size_t allocated = size;
 
137
  size_t used;
 
138
 
 
139
#if HAVE_PARTLY_WORKING_GETCWD
 
140
  /* The system getcwd works, except it sometimes fails when it
 
141
     shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If
 
142
     AT_FDCWD is not defined, the algorithm below is O(N**2) and this
 
143
     is much slower than the system getcwd (at least on GNU/Linux).
 
144
     So trust the system getcwd's results unless they look
 
145
     suspicious.
 
146
 
 
147
     Use the system getcwd even if we have openat support, since the
 
148
     system getcwd works even when a parent is unreadable, while the
 
149
     openat-based approach does not.  */
 
150
 
 
151
# undef getcwd
 
152
  dir = getcwd (buf, size);
 
153
  if (dir || (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT))
 
154
    return dir;
 
155
#endif
 
156
 
 
157
  if (size == 0)
 
158
    {
 
159
      if (buf != NULL)
 
160
        {
 
161
          __set_errno (EINVAL);
 
162
          return NULL;
 
163
        }
 
164
 
 
165
      allocated = BIG_FILE_NAME_LENGTH + 1;
 
166
    }
 
167
 
 
168
  if (buf == NULL)
 
169
    {
 
170
      dir = malloc (allocated);
 
171
      if (dir == NULL)
 
172
        return NULL;
 
173
    }
 
174
  else
 
175
    dir = buf;
 
176
 
 
177
  dirp = dir + allocated;
 
178
  *--dirp = '\0';
 
179
 
 
180
  if (__lstat (".", &st) < 0)
 
181
    goto lose;
 
182
  thisdev = st.st_dev;
 
183
  thisino = st.st_ino;
 
184
 
 
185
  if (__lstat ("/", &st) < 0)
 
186
    goto lose;
 
187
  rootdev = st.st_dev;
 
188
  rootino = st.st_ino;
 
189
 
 
190
  while (!(thisdev == rootdev && thisino == rootino))
 
191
    {
 
192
      struct dirent *d;
 
193
      dev_t dotdev;
 
194
      ino_t dotino;
 
195
      bool mount_point;
 
196
      int parent_status;
 
197
      size_t dirroom;
 
198
      size_t namlen;
 
199
      bool use_d_ino = true;
 
200
 
 
201
      /* Look at the parent directory.  */
 
202
#if HAVE_OPENAT_SUPPORT
 
203
      fd = openat (fd, "..", O_RDONLY);
 
204
      if (fd < 0)
 
205
        goto lose;
 
206
      fd_needs_closing = true;
 
207
      parent_status = fstat (fd, &st);
 
208
#else
 
209
      dotlist[dotlen++] = '.';
 
210
      dotlist[dotlen++] = '.';
 
211
      dotlist[dotlen] = '\0';
 
212
      parent_status = __lstat (dotlist, &st);
 
213
#endif
 
214
      if (parent_status != 0)
 
215
        goto lose;
 
216
 
 
217
      if (dirstream && __closedir (dirstream) != 0)
 
218
        {
 
219
          dirstream = NULL;
 
220
          goto lose;
 
221
        }
 
222
 
 
223
      /* Figure out if this directory is a mount point.  */
 
224
      dotdev = st.st_dev;
 
225
      dotino = st.st_ino;
 
226
      mount_point = dotdev != thisdev;
 
227
 
 
228
      /* Search for the last directory.  */
 
229
#if HAVE_OPENAT_SUPPORT
 
230
      dirstream = fdopendir (fd);
 
231
      if (dirstream == NULL)
 
232
        goto lose;
 
233
      /* Reset fd.  It may have been closed by fdopendir.  */
 
234
      fd = dirfd (dirstream);
 
235
      fd_needs_closing = false;
 
236
#else
 
237
      dirstream = __opendir (dotlist);
 
238
      if (dirstream == NULL)
 
239
        goto lose;
 
240
      dotlist[dotlen++] = '/';
 
241
#endif
 
242
      for (;;)
 
243
        {
 
244
          /* Clear errno to distinguish EOF from error if readdir returns
 
245
             NULL.  */
 
246
          __set_errno (0);
 
247
          d = __readdir (dirstream);
 
248
 
 
249
          /* When we've iterated through all directory entries without finding
 
250
             one with a matching d_ino, rewind the stream and consider each
 
251
             name again, but this time, using lstat.  This is necessary in a
 
252
             chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
 
253
             .., ../.., ../../.., etc. all had the same device number, yet the
 
254
             d_ino values for entries in / did not match those obtained
 
255
             via lstat.  */
 
256
          if (d == NULL && errno == 0 && use_d_ino)
 
257
            {
 
258
              use_d_ino = false;
 
259
              rewinddir (dirstream);
 
260
              d = __readdir (dirstream);
 
261
            }
 
262
 
 
263
          if (d == NULL)
 
264
            {
 
265
              if (errno == 0)
 
266
                /* EOF on dirstream, which can mean e.g., that the current
 
267
                   directory has been removed.  */
 
268
                __set_errno (ENOENT);
 
269
              goto lose;
 
270
            }
 
271
          if (d->d_name[0] == '.' &&
 
272
              (d->d_name[1] == '\0' ||
 
273
               (d->d_name[1] == '.' && d->d_name[2] == '\0')))
 
274
            continue;
 
275
 
 
276
          if (use_d_ino)
 
277
            {
 
278
              bool match = (MATCHING_INO (d, thisino) || mount_point);
 
279
              if (! match)
 
280
                continue;
 
281
            }
 
282
 
 
283
          {
 
284
            int entry_status;
 
285
#if HAVE_OPENAT_SUPPORT
 
286
            entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
 
287
#else
 
288
            /* Compute size needed for this file name, or for the file
 
289
               name ".." in the same directory, whichever is larger.
 
290
               Room for ".." might be needed the next time through
 
291
               the outer loop.  */
 
292
            size_t name_alloc = _D_ALLOC_NAMLEN (d);
 
293
            size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
 
294
 
 
295
            if (filesize < dotlen)
 
296
              goto memory_exhausted;
 
297
 
 
298
            if (dotsize < filesize)
 
299
              {
 
300
                /* My, what a deep directory tree you have, Grandma.  */
 
301
                size_t newsize = MAX (filesize, dotsize * 2);
 
302
                size_t i;
 
303
                if (newsize < dotsize)
 
304
                  goto memory_exhausted;
 
305
                if (dotlist != dots)
 
306
                  free (dotlist);
 
307
                dotlist = malloc (newsize);
 
308
                if (dotlist == NULL)
 
309
                  goto lose;
 
310
                dotsize = newsize;
 
311
 
 
312
                i = 0;
 
313
                do
 
314
                  {
 
315
                    dotlist[i++] = '.';
 
316
                    dotlist[i++] = '.';
 
317
                    dotlist[i++] = '/';
 
318
                  }
 
319
                while (i < dotlen);
 
320
              }
 
321
 
 
322
            memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
 
323
            entry_status = __lstat (dotlist, &st);
 
324
#endif
 
325
            /* We don't fail here if we cannot stat() a directory entry.
 
326
               This can happen when (network) file systems fail.  If this
 
327
               entry is in fact the one we are looking for we will find
 
328
               out soon as we reach the end of the directory without
 
329
               having found anything.  */
 
330
            if (entry_status == 0 && S_ISDIR (st.st_mode)
 
331
                && st.st_dev == thisdev && st.st_ino == thisino)
 
332
              break;
 
333
          }
 
334
        }
 
335
 
 
336
      dirroom = dirp - dir;
 
337
      namlen = _D_EXACT_NAMLEN (d);
 
338
 
 
339
      if (dirroom <= namlen)
 
340
        {
 
341
          if (size != 0)
 
342
            {
 
343
              __set_errno (ERANGE);
 
344
              goto lose;
 
345
            }
 
346
          else
 
347
            {
 
348
              char *tmp;
 
349
              size_t oldsize = allocated;
 
350
 
 
351
              allocated += MAX (allocated, namlen);
 
352
              if (allocated < oldsize
 
353
                  || ! (tmp = realloc (dir, allocated)))
 
354
                goto memory_exhausted;
 
355
 
 
356
              /* Move current contents up to the end of the buffer.
 
357
                 This is guaranteed to be non-overlapping.  */
 
358
              dirp = memcpy (tmp + allocated - (oldsize - dirroom),
 
359
                             tmp + dirroom,
 
360
                             oldsize - dirroom);
 
361
              dir = tmp;
 
362
            }
 
363
        }
 
364
      dirp -= namlen;
 
365
      memcpy (dirp, d->d_name, namlen);
 
366
      *--dirp = '/';
 
367
 
 
368
      thisdev = dotdev;
 
369
      thisino = dotino;
 
370
    }
 
371
 
 
372
  if (dirstream && __closedir (dirstream) != 0)
 
373
    {
 
374
      dirstream = NULL;
 
375
      goto lose;
 
376
    }
 
377
 
 
378
  if (dirp == &dir[allocated - 1])
 
379
    *--dirp = '/';
 
380
 
 
381
#if ! HAVE_OPENAT_SUPPORT
 
382
  if (dotlist != dots)
 
383
    free (dotlist);
 
384
#endif
 
385
 
 
386
  used = dir + allocated - dirp;
 
387
  memmove (dir, dirp, used);
 
388
 
 
389
  if (size == 0)
 
390
    /* Ensure that the buffer is only as large as necessary.  */
 
391
    buf = realloc (dir, used);
 
392
 
 
393
  if (buf == NULL)
 
394
    /* Either buf was NULL all along, or `realloc' failed but
 
395
       we still have the original string.  */
 
396
    buf = dir;
 
397
 
 
398
  return buf;
 
399
 
 
400
 memory_exhausted:
 
401
  __set_errno (ENOMEM);
 
402
 lose:
 
403
  {
 
404
    int save = errno;
 
405
    if (dirstream)
 
406
      __closedir (dirstream);
 
407
#if HAVE_OPENAT_SUPPORT
 
408
    if (fd_needs_closing)
 
409
      close (fd);
 
410
#else
 
411
    if (dotlist != dots)
 
412
      free (dotlist);
 
413
#endif
 
414
    if (buf == NULL)
 
415
      free (dir);
 
416
    __set_errno (save);
 
417
  }
 
418
  return NULL;
 
419
}
 
420
 
 
421
#ifdef weak_alias
 
422
weak_alias (__getcwd, getcwd)
 
423
#endif