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 }