~ubuntu-branches/ubuntu/intrepid/parted/intrepid

« back to all changes in this revision

Viewing changes to gnulib/lib/getcwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2008-06-24 14:31:05 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20080624143105-rd7yw67a9qnvh51i
Tags: 1.8.8.git.2008.03.24-7ubuntu1
* Resynchronise with Debian (LP: #237568). Remaining changes:
  - swap-uuid.dpatch: Create UUIDs on new swap partitions.
  - gptsync.dpatch: On Intel Mac systems, write a synced MBR rather than a
    protective MBR.
  - Add -fno-stack-protector on sparc.
  - sparc-new-label.dpatch: Fix sparc disk label generation. This is
    required for LDOM and parallel installations with Solaris 10.
  - loop-partitions.dpatch: Loop devices can only have one partition, so
    don't generate device names such as "/dev/loop0p1".
  - unpartitioned-disks.dpatch: Don't try to call BLKPG ioctls on
    unpartitionable disks (only implemented for loop devices at the
    moment), as they will always fail.
  - When building with gcc-4.3, add -Wno-array-bounds to CFLAGS.
  - Cell partition tables are misdetected as pc98, so disable pc98 support
    on powerpc.
  - array-bounds.dpatch: Backport patch from git to allow building with
    gcc-4.3.

Show diffs side-by-side

added added

removed removed

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