Update.
[platform/upstream/linaro-glibc.git] / sysdeps / unix / sysv / linux / getcwd.c
1 /* Determine current working directory.  Linux version.
2    Copyright (C) 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #include <sysdep.h>
28 #include <sys/syscall.h>
29 #include <bp-checks.h>
30
31 #include "kernel-features.h"
32
33
34 #if __ASSUME_GETCWD_SYSCALL > 0
35 /* Kernel 2.1.92 introduced a third way to get the current working
36    directory: a syscall.  We've got to be careful that even when
37    compiling under 2.1.92+ the libc still runs under older kernels. */
38 extern int __syscall_getcwd (char *__unbounded buf, unsigned long size);
39 # define no_syscall_getcwd 0
40 # define have_new_dcache 1
41 /* This is a trick since we don't define generic_getcwd.  */
42 # define generic_getcwd getcwd
43 #else
44 /* The "proc" filesystem provides an easy method to retrieve the value.
45    For each process, the corresponding directory contains a symbolic link
46    named `cwd'.  Reading the content of this link immediate gives us the
47    information.  But we have to take care for systems which do not have
48    the proc filesystem mounted.  Use the POSIX implementation in this case.  */
49 static char *generic_getcwd (char *buf, size_t size) internal_function;
50
51 # if __NR_getcwd
52 /* Kernel 2.1.92 introduced a third way to get the current working
53    directory: a syscall.  We've got to be careful that even when
54    compiling under 2.1.92+ the libc still runs under older kernels. */
55 extern int __syscall_getcwd (char *__unbounded buf, unsigned long size);
56 static int no_syscall_getcwd;
57 static int have_new_dcache;
58 # else
59 #  define no_syscall_getcwd 1
60 static int have_new_dcache = 1;
61 # endif
62 #endif
63
64 char *
65 __getcwd (char *buf, size_t size)
66 {
67   char *path;
68   int n;
69   char *result;
70   size_t alloc_size = size;
71
72   if (no_syscall_getcwd && !have_new_dcache)
73     return generic_getcwd (buf, size);
74
75   if (size == 0)
76     {
77       if (buf != NULL)
78         {
79           __set_errno (EINVAL);
80           return NULL;
81         }
82
83       alloc_size = PATH_MAX;
84     }
85
86   if (buf != NULL)
87     path = buf;
88   else
89     {
90       path = malloc (alloc_size);
91       if (path == NULL)
92         return NULL;
93     }
94
95 #if defined __NR_getcwd || __LINUX_GETCWD_SYSCALL > 0
96   if (!no_syscall_getcwd)
97     {
98       int retval;
99
100       retval = INLINE_SYSCALL (getcwd, 2, CHECK_STRING (path), alloc_size);
101       if (retval >= 0)
102         {
103           if (buf == NULL && size == 0)
104             /* Ensure that the buffer is only as large as necessary.  */
105             buf = realloc (path, (size_t) retval);
106
107           if (buf == NULL)
108             /* Either buf was NULL all along, or `realloc' failed but
109                we still have the original string.  */
110             buf = path;
111
112           return buf;
113         }
114
115 # if __ASSUME_GETCWD_SYSCALL
116       /* It should never happen that the `getcwd' syscall failed because
117          the buffer is too small if we allocated the buffer ourselves
118          large enough.  */
119       assert (errno != ERANGE || buf != NULL || size != 0);
120
121       if (buf == NULL)
122         free (path);
123
124       return NULL;
125 # else
126       if (errno == ENOSYS)
127         {
128            no_syscall_getcwd = 1;
129            have_new_dcache = 1; /* Now we will try the /proc method.  */
130         }
131       else if (errno != ERANGE || buf != NULL)
132         {
133           if (buf == NULL)
134             free (path);
135           return NULL;
136         }
137 # endif
138     }
139 #endif
140
141   n = __readlink ("/proc/self/cwd", path, alloc_size - 1);
142   if (n != -1)
143     {
144       if (path[0] == '/')
145         {
146           if (n >= alloc_size - 1)
147             {
148               if (buf == NULL)
149                 free (path);
150               return NULL;
151             }
152
153           path[n] = '\0';
154           if (buf == NULL && size == 0)
155             /* Ensure that the buffer is only as large as necessary.  */
156             buf = realloc (path, (size_t) n + 1);
157           if (buf == NULL)
158             /* Either buf was NULL all along, or `realloc' failed but
159                we still have the original string.  */
160             buf = path;
161
162           return buf;
163         }
164 #ifndef have_new_dcache
165       else
166         have_new_dcache = 0;
167 #endif
168     }
169
170 #if __ASSUME_GETCWD_SYSCALL == 0
171   /* Set to have_new_dcache only if error indicates that proc doesn't
172      exist.  */
173   if (errno != EACCES && errno != ENAMETOOLONG)
174     have_new_dcache = 0;
175 #endif
176
177   /* Don't put restrictions on the length of the path unless the user does.  */
178   if (size == 0)
179     {
180       free (path);
181       path = NULL;
182     }
183
184   result = generic_getcwd (path, size);
185
186   if (result == NULL && buf == NULL && size != 0)
187     free (path);
188
189   return result;
190 }
191 weak_alias (__getcwd, getcwd)
192
193 #if __ASSUME_GETCWD_SYSCALL == 0
194 /* Get the code for the generic version.  */
195 # define GETCWD_RETURN_TYPE     static char * internal_function
196 # define __getcwd               generic_getcwd
197 # include <sysdeps/posix/getcwd.c>
198 #endif