2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2004
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.
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.
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.
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.
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
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.
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.
43 #include "librpc/gen_ndr/ndr_security.h"
48 struct messaging_context *messaging_ctx;
52 the database is indexed by a file_key, and contains entries of the
59 uint32_t share_access;
60 uint32_t create_options;
68 an odb lock handle. You must obtain one of these using odb_lock() before doing
72 struct odb_context *odb;
77 Open up the openfiles.tdb database. Close it down using
78 talloc_free(). We need the messaging_ctx to allow for pending open
81 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, uint32_t server,
82 struct messaging_context *messaging_ctx)
85 struct odb_context *odb;
87 odb = talloc(mem_ctx, struct odb_context);
92 path = smbd_tmp_path(odb, "openfiles.tdb");
93 odb->w = tdb_wrap_open(odb, path, 0,
95 O_RDWR|O_CREAT, 0600);
102 odb->server = server;
103 odb->messaging_ctx = messaging_ctx;
109 destroy a lock on the database
111 static int odb_lock_destructor(void *ptr)
113 struct odb_lock *lck = ptr;
114 tdb_chainunlock(lck->odb->w->tdb, lck->key);
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().
122 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
123 struct odb_context *odb, DATA_BLOB *file_key)
125 struct odb_lock *lck;
127 lck = talloc(mem_ctx, struct odb_lock);
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) {
140 if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
145 talloc_set_destructor(lck, odb_lock_destructor);
151 determine if two odb_entry structures conflict
153 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
155 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
157 if (e1->pending || e2->pending) return False;
159 /* if either open involves no read.write or delete access then
161 if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
162 SEC_FILE_APPEND_DATA |
168 if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
169 SEC_FILE_APPEND_DATA |
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
179 if (e1->stream_id != e2->stream_id) {
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);
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);
197 CHECK_MASK(e1->access_mask, e2->share_access,
199 NTCREATEX_SHARE_ACCESS_DELETE);
200 CHECK_MASK(e2->access_mask, e1->share_access,
202 NTCREATEX_SHARE_ACCESS_DELETE);
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)) {
214 register an open file in the open files database. This implements the share_access
217 NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
219 uint32_t share_access, uint32_t create_options,
220 uint32_t access_mask)
222 struct odb_context *odb = lck->odb;
226 struct odb_entry *elist;
228 dbuf = tdb_fetch(odb->w->tdb, lck->key);
230 e.server = odb->server;
231 e.file_handle = file_handle;
232 e.stream_id = stream_id;
233 e.share_access = share_access;
234 e.create_options = create_options;
235 e.access_mask = access_mask;
239 /* check the existing file opens to see if they
241 elist = (struct odb_entry *)dbuf.dptr;
242 count = dbuf.dsize / sizeof(struct odb_entry);
244 for (i=0;i<count;i++) {
245 if (share_conflict(elist+i, &e)) {
246 if (dbuf.dptr) free(dbuf.dptr);
247 return NT_STATUS_SHARING_VIOLATION;
251 elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
253 if (dbuf.dptr) free(dbuf.dptr);
254 return NT_STATUS_NO_MEMORY;
257 dbuf.dptr = (char *)elist;
258 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
260 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
261 &e, sizeof(struct odb_entry));
263 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
265 return NT_STATUS_INTERNAL_DB_CORRUPTION;
274 register a pending open file in the open files database
276 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
278 struct odb_context *odb = lck->odb;
281 struct odb_entry *elist;
284 dbuf = tdb_fetch(odb->w->tdb, lck->key);
286 e.server = odb->server;
287 e.file_handle = NULL;
290 e.create_options = 0;
292 e.notify_ptr = private;
295 /* check the existing file opens to see if they
297 elist = (struct odb_entry *)dbuf.dptr;
298 count = dbuf.dsize / sizeof(struct odb_entry);
300 elist = realloc_p(dbuf.dptr, struct odb_entry, count+1);
302 if (dbuf.dptr) free(dbuf.dptr);
303 return NT_STATUS_NO_MEMORY;
306 dbuf.dptr = (char *)elist;
307 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
309 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
310 &e, sizeof(struct odb_entry));
312 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
314 return NT_STATUS_INTERNAL_DB_CORRUPTION;
323 remove a opendb entry
325 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
327 struct odb_context *odb = lck->odb;
329 struct odb_entry *elist;
333 dbuf = tdb_fetch(odb->w->tdb, lck->key);
335 if (dbuf.dptr == NULL) {
336 return NT_STATUS_UNSUCCESSFUL;
339 elist = (struct odb_entry *)dbuf.dptr;
340 count = dbuf.dsize / sizeof(struct odb_entry);
342 /* send any pending notifications, removing them once sent */
343 for (i=0;i<count;i++) {
344 if (elist[i].pending) {
345 messaging_send_ptr(odb->messaging_ctx, elist[i].server,
346 MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
347 memmove(&elist[i], &elist[i+1], sizeof(struct odb_entry)*(count-(i+1)));
353 /* find the entry, and delete it */
354 for (i=0;i<count;i++) {
355 if (file_handle == elist[i].file_handle &&
356 odb->server == elist[i].server) {
358 memmove(elist+i, elist+i+1,
359 (count - (i+1)) * sizeof(struct odb_entry));
365 status = NT_STATUS_OK;
368 status = NT_STATUS_UNSUCCESSFUL;
369 } else if (count == 1) {
370 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
371 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
374 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
375 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
376 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
387 remove a pending opendb entry
389 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
391 struct odb_context *odb = lck->odb;
393 struct odb_entry *elist;
397 dbuf = tdb_fetch(odb->w->tdb, lck->key);
399 if (dbuf.dptr == NULL) {
400 return NT_STATUS_UNSUCCESSFUL;
403 elist = (struct odb_entry *)dbuf.dptr;
404 count = dbuf.dsize / sizeof(struct odb_entry);
406 /* find the entry, and delete it */
407 for (i=0;i<count;i++) {
408 if (private == elist[i].notify_ptr &&
409 odb->server == elist[i].server) {
411 memmove(elist+i, elist+i+1,
412 (count - (i+1)) * sizeof(struct odb_entry));
418 status = NT_STATUS_OK;
421 status = NT_STATUS_UNSUCCESSFUL;
422 } else if (count == 1) {
423 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
424 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
427 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
428 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
429 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
440 update create options on an open file
442 NTSTATUS odb_set_create_options(struct odb_lock *lck,
443 void *file_handle, uint32_t create_options)
445 struct odb_context *odb = lck->odb;
447 struct odb_entry *elist;
451 dbuf = tdb_fetch(odb->w->tdb, lck->key);
452 if (dbuf.dptr == NULL) {
453 return NT_STATUS_UNSUCCESSFUL;
456 elist = (struct odb_entry *)dbuf.dptr;
457 count = dbuf.dsize / sizeof(struct odb_entry);
459 /* find the entry, and modify it */
460 for (i=0;i<count;i++) {
461 if (file_handle == elist[i].file_handle &&
462 odb->server == elist[i].server) {
463 elist[i].create_options = create_options;
468 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
469 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
471 status = NT_STATUS_OK;
481 determine if a file can be opened with the given share_access,
482 create_options and access_mask
484 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key,
485 uint32_t share_access, uint32_t create_options,
486 uint32_t access_mask)
490 struct odb_entry *elist;
494 kbuf.dptr = (char *)key->data;
495 kbuf.dsize = key->length;
497 dbuf = tdb_fetch(odb->w->tdb, kbuf);
498 if (dbuf.dptr == NULL) {
502 elist = (struct odb_entry *)dbuf.dptr;
503 count = dbuf.dsize / sizeof(struct odb_entry);
510 e.server = odb->server;
511 e.file_handle = NULL;
513 e.share_access = share_access;
514 e.create_options = create_options;
515 e.access_mask = access_mask;
519 for (i=0;i<count;i++) {
520 if (share_conflict(elist+i, &e)) {
521 if (dbuf.dptr) free(dbuf.dptr);
522 return NT_STATUS_SHARING_VIOLATION;