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;
40 extern connection_struct Connections[];
41 extern files_struct Files[];
43 static struct shmem_ops *shmops;
45 /* share mode record pointed to in shared memory hash bucket */
48 int next_offset; /* offset of next record in chain from hash bucket */
52 int num_share_mode_entries;
53 int share_mode_entries; /* Chain of share mode entries for this file */
57 /* share mode entry pointed to by share_mode_record struct */
60 int next_share_mode_entry;
62 } shm_share_mode_entry;
67 /* Conversion to hash entry index from device and inode numbers. */
68 #define HASH_ENTRY(dev,ino) ((((uint32)(dev)) ^ ((uint32)(ino))) % shmops->hash_size())
71 /*******************************************************************
72 deinitialize the shared memory for share_mode management
73 ******************************************************************/
74 static BOOL shm_stop_share_mode_mgmt(void)
76 return shmops->shm_close();
79 /*******************************************************************
80 lock a hash bucket entry in shared memory for share_mode management
81 ******************************************************************/
82 static BOOL shm_lock_share_entry(int cnum, uint32 dev, uint32 inode, int *ptok)
84 return shmops->lock_hash_entry(HASH_ENTRY(dev, inode));
87 /*******************************************************************
88 unlock a hash bucket entry in shared memory for share_mode management
89 ******************************************************************/
90 static BOOL shm_unlock_share_entry(int cnum, uint32 dev, uint32 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(int cnum, int token, uint32 dev, uint32 inode,
99 share_mode_entry **old_shares)
102 unsigned int hash_entry = HASH_ENTRY(dev, inode);
103 share_mode_record *file_scanner_p;
104 share_mode_record *file_prev_p;
105 shm_share_mode_entry *entry_scanner_p;
106 shm_share_mode_entry *entry_prev_p;
108 int num_entries_copied;
110 share_mode_entry *share_array = (share_mode_entry *)0;
114 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
116 if(mode_array[hash_entry] == 0)
118 DEBUG(5,("get_share_modes hash bucket %d empty\n", hash_entry));
122 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
123 file_prev_p = file_scanner_p;
124 while(file_scanner_p)
126 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
133 file_prev_p = file_scanner_p ;
134 file_scanner_p = (share_mode_record *)shmops->offset2addr(
135 file_scanner_p->next_offset);
141 DEBUG(5,("get_share_modes no entry for file dev = %d ino = %d\n",
146 if(file_scanner_p->locking_version != LOCKING_VERSION)
148 DEBUG(0,("ERROR: get_share_modes Deleting old share mode v1 %d dev=%d ino=%d\n",
149 file_scanner_p->locking_version, dev, inode));
150 if(file_prev_p == file_scanner_p)
151 mode_array[hash_entry] = file_scanner_p->next_offset;
153 file_prev_p->next_offset = file_scanner_p->next_offset;
154 shmops->shm_free(shmops->addr2offset(file_scanner_p));
158 /* Allocate the old_shares array */
159 num_entries = file_scanner_p->num_share_mode_entries;
162 *old_shares = share_array = (share_mode_entry *)
163 malloc(num_entries * sizeof(share_mode_entry));
166 DEBUG(0,("get_share_modes: malloc fail!\n"));
171 num_entries_copied = 0;
173 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
174 file_scanner_p->share_mode_entries);
175 entry_prev_p = entry_scanner_p;
176 while(entry_scanner_p)
178 int pid = entry_scanner_p->e.pid;
180 if (pid && !process_exists(pid))
182 /* Delete this share mode entry */
183 shm_share_mode_entry *delete_entry_p = entry_scanner_p;
185 if(entry_prev_p == entry_scanner_p)
187 /* We are at start of list */
188 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
189 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
190 file_scanner_p->share_mode_entries);
191 entry_prev_p = entry_scanner_p;
195 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
196 entry_scanner_p = (shm_share_mode_entry*)
197 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
199 /* Decrement the number of share mode entries on this share mode record */
200 file_scanner_p->num_share_mode_entries -= 1;
203 if(file_scanner_p->num_share_mode_entries < 0)
205 DEBUG(0,("PANIC ERROR: get_share_mode: entries=%d dev=%d ino=%d\n",
206 file_scanner_p->num_share_mode_entries,dev, inode));
210 DEBUG(0,("get_share_modes: process %d no longer exists\n", pid));
212 shmops->shm_free(shmops->addr2offset(delete_entry_p));
216 /* This is a valid share mode entry and the process that
217 created it still exists. Copy it into the output array.
219 share_array[num_entries_copied].pid = entry_scanner_p->e.pid;
220 share_array[num_entries_copied].share_mode = entry_scanner_p->e.share_mode;
221 share_array[num_entries_copied].op_port = entry_scanner_p->e.op_port;
222 share_array[num_entries_copied].op_type = entry_scanner_p->e.op_type;
223 memcpy(&share_array[num_entries_copied].time, &entry_scanner_p->e.time,
224 sizeof(struct timeval));
225 num_entries_copied++;
226 DEBUG(5,("get_share_modes Read share mode 0x%X pid=%d\n",
227 entry_scanner_p->e.share_mode, entry_scanner_p->e.pid));
228 entry_prev_p = entry_scanner_p;
229 entry_scanner_p = (shm_share_mode_entry *)
230 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
234 /* If no valid share mode entries were found then this record shouldn't exist ! */
235 if(num_entries_copied == 0)
237 DEBUG(0,("get_share_modes: file with dev %d inode %d empty\n",
240 free((char *)*old_shares);
243 if(file_prev_p == file_scanner_p)
244 mode_array[hash_entry] = file_scanner_p->next_offset;
246 file_prev_p->next_offset = file_scanner_p->next_offset;
247 shmops->shm_free(shmops->addr2offset(file_scanner_p));
250 DEBUG(5,("get_share_modes: file with dev %d inode %d -> %d entries\n",
251 dev, inode, num_entries_copied));
253 return(num_entries_copied);
256 /*******************************************************************
257 del the share mode of a file.
258 ********************************************************************/
259 static void shm_del_share_mode(int token, int fnum)
263 unsigned int hash_entry;
264 share_mode_record *file_scanner_p;
265 share_mode_record *file_prev_p;
266 shm_share_mode_entry *entry_scanner_p;
267 shm_share_mode_entry *entry_prev_p;
271 dev = Files[fnum].fd_ptr->dev;
272 inode = Files[fnum].fd_ptr->inode;
274 hash_entry = HASH_ENTRY(dev, inode);
276 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
278 if(mode_array[hash_entry] == 0)
280 DEBUG(0,("PANIC ERROR:del_share_mode hash bucket %d empty\n",
285 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
286 file_prev_p = file_scanner_p;
288 while(file_scanner_p)
290 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
297 file_prev_p = file_scanner_p ;
298 file_scanner_p = (share_mode_record *)
299 shmops->offset2addr(file_scanner_p->next_offset);
305 DEBUG(0,("ERROR: del_share_mode no entry for dev %d inode %d\n",
310 if(file_scanner_p->locking_version != LOCKING_VERSION)
312 DEBUG(0,("ERROR: del_share_modes Deleting old share mode v1 %d dev=%d ino=%d\n",
313 file_scanner_p->locking_version, dev, inode));
314 if(file_prev_p == file_scanner_p)
315 mode_array[hash_entry] = file_scanner_p->next_offset;
317 file_prev_p->next_offset = file_scanner_p->next_offset;
318 shmops->shm_free(shmops->addr2offset(file_scanner_p));
323 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
324 file_scanner_p->share_mode_entries);
325 entry_prev_p = entry_scanner_p;
326 while(entry_scanner_p)
328 if( (pid == entry_scanner_p->e.pid) &&
329 (memcmp(&entry_scanner_p->e.time,
330 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
337 entry_prev_p = entry_scanner_p;
338 entry_scanner_p = (shm_share_mode_entry *)
339 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
345 /* Decrement the number of entries in the record. */
346 file_scanner_p->num_share_mode_entries -= 1;
348 DEBUG(2,("del_share_modes Deleting share mode entry dev=%d ino=%d\n",
350 if(entry_prev_p == entry_scanner_p)
351 /* We are at start of list */
352 file_scanner_p->share_mode_entries = entry_scanner_p->next_share_mode_entry;
354 entry_prev_p->next_share_mode_entry = entry_scanner_p->next_share_mode_entry;
355 shmops->shm_free(shmops->addr2offset(entry_scanner_p));
358 if(file_scanner_p->num_share_mode_entries < 0)
360 DEBUG(0,("PANIC ERROR:del_share_mode num_share_mode_entries=%d\n",
361 file_scanner_p->num_share_mode_entries));
365 /* If we deleted the last share mode entry then remove the share mode record. */
366 if(file_scanner_p->num_share_mode_entries == 0)
368 DEBUG(2,("del_share_modes num entries = 0, deleting share_mode dev=%d ino=%d\n",
370 if(file_prev_p == file_scanner_p)
371 mode_array[hash_entry] = file_scanner_p->next_offset;
373 file_prev_p->next_offset = file_scanner_p->next_offset;
374 shmops->shm_free(shmops->addr2offset(file_scanner_p));
379 DEBUG(0,("ERROR: del_share_modes No share mode dev=%d ino=%d\n",
384 /*******************************************************************
385 set the share mode of a file. Return False on fail, True on success.
386 ********************************************************************/
387 static BOOL shm_set_share_mode(int token, int fnum, uint16 port, uint16 op_type)
389 files_struct *fs_p = &Files[fnum];
392 unsigned int hash_entry;
393 share_mode_record *file_scanner_p;
394 share_mode_record *file_prev_p;
395 shm_share_mode_entry *new_entry_p;
396 int new_entry_offset;
399 dev = fs_p->fd_ptr->dev;
400 inode = fs_p->fd_ptr->inode;
402 hash_entry = HASH_ENTRY(dev, inode);
404 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
406 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
407 file_prev_p = file_scanner_p;
409 while(file_scanner_p)
411 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
418 file_prev_p = file_scanner_p ;
419 file_scanner_p = (share_mode_record *)
420 shmops->offset2addr(file_scanner_p->next_offset);
426 /* We must create a share_mode_record */
427 share_mode_record *new_mode_p = NULL;
428 int new_offset = shmops->shm_alloc(sizeof(share_mode_record) +
429 strlen(fs_p->name) + 1);
430 if(new_offset == 0) {
431 DEBUG(0,("ERROR:set_share_mode shmops->shm_alloc fail!\n"));
434 new_mode_p = shmops->offset2addr(new_offset);
435 new_mode_p->locking_version = LOCKING_VERSION;
436 new_mode_p->st_dev = dev;
437 new_mode_p->st_ino = inode;
438 new_mode_p->num_share_mode_entries = 0;
439 new_mode_p->share_mode_entries = 0;
440 pstrcpy(new_mode_p->file_name, fs_p->name);
442 /* Chain onto the start of the hash chain (in the hope we will be used first). */
443 new_mode_p->next_offset = mode_array[hash_entry];
444 mode_array[hash_entry] = new_offset;
446 file_scanner_p = new_mode_p;
448 DEBUG(3,("set_share_mode: Created share record for %s (dev %d inode %d)\n",
449 fs_p->name, dev, inode));
452 /* Now create the share mode entry */
453 new_entry_offset = shmops->shm_alloc(sizeof(shm_share_mode_entry));
454 if(new_entry_offset == 0) {
455 int delete_offset = mode_array[hash_entry];
456 DEBUG(0,("ERROR:set_share_mode: shmops->shm_alloc fail 1!\n"));
457 /* Unlink the damaged record */
458 mode_array[hash_entry] = file_scanner_p->next_offset;
460 shmops->shm_free( delete_offset );
464 new_entry_p = shmops->offset2addr(new_entry_offset);
466 new_entry_p->e.pid = getpid();
467 new_entry_p->e.share_mode = fs_p->share_mode;
468 new_entry_p->e.op_port = port;
469 new_entry_p->e.op_type = op_type;
470 memcpy( (char *)&new_entry_p->e.time, (char *)&fs_p->open_time, sizeof(struct timeval));
472 /* Chain onto the share_mode_record */
473 new_entry_p->next_share_mode_entry = file_scanner_p->share_mode_entries;
474 file_scanner_p->share_mode_entries = new_entry_offset;
477 if(file_scanner_p->num_share_mode_entries < 0)
479 DEBUG(0,("PANIC ERROR:set_share_mode num_share_mode_entries=%d\n",
480 file_scanner_p->num_share_mode_entries));
484 /* Increment the share_mode_entries counter */
485 file_scanner_p->num_share_mode_entries += 1;
487 DEBUG(3,("set_share_mode: Created share entry for %s with mode 0x%X pid=%d\n",
488 fs_p->name, fs_p->share_mode, new_entry_p->e.pid));
493 /*******************************************************************
494 Remove an oplock port and mode entry from a share mode.
495 ********************************************************************/
496 static BOOL shm_remove_share_oplock(int fnum, int token)
500 unsigned int hash_entry;
501 share_mode_record *file_scanner_p;
502 share_mode_record *file_prev_p;
503 shm_share_mode_entry *entry_scanner_p;
504 shm_share_mode_entry *entry_prev_p;
508 dev = Files[fnum].fd_ptr->dev;
509 inode = Files[fnum].fd_ptr->inode;
511 hash_entry = HASH_ENTRY(dev, inode);
513 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
515 if(mode_array[hash_entry] == 0)
517 DEBUG(0,("PANIC ERROR:remove_share_oplock: hash bucket %d empty\n",
522 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[hash_entry]);
523 file_prev_p = file_scanner_p;
525 while(file_scanner_p)
527 if( (file_scanner_p->st_dev == dev) && (file_scanner_p->st_ino == inode) )
534 file_prev_p = file_scanner_p ;
535 file_scanner_p = (share_mode_record *)
536 shmops->offset2addr(file_scanner_p->next_offset);
542 DEBUG(0,("ERROR:remove_share_oplock: no entry found for dev=%d ino=%d\n",
547 if(file_scanner_p->locking_version != LOCKING_VERSION)
549 DEBUG(0,("ERROR: remove_share_oplock: Deleting old share mode v1=%d dev=%d ino=%d\n",
550 file_scanner_p->locking_version, dev, inode));
551 if(file_prev_p == file_scanner_p)
552 mode_array[hash_entry] = file_scanner_p->next_offset;
554 file_prev_p->next_offset = file_scanner_p->next_offset;
555 shmops->shm_free(shmops->addr2offset(file_scanner_p));
560 entry_scanner_p = (shm_share_mode_entry*)shmops->offset2addr(
561 file_scanner_p->share_mode_entries);
562 entry_prev_p = entry_scanner_p;
563 while(entry_scanner_p)
565 if( (pid == entry_scanner_p->e.pid) &&
566 (entry_scanner_p->e.share_mode == Files[fnum].share_mode) &&
567 (memcmp(&entry_scanner_p->e.time,
568 &Files[fnum].open_time,sizeof(struct timeval)) == 0) )
570 /* Delete the oplock info. */
571 entry_scanner_p->e.op_port = 0;
572 entry_scanner_p->e.op_type = 0;
578 entry_prev_p = entry_scanner_p;
579 entry_scanner_p = (shm_share_mode_entry *)
580 shmops->offset2addr(entry_scanner_p->next_share_mode_entry);
586 DEBUG(0,("ERROR: remove_share_oplock: No oplock granted. dev=%d ino=%d\n",
595 /*******************************************************************
596 call the specified function on each entry under management by the
598 ********************************************************************/
599 static int shm_share_forall(void (*fn)(share_mode_entry *, char *))
603 share_mode_record *file_scanner_p;
605 mode_array = (int *)shmops->offset2addr(shmops->get_userdef_off());
607 for( i = 0; i < shmops->hash_size(); i++) {
608 shmops->lock_hash_entry(i);
609 if(mode_array[i] == 0) {
610 shmops->unlock_hash_entry(i);
614 file_scanner_p = (share_mode_record *)shmops->offset2addr(mode_array[i]);
615 while((file_scanner_p != 0) &&
616 (file_scanner_p->num_share_mode_entries != 0)) {
617 shm_share_mode_entry *entry_scanner_p =
618 (shm_share_mode_entry *)
619 shmops->offset2addr(file_scanner_p->share_mode_entries);
621 while(entry_scanner_p != 0) {
623 if (process_exists(entry_scanner_p->e.pid)) {
624 fn(&entry_scanner_p->e,
625 file_scanner_p->file_name);
630 (shm_share_mode_entry *)
632 entry_scanner_p->next_share_mode_entry);
633 } /* end while entry_scanner_p */
634 file_scanner_p = (share_mode_record *)
635 shmops->offset2addr(file_scanner_p->next_offset);
636 } /* end while file_scanner_p */
637 shmops->unlock_hash_entry(i);
644 /*******************************************************************
645 dump the state of the system
646 ********************************************************************/
647 static void shm_share_status(FILE *f)
649 int bytes_free, bytes_used, bytes_overhead, bytes_total;
651 shmops->get_usage(&bytes_free, &bytes_used, &bytes_overhead);
652 bytes_total = bytes_free + bytes_used + bytes_overhead;
654 fprintf(f, "Share mode memory usage (bytes):\n");
655 fprintf(f, " %d(%d%%) free + %d(%d%%) used + %d(%d%%) overhead = %d(100%%) total\n",
656 bytes_free, (bytes_free * 100)/bytes_total,
657 bytes_used, (bytes_used * 100)/bytes_total,
658 bytes_overhead, (bytes_overhead * 100)/bytes_total,
663 static struct share_ops share_ops = {
664 shm_stop_share_mode_mgmt,
665 shm_lock_share_entry,
666 shm_unlock_share_entry,
670 shm_remove_share_oplock,
675 /*******************************************************************
676 initialize the shared memory for share_mode management
677 ******************************************************************/
678 struct share_ops *locking_shm_init(int ronly)
683 shmops = sysv_shm_open(read_only);
684 if (shmops) return &share_ops;
687 #ifdef HAVE_SHARED_MMAP
688 shmops = smb_shm_open(read_only);
689 if (shmops) return &share_ops;
696 int locking_shm_dummy_procedure(void)
698 #endif /* FAST_SHARE_MODES */