1/* 2 * Copyright (c) 2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * Portions of this software have been released under the following terms: 31 * 32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. 33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY 34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION 35 * 36 * To anyone who acknowledges that this file is provided "AS IS" 37 * without any express or implied warranty: 38 * permission to use, copy, modify, and distribute this file for any 39 * purpose is hereby granted without fee, provided that the above 40 * copyright notices and this notice appears in all source code copies, 41 * and that none of the names of Open Software Foundation, Inc., Hewlett- 42 * Packard Company or Digital Equipment Corporation be used 43 * in advertising or publicity pertaining to distribution of the software 44 * without specific, written prior permission. Neither Open Software 45 * Foundation, Inc., Hewlett-Packard Company nor Digital 46 * Equipment Corporation makes any representations about the suitability 47 * of this software for any purpose. 48 * 49 * Copyright (c) 2007, Novell, Inc. All rights reserved. 50 * Redistribution and use in source and binary forms, with or without 51 * modification, are permitted provided that the following conditions 52 * are met: 53 * 54 * 1. Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 2. Redistributions in binary form must reproduce the above copyright 57 * notice, this list of conditions and the following disclaimer in the 58 * documentation and/or other materials provided with the distribution. 59 * 3. Neither the name of Novell Inc. nor the names of its contributors 60 * may be used to endorse or promote products derived from this 61 * this software without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY 67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 * 74 * @APPLE_LICENSE_HEADER_END@ 75 */ 76 77/* 78** 79** NAME: 80** 81** rpcddb.c 82** 83** FACILITY: 84** 85** RPC Daemon Basic Database Routines 86** 87** ABSTRACT: 88** 89** Hash table management, list management, context handle management, 90** database concurrency control (lock/unlock) routines. Basic database 91** open and update routine. 92** 93** 94*/ 95 96#include <dce/ep.h> 97#include <dsm.h> 98 99#include <commonp.h> 100#include <com.h> 101 102#include <rpcdp.h> 103#include <rpcddb.h> 104 105typedef struct { 106 struct db *db_handle; 107 db_list_type_t list_type; 108 db_lists_t *lp; 109 unsigned32 pass; 110} db_contexth_t, *db_contexth_p_t; 111 112INTERNAL void list_add 113 ( 114 db_list_t *list, 115 db_list_t *elp 116 ); 117 118INTERNAL void list_remove 119 ( 120 db_list_t *list, 121 db_list_t *elp 122 ); 123 124INTERNAL boolean32 db_bad_context 125 ( 126 struct db *h, 127 ept_lookup_handle_t *entry_handle 128 ); 129 130/* 131 * Setup the persistent database image. 132 * If no database exists on disk, create it. 133 */ 134PRIVATE void db_open(h, database_file, version, status) 135struct db *h; 136unsigned char *database_file; 137unsigned32 version; 138error_status_t *status; 139{ 140 struct db_file_hdr hdr; 141 error_status_t tmp_st; 142 143 SET_STATUS_OK(status); 144 145 dsm_open(database_file, (dsm_handle_t *) &h->dsh, status); 146 if (STATUS_OK(status)) 147 { 148 /* read and check the file header info */ 149 dsm_get_info(h->dsh,&hdr,sizeof(hdr),status); 150 if (! STATUS_OK(status)) 151 { 152 if (dflag) show_st("Error reading endpoint database", status); 153 db_to_ept_ecode(status); 154 return; 155 } 156 157 if (hdr.version != version) 158 { 159 /* Bad database version 160 * Close database 161 * If earlier version, just delete database, 162 * we'll try to create a new one later. 163 * If later version, return an error 164 */ 165 dsm_close((dsm_handle_t *) &h->dsh, &tmp_st); 166 h->dsh = NULL; 167 168 if (hdr.version < version) 169 { 170 remove((char *)database_file); 171 } 172 else 173 { 174 SET_STATUS(status, ept_s_database_invalid); 175 if (dflag) show_st("Newer persistent database version", status); 176 return; 177 } 178 } 179 else 180 h->object = hdr.object; 181 } 182 else 183 if (*status != dsm_err_open_failed) 184 { 185 /* file exists but couldn't open it */ 186 if (dflag) 187 show_st("Error opening endpoint database", status); 188 db_to_ept_ecode(status); 189 return; 190 } 191 192 if (h->dsh == NULL) 193 { 194 /* Create and initialize file 195 */ 196 dsm_create(database_file, (dsm_handle_t *) &h->dsh, status); 197 if (! STATUS_OK(status)) 198 { 199 if (dflag) 200 show_st("Error creating endpoint database", status); 201 db_to_ept_ecode(status); 202 return; 203 } 204 205 uuid_create(&h->object, &tmp_st); 206 207 hdr.version = version; 208 hdr.object = h->object; 209 dsm_set_info(h->dsh, &hdr, sizeof(hdr), status); 210 if (! STATUS_OK(status)) 211 { 212 if (dflag) 213 show_st("Error writing to endpoint database", status); 214 db_to_ept_ecode(status); 215 return; 216 } 217 } 218 219 return; 220} 221 222/* db_update_entry 223 * Update an existing record in the disk copy 224 * of a database. 225 * DSM update is really a delete and add so 226 * if the process crashes between dsm_detach 227 * and dsm_write the record is lost. 228 * 229 * dsm_detach marks an entry as free on disk, used 230 * in volatile memory (so it won't be given out to anyone 231 * else during this process's lifetime but it will be 232 * lost in a crash) 233 */ 234 235PRIVATE void db_update_entry(h, entp, status) 236struct db *h; 237db_entry_p_t entp; 238error_status_t *status; 239{ 240 dsm_detach(h->dsh, (void *)entp, status); 241 if (! STATUS_OK(status)) return; 242 243 dsm_write(h->dsh, (void *)entp, status); 244 if (! STATUS_OK(status)) return; 245} 246 247 248/* 249 * Each entry is on 3 lists: the entry list (of all entries), a list that is 250 * keyed by the entry's object uuid, and a list that is keyed by the entry's 251 * interface uuid. An entry's object list and interface list are accessed 252 * via uuid hash into a hash table which marks the beginning of a list for 253 * all uuids that hash to the same value. Separate hash tables are used for 254 * the object lists and the interface lists. 255 * 256 * The entry lists are not stably stored. They are recreated at startup 257 * time. 258 * 259 * A database's lists are accessed via its struct db.lists_mgmt field. 260 * The lists contain forward and back pointers with the last entry on 261 * a list having its fwd ptr = NULL. 262 * 263 * The procedures that traverse lists (db_list_first, db_list_next) return a pointer 264 * to the beginning of an entry (assuming that db_lists_t is at the beginning of 265 * db_entry_t). To get a pointer to the beginning of the entry, these procedures 266 * subtract a pre-inited offset from the entry's object or interface list ptr. 267 */ 268INTERNAL unsigned32 db_c_object_list_offset; 269INTERNAL unsigned32 db_c_interface_list_offset; 270 271PRIVATE void db_init_lists(h) 272struct db *h; 273{ 274 int i; 275 db_lists_mgmt_t *lists_mgmt; 276 db_lists_t lists; 277 278 lists_mgmt = &h->lists_mgmt; 279 280 lists_mgmt->entry_list.fwd = NULL; 281 lists_mgmt->entry_list.back = &lists_mgmt->entry_list; 282 283 for (i = 0; i < db_c_nbucket; i++) 284 { 285 lists_mgmt->object_table[i].fwd = NULL; 286 lists_mgmt->object_table[i].back = &lists_mgmt->object_table[i]; 287 lists_mgmt->interface_table[i].fwd = NULL; 288 lists_mgmt->interface_table[i].back = &lists_mgmt->interface_table[i]; 289 } 290 291 db_c_object_list_offset = ((char *) &lists.object_list) - ((char *) &lists.entry_list); 292 db_c_interface_list_offset = ((char *) &lists.interface_list) - ((char *) &lists.entry_list); 293} 294 295PRIVATE void db_lists_add(h, entp) 296struct db *h; 297db_entry_t *entp; 298{ 299 db_list_add(&h->lists_mgmt.entry_list, db_c_entry_list, (db_lists_t *) entp); 300 301 db_htable_add(h->lists_mgmt.object_table, db_c_object_list, &entp->object, 302 (db_lists_t *) entp); 303 304 db_htable_add(h->lists_mgmt.interface_table, db_c_interface_list, &entp->interface.uuid, 305 (db_lists_t *) entp); 306} 307 308PRIVATE void db_lists_remove(h, entp) 309struct db *h; 310db_entry_t *entp; 311{ 312 db_list_remove(&h->lists_mgmt.entry_list, db_c_entry_list, (db_lists_t *) entp); 313 314 db_htable_remove(h->lists_mgmt.object_table, db_c_object_list, &entp->object, 315 (db_lists_t *) entp); 316 317 db_htable_remove(h->lists_mgmt.interface_table, db_c_interface_list, &entp->interface.uuid, 318 (db_lists_t *) entp); 319} 320 321PRIVATE void db_htable_add(htable, table_type, id, entp) 322db_hash_table_t htable; 323db_list_type_t table_type; 324uuid_p_t id; 325db_lists_t *entp; 326{ 327 unsigned16 bucket; 328 error_status_t tmp_st; 329 330 bucket = uuid_hash(id, &tmp_st); 331 332 bucket = bucket % db_c_nbucket; 333 334 db_list_add(&htable[bucket], table_type, entp); 335} 336 337PRIVATE void db_htable_remove(htable, table_type, id, entp) 338db_hash_table_t htable; 339db_list_type_t table_type; 340uuid_p_t id; 341db_lists_t *entp; 342{ 343 unsigned16 bucket; 344 error_status_t tmp_st; 345 346 bucket = uuid_hash(id, &tmp_st); 347 348 bucket = bucket % db_c_nbucket; 349 350 db_list_remove(&htable[bucket], table_type, entp); 351} 352 353/* 354 * Add an entry (entp) to the beginning of list. 355 * The entry is being added to one of 3 lists 356 * which is defined by list_type 357 */ 358PRIVATE void db_list_add(list, list_type, entp) 359db_list_t *list; 360db_list_type_t list_type; 361db_lists_t *entp; 362{ 363 db_list_t *elp; 364 error_status_t tmp_st; 365 366 switch(list_type) 367 { 368 case db_c_entry_list: 369 elp = &entp->entry_list; 370 break; 371 case db_c_object_list: 372 elp = &entp->object_list; 373 break; 374 case db_c_interface_list: 375 elp = &entp->interface_list; 376 break; 377 default: 378 tmp_st = ept_s_database_invalid; 379 show_st("db_list_add - bad list type", &tmp_st); 380 return; 381 } 382 383 list_add(list, elp); 384} 385 386PRIVATE void db_list_remove(list, list_type, entp) 387db_list_t *list; 388db_list_type_t list_type; 389db_lists_t *entp; 390{ 391 db_list_t *elp; 392 error_status_t tmp_st; 393 394 switch(list_type) 395 { 396 case db_c_entry_list: 397 elp = &entp->entry_list; 398 break; 399 case db_c_object_list: 400 elp = &entp->object_list; 401 break; 402 case db_c_interface_list: 403 elp = &entp->interface_list; 404 break; 405 default: 406 tmp_st = ept_s_database_invalid; 407 show_st("db_list_remove - bad list type", &tmp_st); 408 return; 409 } 410 411 list_remove(list, elp); 412} 413 414/* 415 * Add entry elp to the end of list 416 */ 417INTERNAL void list_add(list, elp) 418db_list_t *list; 419db_list_t *elp; 420{ 421 elp->fwd = NULL; 422 elp->back = list->back; 423 424 elp->back->fwd = elp; 425 list->back = elp; 426} 427 428/* Remove entry elp from a list 429 */ 430INTERNAL void list_remove(list, elp) 431db_list_t *list; 432db_list_t *elp; 433{ 434 elp->back->fwd = elp->fwd; 435 if (elp->fwd != NULL) 436 elp->fwd->back = elp->back; 437 else 438 list->back = elp->back; /* remove from end of list */ 439 440 elp->fwd = NULL; 441 elp->back = NULL; 442} 443 444/* Return a pointer to the first entry on list list_type 445 */ 446PRIVATE db_lists_t *db_list_first(lists_mgmt, list_type, id) 447db_lists_mgmt_t *lists_mgmt; 448db_list_type_t list_type; 449uuid_p_t id; 450{ 451 unsigned16 bucket; 452 error_status_t tmp_st; 453 db_list_t *elp; 454 db_lists_t *entp; 455 456 entp = NULL; 457 458 switch(list_type) 459 { 460 case db_c_entry_list: 461 entp = (db_lists_t *) lists_mgmt->entry_list.fwd; 462 break; 463 case db_c_object_list: 464 bucket = uuid_hash(id, &tmp_st); 465 bucket = bucket % db_c_nbucket; 466 elp = lists_mgmt->object_table[bucket].fwd; 467 if (elp != NULL) 468 entp = (db_lists_t *) (((char *) elp) - db_c_object_list_offset); 469 break; 470 case db_c_interface_list: 471 bucket = uuid_hash(id, &tmp_st); 472 bucket = bucket % db_c_nbucket; 473 elp = lists_mgmt->interface_table[bucket].fwd; 474 if (elp != NULL) 475 entp = (db_lists_t *) (((char *) elp) - db_c_interface_list_offset); 476 break; 477 default: 478 tmp_st = ept_s_database_invalid; 479 show_st("db_list_first - bad list type", &tmp_st); 480 break; 481 } 482 483 return(entp); 484} 485 486/* Return a pointer to the next entry (after xentp) on list list_type 487 */ 488PRIVATE db_lists_t *db_list_next(list_type, xentp) 489db_list_type_t list_type; 490db_lists_t *xentp; 491{ 492 db_list_t *elp; 493 db_lists_t *entp; 494 error_status_t tmp_st; 495 496 entp = NULL; 497 498 switch(list_type) 499 { 500 case db_c_entry_list: 501 entp = (db_lists_t *) xentp->entry_list.fwd; 502 break; 503 case db_c_object_list: 504 elp = xentp->object_list.fwd; 505 if (elp != NULL) 506 entp = (db_lists_t *) (((char *) elp) - db_c_object_list_offset); 507 break; 508 case db_c_interface_list: 509 elp = xentp->interface_list.fwd; 510 if (elp != NULL) 511 entp = (db_lists_t *) (((char *) elp) - db_c_interface_list_offset); 512 break; 513 default: 514 tmp_st = ept_s_database_invalid; 515 show_st("db_list_next - bad list type", &tmp_st); 516 break; 517 } 518 519 return(entp); 520} 521 522/* Save context 523 * epdb_lookup, fwd, map take an ept_entry_handle_t context handle 524 * argument which supports iterative search of the database. The list 525 * type, list entry pointer, and pass number are saved so the search 526 * can resume where it left off. 527 */ 528PRIVATE void db_save_context(h, entry_handle, list_type, lp, pass) 529struct db *h; 530ept_lookup_handle_t *entry_handle; 531db_list_type_t list_type; 532db_lists_t *lp; 533unsigned32 pass; 534{ 535 db_contexth_t *chp; 536 db_entry_t *entp; 537 538 /* 539 * If the entry list pointer is NULL assume that the 540 * job is done - just delete the context (if one has been 541 * established) and return 542 */ 543 if (lp == NULL) 544 { 545 db_delete_context(h, entry_handle); 546 return; 547 } 548 549 /* Check that context handle is ok 550 */ 551 if (db_bad_context(h, entry_handle)) 552 return; 553 554 /* If the context already exists, decrement the number of 555 * read references in the entry to which the old context 556 * points; this context won't point to the entry any more. 557 * If the context does not exist, malloc space for it. 558 * 559 * Set chp and *entry_handle to the context's address. 560 */ 561 if (*entry_handle != NULL) 562 { 563 chp = (db_contexth_t *) (*entry_handle); 564 entp = (db_entry_t *) chp->lp; 565 entp->read_nrefs--; 566 } 567 else 568 { 569 *entry_handle = (ept_lookup_handle_t *) malloc(sizeof(db_contexth_t)); 570 if (*entry_handle == NULL) 571 return; 572 chp = (db_contexth_t *) (*entry_handle); 573 } 574 575 /* Increment read ref count of the entry to which context will point. 576 * Check for read refs overflow 577 */ 578 entp = (db_entry_t *) lp; 579 entp->read_nrefs++; 580 if (entp->read_nrefs >= db_c_max_read_nrefs) 581 { 582 db_delete_context(h, entry_handle); 583 return; 584 } 585 586 /* Save new context info 587 */ 588 chp->db_handle = h; 589 chp->list_type = list_type; 590 chp->lp = lp; 591 chp->pass = pass; 592} 593 594PRIVATE void db_delete_context(h, entry_handle) 595struct db *h; 596ept_lookup_handle_t *entry_handle; 597{ 598 db_contexth_t *chp; 599 db_entry_t *entp; 600 601 if (db_bad_context(h, entry_handle) || (*entry_handle == NULL)) 602 return; 603 604 chp = (db_contexth_t *) (*entry_handle); 605 entp = (db_entry_t *) chp->lp; 606 entp->read_nrefs--; 607 608 free(chp); 609 610 *entry_handle = NULL; 611} 612 613PRIVATE void db_get_context(h, entry_handle, list_type, lp, pass, status) 614struct db *h; 615ept_lookup_handle_t *entry_handle; 616db_list_type_t *list_type; 617db_lists_t **lp; 618unsigned32 *pass; 619error_status_t *status; 620{ 621 db_contexth_t *chp; 622 623 if (db_bad_context(h, entry_handle) || (*entry_handle == NULL)) 624 { 625 SET_STATUS(status, ept_s_invalid_context); 626 return; 627 } 628 629 chp = (db_contexth_t *) *entry_handle; 630 *list_type = chp->list_type; 631 *lp = chp->lp; 632 *pass = chp->pass; 633 634 SET_STATUS_OK(status); 635} 636 637/* Check whether this entry_handle's context 638 * points to the dbase denoted by h. 639 * Return true and status = ept_s_invalid_context 640 * if entry_handle's db_handle field != h arg. 641 * Otherwise return false. 642 */ 643PRIVATE boolean32 db_different_context(h, entry_handle, status) 644struct db *h; 645ept_lookup_handle_t *entry_handle; 646error_status_t *status; 647{ 648 db_contexth_t *chp; 649 650 SET_STATUS_OK(status); 651 652 if (entry_handle == NULL) 653 return(false); 654 655 if (*entry_handle != NULL) 656 { 657 chp = (db_contexth_t *) *entry_handle; 658 if (chp->db_handle != h) 659 { 660 SET_STATUS(status, ept_s_invalid_context); 661 return(true); 662 } 663 } 664 665 return(false); 666} 667 668/* return true if entry_handle is a "bad" context 669 * handle - ie. 670 * there's no place to pass the handle back 671 * to the caller (ie. he really didn't want to 672 * save context) 673 * the context handle doesn't point to this database 674 * return false if entry_handle appears to be ok 675 */ 676INTERNAL boolean32 db_bad_context(h, entry_handle) 677struct db *h; 678ept_lookup_handle_t *entry_handle; 679{ 680 db_contexth_t *chp; 681 682 if (entry_handle == NULL) 683 return(true); 684 685 if (*entry_handle != NULL) 686 { 687 chp = (db_contexth_t *) *entry_handle; 688 if (chp->db_handle != h) 689 return(true); 690 } 691 692 return(false); 693} 694 695 696/* 697 * Note that the rpcddb package must protect a database from 698 * concurrent access since there can be a call executor thread, 699 * a dg forward map callback thread, and a check server liveness 700 * thread. 701 */ 702 703PRIVATE void db_lock(h) 704struct db *h; 705{ 706 dcethread_mutex_lock_throw(&h->lock); 707} 708 709PRIVATE void db_unlock(h) 710struct db *h; 711{ 712 dcethread_mutex_unlock_throw(&h->lock); 713} 714 715PRIVATE void db_init_lock(h) 716struct db *h; 717{ 718 dcethread_mutex_init_throw(&h->lock, NULL); 719} 720 721 722/* Map dsm error codes to ept error codes 723 */ 724PRIVATE void db_to_ept_ecode(status) 725error_status_t *status; 726{ 727 switch ((int)*status) 728 { 729 case dsm_err_create_failed: 730 *status = ept_s_cant_create; 731 break; 732 case dsm_err_file_io_error: 733 *status = ept_s_update_failed; 734 break; 735 case dsm_err_open_failed: 736 *status = ept_s_cant_access; 737 break; 738 case dsm_err_version: 739 *status = ept_s_database_invalid; 740 break; 741 case dsm_err_no_memory: 742 *status = ept_s_no_memory; 743 break; 744 case dsm_err_duplicate_write: 745 *status = ept_s_update_failed; 746 break; 747 case dsm_err_header_too_long: 748 *status = ept_s_database_invalid; 749 break; 750 case dsm_err_no_more_entries: 751 *status = ept_s_not_registered; 752 break; 753 case dsm_err_invalid_handle: 754 *status = ept_s_cant_perform_op; 755 break; 756 case dsm_err_invalid_pointer: 757 *status = ept_s_cant_perform_op; 758 break; 759 case dsm_err_info_too_long: 760 *status = ept_s_cant_perform_op; 761 break; 762 case dsm_err_file_busy: 763 *status = ept_s_database_already_open; 764 break; 765 case dsm_err_invalid_marker: 766 *status = ept_s_cant_perform_op; 767 break; 768 default: 769 break; 770 } 771} 772