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