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-2001 by Sun Microsystems, Inc.
  24  * All rights reserved.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * Internal libdhcpsvc public module utility functions: a collection of
  31  * general-purpose routines that are used by assorted public modules.
  32  * Someday we should integrate this into the build process a bit more
  33  * intelligently.
  34  */
  35 
  36 #include <sys/types.h>
  37 #include <sys/mman.h>
  38 #include <sys/isa_defs.h>
  39 #include <dhcp_svc_public.h>
  40 #include <assert.h>
  41 #include <stdlib.h>
  42 #include <fcntl.h>
  43 #include <errno.h>
  44 #include <string.h>
  45 #include <sys/sysmacros.h>
  46 #include <unistd.h>
  47 #include <ctype.h>
  48 
  49 #include "util.h"
  50 
  51 /*
  52  * Open a file at path `pathname'; depending on the flags passed in through
  53  * `dsvc_flags', this file may be optionally created or opened read-only.
  54  * On success, DSVC_SUCCESS is returned and `fdp' points to the opened file
  55  * descriptor.  On failure, a DSVC_* error code is returned.
  56  */
  57 int
  58 open_file(const char *pathname, unsigned int dsvc_flags, int *fdp)
  59 {
  60         int open_flags;
  61 
  62         /*
  63          * Note that we always open with read access, independent of
  64          * dsvc_flags, because an update operation (add, delete, modify)
  65          * needs to lookup records to detect collisions.
  66          */
  67         open_flags = O_RDONLY;
  68         if (dsvc_flags & DSVC_WRITE)
  69                 open_flags = O_RDWR;
  70         if (dsvc_flags & DSVC_CREATE)
  71                 open_flags |= O_CREAT|O_EXCL;
  72 
  73         *fdp = open(pathname, open_flags, 0644);
  74         if (*fdp == -1)
  75                 return (syserr_to_dsvcerr(errno));
  76 
  77         return (DSVC_SUCCESS);
  78 }
  79 
  80 /*
  81  * Read input a chunk at a time, avoiding as much copying as possible.  To
  82  * this end, we don't read into a temporary buffer, but rather read
  83  * directly into dynamically reallocated storage (on the assumption that
  84  * most of the time we will have to return something).  Returns NULL either
  85  * on failure or EOF; use feof(3C) on `fp' to determine which condition
  86  * occurred.
  87  */
  88 char *
  89 read_entry(FILE *fp)
  90 {
  91         char            *newline, *new_result, *result = NULL;
  92         unsigned int    len = 0, size = 0, chunksize = BUFSIZ;
  93 
  94         for (;;) {
  95                 /*
  96                  * See if we need to grow the buffer; we always try to read
  97                  * `chunksize' bytes, so we need at least `chunksize' around;
  98                  * grab a little more just to avoid constant realloc'ing
  99                  */
 100                 if (len + chunksize > size) {
 101                         size = len + (chunksize * 2);
 102                         new_result = realloc(result, size);
 103                         if (new_result == NULL) {
 104                                 free(result);
 105                                 return (NULL);
 106                         }
 107                 }
 108 
 109                 if (fgets(&new_result[len], chunksize, fp) == NULL) {
 110                         /*
 111                          * Hit EOF; if we never read any data, then free
 112                          * `new_result' and return NULL.  If we are
 113                          * returning data, it's in `new_result', not
 114                          * `result'.
 115                          */
 116                         if (result == NULL)
 117                                 free(new_result);
 118                         else
 119                                 result = new_result;
 120                         break;
 121                 }
 122                 result = new_result;
 123 
 124                 /*
 125                  * If we read a newline, then see if the preceding
 126                  * character was an escape.  If so, remove the escape and
 127                  * continue; otherwise we're done.  Note that we need to
 128                  * do the strrchr() on `&result[len]' so that NUL's that
 129                  * may be lurking elsewhere on the line don't confuse us.
 130                  */
 131                 newline = strrchr(&result[len], '\n');
 132                 if (newline != NULL) {
 133                         len = newline - result;
 134                         if (newline == result || newline[-1] != '\\') {
 135                                 newline[0] = '\0';
 136                                 break;
 137                         }
 138                         newline[-1] = '\0';
 139                         len -= 2;
 140                 } else {
 141                         /*
 142                          * We either `chunksize' worth of data or we hit a
 143                          * NUL somewhere in the data stream.  If we hit a
 144                          * NUL, then we can't "see" beyond the NUL; just
 145                          * advance to the NUL itself and continue.
 146                          */
 147                         len += strlen(&result[len]);
 148                 }
 149         }
 150         return (result);
 151 }
 152 
 153 /*
 154  * Given a buffer `buf' of words separated by one or more of the characters
 155  * in `seps', split it into at most `nfields' fields, by changing the
 156  * separator character following a field to a NUL character.  Set
 157  * `fields[i]' to point to the beginning of field i in `buf'.  Return the
 158  * number of fields set in `fields[]'.  This routine is quite similar to
 159  * bufsplit(3G), but less general, faster, and also handles multiple
 160  * multiple whitespace separator characters differently.
 161  */
 162 unsigned int
 163 field_split(char *buf, unsigned int nfields, char *fields[], const char *seps)
 164 {
 165         unsigned int    i = 0;
 166         char            *ch;
 167 
 168         for (;;) {
 169                 fields[i] = buf;
 170 
 171                 /*
 172                  * Look for the field separator, byte-at-a-time; ignore
 173                  * separators that have been escaped.  Believe it or not,
 174                  * strchr(3C) will match `seps' if `*buf' is the NUL byte
 175                  * (which indicates we're done).
 176                  */
 177                 for (;;) {
 178                         ch = strchr(seps, *buf);
 179                         if (ch != NULL && *ch == '\0')
 180                                 return (i + 1);
 181                         if (ch != NULL && (buf == fields[i] || buf[-1] != '\\'))
 182                                 break;
 183                         buf++;
 184                 }
 185 
 186                 /*
 187                  * If this is the last field, then consider any remaining
 188                  * text on the line part of the last field.  This is
 189                  * similar to how `read' in sh(1) works.
 190                  */
 191                 if (++i == nfields)
 192                         return (i);
 193 
 194                 if (*buf == '\0')
 195                         return (i);
 196 
 197                 *buf = '\0';
 198 
 199                 /*
 200                  * If separator is whitespace, then skip all consecutive
 201                  * pieces of whitespace.
 202                  */
 203                 while (ch != NULL && isspace(*ch)) {
 204                         ch = strchr(seps, buf[1]);
 205                         if (ch != NULL && isspace(*ch))
 206                                 buf++;
 207                 }
 208                 buf++;
 209         }
 210 }
 211 
 212 /*
 213  * Map a standard errno into a corresponding DSVC_* code.  If there
 214  * is no translation, default to DSVC_INTERNAL.
 215  */
 216 int
 217 syserr_to_dsvcerr(int error)
 218 {
 219         switch (error) {
 220 
 221         case EEXIST:
 222                 return (DSVC_TABLE_EXISTS);
 223 
 224         case ENOMEM:
 225                 return (DSVC_NO_MEMORY);
 226 
 227         case ENOSPC:
 228                 return (DSVC_NO_RESOURCES);
 229 
 230         case EROFS:
 231         case EPERM:
 232         case EACCES:
 233                 return (DSVC_ACCESS);
 234 
 235         case ENOENT:
 236                 return (DSVC_NO_TABLE);
 237 
 238         default:
 239                 break;
 240         }
 241 
 242         return (DSVC_INTERNAL);
 243 }
 244 
 245 /*
 246  * Convert an object of `len' bytes pointed to by `srcraw' between
 247  * network-order and host-order and store in `dstraw'.  The length `len'
 248  * must be the actual length of the objects pointed to by `srcraw' and
 249  * `dstraw' (or zero) or the results are undefined.  Note that `srcraw' and
 250  * `dstraw' may be the same, in which case the object is converted
 251  * in-place.  This routine will convert from host-order to network-order or
 252  * network-order to host-order, since the conversion is the same.
 253  */
 254 void
 255 nhconvert(void *dstraw, const void *srcraw, size_t len)
 256 {
 257 #ifdef  _LITTLE_ENDIAN
 258         uint8_t b1, b2;
 259         uint8_t *dst, *src;
 260         size_t i;
 261 
 262         /*
 263          * If both `srcraw' and `dstraw' are 32-bit aligned and `len' is 4,
 264          * then use ntohl() to do the byteswap, since it's hand-tuned.
 265          */
 266         if (IS_P2ALIGNED(dstraw, 4) && IS_P2ALIGNED(srcraw, 4) && len == 4) {
 267                 *(uint32_t *)dstraw = ntohl(*(uint32_t *)srcraw);
 268                 return;
 269         }
 270 
 271         dst = (uint8_t *)dstraw;
 272         src = (uint8_t *)srcraw;
 273 
 274         for (i = 0; i < len / 2; i++) {
 275                 b1 = src[i];
 276                 b2 = src[len - i - 1];
 277                 dst[i] = b2;
 278                 dst[len - i - 1] = b1;
 279         }
 280 #else
 281         if (srcraw != dstraw)
 282                 (void) memmove(dstraw, srcraw, len);
 283 #endif
 284 }
 285 
 286 /*
 287  * Positioned n-byte read: read `buflen' bytes at offset `off' at open file
 288  * `fd' into `buffer', or "read" none at all.  Returns -1 if all `buflen'
 289  * bytes cannot be read; otherwise, returns 0.
 290  */
 291 int
 292 pnread(int fd, void *buffer, size_t buflen, off_t off)
 293 {
 294         size_t  nread;
 295         ssize_t nbytes;
 296         char    *buf = buffer;
 297 
 298         for (nread = 0; nread < buflen; nread += nbytes) {
 299                 nbytes = pread(fd, &buf[nread], buflen - nread, off + nread);
 300                 if (nbytes == -1)
 301                         return (-1);
 302                 if (nbytes == 0) {
 303                         errno = EIO;
 304                         return (-1);
 305                 }
 306         }
 307 
 308         assert(nread == buflen);
 309         return (0);
 310 }
 311 
 312 /*
 313  * Positioned n-byte write: write `buflen' bytes from `buffer' to offset
 314  * `off' in open file `fd'.  Tries to write all `buflen' bytes, but does
 315  * not attempt to "undo" what it has done in the case of failure.  Returns
 316  * -1 if all `buflen' bytes cannot be written, otherwise returns 0.
 317  */
 318 int
 319 pnwrite(int fd, const void *buffer, size_t buflen, off_t off)
 320 {
 321         size_t          nwritten;
 322         ssize_t         nbytes;
 323         const char      *buf = buffer;
 324 
 325         for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
 326                 nbytes = pwrite(fd, &buf[nwritten], buflen - nwritten,
 327                     off + nwritten);
 328                 if (nbytes == -1)
 329                         return (-1);
 330                 if (nbytes == 0) {
 331                         errno = EIO;
 332                         return (-1);
 333                 }
 334         }
 335 
 336         assert(nwritten == buflen);
 337         return (0);
 338 }
 339 
 340 /*
 341  * Copy `nbytes' efficiently from offset `srcoff' in `srcfd' to offset
 342  * `dstoff' in `dstfd'; returns a DSVC_* return code.  Note that we make
 343  * `nbytes' a uint64_t (rather than a size_t) so that we can copy 2^64
 344  * bits even when compiled ILP32.
 345  */
 346 int
 347 copy_range(int srcfd, off_t srcoff, int dstfd, off_t dstoff, uint64_t nbytes)
 348 {
 349         const size_t    chunksize = 16 * PAGESIZE;
 350         size_t          validsize;
 351         size_t          skip;
 352         uint64_t        nwritten = 0;
 353         int             mflags = MAP_PRIVATE;
 354         char            *buf = NULL;
 355         int             error;
 356 
 357         /*
 358          * Handle trivial copy specially so we don't call munmap() below.
 359          */
 360         if (nbytes == 0)
 361                 return (DSVC_SUCCESS);
 362 
 363         /*
 364          * The `off' argument to mmap(2) must be page-aligned, so align it;
 365          * compute how many bytes we need to skip over in the mmap()'d
 366          * buffer as a result.
 367          */
 368         skip = srcoff % PAGESIZE;
 369         srcoff -= skip;
 370 
 371         while (nwritten < nbytes) {
 372                 buf = mmap(buf, chunksize, PROT_READ, mflags, srcfd, srcoff);
 373                 if (buf == MAP_FAILED)
 374                         return (DSVC_INTERNAL);
 375                 mflags |= MAP_FIXED;
 376 
 377                 validsize = MIN(chunksize, nbytes - nwritten + skip);
 378                 if (pnwrite(dstfd, &buf[skip], validsize - skip, dstoff)
 379                     == -1) {
 380                         error = errno;
 381                         (void) munmap(buf, chunksize);
 382                         return (syserr_to_dsvcerr(error));
 383                 }
 384 
 385                 nwritten += validsize - skip;
 386                 dstoff += validsize - skip;
 387                 srcoff += validsize;
 388                 skip = 0;
 389         }
 390         (void) munmap(buf, chunksize);
 391 
 392         return (DSVC_SUCCESS);
 393 }
 394 
 395 /*
 396  * Unescape all instances of `delimiter' in `buffer' and store result in
 397  * `unescaped', which is `size' bytes.  To guarantee that all data is
 398  * copied, `unescaped' should be at least as long as `buffer'.
 399  */
 400 void
 401 unescape(char delimiter, const char *buffer, char *unescaped, size_t size)
 402 {
 403         int i, j;
 404 
 405         size--;
 406         for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
 407                 if (buffer[i] == '\\' && buffer[i + 1] == delimiter)
 408                         i++;
 409                 unescaped[j] = buffer[i];
 410         }
 411         unescaped[j] = '\0';
 412 }
 413 
 414 /*
 415  * Escape all instances of `delimiter' in `buffer' and store result in
 416  * `escaped', which is `size' bytes.  To guarantee that all data is
 417  * copied, `escaped' should be at least twice as long as `buffer'.
 418  */
 419 void
 420 escape(char delimiter, const char *buffer, char *escaped, size_t size)
 421 {
 422         int i, j;
 423 
 424         size--;
 425         for (i = 0, j = 0; buffer[i] != '\0' && j < size; i++, j++) {
 426                 if (buffer[i] == delimiter)
 427                         escaped[j++] = '\\';
 428                 escaped[j] = buffer[i];
 429         }
 430         escaped[j] = '\0';
 431 }
 432 
 433 /*
 434  * Generate a signature for a new record.  The signature is conceptually
 435  * divided into two pieces: a random 16-bit "generation number" and a
 436  * 48-bit monotonically increasing integer.  The generation number protects
 437  * against stale updates to records that have been deleted and since
 438  * recreated.
 439  */
 440 uint64_t
 441 gensig(void)
 442 {
 443         static int seeded = 0;
 444 
 445         if (seeded == 0) {
 446                 srand48((long)gethrtime());
 447                 seeded++;
 448         }
 449 
 450         return ((uint64_t)lrand48() << 48 | 1);
 451 }