2 Unix SMB/Netbios implementation.
4 shared memory locking implementation
5 Copyright (C) Andrew Tridgell 1992-1997
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)
35 #ifdef FAST_SHARE_MODES
38 extern int DEBUGLEVEL;
39 extern connection_struct Connections[];
40 extern files_struct Files[];
42 static struct shmem_ops *shmops;
44 /* share mode record pointed to in shared memory hash bucket */
47 int next_offset; /* offset of next record in chain from hash bucket */
51 int num_share_mode_entries;
52 int share_mode_entries; /* Chain of share mode entries for this file */
56 /* share mode entry pointed to by share_mode_record struct */
59 int next_share_mode_entry;
61 } shm_share_mode_entry;
66 /* Conversion to hash entry index from device and inode numbers. */
67 #define HASH_ENTRY(dev,ino) ((( (uint32)(dev) )* ( (uint32)(ino) )) % lp_shmem_hash_size())
70 /*******************************************************************
71 deinitialize the shared memory for share_mode management
72 ******************************************************************/
73 static BOOL shm_stop_share_mode_mgmt(void)
75 return shmops->close();
78 /*******************************************************************
79 lock a hash bucket entry in shared memory for share_mode management
80 ******************************************************************/
81 static BOOL shm_lock_share_entry(int cnum, uint32 dev, uint32 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(int cnum, uint32 dev, uint32 inode, int token)
91 return shmops->unlock_hash_entry(HASH_ENTRY(dev, inode));
94 /*******************************************************************
95 get all share mode entries in shared memory for a dev/inode pair.
96 ********************************************************************/
97 static int shm_get_share_modes(int cnum, int token, uint32 dev, uint32 inode,
98 share_mode_entry **old_shares)
101 unsigned int hash_entry = HASH_ENTRY(dev, inode);
102 share_mode_record *file_scanner_p;
103 share_mode_record *file_prev_p;
104 shm_share_mode_entry *entry_scanner_p;
105 shm_share_mode_entry *entry_prev_p;
107 int num_entries_copied;
109 share_mode_entry *share_array = (share_mode_entry *)0;
113 if(hash_entry > lp_shmem_hash_size() )
116 ("PANIC ERROR : get_share_modes (FAST_SHARE_MODES): hash_entry %d too large \
118 hash_entry, lp_shmem_hash_size() ));
122 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
124 if(mode_array[hash_entry] == NULL_OFFSET)
126 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): hash bucket %d empty\n", hash_entry));
130 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
131 file_prev_p = file_scanner_p;
132 while(file_scanner_p)
134 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
141 file_prev_p = file_scanner_p ;
142 file_scanner_p = (share_mode_record *)shmops->offset2addr(
143 file_scanner_p->next_offset);
149 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): no entry for \
150 file dev = %d, ino = %d in hash_bucket %d\n", dev, inode, hash_entry));
154 if(file_scanner_p->locking_version != LOCKING_VERSION)
156 DEBUG(0,("ERROR:get_share_modes (FAST_SHARE_MODES): Deleting old share mode \
157 record due to old locking version %d for file dev = %d, inode = %d in hash \
158 bucket %d\n", file_scanner_p->locking_version, dev, inode, hash_entry));
159 if(file_prev_p == file_scanner_p)
160 mode_array[hash_entry] = file_scanner_p->next_offset;
162 file_prev_p->next_offset = file_scanner_p->next_offset;
163 shmops->free(shmops->addr2offset(file_scanner_p));
167 /* Allocate the old_shares array */
168 num_entries = file_scanner_p->num_share_mode_entries;
171 *old_shares = share_array = (share_mode_entry *)
172 malloc(num_entries * sizeof(share_mode_entry));
175 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): malloc fail !\n"));
180 num_entries_copied = 0;
182 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
183 file_scanner_p->share_mode_entries);
184 entry_prev_p = entry_scanner_p;
185 while(entry_scanner_p)
187 int pid = entry_scanner_p->e.pid;
189 if (pid && !process_exists(pid))
191 /* Delete this share mode entry */
192 shm_share_mode_entry *delete_entry_p = entry_scanner_p;
193 int share_mode = entry_scanner_p->e.share_mode;
195 if(entry_prev_p == entry_scanner_p)
197 /* We are at start of list */
198 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
199 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
200 file_scanner_p->share_mode_entries);
201 entry_prev_p = entry_scanner_p;
205 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
206 entry_scanner_p = (shm_share_mode_entry*)
207 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
209 /* Decrement the number of share mode entries on this share mode record */
210 file_scanner_p->num_share_mode_entries -= 1;
213 if(file_scanner_p->num_share_mode_entries < 0)
215 DEBUG(0,("PANIC ERROR:get_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
216 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
217 dev, inode, hash_entry));
221 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): process %d no longer exists and \
222 it left a share mode entry with mode 0x%X for file dev = %d, ino = %d in hash \
223 bucket %d (number of entries now = %d)\n",
224 pid, share_mode, dev, inode, hash_entry,
225 file_scanner_p->num_share_mode_entries));
227 shmops->free(shmops->addr2offset(delete_entry_p));
231 /* This is a valid share mode entry and the process that
232 created it still exists. Copy it into the output array.
234 share_array[num_entries_copied].pid = entry_scanner_p->e.pid;
235 share_array[num_entries_copied].share_mode = entry_scanner_p->e.share_mode;
236 share_array[num_entries_copied].op_port = entry_scanner_p->e.op_port;
237 share_array[num_entries_copied].op_type = entry_scanner_p->e.op_type;
238 memcpy(&share_array[num_entries_copied].time, &entry_scanner_p->e.time,
239 sizeof(struct timeval));
240 num_entries_copied++;
241 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): Read share mode \
242 record mode 0x%X pid=%d\n", entry_scanner_p->e.share_mode, entry_scanner_p->e.pid));
243 entry_prev_p = entry_scanner_p;
244 entry_scanner_p = (shm_share_mode_entry *)
245 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
249 /* If no valid share mode entries were found then this record shouldn't exist ! */
250 if(num_entries_copied == 0)
252 DEBUG(0,("get_share_modes (FAST_SHARE_MODES): file with dev %d, inode %d in \
253 hash bucket %d has a share mode record but no entries - deleting\n",
254 dev, inode, hash_entry));
256 free((char *)*old_shares);
259 if(file_prev_p == file_scanner_p)
260 mode_array[hash_entry] = file_scanner_p->next_offset;
262 file_prev_p->next_offset = file_scanner_p->next_offset;
263 shmops->free(shmops->addr2offset(file_scanner_p));
266 DEBUG(5,("get_share_modes (FAST_SHARE_MODES): file with dev %d, inode %d in \
267 hash bucket %d returning %d entries\n", dev, inode, hash_entry, num_entries_copied));
269 return(num_entries_copied);
272 /*******************************************************************
273 del the share mode of a file.
274 ********************************************************************/
275 static void shm_del_share_mode(int token, int fnum)
279 unsigned int hash_entry;
280 share_mode_record *file_scanner_p;
281 share_mode_record *file_prev_p;
282 shm_share_mode_entry *entry_scanner_p;
283 shm_share_mode_entry *entry_prev_p;
287 dev = Files[fnum].fd_ptr->dev;
288 inode = Files[fnum].fd_ptr->inode;
290 hash_entry = HASH_ENTRY(dev, inode);
292 if(hash_entry > lp_shmem_hash_size() )
295 ("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): hash_entry %d too large \
297 hash_entry, lp_shmem_hash_size() ));
301 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
303 if(mode_array[hash_entry] == NULL_OFFSET)
305 DEBUG(0,("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): hash bucket %d empty\n",
310 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
311 file_prev_p = file_scanner_p;
313 while(file_scanner_p)
315 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
322 file_prev_p = file_scanner_p ;
323 file_scanner_p = (share_mode_record *)
324 shmops->offset2addr(file_scanner_p->next_offset);
330 DEBUG(0,("ERROR:del_share_mode (FAST_SHARE_MODES): no entry found for dev %d, \
331 inode %d in hash bucket %d\n", dev, inode, hash_entry));
335 if(file_scanner_p->locking_version != LOCKING_VERSION)
337 DEBUG(0,("ERROR: del_share_modes (FAST_SHARE_MODES): Deleting old share mode \
338 record due to old locking version %d for file dev %d, inode %d hash bucket %d\n",
339 file_scanner_p->locking_version, dev, inode, hash_entry ));
340 if(file_prev_p == file_scanner_p)
341 mode_array[hash_entry] = file_scanner_p->next_offset;
343 file_prev_p->next_offset = file_scanner_p->next_offset;
344 shmops->free(shmops->addr2offset(file_scanner_p));
349 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
350 file_scanner_p->share_mode_entries);
351 entry_prev_p = entry_scanner_p;
352 while(entry_scanner_p)
354 if( (pid == entry_scanner_p->e.pid) &&
355 (memcmp(&entry_scanner_p->e.time,
356 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
363 entry_prev_p = entry_scanner_p;
364 entry_scanner_p = (shm_share_mode_entry *)
365 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
371 /* Decrement the number of entries in the record. */
372 file_scanner_p->num_share_mode_entries -= 1;
374 DEBUG(2,("del_share_modes (FAST_SHARE_MODES): \
375 Deleting share mode entry dev = %d, inode = %d in hash bucket %d (num entries now = %d)\n",
376 dev, inode, hash_entry, file_scanner_p->num_share_mode_entries));
377 if(entry_prev_p == entry_scanner_p)
378 /* We are at start of list */
379 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
381 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
382 shmops->free(shmops->addr2offset(entry_scanner_p));
385 if(file_scanner_p->num_share_mode_entries < 0)
387 DEBUG(0,("PANIC ERROR:del_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
388 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
389 dev, inode, hash_entry));
393 /* If we deleted the last share mode entry then remove the share mode record. */
394 if(file_scanner_p->num_share_mode_entries == 0)
396 DEBUG(2,("del_share_modes (FAST_SHARE_MODES): num entries = 0, deleting share_mode \
397 record dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
398 if(file_prev_p == file_scanner_p)
399 mode_array[hash_entry] = file_scanner_p->next_offset;
401 file_prev_p->next_offset = file_scanner_p->next_offset;
402 shmops->free(shmops->addr2offset(file_scanner_p));
407 DEBUG(0,("ERROR: del_share_modes (FAST_SHARE_MODES): No share mode record found \
408 dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
412 /*******************************************************************
413 set the share mode of a file. Return False on fail, True on success.
414 ********************************************************************/
415 static BOOL shm_set_share_mode(int token, int fnum, uint16 port, uint16 op_type)
417 files_struct *fs_p = &Files[fnum];
420 unsigned int hash_entry;
421 share_mode_record *file_scanner_p;
422 share_mode_record *file_prev_p;
423 shm_share_mode_entry *new_entry_p;
424 int new_entry_offset;
427 dev = fs_p->fd_ptr->dev;
428 inode = fs_p->fd_ptr->inode;
430 hash_entry = HASH_ENTRY(dev, inode);
431 if(hash_entry > lp_shmem_hash_size() )
434 ("PANIC ERROR:set_share_mode (FAST_SHARE_MODES): hash_entry %d too large \
436 hash_entry, lp_shmem_hash_size() ));
440 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
442 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
443 file_prev_p = file_scanner_p;
445 while(file_scanner_p)
447 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
454 file_prev_p = file_scanner_p ;
455 file_scanner_p = (share_mode_record *)
456 shmops->offset2addr(file_scanner_p->next_offset);
462 /* We must create a share_mode_record */
463 share_mode_record *new_mode_p = NULL;
464 int new_offset = shmops->alloc( sizeof(share_mode_record) +
465 strlen(fs_p->name) + 1);
466 if(new_offset == NULL_OFFSET)
468 DEBUG(0,("ERROR:set_share_mode (FAST_SHARE_MODES): shmops->alloc fail !\n"));
471 new_mode_p = shmops->offset2addr(new_offset);
472 new_mode_p->locking_version = LOCKING_VERSION;
473 new_mode_p->st_dev = dev;
474 new_mode_p->st_ino = inode;
475 new_mode_p->num_share_mode_entries = 0;
476 new_mode_p->share_mode_entries = NULL_OFFSET;
477 strcpy(new_mode_p->file_name, fs_p->name);
479 /* Chain onto the start of the hash chain (in the hope we will be used first). */
480 new_mode_p->next_offset = mode_array[hash_entry];
481 mode_array[hash_entry] = new_offset;
483 file_scanner_p = new_mode_p;
485 DEBUG(3,("set_share_mode (FAST_SHARE_MODES): Created share record for %s (dev %d \
486 inode %d in hash bucket %d\n", fs_p->name, dev, inode, hash_entry));
489 /* Now create the share mode entry */
490 new_entry_offset = shmops->alloc( sizeof(shm_share_mode_entry));
491 if(new_entry_offset == NULL_OFFSET)
493 int delete_offset = mode_array[hash_entry];
494 DEBUG(0,("ERROR:set_share_mode (FAST_SHARE_MODES): shmops->alloc fail 1!\n"));
495 /* Unlink the damaged record */
496 mode_array[hash_entry] = file_scanner_p->next_offset;
498 shmops->free( delete_offset );
502 new_entry_p = shmops->offset2addr(new_entry_offset);
504 new_entry_p->e.pid = getpid();
505 new_entry_p->e.share_mode = fs_p->share_mode;
506 new_entry_p->e.op_port = port;
507 new_entry_p->e.op_type = op_type;
508 memcpy( (char *)&new_entry_p->e.time, (char *)&fs_p->open_time, sizeof(struct timeval));
510 /* Chain onto the share_mode_record */
511 new_entry_p->next_share_mode_entry = file_scanner_p->share_mode_entries;
512 file_scanner_p->share_mode_entries = new_entry_offset;
515 if(file_scanner_p->num_share_mode_entries < 0)
517 DEBUG(0,("PANIC ERROR:set_share_mode (FAST_SHARE_MODES): num_share_mode_entries < 0 (%d) \
518 for dev = %d, ino = %d, hashbucket %d\n", file_scanner_p->num_share_mode_entries,
519 dev, inode, hash_entry));
523 /* Increment the share_mode_entries counter */
524 file_scanner_p->num_share_mode_entries += 1;
526 DEBUG(3,("set_share_mode (FAST_SHARE_MODES): Created share entry for %s with mode \
527 0x%X pid=%d (num_entries now = %d)\n",fs_p->name, fs_p->share_mode, new_entry_p->e.pid,
528 file_scanner_p->num_share_mode_entries));
533 /*******************************************************************
534 Remove an oplock port and mode entry from a share mode.
535 ********************************************************************/
536 static BOOL shm_remove_share_oplock(int fnum, int token)
540 unsigned int hash_entry;
541 share_mode_record *file_scanner_p;
542 share_mode_record *file_prev_p;
543 shm_share_mode_entry *entry_scanner_p;
544 shm_share_mode_entry *entry_prev_p;
548 dev = Files[fnum].fd_ptr->dev;
549 inode = Files[fnum].fd_ptr->inode;
551 hash_entry = HASH_ENTRY(dev, inode);
553 if(hash_entry > lp_shmem_hash_size() )
556 ("PANIC ERROR:remove_share_oplock (FAST_SHARE_MODES): hash_entry %d too large \
558 hash_entry, lp_shmem_hash_size() ));
562 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
564 if(mode_array[hash_entry] == NULL_OFFSET)
566 DEBUG(0,("PANIC ERROR:remove_share_oplock (FAST_SHARE_MODES): hash bucket %d empty\n",
571 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
572 file_prev_p = file_scanner_p;
574 while(file_scanner_p)
576 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
583 file_prev_p = file_scanner_p ;
584 file_scanner_p = (share_mode_record *)
585 shmops->offset2addr(file_scanner_p->next_offset);
591 DEBUG(0,("ERROR:remove_share_oplock (FAST_SHARE_MODES): no entry found for dev %d, \
592 inode %d in hash bucket %d\n", dev, inode, hash_entry));
596 if(file_scanner_p->locking_version != LOCKING_VERSION)
598 DEBUG(0,("ERROR: remove_share_oplock (FAST_SHARE_MODES): Deleting old share mode \
599 record due to old locking version %d for file dev %d, inode %d hash bucket %d\n",
600 file_scanner_p->locking_version, dev, inode, hash_entry ));
601 if(file_prev_p == file_scanner_p)
602 mode_array[hash_entry] = file_scanner_p->next_offset;
604 file_prev_p->next_offset = file_scanner_p->next_offset;
605 shmops->free(shmops->addr2offset(file_scanner_p));
610 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
611 file_scanner_p->share_mode_entries);
612 entry_prev_p = entry_scanner_p;
613 while(entry_scanner_p)
615 if( (pid == entry_scanner_p->e.pid) &&
616 (entry_scanner_p->e.share_mode == Files[fnum].share_mode) &&
617 (memcmp(&entry_scanner_p->e.time,
618 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
620 /* Delete the oplock info. */
621 entry_scanner_p->e.op_port = 0;
622 entry_scanner_p->e.op_type = 0;
628 entry_prev_p = entry_scanner_p;
629 entry_scanner_p = (shm_share_mode_entry *)
630 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
636 DEBUG(0,("ERROR: remove_share_oplock (FAST_SHARE_MODES): No oplock granted share \
637 mode record found dev = %d, inode = %d in hash bucket %d\n", dev, inode, hash_entry));
645 /*******************************************************************
646 call the specified function on each entry under management by the
648 ********************************************************************/
649 static int shm_share_forall(void (*fn)(share_mode_entry *, char *))
653 share_mode_record *file_scanner_p;
655 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
657 for( i = 0; i < lp_shmem_hash_size(); i++) {
658 shmops->lock_hash_entry(i);
659 if(mode_array[i] == NULL_OFFSET) {
660 shmops->unlock_hash_entry(i);
664 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[i]);
665 while((file_scanner_p != 0) &&
666 (file_scanner_p->num_share_mode_entries != 0)) {
667 shm_share_mode_entry *entry_scanner_p =
668 (shm_share_mode_entry *)
669 shmops->offset2addr(file_scanner_p->share_mode_entries);
671 while(entry_scanner_p != 0) {
673 fn(&entry_scanner_p->e,
674 file_scanner_p->file_name);
677 (shm_share_mode_entry *)
679 entry_scanner_p->next_share_mode_entry);
681 } /* end while entry_scanner_p */
682 file_scanner_p = (share_mode_record *)
683 shmops->offset2addr(file_scanner_p->next_offset);
684 } /* end while file_scanner_p */
685 shmops->unlock_hash_entry(i);
692 /*******************************************************************
693 dump the state of the system
694 ********************************************************************/
695 static void shm_share_status(FILE *f)
697 int bytes_free, bytes_used, bytes_overhead, bytes_total;
699 shmops->get_usage(&bytes_free, &bytes_used, &bytes_overhead);
700 bytes_total = bytes_free + bytes_used + bytes_overhead;
702 fprintf(f, "Share mode memory usage (bytes):\n");
703 fprintf(f, " %d(%d%%) free + %d(%d%%) used + %d(%d%%) overhead = %d(100%%) total\n",
704 bytes_free, (bytes_free * 100)/bytes_total,
705 bytes_used, (bytes_used * 100)/bytes_total,
706 bytes_overhead, (bytes_overhead * 100)/bytes_total,
711 static struct share_ops share_ops = {
712 shm_stop_share_mode_mgmt,
713 shm_lock_share_entry,
714 shm_unlock_share_entry,
718 shm_remove_share_oplock,
723 /*******************************************************************
724 initialize the shared memory for share_mode management
725 ******************************************************************/
726 struct share_ops *locking_shm_init(int ronly)
728 pstring shmem_file_name;
733 shmops = sysv_shm_open(lp_shmem_size(), read_only);
734 if (shmops) return &share_ops;
737 pstrcpy(shmem_file_name,lp_lockdir());
738 if (!directory_exist(shmem_file_name,NULL)) {
739 if (read_only) return NULL;
740 mkdir(shmem_file_name,0755);
742 trim_string(shmem_file_name,"","/");
743 if (!*shmem_file_name) return(False);
744 strcat(shmem_file_name, "/SHARE_MEM_FILE");
745 shmops = smb_shm_open(shmem_file_name, lp_shmem_size(), read_only);
746 if (shmops) return &share_ops;
752 int locking_shm_dummy_procedure(void)