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