67e9df2e455b87200998bb016aa2ab31655c8dbc
[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
34 void
35 attribute_hidden
36 __atfct_seterrno (int errval, int fd, const char *buf)
37 {
38   if (buf != NULL)
39     {
40       struct stat64 st;
41
42       if (errval == ENOTDIR)
43         {
44           /* This can mean either the file descriptor is invalid or
45              /proc is not mounted.  */
46           if (__fxstat64 (_STAT_VER, fd, &st) != 0)
47             /* errno is already set correctly.  */
48             return;
49
50           /* If /proc is not mounted there is nothing we can do.  */
51           if (S_ISDIR (st.st_mode)
52               && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0
53                   || !S_ISDIR (st.st_mode)))
54             errval = ENOSYS;
55         }
56       else if (errval == ENOENT)
57         {
58           /* This could mean the file descriptor is not valid.  We
59              reuse BUF for the stat call.  Find the slash after the
60              file descriptor number.  */
61           *(char *) strchr (buf + sizeof "/proc/self/fd", '/') = '\0';
62
63           int e = __lxstat64 (_STAT_VER, buf, &st);
64           if ((e == -1 && errno == ENOENT)
65               ||(e == 0 && !S_ISLNK (st.st_mode)))
66             errval = EBADF;
67         }
68     }
69
70   __set_errno (errval);
71 }
72
73 int __have_atfcts;
74 #endif
75
76 /* Open FILE with access OFLAG.  Interpret relative paths relative to
77    the directory associated with FD.  If OFLAG includes O_CREAT, a
78    third argument is the file protection.  */
79 int
80 OPENAT (fd, file, oflag)
81      int fd;
82      const char *file;
83      int oflag;
84 {
85   mode_t mode = 0;
86   if (oflag & O_CREAT)
87     {
88       va_list arg;
89       va_start (arg, oflag);
90       mode = va_arg (arg, mode_t);
91       va_end (arg);
92     }
93
94   /* We have to add the O_LARGEFILE flag for openat64.  */
95 #ifdef MORE_OFLAGS
96   oflag |= MORE_OFLAGS;
97 #endif
98
99   INTERNAL_SYSCALL_DECL (err);
100   int res;
101
102 #ifdef __NR_openat
103 # ifndef __ASSUME_ATFCTS
104   if (__have_atfcts >= 0)
105 # endif
106     {
107       if (SINGLE_THREAD_P)
108         res = INLINE_SYSCALL (openat, 4, fd, file, oflag, mode);
109       else
110         {
111           int oldtype = LIBC_CANCEL_ASYNC ();
112
113           res = INLINE_SYSCALL (openat, 4, fd, file, oflag, mode);
114
115           LIBC_CANCEL_RESET (oldtype);
116         }
117
118 # ifndef __ASSUME_ATFCTS
119       if (res == -1 && errno == ENOSYS)
120         __have_atfcts = -1;
121       else
122 # endif
123         return res;
124     }
125 #endif
126
127 #ifndef __ASSUME_ATFCTS
128   char *buf = NULL;
129
130   if (fd != AT_FDCWD && file[0] != '/')
131     {
132       size_t filelen = strlen (file);
133       static const char procfd[] = "/proc/self/fd/%d/%s";
134       /* Buffer for the path name we are going to use.  It consists of
135          - the string /proc/self/fd/
136          - the file descriptor number
137          - the file name provided.
138          The final NUL is included in the sizeof.   A bit of overhead
139          due to the format elements compensates for possible negative
140          numbers.  */
141       size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen;
142       buf = alloca (buflen);
143
144       __snprintf (buf, buflen, procfd, fd, file);
145       file = buf;
146     }
147
148   if (SINGLE_THREAD_P)
149     res = INTERNAL_SYSCALL (open, err, 3, file, oflag, mode);
150   else
151     {
152       int oldtype = LIBC_CANCEL_ASYNC ();
153
154       res = INTERNAL_SYSCALL (open, err, 3, file, oflag, mode);
155
156       LIBC_CANCEL_RESET (oldtype);
157     }
158
159   if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0))
160     {
161       __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (res, err), fd, buf);
162       res = -1;
163     }
164
165   return res;
166 #endif
167 }