xref: /onnv-gate/usr/src/lib/libdhcpsvc/modules/binfiles/dhcptab.c (revision 0:68f95e015346)
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
open_dt(void ** handlep,const char * location,uint_t flags)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
close_dt(void ** handlep)154 close_dt(void **handlep)
155 {
156 	free(*handlep);
157 	return (DSVC_SUCCESS);
158 }
159 
160 int
remove_dt(const char * location)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
lookup_dt(void * handle,boolean_t partial,uint_t query,int count,const dt_rec_t * targetp,dt_rec_list_t ** recordsp,uint_t * nrecordsp)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
find_dt(int fd,uint_t flags,uint_t query,int count,const dt_rec_t * targetp,dt_rec_list_t ** recordsp,uint_t * nrecordsp)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
record_match(const dt_rec_t * dtp,const dt_rec_t * targetp,uint_t query)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
add_dt(void * handle,dt_rec_t * addp)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
modify_dt(void * handle,const dt_rec_t * origp,dt_rec_t * newp)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
delete_dt(void * handle,const dt_rec_t * delp)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
list_dt(const char * location,char *** listppp,uint_t * countp)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
dt2path(char * path,size_t pathlen,const char * dir,const char * suffix)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
nhconvert_header(dt_header_t * headerp)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
nhconvert_rec(dt_filerec_t * rec)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
read_header(int fd,dt_header_t * headerp)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
write_header(int fd,dt_header_t * headerp)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
read_rec(int fd,dt_filerec_t * rec,off_t recoff)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
write_rec(int fd,dt_filerec_t * rec,off_t recoff)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 }
780