Reasonably large change to give us *exactly* correct NT delete on close semantics.
[samba.git] / source / locking / locking_shm.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    shared memory locking implementation
5    Copyright (C) Andrew Tridgell 1992-1998
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    Revision History:
22
23    12 aug 96: Erik.Devriendt@te6.siemens.be
24    added support for shared memory implementation of share mode locking
25
26    May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
27    locking to deal with multiple share modes per open file.
28
29    September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
30    support.
31
32    October 1997 - split into separate file (tridge)
33 */
34
35 #include "includes.h"
36
37 #ifdef FAST_SHARE_MODES
38
39 extern int DEBUGLEVEL;
40
41 static struct shmem_ops *shmops;
42
43 /* share mode record pointed to in shared memory hash bucket */
44 typedef struct
45 {
46   int next_offset; /* offset of next record in chain from hash bucket */
47   int locking_version;
48   SMB_DEV_T st_dev;
49   SMB_INO_T st_ino;
50   int num_share_mode_entries;
51   int share_mode_entries; /* Chain of share mode entries for this file */
52   char file_name[1];
53 } share_mode_record;
54
55 /* share mode entry pointed to by share_mode_record struct */
56 typedef struct
57 {
58         int next_share_mode_entry;
59         share_mode_entry e;
60 } shm_share_mode_entry;
61
62 static int read_only;
63
64
65 /* Conversion to hash entry index from device and inode numbers. */
66 #define HASH_ENTRY(dev,ino) ((unsigned int)(((dev) ^ (ino)) % shmops->hash_size()))
67
68
69 /*******************************************************************
70   deinitialize the shared memory for share_mode management 
71   ******************************************************************/
72 static BOOL shm_stop_share_mode_mgmt(void)
73 {
74    return shmops->shm_close();
75 }
76
77 /*******************************************************************
78   lock a hash bucket entry in shared memory for share_mode management 
79   ******************************************************************/
80 static BOOL shm_lock_share_entry(connection_struct *conn,
81                                  SMB_DEV_T dev, SMB_INO_T inode, int *ptok)
82 {
83         return shmops->lock_hash_entry(HASH_ENTRY(dev, inode));
84 }
85
86 /*******************************************************************
87   unlock a hash bucket entry in shared memory for share_mode management 
88   ******************************************************************/
89 static BOOL shm_unlock_share_entry(connection_struct *conn,
90                                    SMB_DEV_T dev, SMB_INO_T inode, int token)
91 {
92   return shmops->unlock_hash_entry(HASH_ENTRY(dev, inode));
93 }
94
95 /*******************************************************************
96 get all share mode entries in shared memory for a dev/inode pair.
97 ********************************************************************/
98 static int shm_get_share_modes(connection_struct *conn,
99                                int token, SMB_DEV_T dev, SMB_INO_T inode, 
100                                share_mode_entry **old_shares)
101 {
102   int *mode_array;
103   unsigned int hash_entry = HASH_ENTRY(dev, inode); 
104   share_mode_record *file_scanner_p;
105   share_mode_record *file_prev_p;
106   shm_share_mode_entry *entry_scanner_p;
107   shm_share_mode_entry *entry_prev_p;
108   int num_entries;
109   int num_entries_copied;
110   BOOL found = False;
111   share_mode_entry *share_array = (share_mode_entry *)0;
112
113   *old_shares = 0;
114
115   mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
116   
117   if(mode_array[hash_entry] == 0)
118   {
119     DEBUG(5,("get_share_modes hash bucket %d empty\n", hash_entry));
120     return 0;
121   }
122
123   file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
124   file_prev_p = file_scanner_p;
125   while(file_scanner_p)
126   {
127     if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
128     {
129       found = True;
130       break;
131     }
132     else
133     {
134       file_prev_p = file_scanner_p ;
135       file_scanner_p = (share_mode_record *)shmops->offset2addr(
136                                     file_scanner_p->next_offset);
137     }
138   }
139   
140   if(!found)
141   {
142     DEBUG(5,("get_share_modes no entry for file dev = %x ino = %.0f\n",
143              (unsigned int)dev, (double)inode));
144     return (0);
145   }
146   
147   if(file_scanner_p->locking_version != LOCKING_VERSION)
148   {
149     DEBUG(0,("ERROR: get_share_modes  Deleting old share mode v1 %d dev=%x ino=%.0f\n", 
150              file_scanner_p->locking_version, (unsigned int)dev, (double)inode));
151
152     if(file_prev_p == file_scanner_p)
153       mode_array[hash_entry] = file_scanner_p->next_offset;
154     else
155       file_prev_p->next_offset = file_scanner_p->next_offset;
156     shmops->shm_free(shmops->addr2offset(file_scanner_p));
157     return (0);
158   }
159
160   /* Allocate the old_shares array */
161   num_entries = file_scanner_p->num_share_mode_entries;
162   if(num_entries)
163   {
164     *old_shares = share_array = (share_mode_entry *)
165                  malloc(num_entries * sizeof(share_mode_entry));
166     if(*old_shares == 0)
167     {
168       DEBUG(0,("get_share_modes: malloc fail!\n"));
169       return 0;
170     }
171   }
172
173   num_entries_copied = 0;
174   
175   entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
176                                            file_scanner_p->share_mode_entries);
177   entry_prev_p = entry_scanner_p;
178   while(entry_scanner_p)
179   {
180     int pid = entry_scanner_p->e.pid;
181
182     if (pid && !process_exists(pid))
183     {
184       /* Delete this share mode entry */
185       shm_share_mode_entry *delete_entry_p = entry_scanner_p;
186
187       if(entry_prev_p == entry_scanner_p)
188       {
189         /* We are at start of list */
190         file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
191         entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
192                                            file_scanner_p->share_mode_entries);
193         entry_prev_p = entry_scanner_p;
194       }
195       else
196       {
197         entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
198         entry_scanner_p = (shm_share_mode_entry*)
199                            shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
200       }
201       /* Decrement the number of share mode entries on this share mode record */
202       file_scanner_p->num_share_mode_entries -= 1;
203
204       /* PARANOIA TEST */
205       if(file_scanner_p->num_share_mode_entries < 0)
206       {
207         DEBUG(0,("PANIC ERROR: get_share_mode: entries=%d dev=%x ino=%.0f\n",
208                  file_scanner_p->num_share_mode_entries, (unsigned int)dev, (double)inode));
209         return 0;
210       }
211
212       DEBUG(0,("get_share_modes: process %d no longer exists\n", pid));
213
214       shmops->shm_free(shmops->addr2offset(delete_entry_p));
215     } 
216     else
217     {
218        /* This is a valid share mode entry and the process that
219            created it still exists. Copy it into the output array.
220        */
221        share_array[num_entries_copied].pid = entry_scanner_p->e.pid;
222        share_array[num_entries_copied].share_mode = entry_scanner_p->e.share_mode;
223        share_array[num_entries_copied].op_port = entry_scanner_p->e.op_port;
224        share_array[num_entries_copied].op_type = entry_scanner_p->e.op_type;
225        memcpy(&share_array[num_entries_copied].time, &entry_scanner_p->e.time,
226               sizeof(struct timeval));
227        num_entries_copied++;
228        DEBUG(5,("get_share_modes Read share mode 0x%X pid=%d\n", 
229                 entry_scanner_p->e.share_mode, entry_scanner_p->e.pid));
230        entry_prev_p = entry_scanner_p;
231        entry_scanner_p = (shm_share_mode_entry *)
232                            shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
233     }
234   }
235   
236   /* If no valid share mode entries were found then this record shouldn't exist ! */
237   if(num_entries_copied == 0)
238   {
239     DEBUG(0,("get_share_modes: file with dev %x inode %.0f empty\n", 
240              (unsigned int)dev, (double)inode));
241
242     if(*old_shares)
243       free((char *)*old_shares);
244     *old_shares = 0;
245
246     if(file_prev_p == file_scanner_p)
247       mode_array[hash_entry] = file_scanner_p->next_offset;
248     else
249       file_prev_p->next_offset = file_scanner_p->next_offset;
250     shmops->shm_free(shmops->addr2offset(file_scanner_p));
251   }
252
253   DEBUG(5,("get_share_modes: file with dev %x inode %.0f -> %d entries\n",
254            (unsigned int)dev, (double)inode, num_entries_copied));
255
256   return(num_entries_copied);
257 }  
258
259 /*******************************************************************
260 del the share mode of a file.
261 ********************************************************************/
262 static void shm_del_share_mode(int token, files_struct *fsp)
263 {
264   SMB_DEV_T dev;
265   SMB_INO_T inode;
266   int *mode_array;
267   unsigned int hash_entry;
268   share_mode_record *file_scanner_p;
269   share_mode_record *file_prev_p;
270   shm_share_mode_entry *entry_scanner_p;
271   shm_share_mode_entry *entry_prev_p;
272   BOOL found = False;
273   int pid = getpid();
274
275   dev = fsp->fd_ptr->dev;
276   inode = fsp->fd_ptr->inode;
277
278   hash_entry = HASH_ENTRY(dev, inode);
279
280   mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
281  
282   if(mode_array[hash_entry] == 0)
283   {  
284     DEBUG(0,("PANIC ERROR:del_share_mode hash bucket %d empty\n", 
285                   hash_entry));
286     return;
287   }  
288   
289   file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
290   file_prev_p = file_scanner_p;
291
292   while(file_scanner_p)
293   {
294     if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
295     {
296       found = True;
297       break;
298     }
299     else
300     {
301       file_prev_p = file_scanner_p ;
302       file_scanner_p = (share_mode_record *)
303                         shmops->offset2addr(file_scanner_p->next_offset);
304     }
305   }
306     
307   if(!found)
308   {
309      DEBUG(0,("ERROR: del_share_mode no entry for dev %x inode %.0f\n",
310               (unsigned int)dev, (double)inode));
311      return;
312   }
313   
314   if(file_scanner_p->locking_version != LOCKING_VERSION)
315   {
316     DEBUG(0,("ERROR: del_share_modes Deleting old share mode v1 %d dev=%x ino=%.0f\n",
317              file_scanner_p->locking_version, (unsigned int)dev, (double)inode));
318
319     if(file_prev_p == file_scanner_p)
320       mode_array[hash_entry] = file_scanner_p->next_offset;
321     else
322       file_prev_p->next_offset = file_scanner_p->next_offset;
323     shmops->shm_free(shmops->addr2offset(file_scanner_p));
324     return;
325   }
326
327   found = False;
328   entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
329                                          file_scanner_p->share_mode_entries);
330   entry_prev_p = entry_scanner_p;
331   while(entry_scanner_p)
332   {
333     if( (pid == entry_scanner_p->e.pid) && 
334           (memcmp(&entry_scanner_p->e.time, 
335                  &fsp->open_time,sizeof(struct timeval)) == 0) )
336     {
337       found = True;
338       break;
339     }
340     else
341     {
342       entry_prev_p = entry_scanner_p;
343       entry_scanner_p = (shm_share_mode_entry *)
344                           shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
345     }
346   } 
347
348   if (found)
349   {
350     /* Decrement the number of entries in the record. */
351     file_scanner_p->num_share_mode_entries -= 1;
352
353     DEBUG(2,("del_share_modes Deleting share mode entry dev=%x ino=%.0f\n",
354           (unsigned int)dev, (double)inode));
355
356     if(entry_prev_p == entry_scanner_p)
357       /* We are at start of list */
358       file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
359     else
360       entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
361     shmops->shm_free(shmops->addr2offset(entry_scanner_p));
362
363     /* PARANOIA TEST */
364     if(file_scanner_p->num_share_mode_entries < 0)
365     {
366       DEBUG(0,("PANIC ERROR:del_share_mode num_share_mode_entries=%d\n", 
367                file_scanner_p->num_share_mode_entries));
368       return;
369     }
370
371     /* If we deleted the last share mode entry then remove the share mode record. */
372     if(file_scanner_p->num_share_mode_entries == 0)
373     {
374       DEBUG(2,("del_share_modes num entries = 0, deleting share_mode dev=%x ino=%.0f\n", 
375                (unsigned int)dev, (double)inode));
376
377       if(file_prev_p == file_scanner_p)
378         mode_array[hash_entry] = file_scanner_p->next_offset;
379       else
380         file_prev_p->next_offset = file_scanner_p->next_offset;
381       shmops->shm_free(shmops->addr2offset(file_scanner_p));
382     }
383   }
384   else
385   {
386     DEBUG(0,("ERROR: del_share_modes No share mode dev=%x ino=%.0f\n", 
387              (unsigned int)dev, (double)inode));
388   }
389 }
390
391 /*******************************************************************
392 set the share mode of a file. Return False on fail, True on success.
393 ********************************************************************/
394 static BOOL shm_set_share_mode(int token, files_struct *fsp, uint16 port, uint16 op_type)
395 {
396   SMB_DEV_T dev;
397   SMB_INO_T inode;
398   int *mode_array;
399   unsigned int hash_entry;
400   share_mode_record *file_scanner_p;
401   shm_share_mode_entry *new_entry_p;
402   int new_entry_offset;
403   BOOL found = False;
404
405   dev = fsp->fd_ptr->dev;
406   inode = fsp->fd_ptr->inode;
407
408   hash_entry = HASH_ENTRY(dev, inode);
409
410   mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
411
412   file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
413   
414   while(file_scanner_p)
415   {
416     if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
417     {
418       found = True;
419       break;
420     }
421     else
422     {
423       file_scanner_p = (share_mode_record *)
424                          shmops->offset2addr(file_scanner_p->next_offset);
425     }
426   }
427   
428   if(!found)
429   {
430     /* We must create a share_mode_record */
431     share_mode_record *new_mode_p = NULL;
432     int new_offset = shmops->shm_alloc(sizeof(share_mode_record) +
433                                    strlen(fsp->fsp_name) + 1);
434     if(new_offset == 0) {
435             DEBUG(0,("ERROR:set_share_mode shmops->shm_alloc fail!\n"));
436             return False;
437     }
438     new_mode_p = shmops->offset2addr(new_offset);
439     new_mode_p->locking_version = LOCKING_VERSION;
440     new_mode_p->st_dev = dev;
441     new_mode_p->st_ino = inode;
442     new_mode_p->num_share_mode_entries = 0;
443     new_mode_p->share_mode_entries = 0;
444     pstrcpy(new_mode_p->file_name, fsp->fsp_name);
445
446     /* Chain onto the start of the hash chain (in the hope we will be used first). */
447     new_mode_p->next_offset = mode_array[hash_entry];
448     mode_array[hash_entry] = new_offset;
449
450     file_scanner_p = new_mode_p;
451
452     DEBUG(3,("set_share_mode: Created share record for %s (dev %x inode %.0f)\n", 
453              fsp->fsp_name, (unsigned int)dev, (double)inode));
454   }
455  
456   /* Now create the share mode entry */ 
457   new_entry_offset = shmops->shm_alloc(sizeof(shm_share_mode_entry));
458   if(new_entry_offset == 0) {
459           int delete_offset = mode_array[hash_entry];
460           DEBUG(0,("ERROR:set_share_mode: shmops->shm_alloc fail 1!\n"));
461           /* Unlink the damaged record */
462           mode_array[hash_entry] = file_scanner_p->next_offset;
463           /* And delete it */
464           shmops->shm_free( delete_offset );
465           return False;
466   }
467
468   new_entry_p = shmops->offset2addr(new_entry_offset);
469
470   new_entry_p->e.pid = getpid();
471   new_entry_p->e.share_mode = fsp->share_mode;
472   new_entry_p->e.op_port = port;
473   new_entry_p->e.op_type = op_type;
474   memcpy( (char *)&new_entry_p->e.time, (char *)&fsp->open_time, sizeof(struct timeval));
475
476   /* Chain onto the share_mode_record */
477   new_entry_p->next_share_mode_entry = file_scanner_p->share_mode_entries;
478   file_scanner_p->share_mode_entries = new_entry_offset;
479
480   /* PARANOIA TEST */
481   if(file_scanner_p->num_share_mode_entries < 0)
482   {
483     DEBUG(0,("PANIC ERROR:set_share_mode num_share_mode_entries=%d\n", 
484              file_scanner_p->num_share_mode_entries));
485     return False;
486   }
487
488   /* Increment the share_mode_entries counter */
489   file_scanner_p->num_share_mode_entries += 1;
490
491   DEBUG(3,("set_share_mode: Created share entry for %s with mode 0x%X pid=%d\n",
492            fsp->fsp_name, fsp->share_mode, new_entry_p->e.pid));
493
494   return(True);
495 }
496
497 /*******************************************************************
498  Call a generic modify function for a share mode entry.
499 ********************************************************************/
500
501 static BOOL shm_mod_share_entry(int token, files_struct *fsp,
502                                 void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
503                                 void *param)
504 {
505   SMB_DEV_T dev;
506   SMB_INO_T inode;
507   int *mode_array;
508   unsigned int hash_entry;
509   share_mode_record *file_scanner_p;
510   share_mode_record *file_prev_p;
511   shm_share_mode_entry *entry_scanner_p;
512   BOOL found = False;
513   int pid = getpid();
514
515   dev = fsp->fd_ptr->dev;
516   inode = fsp->fd_ptr->inode;
517
518   hash_entry = HASH_ENTRY(dev, inode);
519
520   mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
521
522   if(mode_array[hash_entry] == 0)
523   {
524     DEBUG(0,("PANIC ERROR:modify_share_entry: hash bucket %d empty\n",
525                   hash_entry));
526     return False;
527   } 
528     
529   file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
530   file_prev_p = file_scanner_p;
531     
532   while(file_scanner_p)
533   { 
534     if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
535     {
536       found = True;
537       break;
538     }
539     else
540     {
541       file_prev_p = file_scanner_p ;
542       file_scanner_p = (share_mode_record *)
543                         shmops->offset2addr(file_scanner_p->next_offset);
544     }
545   } 
546    
547   if(!found)
548   { 
549      DEBUG(0,("ERROR:modify_share_entry: no entry found for dev=%x ino=%.0f\n", 
550               (unsigned int)dev, (double)inode));
551      return False;
552   } 
553
554   if(file_scanner_p->locking_version != LOCKING_VERSION)
555   {
556     DEBUG(0,("ERROR: modify_share_entry: Deleting old share mode v1=%d dev=%x ino=%.0f\n",
557              file_scanner_p->locking_version, (unsigned int)dev, (double)inode));
558
559     if(file_prev_p == file_scanner_p)
560       mode_array[hash_entry] = file_scanner_p->next_offset;
561     else
562       file_prev_p->next_offset = file_scanner_p->next_offset;
563     shmops->shm_free(shmops->addr2offset(file_scanner_p));
564     return False;
565   }
566
567   found = False;
568   entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
569                                          file_scanner_p->share_mode_entries);
570   while(entry_scanner_p)
571   {
572     if( (pid == entry_scanner_p->e.pid) && 
573         (entry_scanner_p->e.share_mode == fsp->share_mode) &&
574         (memcmp(&entry_scanner_p->e.time, 
575                 &fsp->open_time,sizeof(struct timeval)) == 0) )
576     {
577       /*
578        * Call the generic function with the given parameter.
579        */
580
581       DEBUG(5,("modify_share_entry: Calling generic function to modify entry for dev=%x ino=%.0f\n",
582             (unsigned int)dev, (double)inode));
583
584       (*mod_fn)( &entry_scanner_p->e, dev, inode, param);
585       found = True;
586       break;
587     }
588     else
589     {
590       entry_scanner_p = (shm_share_mode_entry *)
591                           shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
592     }
593   } 
594
595   if(!found)
596   {
597     DEBUG(0,("ERROR: modify_share_entry: No entry found for dev=%x ino=%.0f\n", 
598              (unsigned int)dev, (double)inode));
599     return False;
600   }
601
602   return True;
603 }
604
605 /*******************************************************************
606 call the specified function on each entry under management by the
607 share mode system
608 ********************************************************************/
609 static int shm_share_forall(void (*fn)(share_mode_entry *, char *))
610 {
611         int i, count=0;
612         int *mode_array;
613         share_mode_record *file_scanner_p;
614
615         mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
616
617         for( i = 0; i < shmops->hash_size(); i++) {
618                 shmops->lock_hash_entry(i);
619                 if(mode_array[i] == 0)  {
620                         shmops->unlock_hash_entry(i);
621                         continue;
622                 }
623
624                 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[i]);
625                 while((file_scanner_p != 0) && 
626                       (file_scanner_p->num_share_mode_entries != 0)) {
627                         shm_share_mode_entry *entry_scanner_p = 
628                                 (shm_share_mode_entry *)
629                                 shmops->offset2addr(file_scanner_p->share_mode_entries);
630
631                         while(entry_scanner_p != 0) {
632                                 
633                                 if (process_exists(entry_scanner_p->e.pid)) {
634                                         fn(&entry_scanner_p->e, 
635                                            file_scanner_p->file_name);
636                                         count++;
637                                 }
638
639                                 entry_scanner_p = 
640                                         (shm_share_mode_entry *)
641                                         shmops->offset2addr(
642                                                             entry_scanner_p->next_share_mode_entry);
643                         } /* end while entry_scanner_p */
644                         file_scanner_p = (share_mode_record *)
645                                 shmops->offset2addr(file_scanner_p->next_offset);
646                 } /* end while file_scanner_p */
647                 shmops->unlock_hash_entry(i);
648         } /* end for */
649
650         return count;
651 }
652
653
654 /*******************************************************************
655 dump the state of the system
656 ********************************************************************/
657 static void shm_share_status(FILE *f)
658 {
659         int bytes_free, bytes_used, bytes_overhead, bytes_total;
660
661         shmops->get_usage(&bytes_free, &bytes_used, &bytes_overhead);
662         bytes_total = bytes_free + bytes_used + bytes_overhead;
663
664         fprintf(f, "Share mode memory usage (bytes):\n");
665         fprintf(f, "   %d(%d%%) free + %d(%d%%) used + %d(%d%%) overhead = %d(100%%) total\n",
666                 bytes_free, (bytes_free * 100)/bytes_total,
667                 bytes_used, (bytes_used * 100)/bytes_total,
668                 bytes_overhead, (bytes_overhead * 100)/bytes_total,
669                 bytes_total);
670 }
671
672
673 static struct share_ops share_ops = {
674         shm_stop_share_mode_mgmt,
675         shm_lock_share_entry,
676         shm_unlock_share_entry,
677         shm_get_share_modes,
678         shm_del_share_mode,
679         shm_set_share_mode,
680         shm_mod_share_entry,
681         shm_share_forall,
682         shm_share_status,
683 };
684
685 /*******************************************************************
686   initialize the shared memory for share_mode management 
687   ******************************************************************/
688 struct share_ops *locking_shm_init(int ronly)
689 {
690         read_only = ronly;
691
692 #ifdef HAVE_SYSV_IPC
693         shmops = sysv_shm_open(read_only);
694         if (shmops) return &share_ops;
695 #endif
696
697 #ifdef HAVE_SHARED_MMAP
698         shmops = smb_shm_open(read_only);
699         if (shmops) return &share_ops;
700 #endif
701
702         return NULL;
703 }
704
705 #else
706  int locking_shm_dummy_procedure(void)
707 {return 0;}
708 #endif /* FAST_SHARE_MODES */