xref: /illumos-gate/usr/src/cmd/sgs/crle/common/config.c (revision 57ef7aa9)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include	<sys/mman.h>
27 #include	<sys/types.h>
28 #include	<fcntl.h>
29 #include	<unistd.h>
30 #include	<errno.h>
31 #include	<stdio.h>
32 #include	<string.h>
33 #include	"rtc.h"
34 #include	"_crle.h"
35 #include	"msg.h"
36 
37 
38 #define	MAXNBKTS 10007
39 
40 static const int hashsize[] = {
41 	3,	7,	13,	31,	53,	67,	83,	97,
42 	101,	151,	211,	251,	307,	353,	401,	457,	503,
43 	557,	601,	653,	701,	751,	809,	859,	907,	953,
44 	1009,	1103,	1201,	1301,	1409,	1511,	1601,	1709,	1801,
45 	1901,	2003,	2111,	2203,	2309,	2411,	2503,	2609,	2707,
46 	2801,	2903,	3001,	3109,	3203,	3301,	3407,	3511,	3607,
47 	3701,	3803,	3907,	4001,	5003,   6101,   7001,   8101,   9001,
48 	MAXNBKTS
49 };
50 
51 /*
52  * Generate a configuration file from the internal configuration information.
53  * (very link-editor like).
54  */
55 int
genconfig(Crle_desc * crle)56 genconfig(Crle_desc *crle)
57 {
58 	int		ndx, bkt;
59 	size_t		size, hashoff = 0, stroff = 0, objoff = 0;
60 	size_t		diroff = 0, fileoff = 0, envoff = 0;
61 	size_t		fltroff = 0, flteoff = 0;
62 	Addr		addr;
63 	Rtc_id		*id;
64 	Rtc_head	*head;
65 	Word		*hashtbl, *hashbkt, *hashchn, hashbkts = 0;
66 	char		*strtbl, *_strtbl;
67 	Rtc_obj		*objtbl;
68 	Rtc_dir		*dirtbl;
69 	Rtc_file	*filetbl;
70 	Rtc_env		*envtbl;
71 	Rtc_fltr	*fltrtbl;
72 	Rtc_flte	*fltetbl, * _fltetbl;
73 	Hash_tbl	*stbl = crle->c_strtbl;
74 	Hash_ent	*ent;
75 
76 	/*
77 	 * Establish the size of the configuration file.
78 	 */
79 	size = S_ROUND(sizeof (Rtc_head), sizeof (Word));
80 
81 	if (crle->c_hashstrnum) {
82 		hashoff = size;
83 
84 		/*
85 		 * Increment the hash string number to account for an initial
86 		 * null entry.  Indexes start at 1 to simplify hash lookup.
87 		 */
88 		crle->c_hashstrnum++;
89 
90 		/*
91 		 * Determine the hash table size.  Establish the number of
92 		 * buckets from the number of strings, the number of chains is
93 		 * equivalent to the number of objects, and two entries for the
94 		 * nbucket and nchain entries.
95 		 */
96 		for (ndx = 0; ndx < (sizeof (hashsize) / sizeof (int)); ndx++) {
97 			if (crle->c_hashstrnum > hashsize[ndx])
98 				continue;
99 			hashbkts = hashsize[ndx];
100 			break;
101 		}
102 		if (hashbkts == 0)
103 			hashbkts = MAXNBKTS;
104 		size += ((2 + hashbkts + crle->c_hashstrnum) * sizeof (Word));
105 		size = S_ROUND(size, sizeof (Lword));
106 		objoff = size;
107 
108 		/*
109 		 * Add the object table size (account for an 8-byte alignment
110 		 * requirement for each object).
111 		 */
112 		size += (crle->c_hashstrnum *
113 		    S_ROUND(sizeof (Rtc_obj), sizeof (Lword)));
114 
115 		/*
116 		 * Add the file descriptor arrays.
117 		 */
118 		fileoff = size;
119 		size += S_ROUND((crle->c_filenum * sizeof (Rtc_file)),
120 		    sizeof (Word));
121 
122 		/*
123 		 * Add the directory descriptor array.
124 		 */
125 		diroff = size;
126 		size += S_ROUND((crle->c_dirnum * sizeof (Rtc_dir)),
127 		    sizeof (Word));
128 	}
129 
130 	/*
131 	 * Add any environment string array (insure zero last entry).
132 	 */
133 	if (crle->c_envnum) {
134 		envoff = size;
135 		size += S_ROUND(((crle->c_envnum + 1) * sizeof (Rtc_env)),
136 		    sizeof (Word));
137 	}
138 
139 	/*
140 	 * Add any filter/filtee association arrays (insure zero last entry for
141 	 * the filter array, the filtee arrays are already accounted for).
142 	 */
143 	if (crle->c_fltrnum) {
144 		fltroff = size;
145 		size += S_ROUND(((crle->c_fltrnum + 1) * sizeof (Rtc_fltr)),
146 		    sizeof (Word));
147 		flteoff = size;
148 		size += S_ROUND((crle->c_fltenum * sizeof (Rtc_flte)),
149 		    sizeof (Word));
150 	}
151 
152 	/*
153 	 * Add the string table size (this may contain library and/or secure
154 	 * path strings, in addition to any directory/file strings).
155 	 */
156 	if (crle->c_strsize) {
157 		stroff = size;
158 		size += S_ROUND(crle->c_strsize, sizeof (Word));
159 	}
160 
161 	/* Account for addition of Rtc_id block at the start */
162 	if (crle->c_flags & CRLE_ADDID)
163 		size += sizeof (Rtc_id);
164 
165 	/*
166 	 * Truncate our temporary file now that we know its size and map it.
167 	 */
168 	if (ftruncate(crle->c_tempfd, size) == -1) {
169 		int err = errno;
170 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
171 		    crle->c_name, crle->c_tempname, strerror(err));
172 		(void) close(crle->c_tempfd);
173 		return (1);
174 	}
175 	if ((addr = (Addr)mmap(0, size, (PROT_READ | PROT_WRITE), MAP_SHARED,
176 	    crle->c_tempfd, 0)) == (Addr)-1) {
177 		int err = errno;
178 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP),
179 		    crle->c_name, crle->c_tempname, strerror(err));
180 		(void) close(crle->c_tempfd);
181 		return (1);
182 	}
183 
184 	/*
185 	 * Save the mapped files info for possible dldump(3dl) updates.
186 	 */
187 	crle->c_tempaddr = addr;
188 	crle->c_tempsize = size;
189 
190 	/*
191 	 * Rtc_id goes at the top, followed by the Rtc_head. We base
192 	 * all offset calculations relative to Rtc_head, not from
193 	 * the top of the file. This eases backwards compatability to
194 	 * older versons that lacked the Rtc_id at the top.
195 	 */
196 	if (crle->c_flags & CRLE_ADDID) {
197 		/* The contents of the Rtc_id are all known at compile time */
198 		static const Rtc_id id_template = {
199 			RTC_ID_MAG0, RTC_ID_MAG1, RTC_ID_MAG2, RTC_ID_MAG3,
200 			M_CLASS, M_DATA, M_MACH,
201 			{ 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
202 
203 		id = (Rtc_id *) addr;
204 		*id = id_template;	/* Fill in the Rtc_id data */
205 		addr += sizeof (Rtc_id);
206 	} else {
207 		id = NULL;
208 	}
209 	crle->c_tempheadaddr = addr;
210 	head = (Rtc_head *)addr;
211 
212 	/*
213 	 * Establish the real address of each of the structures within the file.
214 	 */
215 	head->ch_hash = hashoff;
216 	/* LINTED */
217 	hashtbl = (Word *)(CAST_PTRINT(char *, head->ch_hash) + addr);
218 
219 	head->ch_obj = objoff;
220 	/* LINTED */
221 	objtbl = (Rtc_obj *)(CAST_PTRINT(char *, head->ch_obj) + addr);
222 	objtbl = (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword));
223 
224 	head->ch_file = fileoff;
225 	/* LINTED */
226 	filetbl = (Rtc_file *)(CAST_PTRINT(char *, head->ch_file) + addr);
227 
228 	head->ch_dir = diroff;
229 	/* LINTED */
230 	dirtbl = (Rtc_dir *)(CAST_PTRINT(char *, head->ch_dir) + addr);
231 
232 	head->ch_env = envoff;
233 	/* LINTED */
234 	envtbl = (Rtc_env *)(CAST_PTRINT(char *, head->ch_env) + addr);
235 
236 	head->ch_fltr = fltroff;
237 	/* LINTED */
238 	fltrtbl = (Rtc_fltr *)(CAST_PTRINT(char *, head->ch_fltr) + addr);
239 	head->ch_flte = flteoff;
240 	/* LINTED */
241 	fltetbl = _fltetbl = (Rtc_flte *)(CAST_PTRINT(char *, head->ch_flte) +
242 	    addr);
243 
244 	head->ch_str = stroff;
245 	strtbl = _strtbl = (char *)(CAST_PTRINT(char *, head->ch_str) + addr);
246 
247 	/*
248 	 * Fill in additional basic header information.
249 	 */
250 	head->ch_version = RTC_VER_CURRENT;
251 
252 	if (crle->c_flags & CRLE_ALTER)
253 		head->ch_cnflags |= RTC_HDR_ALTER;
254 	if (crle->c_flags & CRLE_DUMP) {
255 		head->ch_cnflags |= RTC_HDR_IGNORE;
256 		head->ch_dlflags = crle->c_dlflags;
257 	}
258 #ifdef _ELF64
259 	head->ch_cnflags |= RTC_HDR_64;
260 #endif
261 
262 #ifndef	SGS_PRE_UNIFIED_PROCESS
263 	head->ch_cnflags |= RTC_HDR_UPM;
264 #endif
265 	/*
266 	 * If we have a hash table then there are directory and file entries
267 	 * to process.
268 	 */
269 	if (crle->c_hashstrnum) {
270 		hashtbl[0] = hashbkts;
271 		hashtbl[1] = crle->c_hashstrnum;
272 		hashbkt = &hashtbl[2];
273 		hashchn = &hashtbl[2 + hashbkts];
274 
275 		/*
276 		 * Insure all hash chain and directory/filename table entries
277 		 * are cleared.
278 		 */
279 		(void) memset(hashchn, 0, (crle->c_hashstrnum * sizeof (Word)));
280 		(void) memset(dirtbl, 0, (strtbl - (char *)dirtbl));
281 
282 		/*
283 		 * Loop through the current string table list inspecting only
284 		 * directories.
285 		 */
286 		for (ndx = 1, bkt = 0; bkt < stbl->t_size; bkt++) {
287 			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
288 				Word		hashval;
289 				Hash_obj	*obj = ent->e_obj;
290 				char		*dir = (char *)ent->e_key;
291 				Rtc_dir		*_dirtbl;
292 
293 				/*
294 				 * Skip any empty and non-directory entries.
295 				 */
296 				if ((obj == NULL) ||
297 				    ((obj->o_flags & RTC_OBJ_DIRENT) == 0))
298 					continue;
299 
300 				/*
301 				 * Assign basic object attributes.
302 				 */
303 				objtbl->co_hash = ent->e_hash;
304 				objtbl->co_id = ent->e_id;
305 				objtbl->co_flags = obj->o_flags | ent->e_flags;
306 				objtbl->co_info = obj->o_info;
307 
308 				ent->e_cobj = objtbl;
309 
310 				/*
311 				 * Assign the directory name (from its key),
312 				 * and copy its name to the string table.
313 				 */
314 				objtbl->co_name = (Addr)(_strtbl - strtbl);
315 				(void) strcpy(_strtbl, dir);
316 				_strtbl += strlen(dir) + 1;
317 
318 				/*
319 				 * Establish an entry in the directory table and
320 				 * reserve space for its associated filename
321 				 * entries (note, we add a trailing null file
322 				 * entry to simplify later inspection of the
323 				 * final configuration file.
324 				 */
325 				_dirtbl = &dirtbl[ent->e_id - 1];
326 				_dirtbl->cd_file =
327 				    CAST_PTRINT(Word, ((char *)filetbl- addr));
328 				_dirtbl->cd_obj =
329 				    CAST_PTRINT(Word, ((char *)objtbl - addr));
330 
331 				/* LINTED */
332 				filetbl = (Rtc_file *)((char *)filetbl +
333 				    ((ent->e_cnt + 1) * sizeof (Rtc_file)));
334 
335 				/*
336 				 * Add this object to the hash table.
337 				 */
338 				hashval = ent->e_hash % hashbkts;
339 				hashchn[ndx] = hashbkt[hashval];
340 				hashbkt[hashval] = ndx++;
341 
342 				/*
343 				 * Increment Rt_obj pointer (make sure pointer
344 				 * falls on an 8-byte boundary).
345 				 */
346 				objtbl =
347 				    (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1),
348 				    sizeof (Lword));
349 			}
350 		}
351 
352 		/*
353 		 * Now collect all pathnames.  These are typically full
354 		 * pathnames, but may also be relative.  Simple filenames are
355 		 * recorded as offsets into these pathnames, thus we need to
356 		 * establish the new pathname first.
357 		 */
358 		for (bkt = 0; bkt < stbl->t_size; bkt++) {
359 			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
360 				Word		hashval;
361 				Hash_obj	*obj = ent->e_obj;
362 				char		*file = (char *)ent->e_key;
363 				char		*_str;
364 				Rtc_dir		*_dirtbl;
365 				Rtc_file	*_filetbl;
366 				int		_id;
367 
368 				/*
369 				 * Skip empty and directory entries, and any
370 				 * simple filename entries.
371 				 */
372 				if ((obj == NULL) ||
373 				    (obj->o_flags & RTC_OBJ_DIRENT) ||
374 				    (ent->e_off))
375 					continue;
376 
377 				/*
378 				 * Assign basic object attributes.
379 				 */
380 				objtbl->co_hash = ent->e_hash;
381 				objtbl->co_id = ent->e_id;
382 				objtbl->co_flags = obj->o_flags | ent->e_flags;
383 				objtbl->co_info = obj->o_info;
384 
385 				ent->e_cobj = objtbl;
386 
387 				/*
388 				 * Assign the file name (from its key),
389 				 * and copy its name to the string table.
390 				 */
391 				objtbl->co_name = (Addr)(_strtbl - strtbl);
392 				(void) strcpy(_strtbl, file);
393 				_strtbl += strlen(file) + 1;
394 
395 				/*
396 				 * Add this file to its associated directory.
397 				 */
398 				_dirtbl = &dirtbl[ent->e_id - 1];
399 				/* LINTED */
400 				_filetbl = (Rtc_file *)(CAST_PTRINT(char *,
401 				    _dirtbl->cd_file) + addr);
402 
403 				_id = --ent->e_dir->e_cnt;
404 				_filetbl[_id].cf_obj =
405 				    CAST_PTRINT(Word, ((char *)objtbl - addr));
406 
407 				/*
408 				 * If object has an alternative, record it in
409 				 * the string table and assign the alternate
410 				 * pointer.  The new alternative offset is
411 				 * retained for reuse in other filename entries.
412 				 */
413 				if ((objtbl->co_flags & RTC_OBJ_ALTER) &&
414 				    (obj->o_calter == 0)) {
415 					_str = obj->o_alter;
416 					objtbl->co_alter = obj->o_calter =
417 					    (Addr)(_strtbl - strtbl);
418 					(void) strcpy(_strtbl, _str);
419 					_strtbl += strlen(_str) + 1;
420 				} else
421 					objtbl->co_alter = obj->o_calter;
422 
423 				/*
424 				 * If object identifies the specific application
425 				 * for which this cache is relevant, record it
426 				 * in the header.
427 				 */
428 				if ((objtbl->co_flags &
429 				    (RTC_OBJ_APP | RTC_OBJ_REALPTH)) ==
430 				    (RTC_OBJ_APP | RTC_OBJ_REALPTH))
431 					head->ch_app = _filetbl[_id].cf_obj;
432 
433 				/*
434 				 * Add this object to the hash table.
435 				 */
436 				hashval = ent->e_hash % hashbkts;
437 				hashchn[ndx] = hashbkt[hashval];
438 				hashbkt[hashval] = ndx++;
439 
440 				/*
441 				 * Increment Rt_obj pointer (make sure pointer
442 				 * falls on an 8-byte boundary).
443 				 */
444 				objtbl = (Rtc_obj *)
445 				    S_ROUND((uintptr_t)(objtbl + 1),
446 				    sizeof (Lword));
447 			}
448 		}
449 
450 		/*
451 		 * Finally pick off any simple filenames.
452 		 */
453 		for (bkt = 0; bkt < stbl->t_size; bkt++) {
454 			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
455 				Word		hashval;
456 				Hash_obj *	obj = ent->e_obj;
457 				Rtc_dir *	_dirtbl;
458 				Rtc_file *	_filetbl;
459 				int		_id;
460 
461 				/*
462 				 * Skip everything except simple filenames.
463 				 */
464 				if (ent->e_off == 0)
465 					continue;
466 
467 				/*
468 				 * Assign basic object attributes.
469 				 */
470 				objtbl->co_hash = ent->e_hash;
471 				objtbl->co_id = ent->e_id;
472 				objtbl->co_flags = obj->o_flags | ent->e_flags;
473 				objtbl->co_info = obj->o_info;
474 				objtbl->co_alter = obj->o_calter;
475 
476 				ent->e_cobj = objtbl;
477 
478 				/*
479 				 * Assign the file name from its full name.
480 				 */
481 				objtbl->co_name = (Addr)(CAST_PTRINT(char *,
482 				    ent->e_path->e_cobj->co_name) + ent->e_off);
483 
484 				/*
485 				 * Add this file to its associated directory.
486 				 */
487 				_dirtbl = &dirtbl[ent->e_id - 1];
488 				/* LINTED */
489 				_filetbl = (Rtc_file *)
490 				    (CAST_PTRINT(char *, _dirtbl->cd_file) +
491 				    addr);
492 
493 				_id = --ent->e_dir->e_cnt;
494 				_filetbl[_id].cf_obj =
495 				    CAST_PTRINT(Word, ((char *)objtbl - addr));
496 
497 				/*
498 				 * Add this object to the hash table.
499 				 */
500 				hashval = ent->e_hash % hashbkts;
501 				hashchn[ndx] = hashbkt[hashval];
502 				hashbkt[hashval] = ndx++;
503 
504 				/*
505 				 * Increment Rt_obj pointer (make sure pointer
506 				 * falls on an 8-byte boundary).
507 				 */
508 				objtbl = (Rtc_obj *)
509 				    S_ROUND((uintptr_t)(objtbl + 1),
510 				    sizeof (Lword));
511 			}
512 		}
513 	}
514 
515 	/*
516 	 * Add any library, or secure path definitions.
517 	 */
518 	if (crle->c_edlibpath) {
519 		head->ch_edlibpath = head->ch_str + (_strtbl - strtbl);
520 
521 		(void) strcpy(_strtbl, crle->c_edlibpath);
522 		_strtbl += strlen((char *)crle->c_edlibpath) + 1;
523 	} else
524 		head->ch_edlibpath = 0;
525 
526 	if (crle->c_adlibpath) {
527 		head->ch_adlibpath = head->ch_str + (_strtbl - strtbl);
528 
529 		(void) strcpy(_strtbl, crle->c_adlibpath);
530 		_strtbl += strlen((char *)crle->c_adlibpath) + 1;
531 	} else
532 		head->ch_adlibpath = 0;
533 
534 	if (crle->c_eslibpath) {
535 		head->ch_eslibpath = head->ch_str + (_strtbl - strtbl);
536 
537 		(void) strcpy(_strtbl, crle->c_eslibpath);
538 		_strtbl += strlen((char *)crle->c_eslibpath) + 1;
539 	} else
540 		head->ch_eslibpath = 0;
541 
542 	if (crle->c_aslibpath) {
543 		head->ch_aslibpath = head->ch_str + (_strtbl - strtbl);
544 
545 		(void) strcpy(_strtbl, crle->c_aslibpath);
546 		_strtbl += strlen((char *)crle->c_aslibpath) + 1;
547 	} else
548 		head->ch_aslibpath = 0;
549 
550 	/*
551 	 * Add any environment variable entries.
552 	 */
553 	if (crle->c_envnum) {
554 		Env_desc	*env;
555 		Aliste		idx;
556 
557 		for (APLIST_TRAVERSE(crle->c_env, idx, env)) {
558 			envtbl->env_str = head->ch_str + (_strtbl - strtbl);
559 			envtbl->env_flags = env->e_flags;
560 
561 			(void) strcpy(_strtbl, env->e_str);
562 			_strtbl += env->e_totsz;
563 
564 			envtbl++;
565 		}
566 		envtbl->env_str = 0;
567 		envtbl->env_flags = 0;
568 	}
569 
570 	/*
571 	 * Add any filter/filtee association entries.
572 	 */
573 	if (crle->c_fltrnum) {
574 		Flt_desc	*flt;
575 		Aliste		idx1;
576 
577 		for (APLIST_TRAVERSE(crle->c_flt, idx1, flt)) {
578 			Hash_ent	*flte;
579 			Aliste		idx2;
580 
581 			/*
582 			 * Establish the filter name, and filtee string, as
583 			 * offsets into the configuration files string table.
584 			 * Establish the filtee as the offset into the filtee
585 			 * table.
586 			 */
587 			fltrtbl->fr_filter = flt->f_fent->e_cobj->co_name;
588 			fltrtbl->fr_string = _strtbl - strtbl;
589 			(void) strcpy(_strtbl, flt->f_str);
590 			_strtbl += flt->f_strsz;
591 			fltrtbl->fr_filtee = (Word)
592 			    ((uintptr_t)_fltetbl - (uintptr_t)fltetbl);
593 
594 			for (APLIST_TRAVERSE(flt->f_filtee, idx2, flte)) {
595 				/*
596 				 * Establish the filtee name as the offset into
597 				 * the configuration files string table.
598 				 */
599 				_fltetbl->fe_filtee = flte->e_cobj->co_name;
600 				_fltetbl++;
601 			}
602 			_fltetbl->fe_filtee = 0;
603 			_fltetbl++, fltrtbl++;
604 		}
605 		fltrtbl->fr_filter = 0;
606 		fltrtbl->fr_filtee = 0;
607 	}
608 
609 	/*
610 	 * Flush everything out.
611 	 */
612 	(void) close(crle->c_tempfd);
613 	if (msync((void *)crle->c_tempaddr, crle->c_tempsize, MS_ASYNC) == -1) {
614 		int err = errno;
615 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
616 		    crle->c_name, crle->c_tempname, strerror(err));
617 		return (1);
618 	}
619 
620 	return (0);
621 }
622 
623 /*
624  * Update a configuration file.  If dldump()'ed images have been created then
625  * the memory reservation of those images is added to the configuration file.
626  * The temporary file is then moved into its final resting place.
627  */
628 int
updateconfig(Crle_desc * crle)629 updateconfig(Crle_desc * crle)
630 {
631 	Rtc_head *head = (Rtc_head *)crle->c_tempheadaddr;
632 
633 	if (crle->c_flags & CRLE_DUMP) {
634 		head->ch_cnflags &= ~RTC_HDR_IGNORE;
635 
636 		if (msync((void *)crle->c_tempaddr, crle->c_tempsize,
637 		    MS_ASYNC) == -1) {
638 			int err = errno;
639 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
640 			    crle->c_name, crle->c_tempname, strerror(err));
641 			return (1);
642 		}
643 	}
644 
645 	/*
646 	 * If an original configuration file exists, remove it.
647 	 */
648 	if (crle->c_flags & CRLE_EXISTS)
649 		(void) unlink(crle->c_confil);
650 
651 	/*
652 	 * Move the config file to its final resting place.  If the two files
653 	 * exist on the same filesystem a rename is sufficient.
654 	 */
655 	if (crle->c_flags & CRLE_DIFFDEV) {
656 		int	fd;
657 
658 		if ((fd = open(crle->c_confil, (O_RDWR | O_CREAT | O_TRUNC),
659 		    0666)) == -1) {
660 			int err = errno;
661 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
662 			    crle->c_name, crle->c_confil, strerror(err));
663 			return (1);
664 		}
665 		if (write(fd, (void *)crle->c_tempaddr, crle->c_tempsize) !=
666 		    crle->c_tempsize) {
667 			int err = errno;
668 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_WRITE),
669 			    crle->c_name, crle->c_confil, strerror(err));
670 			return (1);
671 		}
672 		(void) close(fd);
673 		(void) unlink(crle->c_tempname);
674 	} else
675 		(void) rename(crle->c_tempname, crle->c_confil);
676 
677 	return (0);
678 }
679