r12694: Move some headers to the directory of the subsystem they belong to.
[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 "system/filesys.h"
43 #include "lib/tdb/include/tdb.h"
44 #include "messaging/messaging.h"
45 #include "librpc/gen_ndr/ndr_security.h"
46 #include "db_wrap.h"
47 #include "smb_server/smb_server.h"
48 #include "lib/messaging/irpc.h"
49
50 struct odb_context {
51         struct tdb_wrap *w;
52         uint32_t server;
53         struct messaging_context *messaging_ctx;
54 };
55
56 /* 
57    the database is indexed by a file_key, and contains entries of the
58    following form
59 */
60 struct odb_entry {
61         uint32_t server;
62         void     *file_handle;
63         uint32_t stream_id;
64         uint32_t share_access;
65         uint32_t create_options;
66         uint32_t access_mask;
67         void     *notify_ptr;
68         BOOL     pending;
69 };
70
71
72 /*
73   an odb lock handle. You must obtain one of these using odb_lock() before doing
74   any other operations. 
75 */
76 struct odb_lock {
77         struct odb_context *odb;
78         TDB_DATA key;
79 };
80
81 /*
82   Open up the openfiles.tdb database. Close it down using
83   talloc_free(). We need the messaging_ctx to allow for pending open
84   notifications.
85 */
86 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, uint32_t server, 
87                              struct messaging_context *messaging_ctx)
88 {
89         char *path;
90         struct odb_context *odb;
91
92         odb = talloc(mem_ctx, struct odb_context);
93         if (odb == NULL) {
94                 return NULL;
95         }
96
97         path = smbd_tmp_path(odb, "openfiles.tdb");
98         odb->w = tdb_wrap_open(odb, path, 0,  
99                                TDB_DEFAULT,
100                                O_RDWR|O_CREAT, 0600);
101         talloc_free(path);
102         if (odb->w == NULL) {
103                 talloc_free(odb);
104                 return NULL;
105         }
106
107         odb->server = server;
108         odb->messaging_ctx = messaging_ctx;
109
110         return odb;
111 }
112
113 /*
114   destroy a lock on the database
115 */
116 static int odb_lock_destructor(void *ptr)
117 {
118         struct odb_lock *lck = ptr;
119         tdb_chainunlock(lck->odb->w->tdb, lck->key);
120         return 0;
121 }
122
123 /*
124   get a lock on a entry in the odb. This call returns a lock handle,
125   which the caller should unlock using talloc_free().
126 */
127 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
128                           struct odb_context *odb, DATA_BLOB *file_key)
129 {
130         struct odb_lock *lck;
131
132         lck = talloc(mem_ctx, struct odb_lock);
133         if (lck == NULL) {
134                 return NULL;
135         }
136
137         lck->odb = talloc_reference(lck, odb);
138         lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
139         lck->key.dsize = file_key->length;
140         if (lck->key.dptr == NULL) {
141                 talloc_free(lck);
142                 return NULL;
143         }
144
145         if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
146                 talloc_free(lck);
147                 return NULL;
148         }
149
150         talloc_set_destructor(lck, odb_lock_destructor);
151         
152         return lck;
153 }
154
155 /*
156   determine if two odb_entry structures conflict
157 */
158 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
159 {
160         if (e1->pending || e2->pending) return False;
161
162         /* if either open involves no read.write or delete access then
163            it can't conflict */
164         if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
165                                  SEC_FILE_APPEND_DATA |
166                                  SEC_FILE_READ_DATA |
167                                  SEC_FILE_EXECUTE |
168                                  SEC_STD_DELETE))) {
169                 return False;
170         }
171         if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
172                                  SEC_FILE_APPEND_DATA |
173                                  SEC_FILE_READ_DATA |
174                                  SEC_FILE_EXECUTE |
175                                  SEC_STD_DELETE))) {
176                 return False;
177         }
178
179         /* data IO access masks. This is skipped if the two open handles
180            are on different streams (as in that case the masks don't
181            interact) */
182         if (e1->stream_id != e2->stream_id) {
183                 return False;
184         }
185
186 #define CHECK_MASK(am, right, sa, share) \
187         if (((am) & (right)) && !((sa) & (share))) return True
188
189         CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
190                    e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
191         CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
192                    e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
193         
194         CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
195                    e2->share_access, NTCREATEX_SHARE_ACCESS_READ);
196         CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
197                    e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
198
199         CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
200                    e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
201         CHECK_MASK(e2->access_mask, SEC_STD_DELETE,
202                    e1->share_access, 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 }