Updated to fedora-glibc-20060203T0932
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / linux / openat.c
1 /* Copyright (C) 2005, 2006 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
29
30 #if !defined OPENAT && !defined __ASSUME_ATFCTS
31 # define OPENAT openat
32
33 /* Set errno after a failed call.  If BUF is not null,
34    it is a /proc/self/fd/ path name we just tried to use.  */
35 void
36 attribute_hidden
37 __atfct_seterrno (int errval, int fd, const char *buf)
38 {
39   if (buf != NULL)
40     {
41       struct stat64 st;
42
43       if (errval == ENOTDIR || errval == ENOENT)
44         {
45           /* This can mean either the file descriptor is invalid or
46              /proc is not mounted.  */
47           if (__fxstat64 (_STAT_VER, fd, &st) != 0)
48             /* errno is already set correctly.  */
49             return;
50
51           /* If /proc is not mounted there is nothing we can do.  */
52           if ((errval != ENOTDIR || S_ISDIR (st.st_mode))
53               && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0
54                   || !S_ISDIR (st.st_mode)))
55             errval = ENOSYS;
56         }
57     }
58
59   __set_errno (errval);
60 }
61
62 int __have_atfcts;
63 #endif
64
65 /* Open FILE with access OFLAG.  Interpret relative paths relative to
66    the directory associated with FD.  If OFLAG includes O_CREAT, a
67    third argument is the file protection.  */
68 int
69 OPENAT (fd, file, oflag)
70      int fd;
71      const char *file;
72      int oflag;
73 {
74   mode_t mode = 0;
75   if (oflag & O_CREAT)
76     {
77       va_list arg;
78       va_start (arg, oflag);
79       mode = va_arg (arg, mode_t);
80       va_end (arg);
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       if (SINGLE_THREAD_P)
97         res = INLINE_SYSCALL (openat, 4, fd, file, oflag, mode);
98       else
99         {
100           int oldtype = LIBC_CANCEL_ASYNC ();
101
102           res = INLINE_SYSCALL (openat, 4, fd, file, oflag, mode);
103
104           LIBC_CANCEL_RESET (oldtype);
105         }
106
107 # ifndef __ASSUME_ATFCTS
108       if (res == -1 && errno == ENOSYS)
109         __have_atfcts = -1;
110       else
111 # endif
112         return res;
113     }
114 #endif
115
116 #ifndef __ASSUME_ATFCTS
117   char *buf = NULL;
118
119   if (fd != AT_FDCWD && file[0] != '/')
120     {
121       size_t filelen = strlen (file);
122       static const char procfd[] = "/proc/self/fd/%d/%s";
123       /* Buffer for the path name we are going to use.  It consists of
124          - the string /proc/self/fd/
125          - the file descriptor number
126          - the file name provided.
127          The final NUL is included in the sizeof.   A bit of overhead
128          due to the format elements compensates for possible negative
129          numbers.  */
130       size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen;
131       buf = alloca (buflen);
132
133       __snprintf (buf, buflen, procfd, fd, file);
134       file = buf;
135     }
136
137   if (SINGLE_THREAD_P)
138     res = INTERNAL_SYSCALL (open, err, 3, file, oflag, mode);
139   else
140     {
141       int oldtype = LIBC_CANCEL_ASYNC ();
142
143       res = INTERNAL_SYSCALL (open, err, 3, file, oflag, mode);
144
145       LIBC_CANCEL_RESET (oldtype);
146     }
147
148   if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
149     {
150       __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (res, err), fd, buf);
151       res = -1;
152     }
153
154   return res;
155 #endif
156 }