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.
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, servid_t server, uint16_t tid,
82 struct messaging_context *messaging_ctx)
85 struct odb_context *odb;
87 odb = talloc_p(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;
104 odb->messaging_ctx = messaging_ctx;
110 destroy a lock on the database
112 static int odb_lock_destructor(void *ptr)
114 struct odb_lock *lck = ptr;
115 tdb_chainunlock(lck->odb->w->tdb, lck->key);
120 get a lock on a entry in the odb. This call returns a lock handle,
121 which the caller should unlock using talloc_free().
123 struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
124 struct odb_context *odb, DATA_BLOB *file_key)
126 struct odb_lock *lck;
128 lck = talloc_p(mem_ctx, struct odb_lock);
134 lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
135 lck->key.dsize = file_key->length;
136 if (lck->key.dptr == NULL) {
141 if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
146 talloc_set_destructor(lck, odb_lock_destructor);
152 determine if two odb_entry structures conflict
154 static BOOL share_conflict(struct odb_entry *e1, struct odb_entry *e2)
156 #define CHECK_MASK(am, sa, right, share) if (((am) & (right)) && !((sa) & (share))) return True
158 if (e1->pending || e2->pending) return False;
160 /* if either open involves no read.write or delete access then
162 if (!(e1->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
163 SA_RIGHT_FILE_READ_EXEC |
164 STD_RIGHT_DELETE_ACCESS))) {
167 if (!(e2->access_mask & (SA_RIGHT_FILE_WRITE_APPEND |
168 SA_RIGHT_FILE_READ_EXEC |
169 STD_RIGHT_DELETE_ACCESS))) {
173 /* check the basic share access */
174 CHECK_MASK(e1->access_mask, e2->share_access,
175 SA_RIGHT_FILE_WRITE_APPEND,
176 NTCREATEX_SHARE_ACCESS_WRITE);
177 CHECK_MASK(e2->access_mask, e1->share_access,
178 SA_RIGHT_FILE_WRITE_APPEND,
179 NTCREATEX_SHARE_ACCESS_WRITE);
181 CHECK_MASK(e1->access_mask, e2->share_access,
182 SA_RIGHT_FILE_READ_EXEC,
183 NTCREATEX_SHARE_ACCESS_READ);
184 CHECK_MASK(e2->access_mask, e1->share_access,
185 SA_RIGHT_FILE_READ_EXEC,
186 NTCREATEX_SHARE_ACCESS_READ);
188 CHECK_MASK(e1->access_mask, e2->share_access,
189 STD_RIGHT_DELETE_ACCESS,
190 NTCREATEX_SHARE_ACCESS_DELETE);
191 CHECK_MASK(e2->access_mask, e1->share_access,
192 STD_RIGHT_DELETE_ACCESS,
193 NTCREATEX_SHARE_ACCESS_DELETE);
195 /* if a delete is pending then a second open is not allowed */
196 if ((e1->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) ||
197 (e2->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
205 register an open file in the open files database. This implements the share_access
208 NTSTATUS odb_open_file(struct odb_lock *lck, uint16_t fnum,
209 uint32_t share_access, uint32_t create_options,
210 uint32_t access_mask)
212 struct odb_context *odb = lck->odb;
217 struct odb_entry *elist;
219 dbuf = tdb_fetch(odb->w->tdb, lck->key);
221 e.server = odb->server;
224 e.share_access = share_access;
225 e.create_options = create_options;
226 e.access_mask = access_mask;
230 /* check the existing file opens to see if they
232 elist = (struct odb_entry *)dbuf.dptr;
233 count = dbuf.dsize / sizeof(struct odb_entry);
235 for (i=0;i<count;i++) {
236 if (share_conflict(elist+i, &e)) {
237 if (dbuf.dptr) free(dbuf.dptr);
238 return NT_STATUS_SHARING_VIOLATION;
242 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
244 if (dbuf.dptr) free(dbuf.dptr);
245 return NT_STATUS_NO_MEMORY;
249 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
251 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
252 &e, sizeof(struct odb_entry));
254 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
256 return NT_STATUS_INTERNAL_DB_CORRUPTION;
265 register a pending open file in the open files database
267 NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
269 struct odb_context *odb = lck->odb;
273 struct odb_entry *elist;
276 dbuf = tdb_fetch(odb->w->tdb, lck->key);
278 e.server = odb->server;
282 e.create_options = 0;
284 e.notify_ptr = private;
287 /* check the existing file opens to see if they
289 elist = (struct odb_entry *)dbuf.dptr;
290 count = dbuf.dsize / sizeof(struct odb_entry);
292 tp = Realloc(dbuf.dptr, (count+1) * sizeof(struct odb_entry));
294 if (dbuf.dptr) free(dbuf.dptr);
295 return NT_STATUS_NO_MEMORY;
299 dbuf.dsize = (count+1) * sizeof(struct odb_entry);
301 memcpy(dbuf.dptr + (count*sizeof(struct odb_entry)),
302 &e, sizeof(struct odb_entry));
304 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
306 return NT_STATUS_INTERNAL_DB_CORRUPTION;
315 remove a opendb entry
317 NTSTATUS odb_close_file(struct odb_lock *lck, uint16_t fnum)
319 struct odb_context *odb = lck->odb;
321 struct odb_entry *elist;
325 dbuf = tdb_fetch(odb->w->tdb, lck->key);
327 if (dbuf.dptr == NULL) {
328 return NT_STATUS_UNSUCCESSFUL;
331 elist = (struct odb_entry *)dbuf.dptr;
332 count = dbuf.dsize / sizeof(struct odb_entry);
334 /* send any pending notifications */
335 for (i=0;i<count;i++) {
336 if (elist[i].pending) {
337 messaging_send_ptr(odb->messaging_ctx, elist[i].server,
338 MSG_PVFS_RETRY_OPEN, elist[i].notify_ptr);
343 /* find the entry, and delete it */
344 for (i=0;i<count;i++) {
345 if (fnum == elist[i].fnum &&
346 odb->server == elist[i].server &&
347 odb->tid == elist[i].tid) {
349 memmove(elist+i, elist+i+1,
350 (count - (i+1)) * sizeof(struct odb_entry));
356 status = NT_STATUS_OK;
359 status = NT_STATUS_UNSUCCESSFUL;
360 } else if (count == 1) {
361 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
362 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
365 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
366 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
367 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
378 remove a pending opendb entry
380 NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
382 struct odb_context *odb = lck->odb;
384 struct odb_entry *elist;
388 dbuf = tdb_fetch(odb->w->tdb, lck->key);
390 if (dbuf.dptr == NULL) {
391 return NT_STATUS_UNSUCCESSFUL;
394 elist = (struct odb_entry *)dbuf.dptr;
395 count = dbuf.dsize / sizeof(struct odb_entry);
397 /* find the entry, and delete it */
398 for (i=0;i<count;i++) {
399 if (private == elist[i].notify_ptr &&
400 odb->server == elist[i].server &&
401 odb->tid == elist[i].tid) {
403 memmove(elist+i, elist+i+1,
404 (count - (i+1)) * sizeof(struct odb_entry));
410 status = NT_STATUS_OK;
413 status = NT_STATUS_UNSUCCESSFUL;
414 } else if (count == 1) {
415 if (tdb_delete(odb->w->tdb, lck->key) != 0) {
416 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
419 dbuf.dsize = (count-1) * sizeof(struct odb_entry);
420 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
421 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
432 update create options on an open file
434 NTSTATUS odb_set_create_options(struct odb_lock *lck,
435 uint16_t fnum, uint32_t create_options)
437 struct odb_context *odb = lck->odb;
439 struct odb_entry *elist;
443 dbuf = tdb_fetch(odb->w->tdb, lck->key);
444 if (dbuf.dptr == NULL) {
445 return NT_STATUS_UNSUCCESSFUL;
448 elist = (struct odb_entry *)dbuf.dptr;
449 count = dbuf.dsize / sizeof(struct odb_entry);
451 /* find the entry, and modify it */
452 for (i=0;i<count;i++) {
453 if (fnum == elist[i].fnum &&
454 odb->server == elist[i].server &&
455 odb->tid == elist[i].tid) {
456 elist[i].create_options = create_options;
461 if (tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE) != 0) {
462 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
464 status = NT_STATUS_OK;
474 determine if a file can be opened with the given share_access,
475 create_options and access_mask
477 NTSTATUS odb_can_open(struct odb_context *odb, DATA_BLOB *key,
478 uint32_t share_access, uint32_t create_options,
479 uint32_t access_mask)
483 struct odb_entry *elist;
487 kbuf.dptr = key->data;
488 kbuf.dsize = key->length;
490 dbuf = tdb_fetch(odb->w->tdb, kbuf);
491 if (dbuf.dptr == NULL) {
495 elist = (struct odb_entry *)dbuf.dptr;
496 count = dbuf.dsize / sizeof(struct odb_entry);
503 e.server = odb->server;
506 e.share_access = share_access;
507 e.create_options = create_options;
508 e.access_mask = access_mask;
512 for (i=0;i<count;i++) {
513 if (share_conflict(elist+i, &e)) {
514 if (dbuf.dptr) free(dbuf.dptr);
515 return NT_STATUS_SHARING_VIOLATION;