* sysdeps/posix/getaddrinfo.c (rfc3484_sort): Implement rule 4,
[glibc.git] / sysdeps / unix / sysv / linux / i386 / fchownat.c
blobdb5481705a594883697024e2f6015163e23b119a
1 /* Copyright (C) 2005, 2006 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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. */
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
25 #include <sysdep.h>
26 #include <sys/syscall.h>
27 #include <shlib-compat.h>
28 #include <bp-checks.h>
30 #include <linux/posix_types.h>
31 #include <kernel-features.h>
34 In Linux 2.1.x the chown functions have been changed. A new function lchown
35 was introduced. The new chown now follows symlinks - the old chown and the
36 new lchown do not follow symlinks.
37 The new lchown function has the same number as the old chown had and the
38 new chown has a new number. When compiling with headers from Linux > 2.1.8x
39 it's impossible to run this libc with older kernels. In these cases libc
40 has therefore to route calls to chown to the old chown function.
43 extern int __chown_is_lchown (const char *__file, uid_t __owner,
44 gid_t __group);
45 extern int __real_chown (const char *__file, uid_t __owner, gid_t __group);
48 #if defined __NR_lchown || __ASSUME_LCHOWN_SYSCALL > 0
49 /* Running under Linux > 2.1.80. */
51 # ifdef __NR_chown32
52 # if __ASSUME_32BITUIDS == 0
53 /* This variable is shared with all files that need to check for 32bit
54 uids. */
55 extern int __libc_missing_32bit_uids;
56 # endif
57 # endif /* __NR_chown32 */
58 #endif
61 int
62 fchownat (int fd, const char *file, uid_t owner, gid_t group, int flag)
64 int result;
66 #ifdef __NR_fchownat
67 # ifndef __ASSUME_ATFCTS
68 if (__have_atfcts >= 0)
69 # endif
71 result = INLINE_SYSCALL (fchownat, 5, fd, file, owner, group, flag);
72 # ifndef __ASSUME_ATFCTS
73 if (result == -1 && errno == ENOSYS)
74 __have_atfcts = -1;
75 else
76 # endif
77 return result;
79 #endif
81 #ifndef __ASSUME_ATFCTS
82 if (flag & ~AT_SYMLINK_NOFOLLOW)
84 __set_errno (EINVAL);
85 return -1;
88 char *buf = NULL;
90 if (fd != AT_FDCWD && file[0] != '/')
92 size_t filelen = strlen (file);
93 static const char procfd[] = "/proc/self/fd/%d/%s";
94 /* Buffer for the path name we are going to use. It consists of
95 - the string /proc/self/fd/
96 - the file descriptor number
97 - the file name provided.
98 The final NUL is included in the sizeof. A bit of overhead
99 due to the format elements compensates for possible negative
100 numbers. */
101 size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen;
102 buf = alloca (buflen);
104 __snprintf (buf, buflen, procfd, fd, file);
105 file = buf;
108 INTERNAL_SYSCALL_DECL (err);
110 # if defined __NR_lchown || __ASSUME_LCHOWN_SYSCALL > 0
111 # if __ASSUME_LCHOWN_SYSCALL == 0
112 static int __libc_old_chown;
114 # ifdef __NR_chown32
115 if (__libc_missing_32bit_uids <= 0)
117 if (flag & AT_SYMLINK_NOFOLLOW)
118 result = INTERNAL_SYSCALL (lchown32, err, 3, CHECK_STRING (file),
119 owner, group);
120 else
121 result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file),
122 owner, group);
124 if (!INTERNAL_SYSCALL_ERROR_P (result, err))
125 return result;
126 if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS)
127 goto fail;
129 __libc_missing_32bit_uids = 1;
131 # endif /* __NR_chown32 */
133 if (((owner + 1) > (uid_t) ((__kernel_uid_t) -1U))
134 || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U)))
136 __set_errno (EINVAL);
137 return -1;
140 if (!__libc_old_chown && (flag & AT_SYMLINK_NOFOLLOW) == 0)
142 result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner,
143 group);
145 if (!INTERNAL_SYSCALL_ERROR_P (result, err))
146 return result;
147 if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS)
148 goto fail;
150 __libc_old_chown = 1;
153 result = INTERNAL_SYSCALL (lchown, err, 3, CHECK_STRING (file), owner,
154 group);
155 # elif __ASSUME_32BITUIDS
156 /* This implies __ASSUME_LCHOWN_SYSCALL. */
157 result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner,
158 group);
159 # else
160 /* !__ASSUME_32BITUIDS && ASSUME_LCHOWN_SYSCALL */
161 # ifdef __NR_chown32
162 if (__libc_missing_32bit_uids <= 0)
164 result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner,
165 group);
166 if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1))
167 return result;
168 if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS)
169 goto fail;
171 __libc_missing_32bit_uids = 1;
173 # endif /* __NR_chown32 */
174 if (((owner + 1) > (uid_t) ((__kernel_uid_t) -1U))
175 || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U)))
177 __set_errno (EINVAL);
178 return -1;
181 result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, group);
182 # endif
183 # else
184 result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, group);
185 # endif
187 if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0))
188 goto fail;
190 return result;
192 fail:
193 __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf);
194 return -1;
195 #endif