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 }