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
open_file(const char * pathname,unsigned int dsvc_flags,int * fdp)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 *
read_entry(FILE * fp)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
field_split(char * buf,unsigned int nfields,char * fields[],const char * seps)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
syserr_to_dsvcerr(int error)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
nhconvert(void * dstraw,const void * srcraw,size_t len)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
pnread(int fd,void * buffer,size_t buflen,off_t off)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
pnwrite(int fd,const void * buffer,size_t buflen,off_t off)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
copy_range(int srcfd,off_t srcoff,int dstfd,off_t dstoff,uint64_t nbytes)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
unescape(char delimiter,const char * buffer,char * unescaped,size_t size)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
escape(char delimiter,const char * buffer,char * escaped,size_t size)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
gensig(void)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 }
452