8947a5d25575f38d4e6fb2c954a7e1ef0498fe56
[samba.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         servid_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         servid_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, servid_t server, 
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->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_p(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         char *tp;
226         int i, count;
227         struct odb_entry *elist;
228                 
229         dbuf = tdb_fetch(odb->w->tdb, lck->key);
230
231         e.server         = odb->server;
232         e.file_handle    = file_handle;
233         e.stream_id      = stream_id;
234         e.share_access   = share_access;
235         e.create_options = create_options;
236         e.access_mask    = access_mask;
237         e.notify_ptr     = NULL;
238         e.pending        = False;
239
240         /* check the existing file opens to see if they
241            conflict */
242         elist = (struct odb_entry *)dbuf.dptr;
243         count = dbuf.dsize / sizeof(struct odb_entry);
244
245         for (i=0;i<count;i++) {
246                 if (share_conflict(elist+i, &e)) {
247                         if (dbuf.dptr) free(dbuf.dptr);
248                         return NT_STATUS_SHARING_VIOLATION;
249                 }
250         }
251
252         tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
253         if (tp == NULL) {
254                 if (dbuf.dptr) free(dbuf.dptr);
255                 return NT_STATUS_NO_MEMORY;
256         }
257
258         dbuf.dptr = tp;
259         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
260
261         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
262                &e, sizeof(struct odb_entry));
263
264         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
265                 free(dbuf.dptr);
266                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
267         }
268
269         free(dbuf.dptr);
270         return NT_STATUS_OK;
271 }
272
273
274 /*
275   register a pending open file in the open files database
276 */
277 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
278 {
279         struct odb_context *odb = lck->odb;
280         TDB_DATA dbuf;
281         struct odb_entry e;
282         char *tp;
283         struct odb_entry *elist;
284         int count;
285                 
286         dbuf = tdb_fetch(odb->w->tdb, lck->key);
287
288         e.server         = odb->server;
289         e.file_handle    = NULL;
290         e.stream_id      = 0;
291         e.share_access   = 0;
292         e.create_options = 0;
293         e.access_mask    = 0;
294         e.notify_ptr     = private;
295         e.pending        = True;
296
297         /* check the existing file opens to see if they
298            conflict */
299         elist = (struct odb_entry *)dbuf.dptr;
300         count = dbuf.dsize / sizeof(struct odb_entry);
301
302         tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
303         if (tp == NULL) {
304                 if (dbuf.dptr) free(dbuf.dptr);
305                 return NT_STATUS_NO_MEMORY;
306         }
307
308         dbuf.dptr = tp;
309         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
310
311         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
312                &e, sizeof(struct odb_entry));
313
314         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
315                 free(dbuf.dptr);
316                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
317         }
318
319         free(dbuf.dptr);
320         return NT_STATUS_OK;
321 }
322
323
324 /*
325   remove a opendb entry
326 */
327 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
328 {
329         struct odb_context *odb = lck->odb;
330         TDB_DATA dbuf;
331         struct odb_entry *elist;
332         int i, count;
333         NTSTATUS status;
334
335         dbuf = tdb_fetch(odb->w->tdb, lck->key);
336
337         if (dbuf.dptr == NULL) {
338                 return NT_STATUS_UNSUCCESSFUL;
339         }
340
341         elist = (struct odb_entry *)dbuf.dptr;
342         count = dbuf.dsize / sizeof(struct odb_entry);
343
344         /* send any pending notifications, removing them once sent */
345         for (i=0;i<count;i++) {
346                 if (elist[i].pending) {
347                         messaging_send_ptr(odb->messaging_ctx, elist[i].server, 
348                                            MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
349                         memmove(&elist[i], &elist[i+1], sizeof(struct odb_entry)*(count-(i+1)));
350                         i--;
351                         count--;
352                 }
353         }
354
355         /* find the entry, and delete it */
356         for (i=0;i<count;i++) {
357                 if (file_handle == elist[i].file_handle &&
358                     odb->server == elist[i].server) {
359                         if (i < count-1) {
360                                 memmove(elist+i, elist+i+1, 
361                                         (count - (i+1)) * sizeof(struct odb_entry));
362                         }
363                         break;
364                 }
365         }
366
367         status = NT_STATUS_OK;
368
369         if (i == count) {
370                 status = NT_STATUS_UNSUCCESSFUL;
371         } else if (count == 1) {
372                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
373                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
374                 }
375         } else {
376                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
377                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
378                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
379                 }
380         }
381
382         free(dbuf.dptr);
383
384         return status;
385 }
386
387
388 /*
389   remove a pending opendb entry
390 */
391 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
392 {
393         struct odb_context *odb = lck->odb;
394         TDB_DATA dbuf;
395         struct odb_entry *elist;
396         int i, count;
397         NTSTATUS status;
398
399         dbuf = tdb_fetch(odb->w->tdb, lck->key);
400
401         if (dbuf.dptr == NULL) {
402                 return NT_STATUS_UNSUCCESSFUL;
403         }
404
405         elist = (struct odb_entry *)dbuf.dptr;
406         count = dbuf.dsize / sizeof(struct odb_entry);
407
408         /* find the entry, and delete it */
409         for (i=0;i<count;i++) {
410                 if (private == elist[i].notify_ptr &&
411                     odb->server == elist[i].server) {
412                         if (i < count-1) {
413                                 memmove(elist+i, elist+i+1, 
414                                         (count - (i+1)) * sizeof(struct odb_entry));
415                         }
416                         break;
417                 }
418         }
419
420         status = NT_STATUS_OK;
421
422         if (i == count) {
423                 status = NT_STATUS_UNSUCCESSFUL;
424         } else if (count == 1) {
425                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
426                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
427                 }
428         } else {
429                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
430                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
431                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
432                 }
433         }
434
435         free(dbuf.dptr);
436
437         return status;
438 }
439
440
441 /*
442   update create options on an open file
443 */
444 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
445                                 void *file_handle, uint32_t create_options)
446 {
447         struct odb_context *odb = lck->odb;
448         TDB_DATA dbuf;
449         struct odb_entry *elist;
450         int i, count;
451         NTSTATUS status;
452
453         dbuf = tdb_fetch(odb->w->tdb, lck->key);
454         if (dbuf.dptr == NULL) {
455                 return NT_STATUS_UNSUCCESSFUL;
456         }
457
458         elist = (struct odb_entry *)dbuf.dptr;
459         count = dbuf.dsize / sizeof(struct odb_entry);
460
461         /* find the entry, and modify it */
462         for (i=0;i<count;i++) {
463                 if (file_handle == elist[i].file_handle &&
464                     odb->server == elist[i].server) {
465                         elist[i].create_options = create_options;
466                         break;
467                 }
468         }
469
470         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
471                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
472         } else {
473                 status = NT_STATUS_OK;
474         }
475
476         free(dbuf.dptr);
477
478         return status;
479 }
480
481
482 /*
483   determine if a file can be opened with the given share_access,
484   create_options and access_mask
485 */
486 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, 
487                       uint32_t share_access, uint32_t create_options, 
488                       uint32_t access_mask)
489 {
490         TDB_DATA dbuf;
491         TDB_DATA kbuf;
492         struct odb_entry *elist;
493         int i, count;
494         struct odb_entry e;
495
496         kbuf.dptr = (char *)key->data;
497         kbuf.dsize = key->length;
498
499         dbuf = tdb_fetch(odb->w->tdb, kbuf);
500         if (dbuf.dptr == NULL) {
501                 return NT_STATUS_OK;
502         }
503
504         elist = (struct odb_entry *)dbuf.dptr;
505         count = dbuf.dsize / sizeof(struct odb_entry);
506
507         if (count == 0) {
508                 free(dbuf.dptr);
509                 return NT_STATUS_OK;
510         }
511
512         e.server         = odb->server;
513         e.file_handle    = NULL;
514         e.stream_id      = 0;
515         e.share_access   = share_access;
516         e.create_options = create_options;
517         e.access_mask    = access_mask;
518         e.notify_ptr     = NULL;
519         e.pending        = False;
520
521         for (i=0;i<count;i++) {
522                 if (share_conflict(elist+i, &e)) {
523                         if (dbuf.dptr) free(dbuf.dptr);
524                         return NT_STATUS_SHARING_VIOLATION;
525                 }
526         }
527
528         free(dbuf.dptr);
529         return NT_STATUS_OK;
530 }