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