r13276: start to work towards the BASE-DELETE test passing. This change
[amitay/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 "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   return NT_STATUS_OK on no conflict
159 */
160 static NTSTATUS share_conflict(struct odb_entry *e1, struct odb_entry *e2)
161 {
162         if (e1->pending || e2->pending) return NT_STATUS_OK;
163
164         /* if either open involves no read.write or delete access then
165            it can't conflict */
166         if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
167                                  SEC_FILE_APPEND_DATA |
168                                  SEC_FILE_READ_DATA |
169                                  SEC_FILE_EXECUTE |
170                                  SEC_STD_DELETE))) {
171                 return NT_STATUS_OK;
172         }
173         if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
174                                  SEC_FILE_APPEND_DATA |
175                                  SEC_FILE_READ_DATA |
176                                  SEC_FILE_EXECUTE |
177                                  SEC_STD_DELETE))) {
178                 return NT_STATUS_OK;
179         }
180
181         /* data IO access masks. This is skipped if the two open handles
182            are on different streams (as in that case the masks don't
183            interact) */
184         if (e1->stream_id != e2->stream_id) {
185                 return NT_STATUS_OK;
186         }
187
188 #define CHECK_MASK(am, right, sa, share) \
189         if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
190
191         CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
192                    e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
193         CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
194                    e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
195         
196         CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
197                    e2->share_access, NTCREATEX_SHARE_ACCESS_READ);
198         CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
199                    e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
200
201         CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
202                    e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
203         CHECK_MASK(e2->access_mask, SEC_STD_DELETE,
204                    e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
205
206         /* if a delete is pending then a second open is not allowed */
207         if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
208             (e2->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
209                 return NT_STATUS_DELETE_PENDING;
210         }
211
212         return NT_STATUS_OK;
213 }
214
215 /*
216   register an open file in the open files database. This implements the share_access
217   rules
218 */
219 NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
220                        uint32_t stream_id,
221                        uint32_t share_access, uint32_t create_options,
222                        uint32_t access_mask)
223 {
224         struct odb_context *odb = lck->odb;
225         TDB_DATA dbuf;
226         struct odb_entry e;
227         int i, count;
228         struct odb_entry *elist;
229                 
230         dbuf = tdb_fetch(odb->w->tdb, lck->key);
231
232         e.server         = odb->server;
233         e.file_handle    = file_handle;
234         e.stream_id      = stream_id;
235         e.share_access   = share_access;
236         e.create_options = create_options;
237         e.access_mask    = access_mask;
238         e.notify_ptr     = NULL;
239         e.pending        = False;
240
241         /* check the existing file opens to see if they
242            conflict */
243         elist = (struct odb_entry *)dbuf.dptr;
244         count = dbuf.dsize / sizeof(struct odb_entry);
245
246         for (i=0;i<count;i++) {
247                 NTSTATUS status;
248                 status = share_conflict(elist+i, &e);
249                 if (!NT_STATUS_IS_OK(status)) {
250                         if (dbuf.dptr) free(dbuf.dptr);
251                         return status;
252                 }
253         }
254
255         elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
256         if (elist == NULL) {
257                 if (dbuf.dptr) free(dbuf.dptr);
258                 return NT_STATUS_NO_MEMORY;
259         }
260
261         dbuf.dptr = (char *)elist;
262         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
263
264         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
265                &e, sizeof(struct odb_entry));
266
267         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
268                 free(dbuf.dptr);
269                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
270         }
271
272         free(dbuf.dptr);
273         return NT_STATUS_OK;
274 }
275
276
277 /*
278   register a pending open file in the open files database
279 */
280 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
281 {
282         struct odb_context *odb = lck->odb;
283         TDB_DATA dbuf;
284         struct odb_entry e;
285         struct odb_entry *elist;
286         int count;
287                 
288         dbuf = tdb_fetch(odb->w->tdb, lck->key);
289
290         e.server         = odb->server;
291         e.file_handle    = NULL;
292         e.stream_id      = 0;
293         e.share_access   = 0;
294         e.create_options = 0;
295         e.access_mask    = 0;
296         e.notify_ptr     = private;
297         e.pending        = True;
298
299         /* check the existing file opens to see if they
300            conflict */
301         elist = (struct odb_entry *)dbuf.dptr;
302         count = dbuf.dsize / sizeof(struct odb_entry);
303
304         elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
305         if (elist == NULL) {
306                 if (dbuf.dptr) free(dbuf.dptr);
307                 return NT_STATUS_NO_MEMORY;
308         }
309
310         dbuf.dptr = (char *)elist;
311         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
312
313         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
314                &e, sizeof(struct odb_entry));
315
316         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
317                 free(dbuf.dptr);
318                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
319         }
320
321         free(dbuf.dptr);
322         return NT_STATUS_OK;
323 }
324
325
326 /*
327   remove a opendb entry
328 */
329 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
330 {
331         struct odb_context *odb = lck->odb;
332         TDB_DATA dbuf;
333         struct odb_entry *elist;
334         int i, count;
335         NTSTATUS status;
336
337         dbuf = tdb_fetch(odb->w->tdb, lck->key);
338
339         if (dbuf.dptr == NULL) {
340                 return NT_STATUS_UNSUCCESSFUL;
341         }
342
343         elist = (struct odb_entry *)dbuf.dptr;
344         count = dbuf.dsize / sizeof(struct odb_entry);
345
346         /* send any pending notifications, removing them once sent */
347         for (i=0;i<count;i++) {
348                 if (elist[i].pending) {
349                         messaging_send_ptr(odb->messaging_ctx, elist[i].server, 
350                                            MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
351                         memmove(&elist[i], &elist[i+1], sizeof(struct odb_entry)*(count-(i+1)));
352                         i--;
353                         count--;
354                 }
355         }
356
357         /* find the entry, and delete it */
358         for (i=0;i<count;i++) {
359                 if (file_handle == elist[i].file_handle &&
360                     odb->server == elist[i].server) {
361                         if (i < count-1) {
362                                 memmove(elist+i, elist+i+1, 
363                                         (count - (i+1)) * sizeof(struct odb_entry));
364                         }
365                         break;
366                 }
367         }
368
369         status = NT_STATUS_OK;
370
371         if (i == count) {
372                 status = NT_STATUS_UNSUCCESSFUL;
373         } else if (count == 1) {
374                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
375                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
376                 }
377         } else {
378                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
379                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
380                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
381                 }
382         }
383
384         free(dbuf.dptr);
385
386         return status;
387 }
388
389
390 /*
391   remove a pending opendb entry
392 */
393 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
394 {
395         struct odb_context *odb = lck->odb;
396         TDB_DATA dbuf;
397         struct odb_entry *elist;
398         int i, count;
399         NTSTATUS status;
400
401         dbuf = tdb_fetch(odb->w->tdb, lck->key);
402
403         if (dbuf.dptr == NULL) {
404                 return NT_STATUS_UNSUCCESSFUL;
405         }
406
407         elist = (struct odb_entry *)dbuf.dptr;
408         count = dbuf.dsize / sizeof(struct odb_entry);
409
410         /* find the entry, and delete it */
411         for (i=0;i<count;i++) {
412                 if (private == elist[i].notify_ptr &&
413                     odb->server == elist[i].server) {
414                         if (i < count-1) {
415                                 memmove(elist+i, elist+i+1, 
416                                         (count - (i+1)) * sizeof(struct odb_entry));
417                         }
418                         break;
419                 }
420         }
421
422         status = NT_STATUS_OK;
423
424         if (i == count) {
425                 status = NT_STATUS_UNSUCCESSFUL;
426         } else if (count == 1) {
427                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
428                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
429                 }
430         } else {
431                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
432                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
433                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
434                 }
435         }
436
437         free(dbuf.dptr);
438
439         return status;
440 }
441
442
443 /*
444   update create options on an open file
445 */
446 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
447                                 void *file_handle, uint32_t create_options)
448 {
449         struct odb_context *odb = lck->odb;
450         TDB_DATA dbuf;
451         struct odb_entry *elist;
452         int i, count;
453         NTSTATUS status;
454
455         dbuf = tdb_fetch(odb->w->tdb, lck->key);
456         if (dbuf.dptr == NULL) {
457                 return NT_STATUS_UNSUCCESSFUL;
458         }
459
460         elist = (struct odb_entry *)dbuf.dptr;
461         count = dbuf.dsize / sizeof(struct odb_entry);
462
463         /* find the entry, and modify it */
464         for (i=0;i<count;i++) {
465                 if (file_handle == elist[i].file_handle &&
466                     odb->server == elist[i].server) {
467                         elist[i].create_options = create_options;
468                         break;
469                 }
470         }
471
472         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
473                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
474         } else {
475                 status = NT_STATUS_OK;
476         }
477
478         free(dbuf.dptr);
479
480         return status;
481 }
482
483
484 /*
485   determine if a file can be opened with the given share_access,
486   create_options and access_mask
487 */
488 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key, 
489                       uint32_t share_access, uint32_t create_options, 
490                       uint32_t access_mask)
491 {
492         TDB_DATA dbuf;
493         TDB_DATA kbuf;
494         struct odb_entry *elist;
495         int i, count;
496         struct odb_entry e;
497
498         kbuf.dptr = (char *)key->data;
499         kbuf.dsize = key->length;
500
501         dbuf = tdb_fetch(odb->w->tdb, kbuf);
502         if (dbuf.dptr == NULL) {
503                 return NT_STATUS_OK;
504         }
505
506         elist = (struct odb_entry *)dbuf.dptr;
507         count = dbuf.dsize / sizeof(struct odb_entry);
508
509         if (count == 0) {
510                 free(dbuf.dptr);
511                 return NT_STATUS_OK;
512         }
513
514         e.server         = odb->server;
515         e.file_handle    = NULL;
516         e.stream_id      = 0;
517         e.share_access   = share_access;
518         e.create_options = create_options;
519         e.access_mask    = access_mask;
520         e.notify_ptr     = NULL;
521         e.pending        = False;
522
523         for (i=0;i<count;i++) {
524                 NTSTATUS status;
525                 status = share_conflict(elist+i, &e);
526                 if (!NT_STATUS_IS_OK(status)) {
527                         if (dbuf.dptr) free(dbuf.dptr);
528                         /* note that we discard the error code
529                            here. We do this as unless we are actually
530                            doing an open (which comes via a sdifferent
531                            function), we need to return a sharing
532                            violation */
533                         return NT_STATUS_SHARING_VIOLATION;
534                 }
535         }
536
537         free(dbuf.dptr);
538         return NT_STATUS_OK;
539 }