Mark compat code with attribute_compat_text_section.
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / linux / i386 / semctl.c
1 /* Copyright (C) 1995,1997,1998,2000,2003,2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <stdarg.h>
22 #include <sys/sem.h>
23 #include <ipc_priv.h>
24
25 #include <sysdep.h>
26 #include <string.h>
27 #include <sys/syscall.h>
28
29 #include "kernel-features.h"
30 #include <shlib-compat.h>
31
32 struct __old_semid_ds
33 {
34   struct __old_ipc_perm sem_perm;       /* operation permission struct */
35   __time_t sem_otime;                   /* last semop() time */
36   __time_t sem_ctime;                   /* last time changed by semctl() */
37   struct sem *__sembase;                /* ptr to first semaphore in array */
38   struct sem_queue *__sem_pending;      /* pending operations */
39   struct sem_queue *__sem_pending_last; /* last pending operation */
40   struct sem_undo *__undo;              /* ondo requests on this array */
41   unsigned short int sem_nsems;         /* number of semaphores in set */
42 };
43
44 /* Define a `union semun' suitable for Linux here.  */
45 union semun
46 {
47   int val;                      /* value for SETVAL */
48   struct semid_ds *buf;         /* buffer for IPC_STAT & IPC_SET */
49   unsigned short int *array;    /* array for GETALL & SETALL */
50   struct seminfo *__buf;        /* buffer for IPC_INFO */
51   struct __old_semid_ds *__old_buf;
52 };
53
54 #include <bp-checks.h>
55 #include <bp-semctl.h>          /* definition of CHECK_SEMCTL needs union semum */
56
57 #ifdef __NR_getuid32
58 # if __ASSUME_32BITUIDS == 0
59 /* This variable is shared with all files that need to check for 32bit
60    uids.  */
61 extern int __libc_missing_32bit_uids;
62 # endif
63 #endif
64
65 /* Return identifier for array of NSEMS semaphores associated with
66    KEY.  */
67 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
68 int __old_semctl (int semid, int semnum, int cmd, ...);
69 #endif
70 int __new_semctl (int semid, int semnum, int cmd, ...);
71
72 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
73 int
74 attribute_compat_text_section
75 __old_semctl (int semid, int semnum, int cmd, ...)
76 {
77   union semun arg;
78   va_list ap;
79
80   va_start (ap, cmd);
81
82   /* Get the argument.  */
83   arg = va_arg (ap, union semun);
84
85   va_end (ap);
86
87   return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
88                          CHECK_SEMCTL (&arg, semid, cmd));
89 }
90 compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
91 #endif
92
93 int
94 __new_semctl (int semid, int semnum, int cmd, ...)
95 {
96   union semun arg;
97   va_list ap;
98
99   va_start (ap, cmd);
100
101   /* Get the argument.  */
102   arg = va_arg (ap, union semun);
103
104   va_end (ap);
105
106 #if __ASSUME_32BITUIDS > 0
107   return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
108                          CHECK_SEMCTL (&arg, semid, cmd | __IPC_64));
109 #else
110   switch (cmd) {
111     case SEM_STAT:
112     case IPC_STAT:
113     case IPC_SET:
114       break;
115     default:
116       return INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
117                              CHECK_SEMCTL (&arg, semid, cmd));
118   }
119
120   {
121     int result;
122     struct __old_semid_ds old;
123     struct semid_ds *buf;
124
125 #ifdef __NR_getuid32
126     if (__libc_missing_32bit_uids <= 0)
127       {
128         if (__libc_missing_32bit_uids < 0)
129           {
130             int save_errno = errno;
131
132             /* Test presence of new IPC by testing for getuid32 syscall.  */
133             result = INLINE_SYSCALL (getuid32, 0);
134             if (result == -1 && errno == ENOSYS)
135               __libc_missing_32bit_uids = 1;
136             else
137               __libc_missing_32bit_uids = 0;
138             __set_errno(save_errno);
139           }
140         if (__libc_missing_32bit_uids <= 0)
141           {
142             result = INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
143                                      CHECK_SEMCTL (&arg, semid, cmd | __IPC_64));
144             return result;
145           }
146       }
147 #endif
148
149     buf = arg.buf;
150     arg.__old_buf = &old;
151     if (cmd == IPC_SET)
152       {
153         old.sem_perm.uid = buf->sem_perm.uid;
154         old.sem_perm.gid = buf->sem_perm.gid;
155         old.sem_perm.mode = buf->sem_perm.mode;
156         if (old.sem_perm.uid != buf->sem_perm.uid ||
157             old.sem_perm.gid != buf->sem_perm.gid)
158           {
159             __set_errno (EINVAL);
160             return -1;
161           }
162       }
163     result = INLINE_SYSCALL (ipc, 5, IPCOP_semctl, semid, semnum, cmd,
164                              CHECK_SEMCTL (&arg, semid, cmd));
165     if (result != -1 && cmd != IPC_SET)
166       {
167         memset(buf, 0, sizeof(*buf));
168         buf->sem_perm.__key = old.sem_perm.__key;
169         buf->sem_perm.uid = old.sem_perm.uid;
170         buf->sem_perm.gid = old.sem_perm.gid;
171         buf->sem_perm.cuid = old.sem_perm.cuid;
172         buf->sem_perm.cgid = old.sem_perm.cgid;
173         buf->sem_perm.mode = old.sem_perm.mode;
174         buf->sem_perm.__seq = old.sem_perm.__seq;
175         buf->sem_otime = old.sem_otime;
176         buf->sem_ctime = old.sem_ctime;
177         buf->sem_nsems = old.sem_nsems;
178       }
179     return result;
180   }
181 #endif
182 }
183
184 versioned_symbol (libc, __new_semctl, semctl, GLIBC_2_2);