1 #include <semaphore.h>
2 #include <sys/mman.h>
3 #include <limits.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdarg.h>
8 #include <errno.h>
9 #include <time.h>
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <stdlib.h>
13 #include <pthread.h>
14 #include "lock.h"
15 #include "fork_impl.h"
16 
17 #define malloc __libc_malloc
18 #define calloc __libc_calloc
19 #define realloc undef
20 #define free undef
21 
22 static struct {
23 	ino_t ino;
24 	sem_t *sem;
25 	int refcnt;
26 } *semtab;
27 static volatile int lock[1];
28 volatile int *const __sem_open_lockptr = lock;
29 
30 #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
31 
sem_open(const char *name, int flags, ...)32 sem_t *sem_open(const char *name, int flags, ...)
33 {
34 	va_list ap;
35 	mode_t mode;
36 	unsigned value;
37 	int fd, i, e, slot, first=1, cnt, cs;
38 	sem_t newsem;
39 	void *map;
40 	char tmp[64];
41 	struct timespec ts;
42 	struct stat st;
43 	char buf[NAME_MAX+10];
44 
45 	if (!(name = __shm_mapname(name, buf)))
46 		return SEM_FAILED;
47 
48 	LOCK(lock);
49 	/* Allocate table if we don't have one yet */
50 	if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
51 		UNLOCK(lock);
52 		return SEM_FAILED;
53 	}
54 
55 	/* Reserve a slot in case this semaphore is not mapped yet;
56 	 * this is necessary because there is no way to handle
57 	 * failures after creation of the file. */
58 	slot = -1;
59 	for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
60 		cnt += semtab[i].refcnt;
61 		if (!semtab[i].sem && slot < 0) slot = i;
62 	}
63 	/* Avoid possibility of overflow later */
64 	if (cnt == INT_MAX || slot < 0) {
65 		errno = EMFILE;
66 		UNLOCK(lock);
67 		return SEM_FAILED;
68 	}
69 	/* Dummy pointer to make a reservation */
70 	semtab[slot].sem = (sem_t *)-1;
71 	UNLOCK(lock);
72 
73 	flags &= (O_CREAT|O_EXCL);
74 
75 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
76 
77 	/* Early failure check for exclusive open; otherwise the case
78 	 * where the semaphore already exists is expensive. */
79 	if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
80 		errno = EEXIST;
81 		goto fail;
82 	}
83 
84 	for (;;) {
85 		/* If exclusive mode is not requested, try opening an
86 		 * existing file first and fall back to creation. */
87 		if (flags != (O_CREAT|O_EXCL)) {
88 			fd = open(name, FLAGS);
89 			if (fd >= 0) {
90 				if (fstat(fd, &st) < 0 ||
91 				    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
92 					close(fd);
93 					goto fail;
94 				}
95 #ifndef __LITEOS_A__
96 				close(fd);
97 #endif
98 				break;
99 			}
100 			if (errno != ENOENT)
101 				goto fail;
102 		}
103 		if (!(flags & O_CREAT))
104 			goto fail;
105 		if (first) {
106 			first = 0;
107 			va_start(ap, flags);
108 			mode = va_arg(ap, mode_t) & 0666;
109 			value = va_arg(ap, unsigned);
110 			va_end(ap);
111 			if (value > SEM_VALUE_MAX) {
112 				errno = EINVAL;
113 				goto fail;
114 			}
115 			sem_init(&newsem, 1, value);
116 		}
117 		/* Create a temp file with the new semaphore contents
118 		 * and attempt to atomically link it as the new name */
119 		clock_gettime(CLOCK_REALTIME, &ts);
120 		snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
121 		fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
122 		if (fd < 0) {
123 			if (errno == EEXIST) continue;
124 			goto fail;
125 		}
126 		if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
127 		    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
128 			close(fd);
129 			unlink(tmp);
130 			goto fail;
131 		}
132 #ifndef __LITEOS_A__
133 		close(fd);
134 #endif
135 		e = link(tmp, name) ? errno : 0;
136 		unlink(tmp);
137 		if (!e) break;
138 		munmap(map, sizeof(sem_t));
139 		/* Failure is only fatal when doing an exclusive open;
140 		 * otherwise, next iteration will try to open the
141 		 * existing file. */
142 		if (e != EEXIST || flags == (O_CREAT|O_EXCL))
143 			goto fail;
144 	}
145 
146 	/* See if the newly mapped semaphore is already mapped. If
147 	 * so, unmap the new mapping and use the existing one. Otherwise,
148 	 * add it to the table of mapped semaphores. */
149 	LOCK(lock);
150 	for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
151 	if (i<SEM_NSEMS_MAX) {
152 		munmap(map, sizeof(sem_t));
153 		semtab[slot].sem = 0;
154 		slot = i;
155 		map = semtab[i].sem;
156 	}
157 	semtab[slot].refcnt++;
158 	semtab[slot].sem = map;
159 	semtab[slot].ino = st.st_ino;
160 	UNLOCK(lock);
161 	pthread_setcancelstate(cs, 0);
162 	return map;
163 
164 fail:
165 	pthread_setcancelstate(cs, 0);
166 	LOCK(lock);
167 	semtab[slot].sem = 0;
168 	UNLOCK(lock);
169 	return SEM_FAILED;
170 }
171 
sem_close(sem_t *sem)172 int sem_close(sem_t *sem)
173 {
174 	int i;
175 	LOCK(lock);
176 	for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
177 #ifdef __LITEOS_A__
178 	if (!--semtab[i].refcnt) {
179 #else
180 	if (--semtab[i].refcnt) {
181 #endif
182 		UNLOCK(lock);
183 		return 0;
184 	}
185 	semtab[i].sem = 0;
186 	semtab[i].ino = 0;
187 	UNLOCK(lock);
188 	munmap(sem, sizeof *sem);
189 	return 0;
190 }
191