fix dup3 ignoring all flags but O_CLOEXEC on archs with SYS_dup2 syscall
[musl] / src / thread / sem_open.c
index 08f98c2..0ad29de 100644 (file)
 #include <sys/stat.h>
 #include <stdlib.h>
 #include <pthread.h>
-#include "libc.h"
+#include "lock.h"
+#include "fork_impl.h"
 
-char *__shm_mapname(const char *, char *);
+#define malloc __libc_malloc
+#define calloc __libc_calloc
+#define realloc undef
+#define free undef
 
 static struct {
        ino_t ino;
        sem_t *sem;
        int refcnt;
 } *semtab;
-static int lock[2];
+static volatile int lock[1];
+volatile int *const __sem_open_lockptr = lock;
 
 #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
 
@@ -29,7 +34,7 @@ sem_t *sem_open(const char *name, int flags, ...)
        va_list ap;
        mode_t mode;
        unsigned value;
-       int fd, i, e, slot, first=1, cnt;
+       int fd, i, e, slot, first=1, cnt, cs;
        sem_t newsem;
        void *map;
        char tmp[64];
@@ -67,11 +72,13 @@ sem_t *sem_open(const char *name, int flags, ...)
 
        flags &= (O_CREAT|O_EXCL);
 
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
        /* Early failure check for exclusive open; otherwise the case
         * where the semaphore already exists is expensive. */
        if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
                errno = EEXIST;
-               return SEM_FAILED;
+               goto fail;
        }
 
        for (;;) {
@@ -80,19 +87,19 @@ sem_t *sem_open(const char *name, int flags, ...)
                if (flags != (O_CREAT|O_EXCL)) {
                        fd = open(name, FLAGS);
                        if (fd >= 0) {
-                               if ((map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED ||
-                                   fstat(fd, &st) < 0) {
+                               if (fstat(fd, &st) < 0 ||
+                                   (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
                                        close(fd);
-                                       return SEM_FAILED;
+                                       goto fail;
                                }
                                close(fd);
                                break;
                        }
                        if (errno != ENOENT)
-                               return SEM_FAILED;
+                               goto fail;
                }
                if (!(flags & O_CREAT))
-                       return SEM_FAILED;
+                       goto fail;
                if (first) {
                        first = 0;
                        va_start(ap, flags);
@@ -101,7 +108,7 @@ sem_t *sem_open(const char *name, int flags, ...)
                        va_end(ap);
                        if (value > SEM_VALUE_MAX) {
                                errno = EINVAL;
-                               return SEM_FAILED;
+                               goto fail;
                        }
                        sem_init(&newsem, 1, value);
                }
@@ -112,23 +119,24 @@ sem_t *sem_open(const char *name, int flags, ...)
                fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
                if (fd < 0) {
                        if (errno == EEXIST) continue;
-                       return SEM_FAILED;
+                       goto fail;
                }
                if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
                    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
                        close(fd);
                        unlink(tmp);
-                       return SEM_FAILED;
+                       goto fail;
                }
                close(fd);
-               if (link(tmp, name) == 0) break;
-               e = errno;
+               e = link(tmp, name) ? errno : 0;
                unlink(tmp);
+               if (!e) break;
+               munmap(map, sizeof(sem_t));
                /* Failure is only fatal when doing an exclusive open;
                 * otherwise, next iteration will try to open the
                 * existing file. */
                if (e != EEXIST || flags == (O_CREAT|O_EXCL))
-                       return SEM_FAILED;
+                       goto fail;
        }
 
        /* See if the newly mapped semaphore is already mapped. If
@@ -138,16 +146,23 @@ sem_t *sem_open(const char *name, int flags, ...)
        for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
        if (i<SEM_NSEMS_MAX) {
                munmap(map, sizeof(sem_t));
-               semtab[i].refcnt++;
-               UNLOCK(lock);
-               return semtab[i].sem;
+               semtab[slot].sem = 0;
+               slot = i;
+               map = semtab[i].sem;
        }
-       semtab[slot].refcnt = 1;
+       semtab[slot].refcnt++;
        semtab[slot].sem = map;
        semtab[slot].ino = st.st_ino;
        UNLOCK(lock);
-
+       pthread_setcancelstate(cs, 0);
        return map;
+
+fail:
+       pthread_setcancelstate(cs, 0);
+       LOCK(lock);
+       semtab[slot].sem = 0;
+       UNLOCK(lock);
+       return SEM_FAILED;
 }
 
 int sem_close(sem_t *sem)
@@ -155,10 +170,12 @@ int sem_close(sem_t *sem)
        int i;
        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;
+       if (--semtab[i].refcnt) {
+               UNLOCK(lock);
+               return 0;
        }
+       semtab[i].sem = 0;
+       semtab[i].ino = 0;
        UNLOCK(lock);
        munmap(sem, sizeof *sem);
        return 0;