]>
Commit | Line | Data |
---|---|---|
fe0ec73e | 1 | /* Determine current working directory. Linux version. |
7fb90fb8 | 2 | Copyright (C) 1997,1998,1999,2000,2002,2003,2006,2011 |
6ddd37a4 | 3 | Free Software Foundation, Inc. |
fe0ec73e UD |
4 | This file is part of the GNU C Library. |
5 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. | |
6 | ||
7 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
8 | modify it under the terms of the GNU Lesser General Public |
9 | License as published by the Free Software Foundation; either | |
10 | version 2.1 of the License, or (at your option) any later version. | |
fe0ec73e UD |
11 | |
12 | The GNU C Library 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 GNU | |
41bdb6e2 | 15 | Lesser General Public License for more details. |
fe0ec73e | 16 | |
41bdb6e2 | 17 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 PE |
18 | License along with the GNU C Library; if not, see |
19 | <http://www.gnu.org/licenses/>. */ | |
fe0ec73e | 20 | |
958f238f | 21 | #include <assert.h> |
fe0ec73e UD |
22 | #include <errno.h> |
23 | #include <limits.h> | |
24 | #include <stdlib.h> | |
25 | #include <unistd.h> | |
0dee6738 UD |
26 | |
27 | #include <sysdep.h> | |
63bda0c1 | 28 | #include <sys/syscall.h> |
4bbb61e4 | 29 | #include <bp-checks.h> |
fe0ec73e | 30 | |
6ddd37a4 | 31 | #include <kernel-features.h> |
fe0ec73e | 32 | |
958f238f | 33 | |
8d04a97c UD |
34 | /* If we compile the file for use in ld.so we don't need the feature |
35 | that getcwd() allocates the buffers itself. */ | |
36 | #ifdef IS_IN_rtld | |
37 | # define NO_ALLOCATION 1 | |
38 | #endif | |
39 | ||
40 | ||
958f238f UD |
41 | #if __ASSUME_GETCWD_SYSCALL > 0 |
42 | /* Kernel 2.1.92 introduced a third way to get the current working | |
43 | directory: a syscall. We've got to be careful that even when | |
44 | compiling under 2.1.92+ the libc still runs under older kernels. */ | |
958f238f UD |
45 | # define no_syscall_getcwd 0 |
46 | # define have_new_dcache 1 | |
958f238f | 47 | #else |
958f238f | 48 | # if __NR_getcwd |
63bda0c1 UD |
49 | /* Kernel 2.1.92 introduced a third way to get the current working |
50 | directory: a syscall. We've got to be careful that even when | |
7fb90fb8 UD |
51 | compiling under 2.1.92+ the libc still runs under older kernels. |
52 | An additional problem is that the system call does not return | |
53 | the path of directories longer than one page. */ | |
bbe0c227 | 54 | static int no_syscall_getcwd; |
c4563d2d | 55 | static int have_new_dcache; |
958f238f UD |
56 | # else |
57 | # define no_syscall_getcwd 1 | |
c4563d2d | 58 | static int have_new_dcache = 1; |
958f238f | 59 | # endif |
63bda0c1 UD |
60 | #endif |
61 | ||
7fb90fb8 UD |
62 | /* The "proc" filesystem provides an easy method to retrieve the value. |
63 | For each process, the corresponding directory contains a symbolic link | |
64 | named `cwd'. Reading the content of this link immediate gives us the | |
65 | information. But we have to take care for systems which do not have | |
66 | the proc filesystem mounted. Use the POSIX implementation in this case. */ | |
67 | static char *generic_getcwd (char *buf, size_t size) internal_function; | |
68 | ||
fe0ec73e UD |
69 | char * |
70 | __getcwd (char *buf, size_t size) | |
71 | { | |
fe0ec73e UD |
72 | char *path; |
73 | int n; | |
74 | char *result; | |
6973fc01 | 75 | |
c4563d2d | 76 | if (no_syscall_getcwd && !have_new_dcache) |
6973fc01 | 77 | return generic_getcwd (buf, size); |
fe0ec73e | 78 | |
8d04a97c UD |
79 | #ifndef NO_ALLOCATION |
80 | size_t alloc_size = size; | |
a2b3aa73 | 81 | if (size == 0) |
fe0ec73e UD |
82 | { |
83 | if (buf != NULL) | |
84 | { | |
85 | __set_errno (EINVAL); | |
86 | return NULL; | |
87 | } | |
88 | ||
6c7a5753 | 89 | alloc_size = MAX (PATH_MAX, __getpagesize ()); |
fe0ec73e UD |
90 | } |
91 | ||
8d04a97c | 92 | if (buf == NULL) |
fe0ec73e | 93 | { |
6973fc01 | 94 | path = malloc (alloc_size); |
fe0ec73e UD |
95 | if (path == NULL) |
96 | return NULL; | |
97 | } | |
8d04a97c UD |
98 | else |
99 | #else | |
100 | # define alloc_size size | |
101 | #endif | |
102 | path = buf; | |
fe0ec73e | 103 | |
958f238f | 104 | #if defined __NR_getcwd || __LINUX_GETCWD_SYSCALL > 0 |
63bda0c1 UD |
105 | if (!no_syscall_getcwd) |
106 | { | |
107 | int retval; | |
108 | ||
4bbb61e4 | 109 | retval = INLINE_SYSCALL (getcwd, 2, CHECK_STRING (path), alloc_size); |
63bda0c1 UD |
110 | if (retval >= 0) |
111 | { | |
8d04a97c | 112 | # ifndef NO_ALLOCATION |
9cd865e0 | 113 | if (buf == NULL && size == 0) |
9ff9add9 UD |
114 | /* Ensure that the buffer is only as large as necessary. */ |
115 | buf = realloc (path, (size_t) retval); | |
116 | ||
117 | if (buf == NULL) | |
118 | /* Either buf was NULL all along, or `realloc' failed but | |
119 | we still have the original string. */ | |
120 | buf = path; | |
8d04a97c | 121 | # endif |
9ff9add9 | 122 | |
63bda0c1 UD |
123 | return buf; |
124 | } | |
125 | ||
7fb90fb8 UD |
126 | /* The system call cannot handle paths longer than a page. |
127 | Neither can the magic symlink in /proc/self. Just use the | |
128 | generic implementation right away. */ | |
129 | if (errno == ENAMETOOLONG) | |
130 | { | |
131 | # ifndef NO_ALLOCATION | |
132 | if (buf == NULL && size == 0) | |
133 | { | |
134 | free (path); | |
135 | path = NULL; | |
136 | } | |
137 | # endif | |
138 | ||
139 | result = generic_getcwd (path, size); | |
140 | ||
141 | # ifndef NO_ALLOCATION | |
142 | if (result == NULL && buf == NULL && size != 0) | |
143 | free (path); | |
144 | # endif | |
145 | ||
146 | return result; | |
147 | } | |
7fb90fb8 | 148 | |
958f238f UD |
149 | # if __ASSUME_GETCWD_SYSCALL |
150 | /* It should never happen that the `getcwd' syscall failed because | |
fe97f974 | 151 | the buffer is too small if we allocated the buffer ourselves |
9cd865e0 UD |
152 | large enough. */ |
153 | assert (errno != ERANGE || buf != NULL || size != 0); | |
958f238f | 154 | |
8d04a97c | 155 | # ifndef NO_ALLOCATION |
958f238f UD |
156 | if (buf == NULL) |
157 | free (path); | |
8d04a97c | 158 | # endif |
958f238f UD |
159 | |
160 | return NULL; | |
161 | # else | |
63bda0c1 UD |
162 | if (errno == ENOSYS) |
163 | { | |
164 | no_syscall_getcwd = 1; | |
c4563d2d | 165 | have_new_dcache = 1; /* Now we will try the /proc method. */ |
63bda0c1 UD |
166 | } |
167 | else if (errno != ERANGE || buf != NULL) | |
168 | { | |
8d04a97c | 169 | # ifndef NO_ALLOCATION |
63bda0c1 UD |
170 | if (buf == NULL) |
171 | free (path); | |
8d04a97c | 172 | # endif |
63bda0c1 UD |
173 | return NULL; |
174 | } | |
958f238f | 175 | # endif |
63bda0c1 UD |
176 | } |
177 | #endif | |
178 | ||
6973fc01 | 179 | n = __readlink ("/proc/self/cwd", path, alloc_size - 1); |
fe0ec73e UD |
180 | if (n != -1) |
181 | { | |
f43ce637 | 182 | if (path[0] == '/') |
fe0ec73e | 183 | { |
57b36a0a | 184 | if ((size_t) n >= alloc_size - 1) |
f43ce637 | 185 | { |
8d04a97c | 186 | #ifndef NO_ALLOCATION |
f43ce637 UD |
187 | if (buf == NULL) |
188 | free (path); | |
8d04a97c | 189 | #endif |
f43ce637 UD |
190 | return NULL; |
191 | } | |
192 | ||
193 | path[n] = '\0'; | |
8d04a97c | 194 | #ifndef NO_ALLOCATION |
9cd865e0 | 195 | if (buf == NULL && size == 0) |
9ff9add9 UD |
196 | /* Ensure that the buffer is only as large as necessary. */ |
197 | buf = realloc (path, (size_t) n + 1); | |
198 | if (buf == NULL) | |
199 | /* Either buf was NULL all along, or `realloc' failed but | |
200 | we still have the original string. */ | |
201 | buf = path; | |
8d04a97c | 202 | #endif |
9ff9add9 | 203 | |
63bda0c1 | 204 | return buf; |
fe0ec73e | 205 | } |
958f238f | 206 | #ifndef have_new_dcache |
6973fc01 | 207 | else |
c4563d2d | 208 | have_new_dcache = 0; |
958f238f | 209 | #endif |
fe0ec73e UD |
210 | } |
211 | ||
958f238f | 212 | #if __ASSUME_GETCWD_SYSCALL == 0 |
c4563d2d UD |
213 | /* Set to have_new_dcache only if error indicates that proc doesn't |
214 | exist. */ | |
6973fc01 | 215 | if (errno != EACCES && errno != ENAMETOOLONG) |
c4563d2d | 216 | have_new_dcache = 0; |
958f238f | 217 | #endif |
6973fc01 | 218 | |
8d04a97c | 219 | #ifndef NO_ALLOCATION |
6973fc01 | 220 | /* Don't put restrictions on the length of the path unless the user does. */ |
7fb90fb8 | 221 | if (buf == NULL && size == 0) |
6973fc01 UD |
222 | { |
223 | free (path); | |
224 | path = NULL; | |
225 | } | |
8d04a97c | 226 | #endif |
6973fc01 | 227 | |
fe0ec73e UD |
228 | result = generic_getcwd (path, size); |
229 | ||
8d04a97c | 230 | #ifndef NO_ALLOCATION |
a2b3aa73 | 231 | if (result == NULL && buf == NULL && size != 0) |
fe0ec73e | 232 | free (path); |
8d04a97c | 233 | #endif |
fe0ec73e UD |
234 | |
235 | return result; | |
236 | } | |
237 | weak_alias (__getcwd, getcwd) | |
238 | ||
239 | /* Get the code for the generic version. */ | |
7fb90fb8 UD |
240 | #define GETCWD_RETURN_TYPE static char * internal_function |
241 | #define __getcwd generic_getcwd | |
242 | #include <sysdeps/posix/getcwd.c> |