r5102: This is a major simplification of the logic for controlling top level
[kai/samba-autobuild/.git] / source4 / 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 #include "librpc/gen_ndr/ndr_security.h"
44
45 struct odb_context {
46         struct tdb_wrap *w;
47         uint32_t server;
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         uint32_t server;
57         void     *file_handle;
58         uint32_t stream_id;
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, uint32_t server, 
82                              struct messaging_context *messaging_ctx)
83 {
84         char *path;
85         struct odb_context *odb;
86
87         odb = talloc(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->messaging_ctx = messaging_ctx;
104
105         return odb;
106 }
107
108 /*
109   destroy a lock on the database
110 */
111 static int odb_lock_destructor(void *ptr)
112 {
113         struct odb_lock *lck = ptr;
114         tdb_chainunlock(lck->odb->w->tdb, lck->key);
115         return 0;
116 }
117
118 /*
119   get a lock on a entry in the odb. This call returns a lock handle,
120   which the caller should unlock using talloc_free().
121 */
122 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
123                           struct odb_context *odb, DATA_BLOB *file_key)
124 {
125         struct odb_lock *lck;
126
127         lck = talloc(mem_ctx, struct odb_lock);
128         if (lck == NULL) {
129                 return NULL;
130         }
131
132         lck->odb = talloc_reference(lck, odb);
133         lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
134         lck->key.dsize = file_key->length;
135         if (lck->key.dptr == NULL) {
136                 talloc_free(lck);
137                 return NULL;
138         }
139
140         if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
141                 talloc_free(lck);
142                 return NULL;
143         }
144
145         talloc_set_destructor(lck, odb_lock_destructor);
146         
147         return lck;
148 }
149
150 /*
151   determine if two odb_entry structures conflict
152 */
153 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
154 {
155 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
156
157         if (e1->pending || e2->pending) return False;
158
159         /* if either open involves no read.write or delete access then
160            it can't conflict */
161         if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
162                                  SEC_FILE_APPEND_DATA |
163                                  SEC_FILE_READ_DATA |
164                                  SEC_FILE_EXECUTE |
165                                  SEC_STD_DELETE))) {
166                 return False;
167         }
168         if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
169                                  SEC_FILE_APPEND_DATA |
170                                  SEC_FILE_READ_DATA |
171                                  SEC_FILE_EXECUTE |
172                                  SEC_STD_DELETE))) {
173                 return False;
174         }
175
176         /* data IO access masks. This is skipped if the two open handles
177            are on different streams (as in that case the masks don't
178            interact) */
179         if (e1->stream_id != e2->stream_id) {
180                 return False;
181         }
182
183         CHECK_MASK(e1->access_mask, e2->share_access, 
184                    SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
185                    NTCREATEX_SHARE_ACCESS_WRITE);
186         CHECK_MASK(e2->access_mask, e1->share_access, 
187                    SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
188                    NTCREATEX_SHARE_ACCESS_WRITE);
189         
190         CHECK_MASK(e1->access_mask, e2->share_access, 
191                    SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
192                    NTCREATEX_SHARE_ACCESS_READ);
193         CHECK_MASK(e2->access_mask, e1->share_access, 
194                    SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
195                    NTCREATEX_SHARE_ACCESS_READ);
196
197         CHECK_MASK(e1->access_mask, e2->share_access, 
198                    SEC_STD_DELETE,
199                    NTCREATEX_SHARE_ACCESS_DELETE);
200         CHECK_MASK(e2->access_mask, e1->share_access, 
201                    SEC_STD_DELETE,
202                    NTCREATEX_SHARE_ACCESS_DELETE);
203
204         /* if a delete is pending then a second open is not allowed */
205         if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
206             (e2->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
207                 return True;
208         }
209
210         return False;
211 }
212
213 /*
214   register an open file in the open files database. This implements the share_access
215   rules
216 */
217 NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
218                        uint32_t stream_id,
219                        uint32_t share_access, uint32_t create_options,
220                        uint32_t access_mask)
221 {
222         struct odb_context *odb = lck->odb;
223         TDB_DATA dbuf;
224         struct odb_entry e;
225         int i, count;
226         struct odb_entry *elist;
227                 
228         dbuf = tdb_fetch(odb->w->tdb, lck->key);
229
230         e.server         = odb->server;
231         e.file_handle    = file_handle;
232         e.stream_id      = stream_id;
233         e.share_access   = share_access;
234         e.create_options = create_options;
235         e.access_mask    = access_mask;
236         e.notify_ptr     = NULL;
237         e.pending        = False;
238
239         /* check the existing file opens to see if they
240            conflict */
241         elist = (struct odb_entry *)dbuf.dptr;
242         count = dbuf.dsize / sizeof(struct odb_entry);
243
244         for (i=0;i<count;i++) {
245                 if (share_conflict(elist+i, &e)) {
246                         if (dbuf.dptr) free(dbuf.dptr);
247                         return NT_STATUS_SHARING_VIOLATION;
248                 }
249         }
250
251         elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
252         if (elist == NULL) {
253                 if (dbuf.dptr) free(dbuf.dptr);
254                 return NT_STATUS_NO_MEMORY;
255         }
256
257         dbuf.dptr = (char *)elist;
258         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
259
260         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
261                &e, sizeof(struct odb_entry));
262
263         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
264                 free(dbuf.dptr);
265                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
266         }
267
268         free(dbuf.dptr);
269         return NT_STATUS_OK;
270 }
271
272
273 /*
274   register a pending open file in the open files database
275 */
276 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
277 {
278         struct odb_context *odb = lck->odb;
279         TDB_DATA dbuf;
280         struct odb_entry e;
281         struct odb_entry *elist;
282         int count;
283                 
284         dbuf = tdb_fetch(odb->w->tdb, lck->key);
285
286         e.server         = odb->server;
287         e.file_handle    = NULL;
288         e.stream_id      = 0;
289         e.share_access   = 0;
290         e.create_options = 0;
291         e.access_mask    = 0;
292         e.notify_ptr     = private;
293         e.pending        = True;
294
295         /* check the existing file opens to see if they
296            conflict */
297         elist = (struct odb_entry *)dbuf.dptr;
298         count = dbuf.dsize / sizeof(struct odb_entry);
299
300         elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
301         if (elist == NULL) {
302                 if (dbuf.dptr) free(dbuf.dptr);
303                 return NT_STATUS_NO_MEMORY;
304         }
305
306         dbuf.dptr = (char *)elist;
307         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
308
309         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
310                &e, sizeof(struct odb_entry));
311
312         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
313                 free(dbuf.dptr);
314                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
315         }
316
317         free(dbuf.dptr);
318         return NT_STATUS_OK;
319 }
320
321
322 /*
323   remove a opendb entry
324 */
325 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
326 {
327         struct odb_context *odb = lck->odb;
328         TDB_DATA dbuf;
329         struct odb_entry *elist;
330         int i, count;
331         NTSTATUS status;
332
333         dbuf = tdb_fetch(odb->w->tdb, lck->key);
334
335         if (dbuf.dptr == NULL) {
336                 return NT_STATUS_UNSUCCESSFUL;
337         }
338
339         elist = (struct odb_entry *)dbuf.dptr;
340         count = dbuf.dsize / sizeof(struct odb_entry);
341
342         /* send any pending notifications, removing them once sent */
343         for (i=0;i<count;i++) {
344                 if (elist[i].pending) {
345                         messaging_send_ptr(odb->messaging_ctx, elist[i].server, 
346                                            MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
347                         memmove(&elist[i], &elist[i+1], sizeof(struct odb_entry)*(count-(i+1)));
348                         i--;
349                         count--;
350                 }
351         }
352
353         /* find the entry, and delete it */
354         for (i=0;i<count;i++) {
355                 if (file_handle == elist[i].file_handle &&
356                     odb->server == elist[i].server) {
357                         if (i < count-1) {
358                                 memmove(elist+i, elist+i+1, 
359                                         (count - (i+1)) * sizeof(struct odb_entry));
360                         }
361                         break;
362                 }
363         }
364
365         status = NT_STATUS_OK;
366
367         if (i == count) {
368                 status = NT_STATUS_UNSUCCESSFUL;
369         } else if (count == 1) {
370                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
371                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
372                 }
373         } else {
374                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
375                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
376                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
377                 }
378         }
379
380         free(dbuf.dptr);
381
382         return status;
383 }
384
385
386 /*
387   remove a pending opendb entry
388 */
389 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
390 {
391         struct odb_context *odb = lck->odb;
392         TDB_DATA dbuf;
393         struct odb_entry *elist;
394         int i, count;
395         NTSTATUS status;
396
397         dbuf = tdb_fetch(odb->w->tdb, lck->key);
398
399         if (dbuf.dptr == NULL) {
400                 return NT_STATUS_UNSUCCESSFUL;
401         }
402
403         elist = (struct odb_entry *)dbuf.dptr;
404         count = dbuf.dsize / sizeof(struct odb_entry);
405
406         /* find the entry, and delete it */
407         for (i=0;i<count;i++) {
408                 if (private == elist[i].notify_ptr &&
409                     odb->server == elist[i].server) {
410                         if (i < count-1) {
411                                 memmove(elist+i, elist+i+1, 
412                                         (count - (i+1)) * sizeof(struct odb_entry));
413                         }
414                         break;
415                 }
416         }
417
418         status = NT_STATUS_OK;
419
420         if (i == count) {
421                 status = NT_STATUS_UNSUCCESSFUL;
422         } else if (count == 1) {
423                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
424                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
425                 }
426         } else {
427                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
428                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
429                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
430                 }
431         }
432
433         free(dbuf.dptr);
434
435         return status;
436 }
437
438
439 /*
440   update create options on an open file
441 */
442 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
443                                 void *file_handle, uint32_t create_options)
444 {
445         struct odb_context *odb = lck->odb;
446         TDB_DATA dbuf;
447         struct odb_entry *elist;
448         int i, count;
449         NTSTATUS status;
450
451         dbuf = tdb_fetch(odb->w->tdb, lck->key);
452         if (dbuf.dptr == NULL) {
453                 return NT_STATUS_UNSUCCESSFUL;
454         }
455
456         elist = (struct odb_entry *)dbuf.dptr;
457         count = dbuf.dsize / sizeof(struct odb_entry);
458
459         /* find the entry, and modify it */
460         for (i=0;i<count;i++) {
461                 if (file_handle == elist[i].file_handle &&
462                     odb->server == elist[i].server) {
463                         elist[i].create_options = create_options;
464                         break;
465                 }
466         }
467
468         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
469                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
470         } else {
471                 status = NT_STATUS_OK;
472         }
473
474         free(dbuf.dptr);
475
476         return status;
477 }
478
479
480 /*
481   determine if a file can be opened with the given share_access,
482   create_options and access_mask
483 */
484 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, 
485                       uint32_t share_access, uint32_t create_options, 
486                       uint32_t access_mask)
487 {
488         TDB_DATA dbuf;
489         TDB_DATA kbuf;
490         struct odb_entry *elist;
491         int i, count;
492         struct odb_entry e;
493
494         kbuf.dptr = (char *)key->data;
495         kbuf.dsize = key->length;
496
497         dbuf = tdb_fetch(odb->w->tdb, kbuf);
498         if (dbuf.dptr == NULL) {
499                 return NT_STATUS_OK;
500         }
501
502         elist = (struct odb_entry *)dbuf.dptr;
503         count = dbuf.dsize / sizeof(struct odb_entry);
504
505         if (count == 0) {
506                 free(dbuf.dptr);
507                 return NT_STATUS_OK;
508         }
509
510         e.server         = odb->server;
511         e.file_handle    = NULL;
512         e.stream_id      = 0;
513         e.share_access   = share_access;
514         e.create_options = create_options;
515         e.access_mask    = access_mask;
516         e.notify_ptr     = NULL;
517         e.pending        = False;
518
519         for (i=0;i<count;i++) {
520                 if (share_conflict(elist+i, &e)) {
521                         if (dbuf.dptr) free(dbuf.dptr);
522                         return NT_STATUS_SHARING_VIOLATION;
523                 }
524         }
525
526         free(dbuf.dptr);
527         return NT_STATUS_OK;
528 }