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
57 uint32_t share_access;
58 uint32_t create_options;
66 an odb lock handle. You must obtain one of these using odb_lock() before doing
70 struct odb_context *odb;
75 Open up the openfiles.tdb database. Close it down using
76 talloc_free(). We need the messaging_ctx to allow for pending open
79 struct odb_context *odb_init(TALLOC_CTX *mem_ctx, servid_t server,
80 struct messaging_context *messaging_ctx)
83 struct odb_context *odb;
85 odb = talloc_p(mem_ctx, struct odb_context);
90 path = smbd_tmp_path(odb, "openfiles.tdb");
91 odb->w = tdb_wrap_open(odb, path, 0,
93 O_RDWR|O_CREAT, 0600);
100 odb->server = server;
101 odb->messaging_ctx = messaging_ctx;
107 destroy a lock on the database
109 static int odb_lock_destructor(void *ptr)
111 struct odb_lock *lck = ptr;
112 tdb_chainunlock(lck->odb->w->tdb, lck->key);
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().
120 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
121 struct odb_context *odb, DATA_BLOB *file_key)
123 struct odb_lock *lck;
125 lck = talloc_p(mem_ctx, struct odb_lock);
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) {
138 if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
143 talloc_set_destructor(lck, odb_lock_destructor);
149 determine if two odb_entry structures conflict
151 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
153 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
155 if (e1->pending || e2->pending) return False;
157 /* if either open involves no read.write or delete access then
159 if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
160 SA_RIGHT_FILE_READ_EXEC |
161 STD_RIGHT_DELETE_ACCESS))) {
164 if (!(e2->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
165 SA_RIGHT_FILE_READ_EXEC |
166 STD_RIGHT_DELETE_ACCESS))) {
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);
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);
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);
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)) {
202 register an open file in the open files database. This implements the share_access
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)
209 struct odb_context *odb = lck->odb;
214 struct odb_entry *elist;
216 dbuf = tdb_fetch(odb->w->tdb, lck->key);
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;
226 /* check the existing file opens to see if they
228 elist = (struct odb_entry *)dbuf.dptr;
229 count = dbuf.dsize / sizeof(struct odb_entry);
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;
238 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
240 if (dbuf.dptr) free(dbuf.dptr);
241 return NT_STATUS_NO_MEMORY;
245 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
247 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
248 &e, sizeof(struct odb_entry));
250 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
252 return NT_STATUS_INTERNAL_DB_CORRUPTION;
261 register a pending open file in the open files database
263 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
265 struct odb_context *odb = lck->odb;
269 struct odb_entry *elist;
272 dbuf = tdb_fetch(odb->w->tdb, lck->key);
274 e.server = odb->server;
275 e.file_handle = NULL;
277 e.create_options = 0;
279 e.notify_ptr = private;
282 /* check the existing file opens to see if they
284 elist = (struct odb_entry *)dbuf.dptr;
285 count = dbuf.dsize / sizeof(struct odb_entry);
287 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
289 if (dbuf.dptr) free(dbuf.dptr);
290 return NT_STATUS_NO_MEMORY;
294 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
296 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
297 &e, sizeof(struct odb_entry));
299 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
301 return NT_STATUS_INTERNAL_DB_CORRUPTION;
310 remove a opendb entry
312 NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
314 struct odb_context *odb = lck->odb;
316 struct odb_entry *elist;
320 dbuf = tdb_fetch(odb->w->tdb, lck->key);
322 if (dbuf.dptr == NULL) {
323 return NT_STATUS_UNSUCCESSFUL;
326 elist = (struct odb_entry *)dbuf.dptr;
327 count = dbuf.dsize / sizeof(struct odb_entry);
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)));
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) {
345 memmove(elist+i, elist+i+1,
346 (count - (i+1)) * sizeof(struct odb_entry));
352 status = NT_STATUS_OK;
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;
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;
374 remove a pending opendb entry
376 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
378 struct odb_context *odb = lck->odb;
380 struct odb_entry *elist;
384 dbuf = tdb_fetch(odb->w->tdb, lck->key);
386 if (dbuf.dptr == NULL) {
387 return NT_STATUS_UNSUCCESSFUL;
390 elist = (struct odb_entry *)dbuf.dptr;
391 count = dbuf.dsize / sizeof(struct odb_entry);
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) {
398 memmove(elist+i, elist+i+1,
399 (count - (i+1)) * sizeof(struct odb_entry));
405 status = NT_STATUS_OK;
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;
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;
427 update create options on an open file
429 NTSTATUS odb_set_create_options(struct odb_lock *lck,
430 void *file_handle, uint32_t create_options)
432 struct odb_context *odb = lck->odb;
434 struct odb_entry *elist;
438 dbuf = tdb_fetch(odb->w->tdb, lck->key);
439 if (dbuf.dptr == NULL) {
440 return NT_STATUS_UNSUCCESSFUL;
443 elist = (struct odb_entry *)dbuf.dptr;
444 count = dbuf.dsize / sizeof(struct odb_entry);
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;
455 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
456 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
458 status = NT_STATUS_OK;
468 determine if a file can be opened with the given share_access,
469 create_options and access_mask
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)
477 struct odb_entry *elist;
481 kbuf.dptr = key->data;
482 kbuf.dsize = key->length;
484 dbuf = tdb_fetch(odb->w->tdb, kbuf);
485 if (dbuf.dptr == NULL) {
489 elist = (struct odb_entry *)dbuf.dptr;
490 count = dbuf.dsize / sizeof(struct odb_entry);
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;
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;