r3507: - added deferred replies on sharing violation in pvfs open. The
[jelmer/samba4-debian.git] / source / ntvfs / common / opendb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   this is the open files database. It implements shared storage of
23   what files are open between server instances, and implements the rules
24   of shared access to files.
25
26   The caller needs to provide a file_key, which specifies what file
27   they are talking about. This needs to be a unique key across all
28   filesystems, and is usually implemented in terms of a device/inode
29   pair.
30
31   Before any operations can be performed the caller needs to establish
32   a lock on the record associated with file_key. That is done by
33   calling odb_lock(). The caller releases this lock by calling
34   talloc_free() on the returned handle.
35
36   All other operations on a record are done by passing the odb_lock()
37   handle back to this module. The handle contains internal
38   information about what file_key is being operated on.
39 */
40
41 #include "includes.h"
42 #include "messages.h"
43
44 struct odb_context {
45         struct tdb_wrap *w;
46         servid_t server;
47         uint16_t tid;
48         struct messaging_context *messaging_ctx;
49 };
50
51 /* 
52    the database is indexed by a file_key, and contains entries of the
53    following form
54 */
55 struct odb_entry {
56         servid_t server;
57         uint16_t tid;
58         uint16_t fnum;
59         uint32_t share_access;
60         uint32_t create_options;
61         uint32_t access_mask;
62         void     *notify_ptr;
63         BOOL     pending;
64 };
65
66
67 /*
68   an odb lock handle. You must obtain one of these using odb_lock() before doing
69   any other operations. 
70 */
71 struct odb_lock {
72         struct odb_context *odb;
73         TDB_DATA key;
74 };
75
76 /*
77   Open up the openfiles.tdb database. Close it down using
78   talloc_free(). We need the messaging_ctx to allow for pending open
79   notifications.
80 */
81 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server, uint16_t tid, 
82                              struct messaging_context *messaging_ctx)
83 {
84         char *path;
85         struct odb_context *odb;
86
87         odb = talloc_p(mem_ctx, struct odb_context);
88         if (odb == NULL) {
89                 return NULL;
90         }
91
92         path = smbd_tmp_path(odb, "openfiles.tdb");
93         odb->w = tdb_wrap_open(odb, path, 0,  
94                                TDB_DEFAULT,
95                                O_RDWR|O_CREAT, 0600);
96         talloc_free(path);
97         if (odb->w == NULL) {
98                 talloc_free(odb);
99                 return NULL;
100         }
101
102         odb->server = server;
103         odb->tid = tid;
104         odb->messaging_ctx = messaging_ctx;
105
106         return odb;
107 }
108
109 /*
110   destroy a lock on the database
111 */
112 static int odb_lock_destructor(void *ptr)
113 {
114         struct odb_lock *lck = ptr;
115         tdb_chainunlock(lck->odb->w->tdb, lck->key);
116         return 0;
117 }
118
119 /*
120   get a lock on a entry in the odb. This call returns a lock handle,
121   which the caller should unlock using talloc_free().
122 */
123 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
124                           struct odb_context *odb, DATA_BLOB *file_key)
125 {
126         struct odb_lock *lck;
127
128         lck = talloc_p(mem_ctx, struct odb_lock);
129         if (lck == NULL) {
130                 return NULL;
131         }
132
133         lck->odb = odb;
134         lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
135         lck->key.dsize = file_key->length;
136         if (lck->key.dptr == NULL) {
137                 talloc_free(lck);
138                 return NULL;
139         }
140
141         if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
142                 talloc_free(lck);
143                 return NULL;
144         }
145
146         talloc_set_destructor(lck, odb_lock_destructor);
147         
148         return lck;
149 }
150
151 /*
152   determine if two odb_entry structures conflict
153 */
154 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
155 {
156 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
157
158         if (e1->pending || e2->pending) return False;
159
160         /* if either open involves no read.write or delete access then
161            it can't conflict */
162         if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_APPEND | 
163                                  SA_RIGHT_FILE_READ_EXEC | 
164                                  STD_RIGHT_DELETE_ACCESS))) {
165                 return False;
166         }
167         if (!(e2->access_mask & (SA_RIGHT_FILE_WRITE_APPEND | 
168                                  SA_RIGHT_FILE_READ_EXEC | 
169                                  STD_RIGHT_DELETE_ACCESS))) {
170                 return False;
171         }
172
173         /* check the basic share access */
174         CHECK_MASK(e1->access_mask, e2->share_access, 
175                    SA_RIGHT_FILE_WRITE_APPEND, 
176                    NTCREATEX_SHARE_ACCESS_WRITE);
177         CHECK_MASK(e2->access_mask, e1->share_access, 
178                    SA_RIGHT_FILE_WRITE_APPEND, 
179                    NTCREATEX_SHARE_ACCESS_WRITE);
180
181         CHECK_MASK(e1->access_mask, e2->share_access, 
182                    SA_RIGHT_FILE_READ_EXEC, 
183                    NTCREATEX_SHARE_ACCESS_READ);
184         CHECK_MASK(e2->access_mask, e1->share_access, 
185                    SA_RIGHT_FILE_READ_EXEC, 
186                    NTCREATEX_SHARE_ACCESS_READ);
187
188         CHECK_MASK(e1->access_mask, e2->share_access, 
189                    STD_RIGHT_DELETE_ACCESS, 
190                    NTCREATEX_SHARE_ACCESS_DELETE);
191         CHECK_MASK(e2->access_mask, e1->share_access, 
192                    STD_RIGHT_DELETE_ACCESS, 
193                    NTCREATEX_SHARE_ACCESS_DELETE);
194
195         /* if a delete is pending then a second open is not allowed */
196         if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
197             (e2->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
198                 return True;
199         }
200
201         return False;
202 }
203
204 /*
205   register an open file in the open files database. This implements the share_access
206   rules
207 */
208 NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum, 
209                        uint32_t share_access, uint32_t create_options,
210                        uint32_t access_mask)
211 {
212         struct odb_context *odb = lck->odb;
213         TDB_DATA dbuf;
214         struct odb_entry e;
215         char *tp;
216         int i, count;
217         struct odb_entry *elist;
218                 
219         dbuf = tdb_fetch(odb->w->tdb, lck->key);
220
221         e.server         = odb->server;
222         e.tid            = odb->tid;
223         e.fnum           = fnum;
224         e.share_access   = share_access;
225         e.create_options = create_options;
226         e.access_mask    = access_mask;
227         e.notify_ptr     = NULL;
228         e.pending        = False;
229
230         /* check the existing file opens to see if they
231            conflict */
232         elist = (struct odb_entry *)dbuf.dptr;
233         count = dbuf.dsize / sizeof(struct odb_entry);
234
235         for (i=0;i<count;i++) {
236                 if (share_conflict(elist+i, &e)) {
237                         if (dbuf.dptr) free(dbuf.dptr);
238                         return NT_STATUS_SHARING_VIOLATION;
239                 }
240         }
241
242         tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
243         if (tp == NULL) {
244                 if (dbuf.dptr) free(dbuf.dptr);
245                 return NT_STATUS_NO_MEMORY;
246         }
247
248         dbuf.dptr = tp;
249         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
250
251         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
252                &e, sizeof(struct odb_entry));
253
254         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
255                 free(dbuf.dptr);
256                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
257         }
258
259         free(dbuf.dptr);
260         return NT_STATUS_OK;
261 }
262
263
264 /*
265   register a pending open file in the open files database
266 */
267 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
268 {
269         struct odb_context *odb = lck->odb;
270         TDB_DATA dbuf;
271         struct odb_entry e;
272         char *tp;
273         struct odb_entry *elist;
274         int count;
275                 
276         dbuf = tdb_fetch(odb->w->tdb, lck->key);
277
278         e.server         = odb->server;
279         e.tid            = odb->tid;
280         e.fnum           = 0;
281         e.share_access   = 0;
282         e.create_options = 0;
283         e.access_mask    = 0;
284         e.notify_ptr     = private;
285         e.pending        = True;
286
287         /* check the existing file opens to see if they
288            conflict */
289         elist = (struct odb_entry *)dbuf.dptr;
290         count = dbuf.dsize / sizeof(struct odb_entry);
291
292         tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
293         if (tp == NULL) {
294                 if (dbuf.dptr) free(dbuf.dptr);
295                 return NT_STATUS_NO_MEMORY;
296         }
297
298         dbuf.dptr = tp;
299         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
300
301         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
302                &e, sizeof(struct odb_entry));
303
304         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
305                 free(dbuf.dptr);
306                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
307         }
308
309         free(dbuf.dptr);
310         return NT_STATUS_OK;
311 }
312
313
314 /*
315   remove a opendb entry
316 */
317 NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
318 {
319         struct odb_context *odb = lck->odb;
320         TDB_DATA dbuf;
321         struct odb_entry *elist;
322         int i, count;
323         NTSTATUS status;
324
325         dbuf = tdb_fetch(odb->w->tdb, lck->key);
326
327         if (dbuf.dptr == NULL) {
328                 return NT_STATUS_UNSUCCESSFUL;
329         }
330
331         elist = (struct odb_entry *)dbuf.dptr;
332         count = dbuf.dsize / sizeof(struct odb_entry);
333
334         /* send any pending notifications */
335         for (i=0;i<count;i++) {
336                 if (elist[i].pending) {
337                         messaging_send_ptr(odb->messaging_ctx, elist[i].server, 
338                                            MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
339                         
340                 }
341         }
342
343         /* find the entry, and delete it */
344         for (i=0;i<count;i++) {
345                 if (fnum == elist[i].fnum &&
346                     odb->server == elist[i].server &&
347                     odb->tid == elist[i].tid) {
348                         if (i < count-1) {
349                                 memmove(elist+i, elist+i+1, 
350                                         (count - (i+1)) * sizeof(struct odb_entry));
351                         }
352                         break;
353                 }
354         }
355
356         status = NT_STATUS_OK;
357
358         if (i == count) {
359                 status = NT_STATUS_UNSUCCESSFUL;
360         } else if (count == 1) {
361                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
362                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
363                 }
364         } else {
365                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
366                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
367                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
368                 }
369         }
370
371         free(dbuf.dptr);
372
373         return status;
374 }
375
376
377 /*
378   remove a pending opendb entry
379 */
380 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
381 {
382         struct odb_context *odb = lck->odb;
383         TDB_DATA dbuf;
384         struct odb_entry *elist;
385         int i, count;
386         NTSTATUS status;
387
388         dbuf = tdb_fetch(odb->w->tdb, lck->key);
389
390         if (dbuf.dptr == NULL) {
391                 return NT_STATUS_UNSUCCESSFUL;
392         }
393
394         elist = (struct odb_entry *)dbuf.dptr;
395         count = dbuf.dsize / sizeof(struct odb_entry);
396
397         /* find the entry, and delete it */
398         for (i=0;i<count;i++) {
399                 if (private == elist[i].notify_ptr &&
400                     odb->server == elist[i].server &&
401                     odb->tid == elist[i].tid) {
402                         if (i < count-1) {
403                                 memmove(elist+i, elist+i+1, 
404                                         (count - (i+1)) * sizeof(struct odb_entry));
405                         }
406                         break;
407                 }
408         }
409
410         status = NT_STATUS_OK;
411
412         if (i == count) {
413                 status = NT_STATUS_UNSUCCESSFUL;
414         } else if (count == 1) {
415                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
416                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
417                 }
418         } else {
419                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
420                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
421                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
422                 }
423         }
424
425         free(dbuf.dptr);
426
427         return status;
428 }
429
430
431 /*
432   update create options on an open file
433 */
434 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
435                                 uint16_t fnum, uint32_t create_options)
436 {
437         struct odb_context *odb = lck->odb;
438         TDB_DATA dbuf;
439         struct odb_entry *elist;
440         int i, count;
441         NTSTATUS status;
442
443         dbuf = tdb_fetch(odb->w->tdb, lck->key);
444         if (dbuf.dptr == NULL) {
445                 return NT_STATUS_UNSUCCESSFUL;
446         }
447
448         elist = (struct odb_entry *)dbuf.dptr;
449         count = dbuf.dsize / sizeof(struct odb_entry);
450
451         /* find the entry, and modify it */
452         for (i=0;i<count;i++) {
453                 if (fnum == elist[i].fnum &&
454                     odb->server == elist[i].server &&
455                     odb->tid == elist[i].tid) {
456                         elist[i].create_options = create_options;
457                         break;
458                 }
459         }
460
461         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
462                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
463         } else {
464                 status = NT_STATUS_OK;
465         }
466
467         free(dbuf.dptr);
468
469         return status;
470 }
471
472
473 /*
474   determine if a file can be opened with the given share_access,
475   create_options and access_mask
476 */
477 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, 
478                       uint32_t share_access, uint32_t create_options, 
479                       uint32_t access_mask)
480 {
481         TDB_DATA dbuf;
482         TDB_DATA kbuf;
483         struct odb_entry *elist;
484         int i, count;
485         struct odb_entry e;
486
487         kbuf.dptr = key->data;
488         kbuf.dsize = key->length;
489
490         dbuf = tdb_fetch(odb->w->tdb, kbuf);
491         if (dbuf.dptr == NULL) {
492                 return NT_STATUS_OK;
493         }
494
495         elist = (struct odb_entry *)dbuf.dptr;
496         count = dbuf.dsize / sizeof(struct odb_entry);
497
498         if (count == 0) {
499                 free(dbuf.dptr);
500                 return NT_STATUS_OK;
501         }
502
503         e.server         = odb->server;
504         e.tid            = odb->tid;
505         e.fnum           = -1;
506         e.share_access   = share_access;
507         e.create_options = create_options;
508         e.access_mask    = access_mask;
509         e.notify_ptr     = NULL;
510         e.pending        = False;
511
512         for (i=0;i<count;i++) {
513                 if (share_conflict(elist+i, &e)) {
514                         if (dbuf.dptr) free(dbuf.dptr);
515                         return NT_STATUS_SHARING_VIOLATION;
516                 }
517         }
518
519         free(dbuf.dptr);
520         return NT_STATUS_OK;
521 }