]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/unix/sysv/linux/getcwd.c
Replace FSF snail mail address with URLs.
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / getcwd.c
CommitLineData
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 54static int no_syscall_getcwd;
c4563d2d 55static int have_new_dcache;
958f238f
UD
56# else
57# define no_syscall_getcwd 1
c4563d2d 58static 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. */
67static char *generic_getcwd (char *buf, size_t size) internal_function;
68
fe0ec73e
UD
69char *
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}
237weak_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>