r3153: pvfs now passes the first 9 of the BASE-DELETE tests
[bbaumbach/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
43 struct odb_context {
44         struct tdb_wrap *w;
45         servid_t server;
46         uint16_t tid;
47         void *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         uint16_t tid;
57         uint16_t fnum;
58         uint32_t share_access;
59         uint32_t create_options;
60         uint32_t access_mask;
61 };
62
63
64 /*
65   an odb lock handle. You must obtain one of these using odb_lock() before doing
66   any other operations. 
67 */
68 struct odb_lock {
69         struct odb_context *odb;
70         TDB_DATA key;
71 };
72
73 /*
74   Open up the openfiles.tdb database. Close it down using
75   talloc_free(). We need the messaging_ctx to allow for pending open
76   notifications.
77 */
78 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server, uint16_t tid, 
79                              void *messaging_ctx)
80 {
81         char *path;
82         struct odb_context *odb;
83
84         odb = talloc_p(mem_ctx, struct odb_context);
85         if (odb == NULL) {
86                 return NULL;
87         }
88
89         path = lock_path(odb, "openfiles.tdb");
90         odb->w = tdb_wrap_open(odb, path, 0,  
91                                TDB_DEFAULT|TDB_CLEAR_IF_FIRST,
92                                O_RDWR|O_CREAT, 0600);
93         talloc_free(path);
94         if (odb->w == NULL) {
95                 talloc_free(odb);
96                 return NULL;
97         }
98
99         odb->server = server;
100         odb->tid = tid;
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 = 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 /*
150   determine if two odb_entry structures conflict
151 */
152 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
153 {
154         uint32_t m1, m2;
155
156         m1 = e1->access_mask & (SA_RIGHT_FILE_WRITE_DATA | SA_RIGHT_FILE_READ_DATA);
157         m2 = e2->share_access & 
158                 (NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_READ);
159
160         if ((m1 & m2) != m1) {
161                 return True;
162         }
163
164         m1 = e2->access_mask & (SA_RIGHT_FILE_WRITE_DATA | SA_RIGHT_FILE_READ_DATA);
165         m2 = e1->share_access & 
166                 (NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_READ);
167
168         if ((m1 & m2) != m1) {
169                 return True;
170         }
171
172         if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
173             (e2->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
174                 return True;
175         }
176
177         if ((e1->access_mask & STD_RIGHT_DELETE_ACCESS) &&
178             !(e2->share_access & NTCREATEX_SHARE_ACCESS_DELETE)) {
179                 return True;
180         }
181             
182
183         return False;
184 }
185
186 /*
187   register an open file in the open files database. This implements the share_access
188   rules
189 */
190 NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum, 
191                        uint32_t share_access, uint32_t create_options,
192                        uint32_t access_mask)
193 {
194         struct odb_context *odb = lck->odb;
195         TDB_DATA dbuf;
196         struct odb_entry e;
197         char *tp;
198         int i, count;
199         struct odb_entry *elist;
200                 
201         dbuf = tdb_fetch(odb->w->tdb, lck->key);
202
203         e.server         = odb->server;
204         e.tid            = odb->tid;
205         e.fnum           = fnum;
206         e.share_access   = share_access;
207         e.create_options = create_options;
208         e.access_mask    = access_mask;
209
210         /* check the existing file opens to see if they
211            conflict */
212         elist = (struct odb_entry *)dbuf.dptr;
213         count = dbuf.dsize / sizeof(struct odb_entry);
214
215         for (i=0;i<count;i++) {
216                 if (share_conflict(elist+i, &e)) {
217                         if (dbuf.dptr) free(dbuf.dptr);
218                         return NT_STATUS_SHARING_VIOLATION;
219                 }
220         }
221
222         tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
223         if (tp == NULL) {
224                 if (dbuf.dptr) free(dbuf.dptr);
225                 return NT_STATUS_NO_MEMORY;
226         }
227
228         dbuf.dptr = tp;
229         dbuf.dsize = (count+1) * sizeof(struct odb_entry);
230
231         memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
232                &e, sizeof(struct odb_entry));
233
234         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
235                 free(dbuf.dptr);
236                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
237         }
238
239         free(dbuf.dptr);
240         return NT_STATUS_OK;
241 }
242
243
244 /*
245   remove a opendb entry
246 */
247 NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
248 {
249         struct odb_context *odb = lck->odb;
250         TDB_DATA dbuf;
251         struct odb_entry *elist;
252         int i, count;
253         NTSTATUS status;
254
255         dbuf = tdb_fetch(odb->w->tdb, lck->key);
256
257         if (dbuf.dptr == NULL) {
258                 return NT_STATUS_UNSUCCESSFUL;
259         }
260
261         elist = (struct odb_entry *)dbuf.dptr;
262         count = dbuf.dsize / sizeof(struct odb_entry);
263
264         /* find the entry, and delete it */
265         for (i=0;i<count;i++) {
266                 if (fnum == elist[i].fnum &&
267                     odb->server == elist[i].server &&
268                     odb->tid == elist[i].tid) {
269                         if (i < count-1) {
270                                 memmove(elist+i, elist+i+1, 
271                                         (count - (i+1)) * sizeof(struct odb_entry));
272                         }
273                         break;
274                 }
275         }
276
277         status = NT_STATUS_OK;
278
279         if (i == count) {
280                 status = NT_STATUS_UNSUCCESSFUL;
281         } else if (count == 1) {
282                 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
283                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
284                 }
285         } else {
286                 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
287                 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
288                         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
289                 }
290         }
291
292         free(dbuf.dptr);
293
294         return status;
295 }
296
297
298 /*
299   update create options on an open file
300 */
301 NTSTATUS odb_set_create_options(struct odb_lock *lck, 
302                                 uint16_t fnum, uint32_t create_options)
303 {
304         struct odb_context *odb = lck->odb;
305         TDB_DATA dbuf;
306         struct odb_entry *elist;
307         int i, count;
308         NTSTATUS status;
309
310         dbuf = tdb_fetch(odb->w->tdb, lck->key);
311         if (dbuf.dptr == NULL) {
312                 return NT_STATUS_UNSUCCESSFUL;
313         }
314
315         elist = (struct odb_entry *)dbuf.dptr;
316         count = dbuf.dsize / sizeof(struct odb_entry);
317
318         /* find the entry, and modify it */
319         for (i=0;i<count;i++) {
320                 if (fnum == elist[i].fnum &&
321                     odb->server == elist[i].server &&
322                     odb->tid == elist[i].tid) {
323                         elist[i].create_options = create_options;
324                         break;
325                 }
326         }
327
328         if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
329                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
330         } else {
331                 status = NT_STATUS_OK;
332         }
333
334         free(dbuf.dptr);
335
336         return status;
337 }