1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2000 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains public functions for managing the dhcptab container. 31 * For the semantics of these functions, please see the Enterprise DHCP 32 * Architecture Document. 33 * 34 * This module uses synchronization guarantees provided by dsvclockd(1M); 35 * please see $SRC/lib/libdhcpsvc/private/README.synch for details. 36 * 37 * Big Theory Statement for the SUNWbinfiles DHCP Table Module 38 * =========================================================== 39 * 40 * Since the dhcptab container does not have any performance-critical 41 * consumers, this module focuses on being simple and robust rather than 42 * fast. The on-disk structure consists of a minimal header followed by a 43 * list of dt_filerec_t's in no particular order. Note that the dt_rec_t's 44 * dt_value can be arbitrarily large, which means each dt_filerec_t is also 45 * of arbitrary size; we deal with this by storing the on-disk size of each 46 * record in the record itself. 47 * 48 * To meet our robustness requirements (see the Big Theory Statement in 49 * dhcp_network.c), each update operation does its work on a copy of the 50 * dhcptab, which is then atomically renamed to the name of the actual 51 * dhcptab upon completion (yes, this is *very slow*). To speed this up a 52 * little, we use mmap(2) to generate the copy, which is about twice as 53 * fast as using read(2)/write(2). 54 */ 55 56 #include <unistd.h> 57 #include <sys/types.h> 58 #include <sys/socket.h> 59 #include <netinet/in.h> 60 #include <dhcp_svc_public.h> 61 #include <sys/stat.h> 62 #include <sys/isa_defs.h> 63 #include <fcntl.h> 64 #include <stdlib.h> 65 #include <stddef.h> 66 #include <string.h> 67 #include <errno.h> 68 #include <stdio.h> 69 #include <alloca.h> 70 71 #include "dhcptab.h" 72 #include "util.h" 73 74 /* 75 * We compute the RECSIZE using the offset of `rec_dtval' rather than the 76 * sizeof (dt_filerec_t) so that we don't include any trailing structure 77 * padding in the size calculation. 78 */ 79 #define RECSIZE(rec) (offsetof(dt_filerec_t, rec_dtval) + ((rec).rec_dtvalsize)) 80 81 static int read_header(int, dt_header_t *); 82 static int write_header(int, dt_header_t *); 83 static int read_rec(int, dt_filerec_t *, off_t); 84 static int write_rec(int, dt_filerec_t *, off_t); 85 static void dt2path(char *, size_t, const char *, const char *); 86 static boolean_t record_match(const dt_rec_t *, const dt_rec_t *, uint_t); 87 static int find_dt(int, uint_t, uint_t, int, const dt_rec_t *, 88 dt_rec_list_t **, uint_t *); 89 90 int 91 open_dt(void **handlep, const char *location, uint_t flags) 92 { 93 dt_handle_t *dhp; 94 dt_header_t header = { 0 }; 95 char dtpath[MAXPATHLEN]; 96 int retval; 97 int fd; 98 99 dhp = malloc(sizeof (dt_handle_t)); 100 if (dhp == NULL) 101 return (DSVC_NO_MEMORY); 102 103 dhp->dh_oflags = flags; 104 (void) strlcpy(dhp->dh_location, location, MAXPATHLEN); 105 106 dt2path(dtpath, MAXPATHLEN, location, ""); 107 retval = open_file(dtpath, flags, &fd); 108 if (retval != DSVC_SUCCESS) { 109 free(dhp); 110 return (retval); 111 } 112 113 if (flags & DSVC_CREATE) { 114 /* 115 * We just created the per-network container; initialize 116 * the header and put it out on disk. 117 */ 118 header.dth_magic = DT_MAGIC; 119 header.dth_version = DSVC_CONVER; 120 121 if (write_header(fd, &header) == -1) { 122 retval = syserr_to_dsvcerr(errno); 123 (void) close(fd); 124 (void) remove_dt(location); 125 (void) close_dt((void **)&dhp); 126 return (retval); 127 } 128 } else { 129 /* 130 * Container already exists; sanity check against the 131 * header that's on-disk. 132 */ 133 if (read_header(fd, &header) == -1) { 134 retval = syserr_to_dsvcerr(errno); 135 (void) close(fd); 136 (void) close_dt((void **)&dhp); 137 return (retval); 138 } 139 140 if (header.dth_magic != DT_MAGIC || 141 header.dth_version != DSVC_CONVER) { 142 (void) close(fd); 143 (void) close_dt((void **)&dhp); 144 return (DSVC_INTERNAL); 145 } 146 } 147 148 (void) close(fd); 149 *handlep = dhp; 150 return (DSVC_SUCCESS); 151 } 152 153 int 154 close_dt(void **handlep) 155 { 156 free(*handlep); 157 return (DSVC_SUCCESS); 158 } 159 160 int 161 remove_dt(const char *location) 162 { 163 char dtpath[MAXPATHLEN]; 164 165 dt2path(dtpath, MAXPATHLEN, location, ""); 166 if (unlink(dtpath) == -1) 167 return (syserr_to_dsvcerr(errno)); 168 169 return (DSVC_SUCCESS); 170 } 171 172 int 173 lookup_dt(void *handle, boolean_t partial, uint_t query, int count, 174 const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp) 175 { 176 int fd; 177 int retval; 178 char dtpath[MAXPATHLEN]; 179 dt_handle_t *dhp = (dt_handle_t *)handle; 180 181 if ((dhp->dh_oflags & DSVC_READ) == 0) 182 return (DSVC_ACCESS); 183 184 dt2path(dtpath, MAXPATHLEN, dhp->dh_location, ""); 185 fd = open(dtpath, O_RDONLY); 186 if (fd == -1) 187 return (syserr_to_dsvcerr(errno)); 188 189 retval = find_dt(fd, partial ? FIND_PARTIAL : 0, query, count, targetp, 190 recordsp, nrecordsp); 191 192 (void) close(fd); 193 return (retval); 194 } 195 196 /* 197 * Internal version of lookup_dt() used by lookup_dt(), modify_dt(), 198 * add_dt(), and delete_dt(); same semantics as lookup_dt() except that the 199 * `partial' argument has been generalized into a `flags' field and the 200 * handle has been turned into a file descriptor. 201 */ 202 static int 203 find_dt(int fd, uint_t flags, uint_t query, int count, 204 const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp) 205 { 206 int retval = DSVC_SUCCESS; 207 uint_t nrecords = 0, n = 0; 208 dt_rec_t *recordp; 209 dt_rec_list_t *records, *new_records; 210 dt_header_t header; 211 dt_filerec_t rec; 212 off_t recoff = sizeof (dt_header_t); 213 struct stat st; 214 215 if (read_header(fd, &header) == -1) 216 return (syserr_to_dsvcerr(errno)); 217 218 if (fstat(fd, &st) == -1) 219 return (DSVC_INTERNAL); 220 221 records = NULL; 222 for (; (recoff < st.st_size) && (count < 0 || nrecords < count); 223 n++, recoff += RECSIZE(rec)) { 224 225 if (read_rec(fd, &rec, recoff) == -1) { 226 retval = syserr_to_dsvcerr(errno); 227 break; 228 } 229 230 /* 231 * See if we've got a match... 232 */ 233 if (!record_match(&rec.rec_dt, targetp, query)) 234 continue; 235 236 /* 237 * Caller just wants a count of the number of matching 238 * records, not the records themselves; continue. 239 */ 240 if (recordsp == NULL) { 241 nrecords++; 242 continue; 243 } 244 245 /* 246 * Allocate record; if FIND_POSITION flag is set, then 247 * we need to allocate an extended (dt_recpos_t) record. 248 */ 249 if (flags & FIND_POSITION) 250 recordp = malloc(sizeof (dt_recpos_t)); 251 else 252 recordp = malloc(sizeof (dt_rec_t)); 253 254 if (recordp == NULL) { 255 if ((flags & FIND_PARTIAL) == 0) 256 retval = DSVC_NO_MEMORY; 257 break; 258 } 259 /* 260 * Fill in record; do a structure copy from our automatic 261 * record. If FIND_POSITION flag is on, pass back 262 * additional location information. 263 */ 264 *recordp = rec.rec_dt; 265 recordp->dt_value = malloc(rec.rec_dtvalsize); 266 if (recordp->dt_value == NULL) { 267 free_dtrec(recordp); 268 if ((flags & FIND_PARTIAL) == 0) 269 retval = DSVC_NO_MEMORY; 270 break; 271 } 272 if (pnread(fd, recordp->dt_value, rec.rec_dtvalsize, 273 recoff + offsetof(dt_filerec_t, rec_dtval)) == -1) { 274 if ((flags & FIND_PARTIAL) == 0) 275 retval = syserr_to_dsvcerr(errno); 276 free_dtrec(recordp); 277 break; 278 } 279 280 if (flags & FIND_POSITION) { 281 ((dt_recpos_t *)recordp)->dtp_off = recoff; 282 ((dt_recpos_t *)recordp)->dtp_size = RECSIZE(rec); 283 } 284 285 /* 286 * Chuck the record on the list and up the counter. 287 */ 288 new_records = add_dtrec_to_list(recordp, records); 289 if (new_records == NULL) { 290 free_dtrec(recordp); 291 if ((flags & FIND_PARTIAL) == 0) 292 retval = DSVC_NO_MEMORY; 293 break; 294 } 295 296 records = new_records; 297 nrecords++; 298 } 299 300 if (retval == DSVC_SUCCESS) { 301 *nrecordsp = nrecords; 302 if (recordsp != NULL) 303 *recordsp = records; 304 return (DSVC_SUCCESS); 305 } 306 307 if (records != NULL) 308 free_dtrec_list(records); 309 310 return (retval); 311 } 312 313 /* 314 * Compares `dtp' to the target `targetp', using `query' to decide what 315 * fields to compare. Returns B_TRUE if `dtp' matches `targetp', B_FALSE 316 * if not. 317 */ 318 static boolean_t 319 record_match(const dt_rec_t *dtp, const dt_rec_t *targetp, uint_t query) 320 { 321 if (DSVC_QISEQ(query, DT_QTYPE) && targetp->dt_type != dtp->dt_type) 322 return (B_FALSE); 323 if (DSVC_QISNEQ(query, DT_QTYPE) && targetp->dt_type == dtp->dt_type) 324 return (B_FALSE); 325 326 if (DSVC_QISEQ(query, DT_QKEY) && 327 strcmp(targetp->dt_key, dtp->dt_key) != 0) 328 return (B_FALSE); 329 330 if (DSVC_QISNEQ(query, DT_QKEY) && 331 strcmp(targetp->dt_key, dtp->dt_key) == 0) 332 return (B_FALSE); 333 334 return (B_TRUE); 335 } 336 337 int 338 add_dt(void *handle, dt_rec_t *addp) 339 { 340 unsigned int found; 341 int query; 342 int fd, newfd; 343 int retval; 344 dt_filerec_t *rec; 345 struct stat st; 346 dt_handle_t *dhp = (dt_handle_t *)handle; 347 char newpath[MAXPATHLEN], dtpath[MAXPATHLEN]; 348 349 if ((dhp->dh_oflags & DSVC_WRITE) == 0) 350 return (DSVC_ACCESS); 351 352 dt2path(dtpath, MAXPATHLEN, dhp->dh_location, ""); 353 fd = open(dtpath, O_RDWR); 354 if (fd == -1) 355 return (syserr_to_dsvcerr(errno)); 356 357 /* 358 * Make sure the record wasn't created when we weren't looking. 359 */ 360 DSVC_QINIT(query); 361 DSVC_QEQ(query, DT_QKEY|DT_QTYPE); 362 363 retval = find_dt(fd, 0, query, 1, addp, NULL, &found); 364 if (retval != DSVC_SUCCESS) { 365 (void) close(fd); 366 return (retval); 367 } 368 if (found != 0) { 369 (void) close(fd); 370 return (DSVC_EXISTS); 371 } 372 373 /* 374 * Make a new copy of the dhcptab with the new record appended. 375 * Once done, atomically rename the new dhcptab to the old name. 376 */ 377 if (fstat(fd, &st) == -1) { 378 (void) close(fd); 379 return (DSVC_INTERNAL); 380 } 381 382 dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new"); 383 (void) unlink(newpath); 384 newfd = open(newpath, O_WRONLY|O_CREAT|O_EXCL, 0644); 385 if (newfd == -1) { 386 retval = syserr_to_dsvcerr(errno); 387 goto out; 388 } 389 390 retval = copy_range(fd, 0, newfd, 0, st.st_size); 391 if (retval != DSVC_SUCCESS) 392 goto out; 393 394 addp->dt_sig = gensig(); 395 rec = alloca(sizeof (dt_filerec_t) + strlen(addp->dt_value)); 396 rec->rec_dt = *addp; 397 rec->rec_dtvalsize = strlen(addp->dt_value) + 1; 398 (void) strcpy(rec->rec_dtval, addp->dt_value); 399 400 if (write_rec(newfd, rec, st.st_size) == -1) { 401 retval = syserr_to_dsvcerr(errno); 402 goto out; 403 } 404 405 /* 406 * Note: we close these descriptors before the rename(2) (rather 407 * than just having the `out:' label clean them up) to save NFS 408 * some work (otherwise, NFS has to save `dtpath' to an alternate 409 * name since its vnode would still be active). 410 */ 411 (void) close(fd); 412 (void) close(newfd); 413 414 if (rename(newpath, dtpath) == -1) 415 retval = syserr_to_dsvcerr(errno); 416 417 return (retval); 418 out: 419 (void) close(fd); 420 (void) close(newfd); 421 (void) unlink(newpath); 422 return (retval); 423 } 424 425 int 426 modify_dt(void *handle, const dt_rec_t *origp, dt_rec_t *newp) 427 { 428 unsigned int found; 429 int query; 430 int fd, newfd; 431 int retval; 432 dt_filerec_t *rec; 433 off_t recoff, recnext; 434 dt_rec_list_t *reclist; 435 struct stat st; 436 dt_handle_t *dhp = (dt_handle_t *)handle; 437 char newpath[MAXPATHLEN], dtpath[MAXPATHLEN]; 438 439 if ((dhp->dh_oflags & DSVC_WRITE) == 0) 440 return (DSVC_ACCESS); 441 442 dt2path(dtpath, MAXPATHLEN, dhp->dh_location, ""); 443 fd = open(dtpath, O_RDWR); 444 if (fd == -1) 445 return (syserr_to_dsvcerr(errno)); 446 447 DSVC_QINIT(query); 448 DSVC_QEQ(query, DT_QKEY|DT_QTYPE); 449 450 /* 451 * If we're changing the key for this record, make sure the key 452 * we're changing to doesn't already exist. 453 */ 454 if (origp->dt_type != newp->dt_type || 455 strcmp(origp->dt_key, newp->dt_key) != 0) { 456 retval = find_dt(fd, 0, query, 1, newp, NULL, &found); 457 if (retval != DSVC_SUCCESS) { 458 (void) close(fd); 459 return (retval); 460 } 461 if (found != 0) { 462 (void) close(fd); 463 return (DSVC_EXISTS); 464 } 465 } 466 467 /* 468 * Fetch the original again to make sure it didn't go stale. 469 */ 470 retval = find_dt(fd, FIND_POSITION, query, 1, origp, &reclist, &found); 471 if (retval != DSVC_SUCCESS) { 472 (void) close(fd); 473 return (retval); 474 } 475 if (found == 0) { 476 (void) close(fd); 477 return (DSVC_NOENT); 478 } 479 480 if (reclist->dtl_rec->dt_sig != origp->dt_sig) { 481 (void) close(fd); 482 free_dtrec_list(reclist); 483 return (DSVC_COLLISION); 484 } 485 486 recoff = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off; 487 recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size; 488 489 free_dtrec_list(reclist); 490 491 /* 492 * Make a new copy of the dhcptab, sans the record we're modifying, 493 * then append modified record at the end. Once done, atomically 494 * rename the new dhcptab to the old name. 495 */ 496 if (fstat(fd, &st) == -1) { 497 (void) close(fd); 498 return (DSVC_INTERNAL); 499 } 500 501 dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new"); 502 (void) unlink(newpath); 503 newfd = open(newpath, O_WRONLY|O_CREAT|O_EXCL, 0644); 504 if (newfd == -1) { 505 retval = syserr_to_dsvcerr(errno); 506 goto out; 507 } 508 509 retval = copy_range(fd, 0, newfd, 0, recoff); 510 if (retval != DSVC_SUCCESS) 511 goto out; 512 513 retval = copy_range(fd, recnext, newfd, recoff, st.st_size - recnext); 514 if (retval != DSVC_SUCCESS) 515 goto out; 516 517 newp->dt_sig = origp->dt_sig + 1; 518 rec = alloca(sizeof (dt_filerec_t) + strlen(newp->dt_value)); 519 rec->rec_dt = *newp; 520 rec->rec_dtvalsize = strlen(newp->dt_value) + 1; 521 (void) strcpy(rec->rec_dtval, newp->dt_value); 522 523 if (write_rec(newfd, rec, st.st_size - (recnext - recoff)) == -1) { 524 retval = syserr_to_dsvcerr(errno); 525 goto out; 526 } 527 528 /* 529 * See comment in add_dt() regarding the next two lines. 530 */ 531 (void) close(fd); 532 (void) close(newfd); 533 534 if (rename(newpath, dtpath) == -1) 535 retval = syserr_to_dsvcerr(errno); 536 537 return (retval); 538 out: 539 (void) close(fd); 540 (void) close(newfd); 541 (void) unlink(newpath); 542 return (retval); 543 } 544 545 int 546 delete_dt(void *handle, const dt_rec_t *delp) 547 { 548 unsigned int found; 549 int query; 550 int fd, newfd; 551 int retval; 552 off_t recoff, recnext; 553 dt_rec_list_t *reclist; 554 struct stat st; 555 dt_handle_t *dhp = (dt_handle_t *)handle; 556 char newpath[MAXPATHLEN], dtpath[MAXPATHLEN]; 557 558 if ((dhp->dh_oflags & DSVC_WRITE) == 0) 559 return (DSVC_ACCESS); 560 561 dt2path(dtpath, MAXPATHLEN, dhp->dh_location, ""); 562 fd = open(dtpath, O_RDWR); 563 if (fd == -1) 564 return (syserr_to_dsvcerr(errno)); 565 566 /* 567 * Make sure the record exists and also that the signatures match; 568 * if `delp->dt_sig' is zero, then skip signature comparison (this 569 * is so one can delete records that were not looked up). 570 */ 571 DSVC_QINIT(query); 572 DSVC_QEQ(query, DT_QKEY|DT_QTYPE); 573 574 retval = find_dt(fd, FIND_POSITION, query, 1, delp, &reclist, &found); 575 if (retval != DSVC_SUCCESS) { 576 (void) close(fd); 577 return (retval); 578 } 579 if (found == 0) { 580 (void) close(fd); 581 return (DSVC_NOENT); 582 } 583 584 if (delp->dt_sig != 0 && reclist->dtl_rec->dt_sig != delp->dt_sig) { 585 (void) close(fd); 586 free_dtrec_list(reclist); 587 return (DSVC_COLLISION); 588 } 589 590 recoff = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off; 591 recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size; 592 593 free_dtrec_list(reclist); 594 595 /* 596 * Make a new copy of the dhcptab, sans the record we're deleting. 597 * Once done, atomically rename the new dhcptab to the old name. 598 */ 599 if (fstat(fd, &st) == -1) { 600 (void) close(fd); 601 return (DSVC_INTERNAL); 602 } 603 604 dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new"); 605 (void) unlink(newpath); 606 newfd = open(newpath, O_WRONLY|O_CREAT|O_EXCL, 0644); 607 if (newfd == -1) { 608 retval = syserr_to_dsvcerr(errno); 609 goto out; 610 } 611 612 retval = copy_range(fd, 0, newfd, 0, recoff); 613 if (retval != DSVC_SUCCESS) 614 goto out; 615 616 retval = copy_range(fd, recnext, newfd, recoff, st.st_size - recnext); 617 if (retval != DSVC_SUCCESS) 618 goto out; 619 620 /* 621 * See comment in add_dt() regarding the next two lines. 622 */ 623 (void) close(fd); 624 (void) close(newfd); 625 626 if (rename(newpath, dtpath) == -1) 627 retval = syserr_to_dsvcerr(errno); 628 629 return (retval); 630 out: 631 (void) close(fd); 632 (void) close(newfd); 633 (void) unlink(newpath); 634 return (retval); 635 } 636 637 int 638 list_dt(const char *location, char ***listppp, uint_t *countp) 639 { 640 char dtpath[MAXPATHLEN]; 641 char **listpp; 642 643 if (access(location, F_OK|R_OK) == -1) { 644 switch (errno) { 645 case EACCES: 646 case EPERM: 647 return (DSVC_ACCESS); 648 case ENOENT: 649 return (DSVC_NO_LOCATION); 650 default: 651 break; 652 } 653 return (DSVC_INTERNAL); 654 } 655 656 dt2path(dtpath, MAXPATHLEN, location, ""); 657 if (access(dtpath, F_OK|R_OK) == -1) { 658 *countp = 0; 659 *listppp = NULL; 660 return (DSVC_SUCCESS); 661 } 662 663 listpp = malloc(sizeof (char **)); 664 if (listpp == NULL) 665 return (DSVC_NO_MEMORY); 666 listpp[0] = strdup(DT_DHCPTAB); 667 if (listpp[0] == NULL) { 668 free(listpp); 669 return (DSVC_NO_MEMORY); 670 } 671 672 *listppp = listpp; 673 *countp = 1; 674 return (DSVC_SUCCESS); 675 } 676 677 /* 678 * Given a buffer `path' of `pathlen' bytes, fill it in with a path to the 679 * dhcptab in directory `dir' with a suffix of `suffix'. 680 */ 681 static void 682 dt2path(char *path, size_t pathlen, const char *dir, const char *suffix) 683 { 684 (void) snprintf(path, pathlen, "%s/SUNWbinfiles%u_%s%s", dir, 685 DSVC_CONVER, DT_DHCPTAB, suffix); 686 } 687 688 /* 689 * Convert dt_header_t pointed to by `headerp' from native (host) to 690 * network order or the other way. 691 */ 692 /* ARGSUSED */ 693 static void 694 nhconvert_header(dt_header_t *headerp) 695 { 696 #ifdef _LITTLE_ENDIAN 697 nhconvert(&headerp->dth_magic, &headerp->dth_magic, sizeof (uint32_t)); 698 #endif 699 } 700 701 /* 702 * Convert dt_filerec_t pointed to by `rec' from native (host) to network 703 * order or the other way. 704 */ 705 /* ARGSUSED */ 706 static void 707 nhconvert_rec(dt_filerec_t *rec) 708 { 709 #ifdef _LITTLE_ENDIAN 710 dt_rec_t *dtp = &rec->rec_dt; 711 712 nhconvert(&rec->rec_dtvalsize, &rec->rec_dtvalsize, sizeof (uint32_t)); 713 nhconvert(&dtp->dt_sig, &dtp->dt_sig, sizeof (uint64_t)); 714 #endif 715 } 716 717 /* 718 * Read the dt_header_t in the container at open file `fd' into the header 719 * pointed to by `headerp'. Returns 0 on success, -1 on failure (errno is 720 * set). 721 */ 722 static int 723 read_header(int fd, dt_header_t *headerp) 724 { 725 if (pnread(fd, headerp, sizeof (dt_header_t), 0) == -1) 726 return (-1); 727 728 nhconvert_header(headerp); 729 return (0); 730 } 731 732 /* 733 * Write the dt_header_t pointed to by `headerp' to the container at open 734 * file `fd'. Returns 0 on success, -1 on failure (errno is set). 735 */ 736 static int 737 write_header(int fd, dt_header_t *headerp) 738 { 739 int retval; 740 741 nhconvert_header(headerp); 742 retval = pnwrite(fd, headerp, sizeof (dt_header_t), 0); 743 nhconvert_header(headerp); 744 return (retval); 745 } 746 747 748 /* 749 * Read the dt_filerec_t in the container from offset `recoff' in the 750 * container at open file `fd'. Note that this only returns the fixed 751 * sized part of the dt_filerec_t; the caller must retrieve `rev_dtval' on 752 * their own. Returns 0 on success, -1 on failure (errno is set). 753 */ 754 static int 755 read_rec(int fd, dt_filerec_t *rec, off_t recoff) 756 { 757 if (pnread(fd, rec, sizeof (dt_filerec_t), recoff) == -1) 758 return (-1); 759 760 nhconvert_rec(rec); 761 return (0); 762 } 763 764 /* 765 * Write the dt_filerec_t pointed to be `rec' to offset `recoff' in the 766 * container at open file `fd'. Returns 0 on success, -1 on failure (errno 767 * is set). 768 */ 769 static int 770 write_rec(int fd, dt_filerec_t *rec, off_t recoff) 771 { 772 int retval; 773 size_t recsize = RECSIZE(*rec); 774 775 nhconvert_rec(rec); 776 retval = pnwrite(fd, rec, recsize, recoff); 777 nhconvert_rec(rec); 778 return (retval); 779 }