converted all our existing shared memory code to use a tdb database
[tprouty/samba.git] / source / locking / locking.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    Locking functions
5    Copyright (C) Andrew Tridgell 1992-1999
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    rewrtten completely to use new tdb code. Tridge, Dec '99
33 */
34
35 #include "includes.h"
36 extern int DEBUGLEVEL;
37
38 /* the locking database handle */
39 static TDB_CONTEXT *tdb;
40
41 /****************************************************************************
42  Utility function to map a lock type correctly depending on the real open
43  mode of a file.
44 ****************************************************************************/
45 static int map_lock_type(files_struct *fsp, int lock_type)
46 {
47         if((lock_type == F_WRLCK) && (fsp->fd_ptr->real_open_flags == O_RDONLY)) {
48                 /*
49                  * Many UNIX's cannot get a write lock on a file opened read-only.
50                  * Win32 locking semantics allow this.
51                  * Do the best we can and attempt a read-only lock.
52                  */
53                 DEBUG(10,("map_lock_type: Downgrading write lock to read due to read-only file.\n"));
54                 return F_RDLCK;
55         } else if( (lock_type == F_RDLCK) && (fsp->fd_ptr->real_open_flags == O_WRONLY)) {
56                 /*
57                  * Ditto for read locks on write only files.
58                  */
59                 DEBUG(10,("map_lock_type: Changing read lock to write due to write-only file.\n"));
60                 return F_WRLCK;
61         }
62         
63         /*
64          * This return should be the most normal, as we attempt
65          * to always open files read/write.
66          */
67         
68         return lock_type;
69 }
70
71 /****************************************************************************
72  Utility function called to see if a file region is locked.
73 ****************************************************************************/
74 BOOL is_locked(files_struct *fsp,connection_struct *conn,
75                SMB_OFF_T count,SMB_OFF_T offset, int lock_type)
76 {
77         int snum = SNUM(conn);
78         
79         if (count == 0)
80                 return(False);
81
82         if (!lp_locking(snum) || !lp_strict_locking(snum))
83                 return(False);
84
85         /*
86          * Note that most UNIX's can *test* for a write lock on
87          * a read-only fd, just not *set* a write lock on a read-only
88          * fd. So we don't need to use map_lock_type here.
89          */
90         
91         return(fcntl_lock(fsp->fd_ptr->fd,SMB_F_GETLK,offset,count,lock_type));
92 }
93
94
95 /****************************************************************************
96  Utility function called by locking requests.
97 ****************************************************************************/
98 BOOL do_lock(files_struct *fsp,connection_struct *conn,
99              SMB_OFF_T count,SMB_OFF_T offset,int lock_type,
100              int *eclass,uint32 *ecode)
101 {
102         BOOL ok = False;
103
104         if (!lp_locking(SNUM(conn)))
105                 return(True);
106
107         if (count == 0) {
108                 *eclass = ERRDOS;
109                 *ecode = ERRnoaccess;
110                 return False;
111         }
112         
113         DEBUG(10,("do_lock: lock type %d start=%.0f len=%.0f requested for file %s\n",
114                   lock_type, (double)offset, (double)count, fsp->fsp_name ));
115
116         if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn))
117                 ok = fcntl_lock(fsp->fd_ptr->fd,SMB_F_SETLK,offset,count,
118                                 map_lock_type(fsp,lock_type));
119
120         if (!ok) {
121                 *eclass = ERRDOS;
122                 *ecode = ERRlock;
123                 return False;
124         }
125         return True; /* Got lock */
126 }
127
128
129 /****************************************************************************
130  Utility function called by unlocking requests.
131 ****************************************************************************/
132 BOOL do_unlock(files_struct *fsp,connection_struct *conn,
133                SMB_OFF_T count,SMB_OFF_T offset,int *eclass,uint32 *ecode)
134 {
135         BOOL ok = False;
136         
137         if (!lp_locking(SNUM(conn)))
138                 return(True);
139         
140         DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n",
141                   (double)offset, (double)count, fsp->fsp_name ));
142         
143         if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn))
144                 ok = fcntl_lock(fsp->fd_ptr->fd,SMB_F_SETLK,offset,count,F_UNLCK);
145    
146         if (!ok) {
147                 *eclass = ERRDOS;
148                 *ecode = ERRlock;
149                 return False;
150         }
151         return True; /* Did unlock */
152 }
153
154 /****************************************************************************
155  Initialise the locking functions.
156 ****************************************************************************/
157 BOOL locking_init(int read_only)
158 {
159         if (tdb) return True;
160
161         tdb = tdb_open(lock_path("locking.tdb"), 
162                        0,
163                        read_only?O_RDONLY:O_RDWR|O_CREAT,
164                        0644);
165
166         if (!tdb) {
167                 DEBUG(0,("ERROR: Failed to initialise share modes\n"));
168                 return False;
169         }
170         
171         return True;
172 }
173
174 /*******************************************************************
175  Deinitialize the share_mode management.
176 ******************************************************************/
177 BOOL locking_end(void)
178 {
179         if (tdb && tdb_close(tdb) != 0) return False;
180         return True;
181 }
182
183 /*******************************************************************
184  form a static locking key for a dev/inode pair 
185 ******************************************************************/
186 static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
187 {
188         static struct locking_key key;
189         TDB_DATA kbuf;
190         key.dev = dev;
191         key.inode = inode;
192         kbuf.dptr = (char *)&key;
193         kbuf.dsize = sizeof(key);
194         return kbuf;
195 }
196 static TDB_DATA locking_key_fsp(files_struct *fsp)
197 {
198         return locking_key(fsp->fd_ptr->dev, fsp->fd_ptr->inode);
199 }
200
201 /*******************************************************************
202  Lock a hash bucket entry.
203 ******************************************************************/
204 BOOL lock_share_entry(connection_struct *conn,
205                       SMB_DEV_T dev, SMB_INO_T inode)
206 {
207         return tdb_lockchain(tdb, locking_key(dev, inode)) == 0;
208 }
209
210 /*******************************************************************
211  Unlock a hash bucket entry.
212 ******************************************************************/
213 BOOL unlock_share_entry(connection_struct *conn,
214                         SMB_DEV_T dev, SMB_INO_T inode)
215 {
216         return tdb_unlockchain(tdb, locking_key(dev, inode)) == 0;
217 }
218
219 /*******************************************************************
220  Get all share mode entries for a dev/inode pair.
221 ********************************************************************/
222 int get_share_modes(connection_struct *conn, 
223                     SMB_DEV_T dev, SMB_INO_T inode, 
224                     share_mode_entry **shares)
225 {
226         TDB_DATA dbuf;
227         struct locking_data *data;
228         int ret;
229
230         dbuf = tdb_fetch(tdb, locking_key(dev, inode));
231         if (!dbuf.dptr) return 0;
232
233         data = (struct locking_data *)dbuf.dptr;
234         ret = data->num_share_mode_entries;
235         *shares = (share_mode_entry *)memdup(dbuf.dptr + sizeof(*data), ret * sizeof(**shares));
236         free(dbuf.dptr);
237
238         if (! *shares) return 0;
239
240         return ret;
241 }
242
243 /*******************************************************************
244  Del the share mode of a file for this process
245 ********************************************************************/
246 void del_share_mode(files_struct *fsp)
247 {
248         TDB_DATA dbuf;
249         struct locking_data *data;
250         int i, del_count=0;
251         share_mode_entry *shares;
252         pid_t pid = getpid();
253
254         /* read in the existing share modes */
255         dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
256         if (!dbuf.dptr) return;
257
258         data = (struct locking_data *)dbuf.dptr;
259         shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
260
261         /* find any with our pid and delete it by overwriting with the rest of the data 
262            from the record */
263         for (i=0;i<data->num_share_mode_entries;) {
264                 if (shares[i].pid == pid &&
265                     memcmp(&shares[i].time, 
266                            &fsp->open_time,sizeof(struct timeval)) == 0) {
267                         data->num_share_mode_entries--;
268                         memmove(&shares[i], &shares[i+1], 
269                                 dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
270                         del_count++;
271                 } else {
272                         i++;
273                 }
274         }
275
276         /* the record has shrunk a bit */
277         dbuf.dsize -= del_count * sizeof(*shares);
278
279         /* store it back in the database */
280         tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
281
282         free(dbuf.dptr);
283 }
284
285 /*******************************************************************
286 fill a share mode entry
287 ********************************************************************/
288 static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
289 {
290         share_mode_entry *e = (share_mode_entry *)p;
291         e->pid = getpid();
292         e->share_mode = fsp->share_mode;
293         e->op_port = port;
294         e->op_type = op_type;
295         memcpy((char *)&e->time, (char *)&fsp->open_time, sizeof(struct timeval));
296 }
297
298 /*******************************************************************
299  Set the share mode of a file. Return False on fail, True on success.
300 ********************************************************************/
301 BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
302 {
303         TDB_DATA dbuf;
304         struct locking_data *data;
305         share_mode_entry *shares;
306         char *p=NULL;
307         int size;
308                 
309         /* read in the existing share modes if any */
310         dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
311         if (!dbuf.dptr) {
312                 /* we'll need to create a new record */
313                 pstring fname;
314
315                 pstrcpy(fname, fsp->conn->connectpath);
316                 pstrcat(fname, "/");
317                 pstrcat(fname, fsp->fsp_name);
318
319                 size = sizeof(*data) + sizeof(*shares) + strlen(fname) + 1;
320                 p = (char *)malloc(size);
321                 data = (struct locking_data *)p;
322                 shares = (share_mode_entry *)(p + sizeof(*data));
323                 data->num_share_mode_entries = 1;
324                 pstrcpy(p + sizeof(*data) + sizeof(*shares), fname);
325                 fill_share_mode(p + sizeof(*data), fsp, port, op_type);
326                 dbuf.dptr = p;
327                 dbuf.dsize = size;
328                 tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
329                 free(p);
330                 return True;
331         }
332
333         /* we're adding to an existing entry - this is a bit fiddly */
334         data = (struct locking_data *)dbuf.dptr;
335         shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
336
337         data->num_share_mode_entries++;
338         size = dbuf.dsize + sizeof(*shares);
339         p = malloc(size);
340         memcpy(p, dbuf.dptr, sizeof(*data));
341         fill_share_mode(p + sizeof(*data), fsp, port, op_type);
342         memcpy(p + sizeof(*data) + sizeof(*shares), dbuf.dptr + sizeof(*data),
343                dbuf.dsize - sizeof(*data));
344         free(dbuf.dptr);
345         dbuf.dptr = p;
346         dbuf.dsize = size;
347         tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
348         free(p);
349         return True;
350 }
351
352
353 /*******************************************************************
354 a generic in-place modification call for share mode entries
355 ********************************************************************/
356 static BOOL mod_share_mode(files_struct *fsp,
357                            void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
358                            void *param)
359 {
360         TDB_DATA dbuf;
361         struct locking_data *data;
362         int i;
363         share_mode_entry *shares;
364         pid_t pid = getpid();
365         int need_store=0;
366
367         /* read in the existing share modes */
368         dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
369         if (!dbuf.dptr) return False;
370
371         data = (struct locking_data *)dbuf.dptr;
372         shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
373
374         /* find any with our pid and call the supplied function */
375         for (i=0;i<data->num_share_mode_entries;i++) {
376                 if (pid == shares[i].pid && 
377                     shares[i].share_mode == fsp->share_mode &&
378                     memcmp(&shares[i].time, 
379                            &fsp->open_time,sizeof(struct timeval)) == 0) {
380                         mod_fn(&shares[i], fsp->fd_ptr->dev, fsp->fd_ptr->inode, param);
381                         need_store=1;
382                 }
383         }
384
385         /* if the mod fn was called then store it back */
386         if (need_store) {
387                 tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
388         }
389
390         free(dbuf.dptr);
391         return need_store;
392 }
393
394
395 /*******************************************************************
396  Static function that actually does the work for the generic function
397  below.
398 ********************************************************************/
399 static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
400                                    void *param)
401 {
402         DEBUG(10,("remove_share_oplock_fn: removing oplock info for entry dev=%x ino=%.0f\n",
403                   (unsigned int)dev, (double)inode ));
404         /* Delete the oplock info. */
405         entry->op_port = 0;
406         entry->op_type = NO_OPLOCK;
407 }
408
409 /*******************************************************************
410  Remove an oplock port and mode entry from a share mode.
411 ********************************************************************/
412 BOOL remove_share_oplock(files_struct *fsp)
413 {
414         return mod_share_mode(fsp, remove_share_oplock_fn, NULL);
415 }
416
417 /*******************************************************************
418  Static function that actually does the work for the generic function
419  below.
420 ********************************************************************/
421 static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
422                                    void *param)
423 {
424         DEBUG(10,("downgrade_share_oplock_fn: downgrading oplock info for entry dev=%x ino=%.0f\n",
425                   (unsigned int)dev, (double)inode ));
426         entry->op_type = LEVEL_II_OPLOCK;
427 }
428
429 /*******************************************************************
430  Downgrade a oplock type from exclusive to level II.
431 ********************************************************************/
432 BOOL downgrade_share_oplock(files_struct *fsp)
433 {
434         return mod_share_mode(fsp, downgrade_share_oplock_fn, NULL);
435 }
436
437
438 /*******************************************************************
439  Static function that actually does the work for the generic function
440  below.
441 ********************************************************************/
442 struct mod_val {
443         int new_share_mode;
444         uint16 new_oplock;
445 };
446
447 static void modify_share_mode_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode, 
448                                    void *param)
449 {
450         struct mod_val *mvp = (struct mod_val *)param;
451
452         DEBUG(10,("modify_share_mode_fn: changing share mode info from %x to %x for entry dev=%x ino=%.0f\n",
453         entry->share_mode, mvp->new_share_mode, (unsigned int)dev, (double)inode ));
454         DEBUG(10,("modify_share_mode_fn: changing oplock state from %x to %x for entry dev=%x ino=%.0f\n",
455         entry->op_type, (int)mvp->new_oplock, (unsigned int)dev, (double)inode ));
456         /* Change the share mode info. */
457         entry->share_mode = mvp->new_share_mode;
458         entry->op_type = mvp->new_oplock;
459 }
460
461 /*******************************************************************
462  Modify a share mode on a file. Used by the delete open file code.
463  Return False on fail, True on success.
464 ********************************************************************/
465 BOOL modify_share_mode(files_struct *fsp, int new_mode, uint16 new_oplock)
466 {
467         struct mod_val mv;
468
469         mv.new_share_mode = new_mode;
470         mv.new_oplock = new_oplock;
471
472         return mod_share_mode(fsp, modify_share_mode_fn, (void *)&mv);
473 }
474
475 static void (*traverse_callback)(share_mode_entry *, char *);
476
477 /****************************************************************************
478 traverse the whole database with this function, calling traverse_callback
479 on each share mode
480 ****************************************************************************/
481 int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf)
482 {
483         struct locking_data *data;
484         share_mode_entry *shares;
485         char *name;
486         int i;
487
488         data = (struct locking_data *)dbuf.dptr;
489         shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
490         name = dbuf.dptr + sizeof(*data) + data->num_share_mode_entries*sizeof(*shares);
491
492         for (i=0;i<data->num_share_mode_entries;i++) {
493                 traverse_callback(&shares[i], name);
494         }
495         return 0;
496 }
497
498 /*******************************************************************
499  Call the specified function on each entry under management by the
500  share mode system.
501 ********************************************************************/
502 int share_mode_forall(void (*fn)(share_mode_entry *, char *))
503 {
504         if (!tdb) return 0;
505         traverse_callback = fn;
506         return tdb_traverse(tdb, traverse_fn);
507 }