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.
47 struct messaging_context *messaging_ctx;
51 the database is indexed by a file_key, and contains entries of the
58 uint32_t share_access;
59 uint32_t create_options;
67 an odb lock handle. You must obtain one of these using odb_lock() before doing
71 struct odb_context *odb;
76 Open up the openfiles.tdb database. Close it down using
77 talloc_free(). We need the messaging_ctx to allow for pending open
80 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server,
81 struct messaging_context *messaging_ctx)
84 struct odb_context *odb;
86 odb = talloc_p(mem_ctx, struct odb_context);
91 path = smbd_tmp_path(odb, "openfiles.tdb");
92 odb->w = tdb_wrap_open(odb, path, 0,
94 O_RDWR|O_CREAT, 0600);
101 odb->server = server;
102 odb->messaging_ctx = messaging_ctx;
108 destroy a lock on the database
110 static int odb_lock_destructor(void *ptr)
112 struct odb_lock *lck = ptr;
113 tdb_chainunlock(lck->odb->w->tdb, lck->key);
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().
121 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
122 struct odb_context *odb, DATA_BLOB *file_key)
124 struct odb_lock *lck;
126 lck = talloc_p(mem_ctx, struct odb_lock);
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) {
139 if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
144 talloc_set_destructor(lck, odb_lock_destructor);
150 determine if two odb_entry structures conflict
152 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
154 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
156 if (e1->pending || e2->pending) return False;
158 /* if either open involves no read.write or delete access then
160 if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
161 SA_RIGHT_FILE_READ_EXEC |
162 STD_RIGHT_DELETE_ACCESS))) {
165 if (!(e2->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
166 SA_RIGHT_FILE_READ_EXEC |
167 STD_RIGHT_DELETE_ACCESS))) {
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
174 if (e1->stream_id != e2->stream_id) {
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);
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);
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);
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)) {
209 register an open file in the open files database. This implements the share_access
212 NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
214 uint32_t share_access, uint32_t create_options,
215 uint32_t access_mask)
217 struct odb_context *odb = lck->odb;
222 struct odb_entry *elist;
224 dbuf = tdb_fetch(odb->w->tdb, lck->key);
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;
235 /* check the existing file opens to see if they
237 elist = (struct odb_entry *)dbuf.dptr;
238 count = dbuf.dsize / sizeof(struct odb_entry);
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;
247 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
249 if (dbuf.dptr) free(dbuf.dptr);
250 return NT_STATUS_NO_MEMORY;
254 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
256 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
257 &e, sizeof(struct odb_entry));
259 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
261 return NT_STATUS_INTERNAL_DB_CORRUPTION;
270 register a pending open file in the open files database
272 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
274 struct odb_context *odb = lck->odb;
278 struct odb_entry *elist;
281 dbuf = tdb_fetch(odb->w->tdb, lck->key);
283 e.server = odb->server;
284 e.file_handle = NULL;
287 e.create_options = 0;
289 e.notify_ptr = private;
292 /* check the existing file opens to see if they
294 elist = (struct odb_entry *)dbuf.dptr;
295 count = dbuf.dsize / sizeof(struct odb_entry);
297 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
299 if (dbuf.dptr) free(dbuf.dptr);
300 return NT_STATUS_NO_MEMORY;
304 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
306 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
307 &e, sizeof(struct odb_entry));
309 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
311 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320 remove a opendb entry
322 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
324 struct odb_context *odb = lck->odb;
326 struct odb_entry *elist;
330 dbuf = tdb_fetch(odb->w->tdb, lck->key);
332 if (dbuf.dptr == NULL) {
333 return NT_STATUS_UNSUCCESSFUL;
336 elist = (struct odb_entry *)dbuf.dptr;
337 count = dbuf.dsize / sizeof(struct odb_entry);
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)));
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) {
355 memmove(elist+i, elist+i+1,
356 (count - (i+1)) * sizeof(struct odb_entry));
362 status = NT_STATUS_OK;
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;
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;
384 remove a pending opendb entry
386 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
388 struct odb_context *odb = lck->odb;
390 struct odb_entry *elist;
394 dbuf = tdb_fetch(odb->w->tdb, lck->key);
396 if (dbuf.dptr == NULL) {
397 return NT_STATUS_UNSUCCESSFUL;
400 elist = (struct odb_entry *)dbuf.dptr;
401 count = dbuf.dsize / sizeof(struct odb_entry);
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) {
408 memmove(elist+i, elist+i+1,
409 (count - (i+1)) * sizeof(struct odb_entry));
415 status = NT_STATUS_OK;
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;
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;
437 update create options on an open file
439 NTSTATUS odb_set_create_options(struct odb_lock *lck,
440 void *file_handle, uint32_t create_options)
442 struct odb_context *odb = lck->odb;
444 struct odb_entry *elist;
448 dbuf = tdb_fetch(odb->w->tdb, lck->key);
449 if (dbuf.dptr == NULL) {
450 return NT_STATUS_UNSUCCESSFUL;
453 elist = (struct odb_entry *)dbuf.dptr;
454 count = dbuf.dsize / sizeof(struct odb_entry);
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;
465 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
466 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
468 status = NT_STATUS_OK;
478 determine if a file can be opened with the given share_access,
479 create_options and access_mask
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)
487 struct odb_entry *elist;
491 kbuf.dptr = key->data;
492 kbuf.dsize = key->length;
494 dbuf = tdb_fetch(odb->w->tdb, kbuf);
495 if (dbuf.dptr == NULL) {
499 elist = (struct odb_entry *)dbuf.dptr;
500 count = dbuf.dsize / sizeof(struct odb_entry);
507 e.server = odb->server;
508 e.file_handle = NULL;
510 e.share_access = share_access;
511 e.create_options = create_options;
512 e.access_mask = access_mask;
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;