- added a check for broken RH5 include files. With the standard RH5
[kai/samba.git] / source3 / locking / shmem_sysv.c
index 793e508c7193b71d217c1e3132cfa6c328a3c7f5..b9d45949479ccd4f8de768adc1bf117042948020 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    Shared memory functions - SYSV IPC implementation
-   Copyright (C) Erik Devriendt 1996-1997
+   Copyright (C) Andrew Tridgell 1997-1998
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -33,7 +33,28 @@ extern int DEBUGLEVEL;
 #define SHM_MAGIC 0x53484100
 #define SHM_VERSION 2
 
+#ifdef SHM_R
 #define IPC_PERMS ((SHM_R | SHM_W) | (SHM_R>>3) | (SHM_R>>6))
+#else
+#define IPC_PERMS 0644
+#endif
+
+
+#ifdef SECURE_SEMAPHORES
+/* secure semaphores are slow because we have to do a become_root()
+   on every call! */
+#define SEMAPHORE_PERMS IPC_PERMS
+#else
+#define SEMAPHORE_PERMS 0666
+#endif
+
+#ifdef SEMMSL
+#define SHMEM_HASH_SIZE (SEMMSL-1)
+#else
+#define SHMEM_HASH_SIZE 63
+#endif
+
+#define MIN_SHM_SIZE 0x1000
 
 static int shm_id;
 static int sem_id;
@@ -63,62 +84,65 @@ struct ShmBlockDesc
 {
    int next;   /* offset of next block in the free list or
                   SHM_NOT_FREE_OFF when block in use */
-   int          size;   /* user size in BlockDescSize units */
+   int size;   /* user size in BlockDescSize units */
 };
 
-#define        EOList_Addr     (struct ShmBlockDesc *)( 0 )
-#define EOList_Off      (NULL_OFFSET)
+#define        EOList_Addr     NULL
+#define EOList_Off      (0)
 
 #define        CellSize        sizeof(struct ShmBlockDesc)
 
-/* HeaderSize aligned on 8 byte boundary */
-#define        AlignedHeaderSize       ((sizeof(struct ShmHeader)+7) & ~7)
-
-static struct ShmHeader *shm_header_p = (struct ShmHeader *)0;
+/* HeaderSize aligned on a 8 byte boundary */
+#define        AlignedHeaderSize ((sizeof(struct ShmHeader)+7) & ~7)
 
+static struct ShmHeader *shm_header_p = NULL;
 static BOOL shm_initialize_called = False;
 
 static int read_only;
 
-static BOOL sem_lock(int i)
+static BOOL sem_change(int i, int op)
 {
+#ifdef SECURE_SEMAPHORES
+       extern struct current_user current_user;
+       int became_root=0;
+#endif
        struct sembuf sb;
+       int ret;
+
        if (read_only) return True;
-       
-       sb.sem_num = i;
-       sb.sem_op = -1;
-       sb.sem_flg = SEM_UNDO;
 
-       if (semop(sem_id, &sb, 1) != 0) {
-               DEBUG(0,("ERROR: IPC lock failed on semaphore %d\n", i));
-               return False;
+#ifdef SECURE_SEMAPHORES
+       if (current_user.uid != 0) {
+               become_root(0);
+               became_root = 1;
        }
+#endif
 
-       return True;
-}
+       sb.sem_num = i;
+       sb.sem_op = op;
+       sb.sem_flg = 0;
 
-static BOOL sem_unlock(int i)
-{
-       struct sembuf sb;
-       if (read_only) return True;
+       ret = semop(sem_id, &sb, 1);
 
-       sb.sem_num = i;
-       sb.sem_op = 1;
-       sb.sem_flg = SEM_UNDO;
+       if (ret != 0) {
+               DEBUG(0,("ERROR: sem_change(%d,%d) failed (%s)\n", 
+                        i, op, strerror(errno)));
+       }
 
-       if (semop(sem_id, &sb, 1) != 0) {
-               DEBUG(0,("ERROR: IPC unlock failed on semaphore %d\n", i));
-               return False;
+#ifdef SECURE_SEMAPHORES
+       if (became_root) {
+               unbecome_root(0);
        }
+#endif
 
-       return True;
+       return ret == 0;
 }
 
 static BOOL global_lock(void)
 {
        global_lock_count++;
        if (global_lock_count == 1)
-               return sem_lock(0);
+               return sem_change(0, -1);
        return True;
 }
 
@@ -126,28 +150,28 @@ static BOOL global_unlock(void)
 {
        global_lock_count--;
        if (global_lock_count == 0)
-               return sem_unlock(0);
+               return sem_change(0, 1);
        return True;
 }
 
 static void *shm_offset2addr(int offset)
 {
-   if (offset == NULL_OFFSET )
+   if (offset == 0 )
       return (void *)(0);
    
    if (!shm_header_p)
       return (void *)(0);
    
-   return (void *)((char *)shm_header_p + offset );
+   return (void *)((char *)shm_header_p + offset);
 }
 
 static int shm_addr2offset(void *addr)
 {
    if (!addr)
-      return NULL_OFFSET;
+      return 0;
    
    if (!shm_header_p)
-      return NULL_OFFSET;
+      return 0;
    
    return (int)((char *)addr - (char *)shm_header_p);
 }
@@ -165,7 +189,7 @@ static int shm_alloc(int size)
        if (!shm_header_p) {
                /* not mapped yet */
                DEBUG(0,("ERROR shm_alloc : shmem not mapped\n"));
-               return NULL_OFFSET;
+               return 0;
        }
        
        global_lock();
@@ -173,13 +197,13 @@ static int shm_alloc(int size)
        if (!shm_header_p->consistent) {
                DEBUG(0,("ERROR shm_alloc : shmem not consistent\n"));
                global_unlock();
-               return NULL_OFFSET;
+               return 0;
        }
        
-       /* calculate    the number of cells */
-       num_cells = (size + CellSize -1) / CellSize;
+       /* calculate the number of cells */
+       num_cells = (size + (CellSize-1)) / CellSize;
        
-       /* set start    of scan */
+       /* set start of scan */
        prev_p = (struct ShmBlockDesc *)shm_offset2addr(shm_header_p->first_free_off);
        scanner_p =     prev_p ;
        
@@ -192,144 +216,50 @@ static int shm_alloc(int size)
        /* at this point scanner point to a block header or to the end of
           the list */
        if (scanner_p == EOList_Addr) {
-               DEBUG(0,("ERROR shm_alloc : alloc of %d bytes failed, no free space found\n",size));
+               DEBUG(0,("ERROR shm_alloc : alloc of %d bytes failed\n",size));
                global_unlock();
-               return (NULL_OFFSET);
+               return (0);
        }
    
        /* going to modify shared mem */
        shm_header_p->consistent = False;
        
        /* if we found a good one : scanner == the good one */
-       if (scanner_p->size <= num_cells + 2) {
-               /* there is no use in making a new one, it will be too small anyway 
-                *       we will link out scanner
-                */
-               if ( prev_p == scanner_p ) {
-                       shm_header_p->first_free_off = scanner_p->next ;
-               } else {
-                       prev_p->next = scanner_p->next ;
-               }
-               shm_header_p->statistics.cells_free -= scanner_p->size;
-               shm_header_p->statistics.cells_used += scanner_p->size;
-       } else {
+       if (scanner_p->size > num_cells + 2) {
                /* Make a new one */
                new_p = scanner_p + 1 + num_cells;
-               new_p->size = scanner_p->size - num_cells - 1;
+               new_p->size = scanner_p->size - (num_cells + 1);
                new_p->next = scanner_p->next;
                scanner_p->size = num_cells;
                scanner_p->next = shm_addr2offset(new_p);
-               
-               if (prev_p != scanner_p) {
-                       prev_p->next       = shm_addr2offset(new_p)  ;
-               } else {
-                       shm_header_p->first_free_off = shm_addr2offset(new_p);
-               }
-               shm_header_p->statistics.cells_free -= num_cells+1;
-               shm_header_p->statistics.cells_used += num_cells;
+
+               shm_header_p->statistics.cells_free -= 1;
                shm_header_p->statistics.cells_system += 1;
        }
 
-       result_offset = shm_addr2offset( &(scanner_p[1]) );
-       scanner_p->next =       SHM_NOT_FREE_OFF ;
+       /* take it from the free list */
+       if (prev_p == scanner_p) {
+               shm_header_p->first_free_off = scanner_p->next;
+       } else {
+               prev_p->next = scanner_p->next;
+       }
+       shm_header_p->statistics.cells_free -= scanner_p->size;
+       shm_header_p->statistics.cells_used += scanner_p->size;
+
+       result_offset = shm_addr2offset(&(scanner_p[1]));
+       scanner_p->next = SHM_NOT_FREE_OFF;
 
        /* end modification of shared mem */
        shm_header_p->consistent = True;
-       
-       DEBUG(6,("shm_alloc : request for %d bytes, allocated %d bytes at offset %d\n",size,scanner_p->size*CellSize,result_offset ));
 
        global_unlock();
+       
+       DEBUG(6,("shm_alloc : allocated %d bytes at offset %d\n",
+                size,result_offset));
+
        return result_offset;
 }   
 
-
-
-/* 
- * Function to create the hash table for the share mode entries. Called
- * when smb shared memory is global locked.
- */
-static BOOL shm_create_hash_table( unsigned int size )
-{
-       size *= sizeof(int);
-
-       global_lock();
-       shm_header_p->userdef_off = shm_alloc( size );
-
-       if(shm_header_p->userdef_off == NULL_OFFSET) {
-               DEBUG(0,("shm_create_hash_table: Failed to create hash table of size %d\n",size));
-               global_unlock();
-               return False;
-       }
-
-       /* Clear hash buckets. */
-       memset( shm_offset2addr(shm_header_p->userdef_off), '\0', size);
-       global_unlock();
-       return True;
-}
-
-static BOOL shm_validate_header(int size)
-{
-       if( !shm_header_p ) {
-               /* not mapped yet */
-               DEBUG(0,("ERROR shm_validate_header : shmem not mapped\n"));
-               return False;
-       }
-   
-       if(shm_header_p->shm_magic != SHM_MAGIC) {
-               DEBUG(0,("ERROR shm_validate_header : bad magic\n"));
-               return False;
-       }
-
-       if(shm_header_p->shm_version != SHM_VERSION) {
-               DEBUG(0,("ERROR shm_validate_header : bad version %X\n",shm_header_p->shm_version));
-               return False;
-       }
-   
-       if(shm_header_p->total_size != size) {
-               DEBUG(0,("ERROR shm_validate_header : shmem size mismatch (old = %d, new = %d)\n",shm_header_p->total_size,size));
-               return False;
-       }
-
-       if(!shm_header_p->consistent) {
-               DEBUG(0,("ERROR shm_validate_header : shmem not consistent\n"));
-               return False;
-       }
-       return True;
-}
-
-static BOOL shm_initialize(int size)
-{
-       struct ShmBlockDesc * first_free_block_p;
-       
-       DEBUG(5,("shm_initialize : initializing shmem file of size %d\n",size));
-   
-       if( !shm_header_p ) {
-               /* not mapped yet */
-               DEBUG(0,("ERROR shm_initialize : shmem not mapped\n"));
-               return False;
-       }
-   
-       shm_header_p->shm_magic = SHM_MAGIC;
-       shm_header_p->shm_version = SHM_VERSION;
-       shm_header_p->total_size = size;
-       shm_header_p->first_free_off = AlignedHeaderSize;
-       shm_header_p->userdef_off = NULL_OFFSET;
-       
-       first_free_block_p = (struct ShmBlockDesc *)shm_offset2addr(shm_header_p->first_free_off);
-       first_free_block_p->next = EOList_Off;
-       first_free_block_p->size = ( size - AlignedHeaderSize - CellSize ) / CellSize ;
-   
-       shm_header_p->statistics.cells_free = first_free_block_p->size;
-       shm_header_p->statistics.cells_used = 0;
-       shm_header_p->statistics.cells_system = 1;
-   
-       shm_header_p->consistent = True;
-   
-       shm_initialize_called = True;
-
-       return True;
-}
-   
 static void shm_solve_neighbors(struct ShmBlockDesc *head_p )
 {
        struct ShmBlockDesc *next_p;
@@ -340,9 +270,9 @@ static void shm_solve_neighbors(struct ShmBlockDesc *head_p )
        if ( head_p->next == EOList_Off ) return ;
    
        next_p = (struct ShmBlockDesc *)shm_offset2addr(head_p->next);
-       if ( ( head_p + head_p->size + 1 ) == next_p) {
-               head_p->size += next_p->size +1 ;       /* adapt size */
-               head_p->next = next_p->next       ; /* link out */
+       if ((head_p + head_p->size + 1) == next_p) {
+               head_p->size += next_p->size + 1; /* adapt size */
+               head_p->next = next_p->next; /* link out */
       
                shm_header_p->statistics.cells_free += 1;
                shm_header_p->statistics.cells_system -= 1;
@@ -350,21 +280,13 @@ static void shm_solve_neighbors(struct ShmBlockDesc *head_p )
 }
 
 
-
-
-static BOOL shm_close( void )
-{
-       return True;
-}
-
-
 static BOOL shm_free(int offset)
 {
        struct ShmBlockDesc *header_p; /* pointer to header of
-                                              block to free */
+                                         block to free */
        struct ShmBlockDesc *scanner_p; /* used to scan the list */
        struct ShmBlockDesc *prev_p; /* holds previous in the
-                                          list */
+                                       list */
    
        if (!shm_header_p) {
                /* not mapped yet */
@@ -392,14 +314,16 @@ static BOOL shm_free(int offset)
        /* find a place in the free_list to put the header in */
        
        /* set scanner and previous pointer to start of list */
-       prev_p = (struct ShmBlockDesc *)shm_offset2addr(shm_header_p->first_free_off);
+       prev_p = (struct ShmBlockDesc *)
+               shm_offset2addr(shm_header_p->first_free_off);
        scanner_p = prev_p ;
        
        while ((scanner_p != EOList_Addr) && 
               (scanner_p < header_p)) { 
                /* while we didn't scan past its position */
                prev_p = scanner_p ;
-               scanner_p = (struct ShmBlockDesc *)shm_offset2addr(scanner_p->next);
+               scanner_p = (struct ShmBlockDesc *)
+                       shm_offset2addr(scanner_p->next);
        }
        
        shm_header_p->consistent = False;
@@ -412,11 +336,12 @@ static BOOL shm_free(int offset)
                shm_header_p->statistics.cells_used -= header_p->size;
                
                /* we must free it at the beginning of the list */
-               shm_header_p->first_free_off = shm_addr2offset(header_p);                                                /*     set     the free_list_pointer to this block_header */
+               shm_header_p->first_free_off = shm_addr2offset(header_p);
+               /* set the free_list_pointer to this block_header */
                
                /* scanner is the one that was first in the list */
                header_p->next = shm_addr2offset(scanner_p);
-               shm_solve_neighbors( header_p ); /* if neighbors then link them */
+               shm_solve_neighbors(header_p); 
                
                shm_header_p->consistent = True;
        } else {
@@ -436,21 +361,119 @@ static BOOL shm_free(int offset)
 }
 
 
+/* 
+ * Function to create the hash table for the share mode entries. Called
+ * when smb shared memory is global locked.
+ */
+static BOOL shm_create_hash_table(unsigned int hash_entries)
+{
+       int size = hash_entries * sizeof(int);
+
+       global_lock();
+       shm_header_p->userdef_off = shm_alloc(size);
+
+       if(shm_header_p->userdef_off == 0) {
+               DEBUG(0,("shm_create_hash_table: Failed to create hash table of size %d\n",
+                        size));
+               global_unlock();
+               return False;
+       }
+
+       /* Clear hash buckets. */
+       memset(shm_offset2addr(shm_header_p->userdef_off), '\0', size);
+       global_unlock();
+       return True;
+}
+
+
+static BOOL shm_validate_header(int size)
+{
+       if(!shm_header_p) {
+               /* not mapped yet */
+               DEBUG(0,("ERROR shm_validate_header : shmem not mapped\n"));
+               return False;
+       }
+   
+       if(shm_header_p->shm_magic != SHM_MAGIC) {
+               DEBUG(0,("ERROR shm_validate_header : bad magic\n"));
+               return False;
+       }
+
+       if(shm_header_p->shm_version != SHM_VERSION) {
+               DEBUG(0,("ERROR shm_validate_header : bad version %X\n",
+                        shm_header_p->shm_version));
+               return False;
+       }
+   
+       if(shm_header_p->total_size != size) {
+               DEBUG(0,("ERROR shmem size mismatch (old = %d, new = %d)\n",
+                        shm_header_p->total_size,size));
+               return False;
+       }
+
+       if(!shm_header_p->consistent) {
+               DEBUG(0,("ERROR shmem not consistent\n"));
+               return False;
+       }
+       return True;
+}
+
+
+static BOOL shm_initialize(int size)
+{
+       struct ShmBlockDesc * first_free_block_p;
+       
+       DEBUG(5,("shm_initialize : initializing shmem size %d\n",size));
+   
+       if( !shm_header_p ) {
+               /* not mapped yet */
+               DEBUG(0,("ERROR shm_initialize : shmem not mapped\n"));
+               return False;
+       }
+   
+       shm_header_p->shm_magic = SHM_MAGIC;
+       shm_header_p->shm_version = SHM_VERSION;
+       shm_header_p->total_size = size;
+       shm_header_p->first_free_off = AlignedHeaderSize;
+       shm_header_p->userdef_off = 0;
+       
+       first_free_block_p = (struct ShmBlockDesc *)
+               shm_offset2addr(shm_header_p->first_free_off);
+       first_free_block_p->next = EOList_Off;
+       first_free_block_p->size = 
+               (size - (AlignedHeaderSize+CellSize))/CellSize;   
+       shm_header_p->statistics.cells_free = first_free_block_p->size;
+       shm_header_p->statistics.cells_used = 0;
+       shm_header_p->statistics.cells_system = 1;
+   
+       shm_header_p->consistent = True;
+   
+       shm_initialize_called = True;
+
+       return True;
+}
+   
+static BOOL shm_close( void )
+{
+       return True;
+}
+
+
 static int shm_get_userdef_off(void)
 {
    if (!shm_header_p)
-      return NULL_OFFSET;
+      return 0;
    else
       return shm_header_p->userdef_off;
 }
 
+
 /*******************************************************************
   Lock a particular hash bucket entry.
   ******************************************************************/
 static BOOL shm_lock_hash_entry(unsigned int entry)
 {
-       DEBUG(0,("hash lock %d\n", entry));
-       return sem_lock(entry+1);
+       return sem_change(entry+1, -1);
 }
 
 /*******************************************************************
@@ -458,8 +481,7 @@ static BOOL shm_lock_hash_entry(unsigned int entry)
   ******************************************************************/
 static BOOL shm_unlock_hash_entry(unsigned int entry)
 {
-       DEBUG(0,("hash unlock %d\n", entry));
-       return sem_unlock(entry+1);
+       return sem_change(entry+1, 1);
 }
 
 
@@ -478,11 +500,22 @@ static BOOL shm_get_usage(int *bytes_free,
 
        *bytes_free = shm_header_p->statistics.cells_free * CellSize;
        *bytes_used = shm_header_p->statistics.cells_used * CellSize;
-       *bytes_overhead = shm_header_p->statistics.cells_system * CellSize + AlignedHeaderSize;
+       *bytes_overhead = shm_header_p->statistics.cells_system * CellSize + 
+               AlignedHeaderSize;
        
        return True;
 }
 
+
+/*******************************************************************
+hash a number into a hash_entry
+  ******************************************************************/
+static unsigned shm_hash_size(void)
+{
+       return hash_size;
+}
+
+
 static struct shmem_ops shmops = {
        shm_close,
        shm_alloc,
@@ -493,12 +526,13 @@ static struct shmem_ops shmops = {
        shm_lock_hash_entry,
        shm_unlock_hash_entry,
        shm_get_usage,
+       shm_hash_size,
 };
 
 /*******************************************************************
   open the shared memory
   ******************************************************************/
-struct shmem_ops *sysv_shm_open(int size, int ronly)
+struct shmem_ops *sysv_shm_open(int ronly)
 {
        BOOL created_new = False;
        BOOL other_processes;
@@ -506,20 +540,37 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
        struct semid_ds sem_ds;
        union semun su;
        int i;
+       int pid;
+
+
+
+#ifdef LINUX
+       if (sizeof(shm_ds) == 52) {
+               DEBUG(0,("WARNING: You probably have a broken set of glibc2 include files - disabling sysv shared memory\n"));
+               return NULL;
+       }
+#endif
 
        read_only = ronly;
 
-       shm_size = size;
+       shm_size = lp_shmem_size();
 
-       DEBUG(4,("Trying sysv shmem open of size %d\n", size));
+       DEBUG(4,("Trying sysv shmem open of size %d\n", shm_size));
 
        /* first the semaphore */
        sem_id = semget(SEMAPHORE_KEY, 0, 0);
        if (sem_id == -1) {
                if (read_only) return NULL;
 
-               sem_id = semget(SEMAPHORE_KEY, lp_shmem_hash_size()+1, 
-                               IPC_CREAT | IPC_EXCL | IPC_PERMS);
+               hash_size = SHMEM_HASH_SIZE;
+
+               while (hash_size > 1) {
+                       sem_id = semget(SEMAPHORE_KEY, hash_size+1, 
+                                       IPC_CREAT|IPC_EXCL| SEMAPHORE_PERMS);
+                       if (sem_id != -1 || 
+                           (errno != EINVAL && errno != ENOSPC)) break;
+                       hash_size -= 5;
+               }
 
                if (sem_id == -1) {
                        DEBUG(0,("Can't create or use semaphore %s\n", 
@@ -528,7 +579,7 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
 
                if (sem_id != -1) {
                        su.val = 1;
-                       for (i=0;i<lp_shmem_hash_size()+1;i++) {
+                       for (i=0;i<hash_size+1;i++) {
                                if (semctl(sem_id, i, SETVAL, su) != 0) {
                                        DEBUG(1,("Failed to init semaphore %d\n", i));
                                }
@@ -548,13 +599,47 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
        if (semctl(sem_id, 0, IPC_STAT, su) != 0) {
                DEBUG(0,("ERROR shm_open : can't IPC_STAT\n"));
        }
-       hash_size = sem_ds.sem_nsems;
-       if (hash_size != lp_shmem_hash_size()+1) {
-               DEBUG(0,("WARNING: nsems=%d\n", hash_size));
+       hash_size = sem_ds.sem_nsems-1;
+
+       if (!read_only) {
+               if (sem_ds.sem_perm.cuid != 0 || sem_ds.sem_perm.cgid != 0) {
+                       DEBUG(0,("ERROR: root did not create the semaphore\n"));
+                       return NULL;
+               }
+
+               if (semctl(sem_id, 0, GETVAL, su) == 0 &&
+                   !process_exists((pid=semctl(sem_id, 0, GETPID, su)))) {
+                       DEBUG(0,("WARNING: clearing global IPC lock set by dead process %d\n",
+                                pid));
+                       su.val = 1;
+                       if (semctl(sem_id, 0, SETVAL, su) != 0) {
+                               DEBUG(0,("ERROR: Failed to clear global lock\n"));
+                       }
+               }
+
+               sem_ds.sem_perm.mode = SEMAPHORE_PERMS;
+               if (semctl(sem_id, 0, IPC_SET, su) != 0) {
+                       DEBUG(0,("ERROR shm_open : can't IPC_SET\n"));
+               }
        }
+
+       
        
        if (!global_lock())
                return NULL;
+
+
+       for (i=1;i<hash_size+1;i++) {
+               if (semctl(sem_id, i, GETVAL, su) == 0 && 
+                   !process_exists((pid=semctl(sem_id, i, GETPID, su)))) {
+                       DEBUG(1,("WARNING: clearing IPC lock %d set by dead process %d\n", 
+                                i, pid));
+                       su.val = 1;
+                       if (semctl(sem_id, i, SETVAL, su) != 0) {
+                               DEBUG(0,("ERROR: Failed to clear IPC lock %d\n", i));
+                       }
+               }
+       }
        
        /* try to use an existing key */
        shm_id = shmget(SHMEM_KEY, shm_size, 0);
@@ -562,7 +647,13 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
        /* if that failed then create one */
        if (shm_id == -1) {
                if (read_only) return NULL;
-               shm_id = shmget(SHMEM_KEY, shm_size, IPC_CREAT | IPC_EXCL);
+               while (shm_size > MIN_SHM_SIZE) {
+                       shm_id = shmget(SHMEM_KEY, shm_size, 
+                                       IPC_CREAT | IPC_EXCL | IPC_PERMS);
+                       if (shm_id != -1 || 
+                           (errno != EINVAL && errno != ENOSPC)) break;
+                       shm_size *= 0.8;
+               }
                created_new = (shm_id != -1);
        }
        
@@ -588,19 +679,23 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
                DEBUG(0,("ERROR shm_open : can't IPC_STAT\n"));
        }
 
-       /* set the permissions */
        if (!read_only) {
-               shm_ds.shm_perm.mode = IPC_PERMS;
-               shmctl(shm_id, IPC_SET, &shm_ds);
+               if (shm_ds.shm_perm.cuid != 0 || shm_ds.shm_perm.cgid != 0) {
+                       DEBUG(0,("ERROR: root did not create the shmem\n"));
+                       global_unlock();
+                       return NULL;
+               }
        }
 
+       shm_size = shm_ds.shm_segsz;
+
        other_processes = (shm_ds.shm_nattch > 1);
 
        if (!read_only && !other_processes) {
                memset((char *)shm_header_p, 0, shm_size);
                shm_initialize(shm_size);
-               shm_create_hash_table(lp_shmem_hash_size());
-               DEBUG(1,("Initialised IPC area of size %d\n", shm_size));
+               shm_create_hash_table(hash_size);
+               DEBUG(3,("Initialised IPC area of size %d\n", shm_size));
        } else if (!shm_validate_header(shm_size)) {
                /* existing file is corrupt, samba admin should remove
                    it by hand */