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