chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / sysdeps / unix / sysv / linux / openat.c
1 /* Copyright (C) 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library 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 GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <kernel-features.h>
27 #include <sysdep-cancel.h>
28 #include <not-cancel.h>
29
30
31 #ifndef OPENAT
32 # define OPENAT openat
33 # define __OPENAT_2 __openat_2
34
35 # ifndef __ASSUME_ATFCTS
36 /* Set errno after a failed call.  If BUF is not null,
37    it is a /proc/self/fd/ path name we just tried to use.  */
38 void
39 attribute_hidden
40 __atfct_seterrno (int errval, int fd, const char *buf)
41 {
42   if (buf != NULL)
43     {
44       struct stat64 st;
45
46       if (errval == ENOTDIR || errval == ENOENT)
47         {
48           /* This can mean either the file descriptor is invalid or
49              /proc is not mounted.  */
50           if (__fxstat64 (_STAT_VER, fd, &st) != 0)
51             /* errno is already set correctly.  */
52             return;
53
54           /* If /proc is not mounted there is nothing we can do.  */
55           if ((errval != ENOTDIR || S_ISDIR (st.st_mode))
56               && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0
57                   || !S_ISDIR (st.st_mode)))
58             errval = ENOSYS;
59         }
60     }
61
62   __set_errno (errval);
63 }
64
65 int __have_atfcts;
66 # endif
67 #endif
68
69
70 #define OPENAT_NOT_CANCEL CONCAT (OPENAT)
71 #define CONCAT(name) CONCAT2 (name)
72 #define CONCAT2(name) __##name##_nocancel
73
74
75 int
76 OPENAT_NOT_CANCEL (fd, file, oflag, mode)
77      int fd;
78      const char *file;
79      int oflag;
80      mode_t mode;
81 {
82
83   /* We have to add the O_LARGEFILE flag for openat64.  */
84 #ifdef MORE_OFLAGS
85   oflag |= MORE_OFLAGS;
86 #endif
87
88   INTERNAL_SYSCALL_DECL (err);
89   int res;
90
91 #ifdef __NR_openat
92 # ifndef __ASSUME_ATFCTS
93   if (__have_atfcts >= 0)
94 # endif
95     {
96       res = INLINE_SYSCALL (openat, 4, fd, file, oflag, mode);
97
98 # ifndef __ASSUME_ATFCTS
99       if (res == -1 && errno == ENOSYS)
100         __have_atfcts = -1;
101       else
102 # endif
103         return res;
104     }
105 #endif
106
107 #ifndef __ASSUME_ATFCTS
108   char *buf = NULL;
109
110   if (fd != AT_FDCWD && file[0] != '/')
111     {
112       size_t filelen = strlen (file);
113       if (__builtin_expect (filelen == 0, 0))
114         {
115           __set_errno (ENOENT);
116           return -1;
117         }
118
119       static const char procfd[] = "/proc/self/fd/%d/%s";
120       /* Buffer for the path name we are going to use.  It consists of
121          - the string /proc/self/fd/
122          - the file descriptor number
123          - the file name provided.
124          The final NUL is included in the sizeof.   A bit of overhead
125          due to the format elements compensates for possible negative
126          numbers.  */
127       size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen;
128       buf = alloca (buflen);
129
130       /* Note: snprintf cannot be canceled.  */
131       __snprintf (buf, buflen, procfd, fd, file);
132       file = buf;
133     }
134
135   res = INTERNAL_SYSCALL (open, err, 3, file, oflag, mode);
136
137   if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
138     {
139       __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (res, err), fd, buf);
140       res = -1;
141     }
142
143   return res;
144 #endif
145 }
146
147 #define UNDERIZE(name) UNDERIZE_1 (name)
148 #define UNDERIZE_1(name) __##name
149 #define __OPENAT UNDERIZE (OPENAT)
150
151
152 /* Open FILE with access OFLAG.  Interpret relative paths relative to
153    the directory associated with FD.  If OFLAG includes O_CREAT, a
154    third argument is the file protection.  */
155 int
156 __OPENAT (fd, file, oflag)
157      int fd;
158      const char *file;
159      int oflag;
160 {
161   mode_t mode = 0;
162   if (oflag & O_CREAT)
163     {
164       va_list arg;
165       va_start (arg, oflag);
166       mode = va_arg (arg, mode_t);
167       va_end (arg);
168     }
169
170   if (SINGLE_THREAD_P)
171     return OPENAT_NOT_CANCEL (fd, file, oflag, mode);
172
173   int oldtype = LIBC_CANCEL_ASYNC ();
174
175   int res = OPENAT_NOT_CANCEL (fd, file, oflag, mode);
176
177   LIBC_CANCEL_RESET (oldtype);
178
179   return res;
180 }
181 libc_hidden_def (__OPENAT)
182 weak_alias (__OPENAT, OPENAT)
183
184
185 int
186 __OPENAT_2 (fd, file, oflag)
187      int fd;
188      const char *file;
189      int oflag;
190 {
191   if (oflag & O_CREAT)
192 #define MSG(s) MSG2 (s)
193 #define MSG2(s) "invalid " #s " call: O_CREAT without mode"
194     __fortify_fail (MSG (OPENAT));
195
196   return __OPENAT (fd, file, oflag);
197 }