SYSV IPC implementation of fast share modes.
[kai/samba.git] / source3 / locking / shmem.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Shared memory functions
5    Copyright (C) Erik Devriendt 1996-1997
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22
23 #include "includes.h"
24
25
26 #ifdef FAST_SHARE_MODES
27
28
29 extern int DEBUGLEVEL;
30
31
32 #define SMB_SHM_MAGIC 0x53484100
33 /* = "SHM" in hex */
34
35 #define SMB_SHM_VERSION 2
36
37 /* we need world read for smbstatus to function correctly */
38 #ifdef SECURE_SHARE_MODES
39 #define SHM_FILE_MODE 0600
40 #else
41 #define SHM_FILE_MODE 0644
42 #endif
43
44 /* WARNING : offsets are used because mmap() does not guarantee that all processes have the 
45    shared memory mapped to the same address */
46
47 struct SmbShmHeader
48 {
49    int smb_shm_magic;
50    int smb_shm_version;
51    int total_size;      /* in bytes */
52    BOOL consistent;
53    int first_free_off;
54    int userdef_off;    /* a userdefined offset. can be used to store root of tree or list */
55    struct {             /* a cell is a range of bytes of sizeof(struct SmbShmBlockDesc) size */
56       int cells_free;
57       int cells_used;
58       int cells_system; /* number of cells used as allocated block descriptors */
59    } statistics;
60 };
61
62 #define SMB_SHM_NOT_FREE_OFF (-1)
63 struct SmbShmBlockDesc
64 {
65    int next;    /* offset of next block in the free list or SMB_SHM_NOT_FREE_OFF when block in use  */
66    int          size;   /* user size in BlockDescSize units */
67 };
68
69 #define EOList_Addr     (struct SmbShmBlockDesc *)( 0 )
70 #define EOList_Off      (NULL_OFFSET)
71
72 #define CellSize        sizeof(struct SmbShmBlockDesc)
73
74 /* HeaderSize aligned on 8 byte boundary */
75 #define AlignedHeaderSize       ((sizeof(struct SmbShmHeader)+7) & ~7)
76
77 static int  smb_shm_fd = -1;
78 static pstring smb_shm_processreg_name = "";
79
80 static struct SmbShmHeader *smb_shm_header_p = (struct SmbShmHeader *)0;
81 static int smb_shm_times_locked = 0;
82
83 static BOOL smb_shm_initialize_called = False;
84
85 static int read_only;
86
87 static BOOL smb_shm_global_lock(void)
88 {
89    if (smb_shm_fd < 0)
90    {
91       DEBUG(0,("ERROR smb_shm_global_lock : bad smb_shm_fd (%d)\n",smb_shm_fd));
92       return False;
93    }
94    
95    smb_shm_times_locked++;
96    
97    if(smb_shm_times_locked > 1)
98    {
99       DEBUG(5,("smb_shm_global_lock : locked %d times\n",smb_shm_times_locked));
100       return True;
101    }
102
103    if (read_only)
104            return True;
105    
106    /* Do an exclusive wait lock on the first byte of the file */
107    if (fcntl_lock(smb_shm_fd, F_SETLKW, 0, 1, F_WRLCK) == False)
108    {
109       DEBUG(0,("ERROR smb_shm_global_lock : fcntl_lock failed with code %s\n",strerror(errno)));
110       smb_shm_times_locked--;
111       return False;
112    }
113    
114    return True;
115    
116 }
117
118 static BOOL smb_shm_global_unlock(void)
119 {
120    if (smb_shm_fd < 0)
121    {
122       DEBUG(0,("ERROR smb_shm_global_unlock : bad smb_shm_fd (%d)\n",smb_shm_fd));
123       return False;
124    }
125    
126    if(smb_shm_times_locked == 0)
127    {
128       DEBUG(0,("ERROR smb_shm_global_unlock : shmem not locked\n",smb_shm_fd));
129       return False;
130    }
131    
132    smb_shm_times_locked--;
133    
134    if(smb_shm_times_locked > 0)
135    {
136       DEBUG(5,("smb_shm_global_unlock : still locked %d times\n",smb_shm_times_locked));
137       return True;
138    }
139
140    if (read_only)
141            return True;
142    
143    /* Do a wait unlock on the first byte of the file */
144    if (fcntl_lock(smb_shm_fd, F_SETLKW, 0, 1, F_UNLCK) == False)
145    {
146       DEBUG(0,("ERROR smb_shm_global_unlock : fcntl_lock failed with code %s\n",strerror(errno)));
147       smb_shm_times_locked++;
148       return False;
149    }
150    
151    return True;
152    
153 }
154
155
156 static void *smb_shm_offset2addr(int offset)
157 {
158    if (offset == NULL_OFFSET )
159       return (void *)(0);
160    
161    if (!smb_shm_header_p)
162       return (void *)(0);
163    
164    return (void *)((char *)smb_shm_header_p + offset );
165 }
166
167 static int smb_shm_addr2offset(void *addr)
168 {
169    if (!addr)
170       return NULL_OFFSET;
171    
172    if (!smb_shm_header_p)
173       return NULL_OFFSET;
174    
175    return (int)((char *)addr - (char *)smb_shm_header_p);
176 }
177
178
179
180 static int smb_shm_alloc(int size)
181 {
182    unsigned num_cells ;
183    struct SmbShmBlockDesc *scanner_p;
184    struct SmbShmBlockDesc *prev_p;
185    struct SmbShmBlockDesc *new_p;
186    int result_offset;
187    
188    
189    if( !smb_shm_header_p )
190    {
191       /* not mapped yet */
192       DEBUG(0,("ERROR smb_shm_alloc : shmem not mapped\n"));
193       return NULL_OFFSET;
194    }
195    
196    smb_shm_global_lock();
197
198    if( !smb_shm_header_p->consistent)
199    {
200       DEBUG(0,("ERROR smb_shm_alloc : shmem not consistent\n"));
201       smb_shm_global_unlock();
202       return NULL_OFFSET;
203    }
204    
205    
206    /* calculate the number of cells */
207    num_cells = (size + CellSize -1) / CellSize;
208
209    /* set start of scan */
210    prev_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(smb_shm_header_p->first_free_off);
211    scanner_p =  prev_p ;
212    
213    /* scan the free list to find a matching free space */
214    while ( ( scanner_p != EOList_Addr ) && ( scanner_p->size < num_cells ) )
215    {
216       prev_p = scanner_p;
217       scanner_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(scanner_p->next);
218    }
219    
220    /* at this point scanner point to a block header or to the end of the list */
221    if ( scanner_p == EOList_Addr )      
222    {
223       DEBUG(0,("ERROR smb_shm_alloc : alloc of %d bytes failed, no free space found\n",size));
224       smb_shm_global_unlock();
225       return (NULL_OFFSET);
226    }
227    
228    /* going to modify shared mem */
229    smb_shm_header_p->consistent = False;
230    
231    /* if we found a good one : scanner == the good one */
232    if ( scanner_p->size <= num_cells + 2 )
233    {
234       /* there is no use in making a new one, it will be too small anyway 
235       *  we will link out scanner
236       */
237       if ( prev_p == scanner_p )
238       {
239          smb_shm_header_p->first_free_off = scanner_p->next ;
240       }
241       else
242       {
243          prev_p->next = scanner_p->next ;
244       }
245       smb_shm_header_p->statistics.cells_free -= scanner_p->size;
246       smb_shm_header_p->statistics.cells_used += scanner_p->size;
247    }
248    else
249    {
250       /* Make a new one */
251       new_p = scanner_p + 1 + num_cells;
252       new_p->size = scanner_p->size - num_cells - 1;
253       new_p->next = scanner_p->next;
254       scanner_p->size = num_cells;
255       scanner_p->next = smb_shm_addr2offset(new_p);
256       
257       if ( prev_p       != scanner_p )
258       {
259          prev_p->next      = smb_shm_addr2offset(new_p)  ;
260       }
261       else
262       {
263          smb_shm_header_p->first_free_off = smb_shm_addr2offset(new_p)  ;
264       }
265       smb_shm_header_p->statistics.cells_free -= num_cells+1;
266       smb_shm_header_p->statistics.cells_used += num_cells;
267       smb_shm_header_p->statistics.cells_system += 1;
268    }
269
270    result_offset = smb_shm_addr2offset( &(scanner_p[1]) );
271    scanner_p->next =    SMB_SHM_NOT_FREE_OFF ;
272
273    /* end modification of shared mem */
274    smb_shm_header_p->consistent = True;
275
276    DEBUG(6,("smb_shm_alloc : request for %d bytes, allocated %d bytes at offset %d\n",size,scanner_p->size*CellSize,result_offset ));
277
278    smb_shm_global_unlock();
279    return ( result_offset );
280 }   
281
282
283
284
285 /* 
286  * Function to create the hash table for the share mode entries. Called
287  * when smb shared memory is global locked.
288  */
289 static BOOL smb_shm_create_hash_table( unsigned int size )
290 {
291   size *= sizeof(int);
292
293   smb_shm_global_lock();
294   smb_shm_header_p->userdef_off = smb_shm_alloc( size );
295
296   if(smb_shm_header_p->userdef_off == NULL_OFFSET)
297     {
298       DEBUG(0,("smb_shm_create_hash_table: Failed to create hash table of size %d\n",size));
299       smb_shm_global_unlock();
300       return False;
301     }
302
303   /* Clear hash buckets. */
304   memset( smb_shm_offset2addr(smb_shm_header_p->userdef_off), '\0', size);
305   smb_shm_global_unlock();
306   return True;
307 }
308
309 static BOOL smb_shm_register_process(char *processreg_file, pid_t pid, BOOL *other_processes)
310 {
311    int smb_shm_processes_fd = -1;
312    int nb_read;
313    pid_t other_pid;
314    int seek_back = -((int)sizeof(other_pid));
315    int free_slot = -1;
316    int erased_slot;   
317    
318    smb_shm_processes_fd = open(processreg_file, 
319                                read_only?O_RDONLY:(O_RDWR|O_CREAT), 
320                                SHM_FILE_MODE);
321
322    if ( smb_shm_processes_fd < 0 )
323    {
324       DEBUG(0,("ERROR smb_shm_register_process : processreg_file open failed with code %s\n",strerror(errno)));
325       return False;
326    }
327    
328    *other_processes = False;
329    
330    while ((nb_read = read(smb_shm_processes_fd, &other_pid, sizeof(other_pid))) > 0)
331    {
332       if(other_pid)
333       {
334          if(process_exists(other_pid))
335             *other_processes = True;
336          else
337          {
338             /* erase old pid */
339             DEBUG(5,("smb_shm_register_process : erasing stale record for pid %d (seek_back = %d)\n",
340                       other_pid, seek_back));
341             other_pid = (pid_t)0;
342             erased_slot = lseek(smb_shm_processes_fd, seek_back, SEEK_CUR);
343             write(smb_shm_processes_fd, &other_pid, sizeof(other_pid));
344             if(free_slot < 0)
345                free_slot = erased_slot;
346          }
347       }
348       else 
349          if(free_slot < 0)
350             free_slot = lseek(smb_shm_processes_fd, seek_back, SEEK_CUR);
351    }
352    if (nb_read < 0)
353    {
354       DEBUG(0,("ERROR smb_shm_register_process : processreg_file read failed with code %s\n",strerror(errno)));
355       close(smb_shm_processes_fd);
356       return False;
357    }
358    
359    if(free_slot < 0)
360       free_slot = lseek(smb_shm_processes_fd, 0, SEEK_END);
361
362    DEBUG(5,("smb_shm_register_process : writing record for pid %d at offset %d\n",pid,free_slot));
363    lseek(smb_shm_processes_fd, free_slot, SEEK_SET);
364    if(write(smb_shm_processes_fd, &pid, sizeof(pid)) < 0)
365    {
366       DEBUG(0,("ERROR smb_shm_register_process : processreg_file write failed with code %s\n",strerror(errno)));
367       close(smb_shm_processes_fd);
368       return False;
369    }
370
371    close(smb_shm_processes_fd);
372
373    return True;
374 }
375
376 static BOOL smb_shm_unregister_process(char *processreg_file, pid_t pid)
377 {
378    int smb_shm_processes_fd = -1;
379    int nb_read;
380    pid_t other_pid;
381    int seek_back = -((int)sizeof(other_pid));
382    int erased_slot;
383    BOOL found = False;
384    
385    
386    smb_shm_processes_fd = open(processreg_file, O_RDWR);
387    if ( smb_shm_processes_fd < 0 )
388    {
389       DEBUG(0,("ERROR smb_shm_unregister_process : processreg_file open failed with code %s\n",strerror(errno)));
390       return False;
391    }
392    
393    while ((nb_read = read(smb_shm_processes_fd, &other_pid, sizeof(other_pid))) > 0)
394    {
395       DEBUG(5,("smb_shm_unregister_process : read record for pid %d\n",other_pid));
396       if(other_pid == pid)
397       {
398          /* erase pid */
399          DEBUG(5,("smb_shm_unregister_process : erasing record for pid %d (seek_val = %d)\n",
400                      other_pid, seek_back));
401          other_pid = (pid_t)0;
402          erased_slot = lseek(smb_shm_processes_fd, seek_back, SEEK_CUR);
403          if(write(smb_shm_processes_fd, &other_pid, sizeof(other_pid)) < 0)
404          {
405             DEBUG(0,("ERROR smb_shm_unregister_process : processreg_file write failed with code %s\n",strerror(errno)));
406             close(smb_shm_processes_fd);
407             return False;
408          }
409          
410          found = True;
411          break;
412       }
413    }
414    if (nb_read < 0)
415    {
416       DEBUG(0,("ERROR smb_shm_unregister_process : processreg_file read failed with code %s\n",strerror(errno)));
417       close(smb_shm_processes_fd);
418       return False;
419    }
420    
421    if(!found)
422    {
423       DEBUG(0,("ERROR smb_shm_unregister_process : couldn't find pid %d in file %s\n",pid,processreg_file));
424       close(smb_shm_processes_fd);
425       return False;
426    }
427       
428    
429    close(smb_shm_processes_fd);
430
431    return True;
432 }
433
434
435 static BOOL smb_shm_validate_header(int size)
436 {
437    if( !smb_shm_header_p )
438    {
439       /* not mapped yet */
440       DEBUG(0,("ERROR smb_shm_validate_header : shmem not mapped\n"));
441       return False;
442    }
443    
444    if(smb_shm_header_p->smb_shm_magic != SMB_SHM_MAGIC)
445    {
446       DEBUG(0,("ERROR smb_shm_validate_header : bad magic\n"));
447       return False;
448    }
449    if(smb_shm_header_p->smb_shm_version != SMB_SHM_VERSION)
450    {
451       DEBUG(0,("ERROR smb_shm_validate_header : bad version %X\n",smb_shm_header_p->smb_shm_version));
452       return False;
453    }
454    
455    if(smb_shm_header_p->total_size != size)
456    {
457       DEBUG(0,("ERROR smb_shm_validate_header : shmem size mismatch (old = %d, new = %d)\n",smb_shm_header_p->total_size,size));
458       return False;
459    }
460
461    if(!smb_shm_header_p->consistent)
462    {
463       DEBUG(0,("ERROR smb_shm_validate_header : shmem not consistent\n"));
464       return False;
465    }
466    return True;
467 }
468
469 static BOOL smb_shm_initialize(int size)
470 {
471    struct SmbShmBlockDesc * first_free_block_p;
472    
473    DEBUG(5,("smb_shm_initialize : initializing shmem file of size %d\n",size));
474    
475    if( !smb_shm_header_p )
476    {
477       /* not mapped yet */
478       DEBUG(0,("ERROR smb_shm_initialize : shmem not mapped\n"));
479       return False;
480    }
481    
482    smb_shm_header_p->smb_shm_magic = SMB_SHM_MAGIC;
483    smb_shm_header_p->smb_shm_version = SMB_SHM_VERSION;
484    smb_shm_header_p->total_size = size;
485    smb_shm_header_p->first_free_off = AlignedHeaderSize;
486    smb_shm_header_p->userdef_off = NULL_OFFSET;
487    
488    first_free_block_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(smb_shm_header_p->first_free_off);
489    first_free_block_p->next = EOList_Off;
490    first_free_block_p->size = ( size - AlignedHeaderSize - CellSize ) / CellSize ;
491    
492    smb_shm_header_p->statistics.cells_free = first_free_block_p->size;
493    smb_shm_header_p->statistics.cells_used = 0;
494    smb_shm_header_p->statistics.cells_system = 1;
495    
496    smb_shm_header_p->consistent = True;
497    
498    smb_shm_initialize_called = True;
499
500    return True;
501 }
502    
503 static void smb_shm_solve_neighbors(struct SmbShmBlockDesc *head_p )
504 {
505    struct SmbShmBlockDesc *next_p;
506    
507    /* Check if head_p and head_p->next are neighbors and if so join them */
508    if ( head_p == EOList_Addr ) return ;
509    if ( head_p->next == EOList_Off ) return ;
510    
511    next_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(head_p->next);
512    if ( ( head_p + head_p->size + 1 ) == next_p)
513    {
514       head_p->size += next_p->size +1 ; /* adapt size */
515       head_p->next = next_p->next         ; /* link out */
516       
517       smb_shm_header_p->statistics.cells_free += 1;
518       smb_shm_header_p->statistics.cells_system -= 1;
519    }
520 }
521
522
523
524 static BOOL smb_shm_close( void )
525 {
526    
527    if(smb_shm_initialize_called == False)
528      return True;
529
530    DEBUG(5,("smb_shm_close\n"));
531    if(smb_shm_times_locked > 0)
532       DEBUG(0,("WARNING smb_shm_close : shmem was still locked %d times\n",smb_shm_times_locked));;
533    if ((smb_shm_header_p != NULL) && 
534               (munmap((caddr_t)smb_shm_header_p, smb_shm_header_p->total_size) < 0))
535    {
536       DEBUG(0,("ERROR smb_shm_close : munmap failed with code %s\n",strerror(errno)));
537    }
538
539    smb_shm_global_lock();
540    DEBUG(5,("calling smb_shm_unregister_process(%s, %d)\n", smb_shm_processreg_name, getpid()));
541    smb_shm_unregister_process(smb_shm_processreg_name, getpid());
542    smb_shm_global_unlock();
543    
544    close(smb_shm_fd);
545    
546    smb_shm_fd = -1;
547    smb_shm_processreg_name[0] = '\0';
548
549    smb_shm_header_p = (struct SmbShmHeader *)0;
550    smb_shm_times_locked = 0;
551    
552    return True;
553 }
554
555
556 static BOOL smb_shm_free(int offset)
557 {
558    struct SmbShmBlockDesc *header_p  ; /*       pointer to header of block to free */
559    struct SmbShmBlockDesc *scanner_p ; /*       used to scan the list                      */
560    struct SmbShmBlockDesc *prev_p          ; /* holds previous in the list                 */
561    
562    if( !smb_shm_header_p )
563    {
564       /* not mapped yet */
565       DEBUG(0,("ERROR smb_shm_free : shmem not mapped\n"));
566       return False;
567    }
568    
569    smb_shm_global_lock();
570
571    if( !smb_shm_header_p->consistent)
572    {
573       DEBUG(0,("ERROR smb_shm_free : shmem not consistent\n"));
574       smb_shm_global_unlock();
575       return False;
576    }
577    
578    header_p = ( (struct SmbShmBlockDesc *)smb_shm_offset2addr(offset) - 1); /* make pointer to header of block */
579
580    if (header_p->next != SMB_SHM_NOT_FREE_OFF)
581    {
582       DEBUG(0,("ERROR smb_shm_free : bad offset (%d)\n",offset));
583       smb_shm_global_unlock();
584       return False;
585    }
586    
587    /* find a place in the free_list to put the header in */
588    
589    /* set scanner and previous pointer to start of list */
590    prev_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(smb_shm_header_p->first_free_off);
591    scanner_p = prev_p ;
592    
593    while ( ( scanner_p != EOList_Addr) && (scanner_p < header_p) ) /* while we didn't scan past its position */
594    {
595       prev_p = scanner_p ;
596       scanner_p = (struct SmbShmBlockDesc *)smb_shm_offset2addr(scanner_p->next);
597    }
598    
599    smb_shm_header_p->consistent = False;
600    
601    DEBUG(6,("smb_shm_free : freeing %d bytes at offset %d\n",header_p->size*CellSize,offset));
602
603    if ( scanner_p == prev_p )
604    {
605       smb_shm_header_p->statistics.cells_free += header_p->size;
606       smb_shm_header_p->statistics.cells_used -= header_p->size;
607
608       /* we must free it at the beginning of the list */
609       smb_shm_header_p->first_free_off = smb_shm_addr2offset(header_p);                                          /*     set     the free_list_pointer to this block_header */
610
611       /* scanner is the one that was first in the list */
612       header_p->next = smb_shm_addr2offset(scanner_p);
613       smb_shm_solve_neighbors( header_p ); /* if neighbors then link them */
614       
615       smb_shm_header_p->consistent = True;
616       smb_shm_global_unlock();
617       return True;
618    } 
619    else
620    {
621       smb_shm_header_p->statistics.cells_free += header_p->size;
622       smb_shm_header_p->statistics.cells_used -= header_p->size;
623
624       prev_p->next = smb_shm_addr2offset(header_p);
625       header_p->next = smb_shm_addr2offset(scanner_p);
626       smb_shm_solve_neighbors(header_p) ;
627       smb_shm_solve_neighbors(prev_p) ;
628
629       smb_shm_header_p->consistent = True;
630       smb_shm_global_unlock();
631       return True;
632    }
633 }
634
635 static int smb_shm_get_userdef_off(void)
636 {
637    if (!smb_shm_header_p)
638       return NULL_OFFSET;
639    else
640       return smb_shm_header_p->userdef_off;
641 }
642
643 /*******************************************************************
644   Lock a particular hash bucket entry.
645   ******************************************************************/
646 static BOOL smb_shm_lock_hash_entry( unsigned int entry)
647 {
648   int start = (smb_shm_header_p->userdef_off + (entry * sizeof(int)));
649
650   if (smb_shm_fd < 0)
651     {
652       DEBUG(0,("ERROR smb_shm_lock_hash_entry : bad smb_shm_fd (%d)\n",smb_shm_fd));
653       return False;
654     }
655
656   if(entry >= lp_shmem_hash_size())
657     {
658       DEBUG(0,("ERROR smb_shm_lock_hash_entry : hash entry size too big (%d)\n", entry));
659       return False;
660     }
661   
662   /* Do an exclusive wait lock on the 4 byte region mapping into this entry  */
663   if (fcntl_lock(smb_shm_fd, F_SETLKW, start, sizeof(int), F_WRLCK) == False)
664     {
665       DEBUG(0,("ERROR smb_shm_lock_hash_entry : fcntl_lock failed with code %s\n",strerror(errno)));
666       return False;
667     }
668   
669   DEBUG(9,("smb_shm_lock_hash_entry: locked hash bucket %d\n", entry)); 
670   return True;
671 }
672
673 /*******************************************************************
674   Unlock a particular hash bucket entry.
675   ******************************************************************/
676 static BOOL smb_shm_unlock_hash_entry( unsigned int entry )
677 {
678   int start = (smb_shm_header_p->userdef_off + (entry * sizeof(int)));
679
680   if (smb_shm_fd < 0)
681     {
682       DEBUG(0,("ERROR smb_shm_unlock_hash_entry : bad smb_shm_fd (%d)\n",smb_shm_fd));
683       return False;
684     }
685    
686   if(entry >= lp_shmem_hash_size())
687     {
688       DEBUG(0,("ERROR smb_shm_unlock_hash_entry : hash entry size too big (%d)\n", entry));
689       return False;
690     }
691
692   /* Do a wait lock on the 4 byte region mapping into this entry  */
693   if (fcntl_lock(smb_shm_fd, F_SETLKW, start, sizeof(int), F_UNLCK) == False)
694     {
695       DEBUG(0,("ERROR smb_shm_unlock_hash_entry : fcntl_lock failed with code %s\n",strerror(errno)));
696       return False;
697     }
698   
699   DEBUG(9,("smb_shm_unlock_hash_entry: unlocked hash bucket %d\n", entry)); 
700   return True;
701 }
702
703 /*******************************************************************
704   Gather statistics on shared memory usage.
705   ******************************************************************/
706 static BOOL smb_shm_get_usage(int *bytes_free,
707                    int *bytes_used,
708                    int *bytes_overhead)
709 {
710    if( !smb_shm_header_p )
711    {
712       /* not mapped yet */
713       DEBUG(0,("ERROR smb_shm_free : shmem not mapped\n"));
714       return False;
715    }
716    *bytes_free = smb_shm_header_p->statistics.cells_free * CellSize;
717    *bytes_used = smb_shm_header_p->statistics.cells_used * CellSize;
718    *bytes_overhead = smb_shm_header_p->statistics.cells_system * CellSize + AlignedHeaderSize;
719    
720    return True;
721 }
722
723
724 static struct shmem_ops shmops = {
725         smb_shm_close,
726         smb_shm_alloc,
727         smb_shm_free,
728         smb_shm_get_userdef_off,
729         smb_shm_offset2addr,
730         smb_shm_addr2offset,
731         smb_shm_lock_hash_entry,
732         smb_shm_unlock_hash_entry,
733         smb_shm_get_usage,
734 };
735
736 /*******************************************************************
737   open the shared memory
738   ******************************************************************/
739 struct shmem_ops *smb_shm_open(char *file_name, int size, int ronly)
740 {
741    int filesize;
742    BOOL created_new = False;
743    BOOL other_processes = True;
744
745    read_only = ronly;
746    
747    DEBUG(5,("smb_shm_open : using shmem file %s to be of size %d\n",file_name,size));
748
749    smb_shm_fd = open(file_name, read_only?O_RDONLY:(O_RDWR|O_CREAT),
750                      SHM_FILE_MODE);
751
752    if ( smb_shm_fd < 0 )
753    {
754       DEBUG(0,("ERROR smb_shm_open : open failed with code %s\n",strerror(errno)));
755       return NULL;
756    }
757    
758    if (!smb_shm_global_lock())
759    {
760       DEBUG(0,("ERROR smb_shm_open : can't do smb_shm_global_lock\n"));
761       return NULL;
762    }
763    
764    if( (filesize = lseek(smb_shm_fd, 0, SEEK_END)) < 0)
765    {
766       DEBUG(0,("ERROR smb_shm_open : lseek failed with code %s\n",strerror(errno)));
767       smb_shm_global_unlock();
768       close(smb_shm_fd);
769       return NULL;
770    }
771
772    /* return the file offset to 0 to save on later seeks */
773    lseek(smb_shm_fd,0,SEEK_SET);
774
775    if (filesize == 0)
776    {
777       /* we just created a new one */
778       created_new = True;
779    }
780    
781    /* to find out if some other process is already mapping the file,
782       we use a registration file containing the processids of the file mapping processes
783       */
784
785    /* construct processreg file name */
786    strcpy(smb_shm_processreg_name, file_name);
787    strcat(smb_shm_processreg_name, ".processes");
788
789    if (!read_only && 
790        !smb_shm_register_process(smb_shm_processreg_name, getpid(), &other_processes))
791    {
792       smb_shm_global_unlock();
793       close(smb_shm_fd);
794       return NULL;
795    }
796
797    if (!read_only && (created_new || !other_processes))
798    {
799       /* we just created a new one, or are the first opener, lets set it size */
800       if( ftruncate(smb_shm_fd, size) <0)
801       {
802          DEBUG(0,("ERROR smb_shm_open : ftruncate failed with code %s\n",strerror(errno)));
803          smb_shm_unregister_process(smb_shm_processreg_name, getpid());
804          smb_shm_global_unlock();
805          close(smb_shm_fd);
806          return NULL;
807       }
808
809       /* paranoia */
810       lseek(smb_shm_fd,0,SEEK_SET);
811
812       filesize = size;
813    }
814    
815    if (size != filesize )
816    {
817       /* the existing file has a different size and we are not the first opener.
818          Since another process is still using it, we will use the file size */
819       DEBUG(0,("WARNING smb_shm_open : filesize (%d) != expected size (%d), using filesize\n",filesize,size));
820       size = filesize;
821    }
822    
823    smb_shm_header_p = (struct SmbShmHeader *)mmap(NULL, size, 
824                                                   read_only?PROT_READ:
825                                                   (PROT_READ | PROT_WRITE), 
826                                                   MAP_FILE | MAP_SHARED, 
827                                                   smb_shm_fd, 0);
828    /* WARNING, smb_shm_header_p can be different for different processes mapping the same file ! */
829    if (smb_shm_header_p  == (struct SmbShmHeader *)(-1))
830    {
831       DEBUG(0,("ERROR smb_shm_open : mmap failed with code %s\n",strerror(errno)));
832       smb_shm_unregister_process(smb_shm_processreg_name, getpid());
833       smb_shm_global_unlock();
834       close(smb_shm_fd);
835       return NULL;
836    }      
837    
838       
839    if (!read_only && (created_new || !other_processes))
840    {
841       smb_shm_initialize(size);
842       /* Create the hash buckets for the share file entries. */
843       smb_shm_create_hash_table( lp_shmem_hash_size() );
844    }
845    else if (!smb_shm_validate_header(size) )
846    {
847       /* existing file is corrupt, samba admin should remove it by hand */
848       DEBUG(0,("ERROR smb_shm_open : corrupt shared mem file, remove it manually\n"));
849       munmap((caddr_t)smb_shm_header_p, size);
850       smb_shm_unregister_process(smb_shm_processreg_name, getpid());
851       smb_shm_global_unlock();
852       close(smb_shm_fd);
853       return NULL;
854    }
855    
856    smb_shm_global_unlock();
857    return &shmops;
858 }
859
860
861 #else /* FAST_SHARE_MODES */
862  int shmem_dummy_procedure(void)
863 {return 0;}
864 #endif /* FAST_SHARE_MODES */