2 Unix SMB/Netbios implementation.
4 shared memory locking implementation
5 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
23 12 aug 96: Erik.Devriendt@te6.siemens.be
24 added support for shared memory implementation of share mode locking
26 May 1997. Jeremy Allison (jallison@whistle.com). Modified share mode
27 locking to deal with multiple share modes per open file.
29 September 1997. Jeremy Allison (jallison@whistle.com). Added oplock
32 October 1997 - split into separate file (tridge)
37 #ifdef FAST_SHARE_MODES
39 extern int DEBUGLEVEL;
41 static struct shmem_ops *shmops;
43 /* share mode record pointed to in shared memory hash bucket */
46 int next_offset; /* offset of next record in chain from hash bucket */
50 int num_share_mode_entries;
51 int share_mode_entries; /* Chain of share mode entries for this file */
55 /* share mode entry pointed to by share_mode_record struct */
58 int next_share_mode_entry;
60 } shm_share_mode_entry;
65 /* Conversion to hash entry index from device and inode numbers. */
66 #define HASH_ENTRY(dev,ino) ((unsigned int)(((dev) ^ (ino)) % shmops->hash_size()))
69 /*******************************************************************
70 deinitialize the shared memory for share_mode management
71 ******************************************************************/
72 static BOOL shm_stop_share_mode_mgmt(void)
74 return shmops->shm_close();
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)
83 return shmops->lock_hash_entry(HASH_ENTRY(dev, inode));
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)
92 return shmops->unlock_hash_entry(HASH_ENTRY(dev, inode));
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)
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;
109 int num_entries_copied;
111 share_mode_entry *share_array = (share_mode_entry *)0;
115 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
117 if(mode_array[hash_entry] == 0)
119 DEBUG(5,("get_share_modes hash bucket %d empty\n", hash_entry));
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)
127 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
134 file_prev_p = file_scanner_p ;
135 file_scanner_p = (share_mode_record *)shmops->offset2addr(
136 file_scanner_p->next_offset);
142 DEBUG(5,("get_share_modes no entry for file dev = %d ino = %d\n",
147 if(file_scanner_p->locking_version != LOCKING_VERSION)
149 DEBUG(0,("ERROR: get_share_modes Deleting old share mode v1 %d dev=%d ino=%d\n",
150 file_scanner_p->locking_version, dev, inode));
151 if(file_prev_p == file_scanner_p)
152 mode_array[hash_entry] = file_scanner_p->next_offset;
154 file_prev_p->next_offset = file_scanner_p->next_offset;
155 shmops->shm_free(shmops->addr2offset(file_scanner_p));
159 /* Allocate the old_shares array */
160 num_entries = file_scanner_p->num_share_mode_entries;
163 *old_shares = share_array = (share_mode_entry *)
164 malloc(num_entries * sizeof(share_mode_entry));
167 DEBUG(0,("get_share_modes: malloc fail!\n"));
172 num_entries_copied = 0;
174 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
175 file_scanner_p->share_mode_entries);
176 entry_prev_p = entry_scanner_p;
177 while(entry_scanner_p)
179 int pid = entry_scanner_p->e.pid;
181 if (pid && !process_exists(pid))
183 /* Delete this share mode entry */
184 shm_share_mode_entry *delete_entry_p = entry_scanner_p;
186 if(entry_prev_p == entry_scanner_p)
188 /* We are at start of list */
189 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
190 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
191 file_scanner_p->share_mode_entries);
192 entry_prev_p = entry_scanner_p;
196 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
197 entry_scanner_p = (shm_share_mode_entry*)
198 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
200 /* Decrement the number of share mode entries on this share mode record */
201 file_scanner_p->num_share_mode_entries -= 1;
204 if(file_scanner_p->num_share_mode_entries < 0)
206 DEBUG(0,("PANIC ERROR: get_share_mode: entries=%d dev=%d ino=%d\n",
207 file_scanner_p->num_share_mode_entries,dev, inode));
211 DEBUG(0,("get_share_modes: process %d no longer exists\n", pid));
213 shmops->shm_free(shmops->addr2offset(delete_entry_p));
217 /* This is a valid share mode entry and the process that
218 created it still exists. Copy it into the output array.
220 share_array[num_entries_copied].pid = entry_scanner_p->e.pid;
221 share_array[num_entries_copied].share_mode = entry_scanner_p->e.share_mode;
222 share_array[num_entries_copied].op_port = entry_scanner_p->e.op_port;
223 share_array[num_entries_copied].op_type = entry_scanner_p->e.op_type;
224 memcpy(&share_array[num_entries_copied].time, &entry_scanner_p->e.time,
225 sizeof(struct timeval));
226 num_entries_copied++;
227 DEBUG(5,("get_share_modes Read share mode 0x%X pid=%d\n",
228 entry_scanner_p->e.share_mode, entry_scanner_p->e.pid));
229 entry_prev_p = entry_scanner_p;
230 entry_scanner_p = (shm_share_mode_entry *)
231 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
235 /* If no valid share mode entries were found then this record shouldn't exist ! */
236 if(num_entries_copied == 0)
238 DEBUG(0,("get_share_modes: file with dev %d inode %d empty\n",
241 free((char *)*old_shares);
244 if(file_prev_p == file_scanner_p)
245 mode_array[hash_entry] = file_scanner_p->next_offset;
247 file_prev_p->next_offset = file_scanner_p->next_offset;
248 shmops->shm_free(shmops->addr2offset(file_scanner_p));
251 DEBUG(5,("get_share_modes: file with dev %d inode %d -> %d entries\n",
252 dev, inode, num_entries_copied));
254 return(num_entries_copied);
257 /*******************************************************************
258 del the share mode of a file.
259 ********************************************************************/
260 static void shm_del_share_mode(int token, files_struct *fsp)
265 unsigned int hash_entry;
266 share_mode_record *file_scanner_p;
267 share_mode_record *file_prev_p;
268 shm_share_mode_entry *entry_scanner_p;
269 shm_share_mode_entry *entry_prev_p;
273 dev = fsp->fd_ptr->dev;
274 inode = fsp->fd_ptr->inode;
276 hash_entry = HASH_ENTRY(dev, inode);
278 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
280 if(mode_array[hash_entry] == 0)
282 DEBUG(0,("PANIC ERROR:del_share_mode hash bucket %d empty\n",
287 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
288 file_prev_p = file_scanner_p;
290 while(file_scanner_p)
292 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
299 file_prev_p = file_scanner_p ;
300 file_scanner_p = (share_mode_record *)
301 shmops->offset2addr(file_scanner_p->next_offset);
307 DEBUG(0,("ERROR: del_share_mode no entry for dev %d inode %d\n",
312 if(file_scanner_p->locking_version != LOCKING_VERSION)
314 DEBUG(0,("ERROR: del_share_modes Deleting old share mode v1 %d dev=%d ino=%d\n",
315 file_scanner_p->locking_version, dev, inode));
316 if(file_prev_p == file_scanner_p)
317 mode_array[hash_entry] = file_scanner_p->next_offset;
319 file_prev_p->next_offset = file_scanner_p->next_offset;
320 shmops->shm_free(shmops->addr2offset(file_scanner_p));
325 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
326 file_scanner_p->share_mode_entries);
327 entry_prev_p = entry_scanner_p;
328 while(entry_scanner_p)
330 if( (pid == entry_scanner_p->e.pid) &&
331 (memcmp(&entry_scanner_p->e.time,
332 &fsp->open_time,sizeof(struct timeval)) == 0) )
339 entry_prev_p = entry_scanner_p;
340 entry_scanner_p = (shm_share_mode_entry *)
341 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
347 /* Decrement the number of entries in the record. */
348 file_scanner_p->num_share_mode_entries -= 1;
350 DEBUG(2,("del_share_modes Deleting share mode entry dev=%d ino=%d\n",
352 if(entry_prev_p == entry_scanner_p)
353 /* We are at start of list */
354 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
356 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
357 shmops->shm_free(shmops->addr2offset(entry_scanner_p));
360 if(file_scanner_p->num_share_mode_entries < 0)
362 DEBUG(0,("PANIC ERROR:del_share_mode num_share_mode_entries=%d\n",
363 file_scanner_p->num_share_mode_entries));
367 /* If we deleted the last share mode entry then remove the share mode record. */
368 if(file_scanner_p->num_share_mode_entries == 0)
370 DEBUG(2,("del_share_modes num entries = 0, deleting share_mode dev=%d ino=%d\n",
372 if(file_prev_p == file_scanner_p)
373 mode_array[hash_entry] = file_scanner_p->next_offset;
375 file_prev_p->next_offset = file_scanner_p->next_offset;
376 shmops->shm_free(shmops->addr2offset(file_scanner_p));
381 DEBUG(0,("ERROR: del_share_modes No share mode dev=%d ino=%d\n",
386 /*******************************************************************
387 set the share mode of a file. Return False on fail, True on success.
388 ********************************************************************/
389 static BOOL shm_set_share_mode(int token, files_struct *fsp, uint16 port, uint16 op_type)
394 unsigned int hash_entry;
395 share_mode_record *file_scanner_p;
396 share_mode_record *file_prev_p;
397 shm_share_mode_entry *new_entry_p;
398 int new_entry_offset;
401 dev = fsp->fd_ptr->dev;
402 inode = fsp->fd_ptr->inode;
404 hash_entry = HASH_ENTRY(dev, inode);
406 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
408 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
409 file_prev_p = file_scanner_p;
411 while(file_scanner_p)
413 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
420 file_prev_p = file_scanner_p ;
421 file_scanner_p = (share_mode_record *)
422 shmops->offset2addr(file_scanner_p->next_offset);
428 /* We must create a share_mode_record */
429 share_mode_record *new_mode_p = NULL;
430 int new_offset = shmops->shm_alloc(sizeof(share_mode_record) +
431 strlen(fsp->fsp_name) + 1);
432 if(new_offset == 0) {
433 DEBUG(0,("ERROR:set_share_mode shmops->shm_alloc fail!\n"));
436 new_mode_p = shmops->offset2addr(new_offset);
437 new_mode_p->locking_version = LOCKING_VERSION;
438 new_mode_p->st_dev = dev;
439 new_mode_p->st_ino = inode;
440 new_mode_p->num_share_mode_entries = 0;
441 new_mode_p->share_mode_entries = 0;
442 pstrcpy(new_mode_p->file_name, fsp->fsp_name);
444 /* Chain onto the start of the hash chain (in the hope we will be used first). */
445 new_mode_p->next_offset = mode_array[hash_entry];
446 mode_array[hash_entry] = new_offset;
448 file_scanner_p = new_mode_p;
450 DEBUG(3,("set_share_mode: Created share record for %s (dev %d inode %d)\n",
451 fsp->fsp_name, dev, inode));
454 /* Now create the share mode entry */
455 new_entry_offset = shmops->shm_alloc(sizeof(shm_share_mode_entry));
456 if(new_entry_offset == 0) {
457 int delete_offset = mode_array[hash_entry];
458 DEBUG(0,("ERROR:set_share_mode: shmops->shm_alloc fail 1!\n"));
459 /* Unlink the damaged record */
460 mode_array[hash_entry] = file_scanner_p->next_offset;
462 shmops->shm_free( delete_offset );
466 new_entry_p = shmops->offset2addr(new_entry_offset);
468 new_entry_p->e.pid = getpid();
469 new_entry_p->e.share_mode = fsp->share_mode;
470 new_entry_p->e.op_port = port;
471 new_entry_p->e.op_type = op_type;
472 memcpy( (char *)&new_entry_p->e.time, (char *)&fsp->open_time, sizeof(struct timeval));
474 /* Chain onto the share_mode_record */
475 new_entry_p->next_share_mode_entry = file_scanner_p->share_mode_entries;
476 file_scanner_p->share_mode_entries = new_entry_offset;
479 if(file_scanner_p->num_share_mode_entries < 0)
481 DEBUG(0,("PANIC ERROR:set_share_mode num_share_mode_entries=%d\n",
482 file_scanner_p->num_share_mode_entries));
486 /* Increment the share_mode_entries counter */
487 file_scanner_p->num_share_mode_entries += 1;
489 DEBUG(3,("set_share_mode: Created share entry for %s with mode 0x%X pid=%d\n",
490 fsp->fsp_name, fsp->share_mode, new_entry_p->e.pid));
495 /*******************************************************************
496 Remove an oplock port and mode entry from a share mode.
497 ********************************************************************/
498 static BOOL shm_remove_share_oplock(files_struct *fsp, int token)
503 unsigned int hash_entry;
504 share_mode_record *file_scanner_p;
505 share_mode_record *file_prev_p;
506 shm_share_mode_entry *entry_scanner_p;
507 shm_share_mode_entry *entry_prev_p;
511 dev = fsp->fd_ptr->dev;
512 inode = fsp->fd_ptr->inode;
514 hash_entry = HASH_ENTRY(dev, inode);
516 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
518 if(mode_array[hash_entry] == 0)
520 DEBUG(0,("PANIC ERROR:remove_share_oplock: hash bucket %d empty\n",
525 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
526 file_prev_p = file_scanner_p;
528 while(file_scanner_p)
530 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
537 file_prev_p = file_scanner_p ;
538 file_scanner_p = (share_mode_record *)
539 shmops->offset2addr(file_scanner_p->next_offset);
545 DEBUG(0,("ERROR:remove_share_oplock: no entry found for dev=%d ino=%d\n",
550 if(file_scanner_p->locking_version != LOCKING_VERSION)
552 DEBUG(0,("ERROR: remove_share_oplock: Deleting old share mode v1=%d dev=%d ino=%d\n",
553 file_scanner_p->locking_version, dev, inode));
554 if(file_prev_p == file_scanner_p)
555 mode_array[hash_entry] = file_scanner_p->next_offset;
557 file_prev_p->next_offset = file_scanner_p->next_offset;
558 shmops->shm_free(shmops->addr2offset(file_scanner_p));
563 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
564 file_scanner_p->share_mode_entries);
565 entry_prev_p = entry_scanner_p;
566 while(entry_scanner_p)
568 if( (pid == entry_scanner_p->e.pid) &&
569 (entry_scanner_p->e.share_mode == fsp->share_mode) &&
570 (memcmp(&entry_scanner_p->e.time,
571 &fsp->open_time,sizeof(struct timeval)) == 0) )
573 /* Delete the oplock info. */
574 entry_scanner_p->e.op_port = 0;
575 entry_scanner_p->e.op_type = 0;
581 entry_prev_p = entry_scanner_p;
582 entry_scanner_p = (shm_share_mode_entry *)
583 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
589 DEBUG(0,("ERROR: remove_share_oplock: No oplock granted. dev=%d ino=%d\n",
598 /*******************************************************************
599 call the specified function on each entry under management by the
601 ********************************************************************/
602 static int shm_share_forall(void (*fn)(share_mode_entry *, char *))
606 share_mode_record *file_scanner_p;
608 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
610 for( i = 0; i < shmops->hash_size(); i++) {
611 shmops->lock_hash_entry(i);
612 if(mode_array[i] == 0) {
613 shmops->unlock_hash_entry(i);
617 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[i]);
618 while((file_scanner_p != 0) &&
619 (file_scanner_p->num_share_mode_entries != 0)) {
620 shm_share_mode_entry *entry_scanner_p =
621 (shm_share_mode_entry *)
622 shmops->offset2addr(file_scanner_p->share_mode_entries);
624 while(entry_scanner_p != 0) {
626 if (process_exists(entry_scanner_p->e.pid)) {
627 fn(&entry_scanner_p->e,
628 file_scanner_p->file_name);
633 (shm_share_mode_entry *)
635 entry_scanner_p->next_share_mode_entry);
636 } /* end while entry_scanner_p */
637 file_scanner_p = (share_mode_record *)
638 shmops->offset2addr(file_scanner_p->next_offset);
639 } /* end while file_scanner_p */
640 shmops->unlock_hash_entry(i);
647 /*******************************************************************
648 dump the state of the system
649 ********************************************************************/
650 static void shm_share_status(FILE *f)
652 int bytes_free, bytes_used, bytes_overhead, bytes_total;
654 shmops->get_usage(&bytes_free, &bytes_used, &bytes_overhead);
655 bytes_total = bytes_free + bytes_used + bytes_overhead;
657 fprintf(f, "Share mode memory usage (bytes):\n");
658 fprintf(f, " %d(%d%%) free + %d(%d%%) used + %d(%d%%) overhead = %d(100%%) total\n",
659 bytes_free, (bytes_free * 100)/bytes_total,
660 bytes_used, (bytes_used * 100)/bytes_total,
661 bytes_overhead, (bytes_overhead * 100)/bytes_total,
666 static struct share_ops share_ops = {
667 shm_stop_share_mode_mgmt,
668 shm_lock_share_entry,
669 shm_unlock_share_entry,
673 shm_remove_share_oplock,
678 /*******************************************************************
679 initialize the shared memory for share_mode management
680 ******************************************************************/
681 struct share_ops *locking_shm_init(int ronly)
686 shmops = sysv_shm_open(read_only);
687 if (shmops) return &share_ops;
690 #ifdef HAVE_SHARED_MMAP
691 shmops = smb_shm_open(read_only);
692 if (shmops) return &share_ops;
699 int locking_shm_dummy_procedure(void)
701 #endif /* FAST_SHARE_MODES */