xref: /onnv-gate/usr/src/lib/libdhcpsvc/modules/files0/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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains public API functions for managing the legacy dhcptab
31  * container format.  For the semantics of these functions, please see the
32  * Enterprise DHCP Architecture Document.
33  */
34 
35 #include <alloca.h>
36 #include <dhcp_svc_public.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <netinet/in.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 
48 #include "dhcptab.h"
49 #include "util.h"
50 
51 static void dt2path(char *, size_t, const char *, const char *);
52 static int write_rec(int, dt_rec_t *, off_t);
53 
54 int
open_dt(void ** handlep,const char * location,uint_t flags)55 open_dt(void **handlep, const char *location, uint_t flags)
56 {
57 	dt_handle_t	*dhp;
58 	int		retval;
59 	int		fd;
60 	char		dtpath[MAXPATHLEN];
61 
62 	dhp = malloc(sizeof (dt_handle_t));
63 	if (dhp == NULL)
64 		return (DSVC_NO_MEMORY);
65 
66 	dhp->dh_oflags = flags;
67 	(void) strlcpy(dhp->dh_location, location, MAXPATHLEN);
68 
69 	/*
70 	 * This is a legacy format which has no header, so we neither write
71 	 * nor verify a header (we just create the file or make sure it
72 	 * exists, depending on the value of `flags').
73 	 */
74 	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
75 	retval = open_file(dtpath, flags, &fd);
76 	if (retval != DSVC_SUCCESS) {
77 		free(dhp);
78 		return (retval);
79 	}
80 	(void) close(fd);
81 
82 	*handlep = dhp;
83 	return (DSVC_SUCCESS);
84 }
85 
86 int
close_dt(void ** handlep)87 close_dt(void **handlep)
88 {
89 	free(*handlep);
90 	return (DSVC_SUCCESS);
91 }
92 
93 int
remove_dt(const char * location)94 remove_dt(const char *location)
95 {
96 	char dtpath[MAXPATHLEN];
97 
98 	dt2path(dtpath, MAXPATHLEN, location, "");
99 	if (unlink(dtpath) == -1)
100 		return (syserr_to_dsvcerr(errno));
101 
102 	return (DSVC_SUCCESS);
103 }
104 
105 /*
106  * Internal version of lookup_dt() used by both lookup_dt() and
107  * update_dt(); same semantics as lookup_dt() except that the `partial'
108  * argument has been generalized into a `flags' field and the handle has
109  * been turned into a FILE pointer.
110  */
111 static int
find_dt(FILE * fp,uint_t flags,uint_t query,int count,const dt_rec_t * targetp,dt_rec_list_t ** recordsp,uint_t * nrecordsp)112 find_dt(FILE *fp, uint_t flags, uint_t query, int count,
113     const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
114 {
115 	int		retval = DSVC_SUCCESS;
116 	char 		*buf = NULL, *fields[DTF_MAX_FIELDS];
117 	uint_t		nrecords;
118 	dt_rec_t	*recordp;
119 	dt_rec_list_t	*records, *new_records;
120 	unsigned int	nfields;
121 	off_t		recoff;
122 
123 	if (fseek(fp, 0, SEEK_SET) == -1)
124 		return (DSVC_INTERNAL);
125 
126 	records = NULL;
127 	for (nrecords = 0; count < 0 || nrecords < count; ) {
128 		free(buf);
129 
130 		if (flags & FIND_POSITION)
131 			recoff = ftello(fp);
132 
133 		buf = read_entry(fp);
134 		if (buf == NULL) {
135 			if (!feof(fp))
136 				retval = DSVC_NO_MEMORY;
137 			break;
138 		}
139 
140 		/*
141 		 * Skip pure comment lines; for now this just skips the
142 		 * header information at the top of the container.
143 		 */
144 		if (buf[0] == DTF_COMMENT_CHAR)
145 			continue;
146 
147 		/*
148 		 * Parse out the entry into the dt_rec_t
149 		 */
150 		nfields = field_split(buf, DTF_MAX_FIELDS, fields, " \t");
151 		if (nfields < DTF_MAX_FIELDS)
152 			continue;
153 
154 		/*
155 		 * See if we've got a match.  If so, allocate the new
156 		 * record, fill it in, and continue.
157 		 */
158 		if (DSVC_QISEQ(query, DT_QTYPE) &&
159 		    targetp->dt_type != fields[DTF_TYPE][0])
160 			continue;
161 		else if (DSVC_QISNEQ(query, DT_QTYPE) &&
162 		    targetp->dt_type == fields[DTF_TYPE][0])
163 			continue;
164 
165 		if (DSVC_QISEQ(query, DT_QKEY) &&
166 		    strcmp(targetp->dt_key, fields[DTF_KEY]) != 0)
167 			continue;
168 		else if (DSVC_QISNEQ(query, DT_QKEY) &&
169 		    strcmp(targetp->dt_key, fields[DTF_KEY]) == 0)
170 			continue;
171 
172 		/*
173 		 * Caller just wants a count of the number of matching
174 		 * records, not the records themselves; continue.
175 		 */
176 		if (recordsp == NULL) {
177 			nrecords++;
178 			continue;
179 		}
180 
181 		/*
182 		 * Allocate record; if FIND_POSITION flag is set, then we
183 		 * need to allocate an extended (dt_recpos_t) record.
184 		 */
185 		if (flags & FIND_POSITION)
186 			recordp = malloc(sizeof (dt_recpos_t));
187 		else
188 			recordp = malloc(sizeof (dt_rec_t));
189 
190 		if (recordp == NULL) {
191 			if ((flags & FIND_PARTIAL) == 0)
192 				retval = DSVC_NO_MEMORY;
193 			break;
194 		}
195 
196 		/*
197 		 * Fill in record; if FIND_POSITION flag is set, then pass
198 		 * back additional location information.
199 		 */
200 		(void) strlcpy(recordp->dt_key, fields[DTF_KEY],
201 		    sizeof (recordp->dt_key));
202 		recordp->dt_sig = 1;
203 		recordp->dt_type = fields[DTF_TYPE][0];
204 		recordp->dt_value = strdup(fields[DTF_VALUE]);
205 		if (recordp->dt_value == NULL) {
206 			free(recordp);
207 			if ((flags & FIND_PARTIAL) == 0)
208 				retval = DSVC_NO_MEMORY;
209 			break;
210 		}
211 
212 		if (flags & FIND_POSITION) {
213 			((dt_recpos_t *)recordp)->dtp_off = recoff;
214 			((dt_recpos_t *)recordp)->dtp_size = ftello(fp) -
215 			    recoff;
216 		}
217 
218 		/*
219 		 * Chuck the record on the list; up the counter.
220 		 */
221 		new_records = add_dtrec_to_list(recordp, records);
222 		if (new_records == NULL) {
223 			free_dtrec(recordp);
224 			if ((flags & FIND_PARTIAL) == 0)
225 				retval = DSVC_NO_MEMORY;
226 			break;
227 		}
228 		records = new_records;
229 		nrecords++;
230 	}
231 
232 	free(buf);
233 
234 	if (retval == DSVC_SUCCESS) {
235 		*nrecordsp = nrecords;
236 		if (recordsp != NULL)
237 			*recordsp = records;
238 		return (DSVC_SUCCESS);
239 	}
240 
241 	if (records != NULL)
242 		free_dtrec_list(records);
243 
244 	return (retval);
245 }
246 
247 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)248 lookup_dt(void *handle, boolean_t partial, uint_t query, int count,
249     const dt_rec_t *targetp, dt_rec_list_t **recordsp, uint_t *nrecordsp)
250 {
251 	int		retval;
252 	char		dtpath[MAXPATHLEN];
253 	FILE		*fp;
254 	dt_handle_t	*dhp = (dt_handle_t *)handle;
255 
256 	if ((dhp->dh_oflags & DSVC_READ) == 0)
257 		return (DSVC_ACCESS);
258 
259 	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
260 	fp = fopen(dtpath, "r");
261 	if (fp == NULL)
262 		return (syserr_to_dsvcerr(errno));
263 
264 	retval = find_dt(fp, partial ? FIND_PARTIAL : 0, query, count, targetp,
265 	    recordsp, nrecordsp);
266 
267 	(void) fclose(fp);
268 	return (retval);
269 }
270 
271 /*
272  * Internal dhcptab record update routine, used to factor out the
273  * common code between add_dt(), delete_dt(), and modify_dt().  If
274  * `origp' is NULL, then act like add_dt(); if `newp' is NULL, then
275  * act like delete_dt(); otherwise act like modify_dt().
276  */
277 static int
update_dt(const dt_handle_t * dhp,const dt_rec_t * origp,dt_rec_t * newp)278 update_dt(const dt_handle_t *dhp, const dt_rec_t *origp, dt_rec_t *newp)
279 {
280 	char		dtpath[MAXPATHLEN], newpath[MAXPATHLEN];
281 	int		retval = DSVC_SUCCESS;
282 	off_t		recoff, recnext;
283 	dt_rec_list_t	*reclist;
284 	FILE		*fp;
285 	int		newfd;
286 	uint_t		found;
287 	int		query;
288 	struct stat	st;
289 
290 	if ((dhp->dh_oflags & DSVC_WRITE) == 0)
291 		return (DSVC_ACCESS);
292 
293 	/*
294 	 * Open the container to update and a new container file which we
295 	 * will store the updated version of the container in.  When the
296 	 * update is done, rename the new file to be the real container.
297 	 */
298 	dt2path(dtpath, MAXPATHLEN, dhp->dh_location, "");
299 	fp = fopen(dtpath, "r");
300 	if (fp == NULL)
301 		return (syserr_to_dsvcerr(errno));
302 
303 	dt2path(newpath, MAXPATHLEN, dhp->dh_location, ".new");
304 	(void) unlink(newpath);
305 	newfd = open(newpath, O_CREAT|O_EXCL|O_WRONLY, 0644);
306 	if (newfd == -1) {
307 		(void) fclose(fp);
308 		return (syserr_to_dsvcerr(errno));
309 	}
310 
311 	DSVC_QINIT(query);
312 	DSVC_QEQ(query, DT_QKEY|DT_QTYPE);
313 
314 	/*
315 	 * If we're adding a new record or changing a key for an existing
316 	 * record, bail if the record we want to add already exists.
317 	 */
318 	if (newp != NULL) {
319 		if (origp == NULL || origp->dt_type != newp->dt_type ||
320 		    strcmp(origp->dt_key, newp->dt_key) != 0) {
321 			retval = find_dt(fp, 0, query, 1, newp, NULL, &found);
322 			if (retval != DSVC_SUCCESS)
323 				goto out;
324 			if (found != 0) {
325 				retval = DSVC_EXISTS;
326 				goto out;
327 			}
328 		}
329 	}
330 
331 	/*
332 	 * If we're deleting or modifying record, make sure the record
333 	 * still exists.  Note that we don't check signatures because this
334 	 * is a legacy format that has no signatures.
335 	 */
336 	if (origp != NULL) {
337 		retval = find_dt(fp, FIND_POSITION, query, 1, origp, &reclist,
338 		    &found);
339 		if (retval != DSVC_SUCCESS)
340 			goto out;
341 		if (found == 0) {
342 			retval = DSVC_NOENT;
343 			goto out;
344 		}
345 
346 		/*
347 		 * Note the offset of the record we're modifying or deleting
348 		 * for use down below.
349 		 */
350 		recoff  = ((dt_recpos_t *)reclist->dtl_rec)->dtp_off;
351 		recnext = recoff + ((dt_recpos_t *)reclist->dtl_rec)->dtp_size;
352 
353 		free_dtrec_list(reclist);
354 	} else {
355 		/*
356 		 * No record to modify or delete, so set `recoff' and
357 		 * `recnext' appropriately.
358 		 */
359 		recoff = 0;
360 		recnext = 0;
361 	}
362 
363 	/*
364 	 * Make a new copy of the container.  If we're deleting or
365 	 * modifying a record, don't copy that record to the new container.
366 	 */
367 	if (fstat(fileno(fp), &st) == -1) {
368 		retval = DSVC_INTERNAL;
369 		goto out;
370 	}
371 
372 	retval = copy_range(fileno(fp), 0, newfd, 0, recoff);
373 	if (retval != DSVC_SUCCESS)
374 		goto out;
375 
376 	retval = copy_range(fileno(fp), recnext, newfd, recoff,
377 	    st.st_size - recnext);
378 	if (retval != DSVC_SUCCESS)
379 		goto out;
380 
381 	/*
382 	 * If there's a new record, append it to the new container.
383 	 */
384 	if (newp != NULL) {
385 		retval = write_rec(newfd, newp, recoff + st.st_size - recnext);
386 		if (retval != DSVC_SUCCESS)
387 			goto out;
388 	}
389 
390 	/*
391 	 * Note: we close these descriptors before the rename(2) (rather
392 	 * than just having the `out:' label clean them up) to save NFS
393 	 * some work (otherwise, NFS has to save `dtpath' to an alternate
394 	 * name since its vnode would still be active).
395 	 */
396 	(void) fclose(fp);
397 	(void) close(newfd);
398 
399 	if (rename(newpath, dtpath) == -1)
400 		retval = syserr_to_dsvcerr(errno);
401 
402 	return (retval);
403 out:
404 	(void) fclose(fp);
405 	(void) close(newfd);
406 	(void) unlink(newpath);
407 	return (retval);
408 }
409 
410 int
delete_dt(void * handle,const dt_rec_t * delp)411 delete_dt(void *handle, const dt_rec_t *delp)
412 {
413 	return (update_dt((dt_handle_t *)handle, delp, NULL));
414 }
415 
416 int
add_dt(void * handle,dt_rec_t * addp)417 add_dt(void *handle, dt_rec_t *addp)
418 {
419 	return (update_dt((dt_handle_t *)handle, NULL, addp));
420 }
421 
422 int
modify_dt(void * handle,const dt_rec_t * origp,dt_rec_t * newp)423 modify_dt(void *handle, const dt_rec_t *origp, dt_rec_t *newp)
424 {
425 	return (update_dt((dt_handle_t *)handle, origp, newp));
426 }
427 
428 int
list_dt(const char * location,char *** listppp,uint_t * countp)429 list_dt(const char *location, char ***listppp, uint_t *countp)
430 {
431 	char	dtpath[MAXPATHLEN];
432 	char	**listpp;
433 
434 	if (access(location, F_OK|R_OK) == -1) {
435 		switch (errno) {
436 		case EACCES:
437 		case EPERM:
438 			return (DSVC_ACCESS);
439 		case ENOENT:
440 			return (DSVC_NO_LOCATION);
441 		default:
442 			break;
443 		}
444 		return (DSVC_INTERNAL);
445 	}
446 
447 	dt2path(dtpath, MAXPATHLEN, location, "");
448 	if (access(dtpath, F_OK|R_OK) == -1) {
449 		*countp = 0;
450 		*listppp = NULL;
451 		return (DSVC_SUCCESS);
452 	}
453 
454 	listpp = malloc(sizeof (char **));
455 	if (listpp == NULL)
456 		return (DSVC_NO_MEMORY);
457 	listpp[0] = strdup(DT_DHCPTAB);
458 	if (listpp[0] == NULL) {
459 		free(listpp);
460 		return (DSVC_NO_MEMORY);
461 	}
462 
463 	*listppp = listpp;
464 	*countp = 1;
465 	return (DSVC_SUCCESS);
466 }
467 
468 /*
469  * Given a buffer `path' of `pathlen' bytes, fill it in with a path to
470  * the dhcptab in directory `dir' with a suffix of `suffix'.
471  */
472 static void
dt2path(char * path,size_t pathlen,const char * dir,const char * suffix)473 dt2path(char *path, size_t pathlen, const char *dir, const char *suffix)
474 {
475 	(void) snprintf(path, pathlen, "%s/%s%s", dir, DT_DHCPTAB, suffix);
476 }
477 
478 /*
479  * Write the dt_rec_t pointed to by `recp' into the open container `fd' at
480  * offset `recoff'.  Returns DSVC_* error code.
481  */
482 static int
write_rec(int fd,dt_rec_t * recp,off_t recoff)483 write_rec(int fd, dt_rec_t *recp, off_t recoff)
484 {
485 	char	entbuf[1024], *ent = entbuf;
486 	size_t	entsize = sizeof (entbuf);
487 	int	entlen;
488 
489 again:
490 	entlen = snprintf(ent, entsize, "%s\t%c\t%s\n", recp->dt_key,
491 	    recp->dt_type, recp->dt_value);
492 	if (entlen == -1)
493 		return (syserr_to_dsvcerr(errno));
494 
495 	if (entlen > entsize) {
496 		entsize = entlen;
497 		ent = alloca(entlen);
498 		goto again;
499 	}
500 
501 	if (pnwrite(fd, ent, entlen, recoff) == -1)
502 		return (syserr_to_dsvcerr(errno));
503 
504 	return (DSVC_SUCCESS);
505 }
506