use O_CLOEXEC to open semaphore files in sem_open
[musl] / src / thread / sem_open.c
index 55d5ef6..0361a76 100644 (file)
@@ -9,32 +9,32 @@
 #include <time.h>
 #include <stdio.h>
 #include <sys/stat.h>
+#include <stdlib.h>
+#include <pthread.h>
 
-static void *find_map(int fd)
+static struct {
+       ino_t ino;
+       sem_t *sem;
+       int refcnt;
+} *semtab;
+
+static int semcnt;
+static pthread_spinlock_t lock;
+static pthread_once_t once;
+
+static void init()
 {
-       char c;
-       struct stat st;
-       FILE *f;
-       void *addr;
-       unsigned long long off, ino;
-       char buf[100];
+       semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX);
+}
 
-       if (fstat(fd, &st) < 0) return 0;
-       if (!(f = fopen("/proc/self/maps", "rb"))) return 0;
-       
-       while (fgets(buf, sizeof buf, f)) {
-               sscanf(buf, "%lx-%*lx %*s %llx %*x:%*x %llu /dev/shm%c",
-                       (long *)&addr, &off, &ino, &c);
-               while (!strchr(buf, '\n') && fgets(buf, sizeof buf, f));
-               if (c!='/') continue;
-               c = 0;
-               if (!off && st.st_ino == ino) {
-                       fclose(f);
-                       return addr;
-               }
-       }
-       fclose(f);
-       return 0;
+static sem_t *find_map(ino_t ino)
+{
+       int i;
+       for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != ino; i++);
+       if (i==SEM_NSEMS_MAX) return 0;
+       if (semtab[i].refcnt == INT_MAX) return (sem_t *)-1;
+       semtab[i].refcnt++;
+       return semtab[i].sem;
 }
 
 sem_t *sem_open(const char *name, int flags, ...)
@@ -47,6 +47,8 @@ sem_t *sem_open(const char *name, int flags, ...)
        void *map;
        char tmp[64];
        struct timespec ts;
+       struct stat st;
+       int i;
 
        while (*name=='/') name++;
        if (strchr(name, '/')) {
@@ -54,6 +56,12 @@ sem_t *sem_open(const char *name, int flags, ...)
                return SEM_FAILED;
        }
 
+       pthread_once(&once, init);
+       if (!semtab) {
+               errno = ENOMEM;
+               return SEM_FAILED;
+       }
+
        if (flags & O_CREAT) {
                va_start(ap, flags);
                mode = va_arg(ap, mode_t) & 0666;
@@ -67,9 +75,9 @@ sem_t *sem_open(const char *name, int flags, ...)
                clock_gettime(CLOCK_REALTIME, &ts);
                snprintf(tmp, sizeof(tmp), "/dev/shm/%p-%p-%d-%d",
                        &name, name, (int)getpid(), (int)ts.tv_nsec);
-               tfd = open(tmp, O_CREAT|O_EXCL|O_RDWR, mode);
+               tfd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC, mode);
                if (tfd<0) return SEM_FAILED;
-               dir = open("/dev/shm", O_DIRECTORY|O_RDONLY);
+               dir = open("/dev/shm", O_DIRECTORY|O_RDONLY|O_CLOEXEC);
                if (dir<0 || write(tfd,&newsem,sizeof newsem)!=sizeof newsem) {
                        if (dir >= 0) close(dir);
                        close(tfd);
@@ -81,21 +89,39 @@ sem_t *sem_open(const char *name, int flags, ...)
        flags &= ~O_ACCMODE;
        flags |= O_RDWR;
 
+       pthread_spin_lock(&lock);
+
        for (;;) {
                if (!(flags & O_EXCL)) {
-                       fd = shm_open(name, flags&~O_CREAT, mode);
+                       fd = shm_open(name, flags&~O_CREAT, 0);
                        if (fd >= 0 || errno != ENOENT) {
                                if (flags & O_CREAT) {
                                        close(dir);
                                        close(tfd);
                                        unlink(tmp);
                                }
-                               if (fd < 0) return SEM_FAILED;
-                               if ((map = find_map(fd)))
+                               if (fd >= 0 && fstat(fd, &st) < 0) {
+                                       close(fd);
+                                       fd = -1;
+                               }
+                               if (fd < 0) {
+                                       pthread_spin_unlock(&lock);
+                                       return SEM_FAILED;
+                               }
+                               if ((map = find_map(st.st_ino))) {
+                                       pthread_spin_unlock(&lock);
+                                       close(fd);
+                                       if (map == (sem_t *)-1)
+                                               return SEM_FAILED;
                                        return map;
+                               }
                                break;
                        }
                }
+               if (!(flags & O_CREAT)) {
+                       pthread_spin_unlock(&lock);
+                       return SEM_FAILED;
+               }
                if (!linkat(AT_FDCWD, tmp, dir, name, 0)) {
                        fd = tfd;
                        close(dir);
@@ -109,8 +135,40 @@ sem_t *sem_open(const char *name, int flags, ...)
                        return SEM_FAILED;
                }
        }
+       if (fstat(fd, &st) < 0) {
+               pthread_spin_unlock(&lock);
+               close(fd);
+               return SEM_FAILED;
+       }
+       if (semcnt == SEM_NSEMS_MAX) {
+               pthread_spin_unlock(&lock);
+               close(fd);
+               errno = EMFILE;
+               return SEM_FAILED;
+       }
+       for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem; i++);
        map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        close(fd);
-       if (map == MAP_FAILED) return SEM_FAILED;
+       if (map == MAP_FAILED) {
+               pthread_spin_unlock(&lock);
+               return SEM_FAILED;
+       }
+       semtab[i].ino = st.st_ino;
+       semtab[i].sem = map;
+       semtab[i].refcnt = 1;
+       pthread_spin_unlock(&lock);
        return map;
 }
+
+int sem_close(sem_t *sem)
+{
+       int i;
+       pthread_spin_lock(&lock);
+       for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
+       if (!--semtab[i].refcnt) {
+               semtab[i].sem = 0;
+               semtab[i].ino = 0;
+       }
+       pthread_spin_unlock(&lock);
+       return munmap(sem, sizeof *sem);
+}