xref: /titanic_50/usr/src/cmd/svr4pkg/libinst/ocfile.c (revision 03d7b3c0e29500d6f69fdb637d3b884fb0e8b5c2)
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 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28 
29 
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/sysmacros.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <sys/statvfs.h>
41 #include <signal.h>
42 #include <limits.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <time.h>
48 #include <errno.h>
49 #include <pkglocs.h>
50 #include <locale.h>
51 #include <libintl.h>
52 #include <pkglib.h>
53 #include "libinst.h"
54 #include "libadm.h"
55 
56 #define	LOCKFILE	".pkg.lock.client"
57 #define	LOCKFILESERV	".pkg.lock"
58 
59 #define	LOCKWAIT	10	/* seconds between retries */
60 #define	LOCKRETRY	20	/* number of retries for a DB lock */
61 
62 #define	ERR_COMMIT	"WARNING: unable to commit contents database update"
63 #define	ERR_NOCLOSE	"WARNING: unable to close <%s>"
64 #define	ERR_NOUNLINK_LATENT	"WARNING: unable to unlink latent <%s>"
65 #define	ERR_LINK_FAIL	"link(%s, %s) failed (errno %d)"
66 #define	ERR_NORENAME_CONTENTS	"unable to establish contents file <%s> "\
67 			"from <%s>"
68 #define	ERR_RENAME_FAIL	"rename(%s, %s) failed (errno %d)"
69 #define	ERR_RESTORE_FAIL	"attempt to restore <%s> failed"
70 #define	ERR_NOUNLINK	"WARNING: unable to unlink <%s>"
71 #define	ERR_FCLOSE_FAIL	"fclose failed (errno %d)"
72 #define	ERR_ERRNO	"(errno %d: %s)"
73 #define	ERR_NOTMPOPEN	"unable to open temporary contents file image"
74 #define	ERR_CFBACK	"Not enough space to backup <%s>"
75 #define	ERR_CREAT_CONT	"unable to create contents file <%s>: %s"
76 #define	ERR_ACCESS_CONT	"unable to access contents file <%s>: %s"
77 #define	ERR_CFBACK1	"Need=%llu blocks, Available=%llu blocks " \
78 			"(block size=%d)"
79 #define	ERR_NOCFILE	"unable to locate contents file <%s>"
80 #define	ERR_NOROPEN	"unable to open <%s> for reading"
81 #define	ERR_NOOPEN	"unable to open <%s> for writing"
82 #define	ERR_NOSTAT	"unable to stat contents file <%s>"
83 #define	ERR_NOSTATV	"statvfs(%s) failed"
84 #define	ERR_NOUPD	"unable to update contents file"
85 #define	ERR_DRCONTCP	"unable to copy contents file to <%s>"
86 
87 #define	MSG_XWTING	"NOTE: Waiting for exclusive access to the package " \
88 				"database."
89 #define	MSG_NOLOCK	"NOTE: Couldn't lock the package database."
90 
91 #define	ERR_NOLOCK	"Database lock failed."
92 #define	ERR_OPLOCK	"unable to open lock file <%s>."
93 #define	ERR_MKLOCK	"unable to create lock file <%s>."
94 #define	ERR_LCKREM	"unable to lock package database - remote host " \
95 				"unavailable."
96 #define	ERR_BADLCK	"unable to lock package database - unknown error."
97 #define	ERR_DEADLCK	"unable to lock package database - deadlock condition."
98 #define	ERR_TMOUT	"unable to lock package database - too many retries."
99 #define	ERR_CFDIR	"unable to locate contents file directory"
100 
101 static int	active_lock;
102 static int	lock_fd;	/* fd of LOCKFILE. */
103 static char	*pkgadm_dir;
104 
105 int		pkgWlock(int verbose);
106 static int	pkgWunlock(void);
107 
108 /* forward declarations */
109 
110 int relslock(void);
111 
112 /*ARGSUSED*/
113 static void
114 do_alarm(int n)
115 {
116 	(void) signal(SIGALRM, SIG_IGN);
117 	(void) signal(SIGALRM, do_alarm);
118 	(void) alarm(LOCKWAIT);
119 }
120 
121 /*
122  * Point packaging to the appropriate contents file. This is primarily used
123  * to establish a dryrun contents file. If the malloc() doesn't work, this
124  * returns 99 (internal error), else 0.
125  */
126 int
127 set_cfdir(char *cfdir)
128 {
129 	char	realcf[PATH_MAX];
130 	char	tmpcf[PATH_MAX];
131 	int	status;
132 
133 	if (cfdir == NULL) {
134 		pkgadm_dir = get_PKGADM();
135 		return (0);
136 	}
137 
138 	if ((pkgadm_dir = strdup(cfdir)) == NULL) {
139 		return (99);
140 	}
141 
142 	(void) snprintf(tmpcf, sizeof (tmpcf), "%s/contents", pkgadm_dir);
143 
144 	/*
145 	 * return if a temporary contents file already exists -
146 	 * assume it is from a prior package in this series.
147 	 */
148 
149 	if (access(tmpcf, F_OK) == 0) {
150 		return (0);
151 	}
152 
153 	/*
154 	 * no temporary contents file exists - create one.
155 	 */
156 
157 	(void) snprintf(realcf, sizeof (realcf), "%s/contents", get_PKGADM());
158 
159 	/*
160 	 * If there's a contents file there already, copy it
161 	 * over, otherwise initialize one.  Make sure that the
162 	 * server, if running, flushes the contents file.
163 	 */
164 
165 	(void) pkgsync(NULL, get_PKGADM(), B_FALSE);
166 
167 	/* create new contents file if one does not already exist */
168 
169 	if (access(realcf, F_OK) != 0) {
170 		int n;
171 
172 		n = open(tmpcf, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
173 		if (n < 0) {
174 			progerr(gettext(ERR_CREAT_CONT), tmpcf,
175 			    strerror(errno));
176 			return (99);
177 		}
178 		(void) close(n);
179 	} else {
180 
181 		/* contents file exists, save in pkgadm-dir */
182 
183 		status = copyf(realcf, tmpcf, (time_t)0);
184 		if (status != 0) {
185 			progerr(gettext(ERR_DRCONTCP), tmpcf);
186 			return (99);
187 		}
188 	}
189 
190 	return (0);
191 }
192 
193 /*
194  * This function installs the database lock, opens the contents file for
195  * reading and creates and opens the temporary contents file for read/write.
196  * It returns 1 if successful, 0 otherwise.
197  */
198 int
199 ocfile(PKGserver *server, VFP_T **r_tmpvfp, fsblkcnt_t map_blks)
200 {
201 	struct	stat64	statb, statl;
202 	struct	statvfs64	svfsb;
203 	fsblkcnt_t free_blocks;
204 	fsblkcnt_t need_blocks;
205 	fsblkcnt_t log_blocks;
206 	VFP_T		*tmpvfp = (VFP_T *)NULL;
207 	char		contents[PATH_MAX];
208 	char		logfile[PATH_MAX];
209 	int		n;
210 	off_t		cdiff_alloc;
211 	PKGserver	newserver;
212 
213 	/* establish package administration contents directory location */
214 
215 	if (pkgadm_dir == NULL) {
216 		if (set_cfdir(NULL) != 0) {
217 			progerr(gettext(ERR_CFDIR));
218 			return (0);
219 		}
220 	}
221 
222 	/* Lock the file for exclusive access */
223 
224 	if (!pkgWlock(1)) {
225 		progerr(gettext(ERR_NOLOCK));
226 		return (0);
227 	}
228 
229 	if (*server != NULL) {
230 		vfpTruncate(*r_tmpvfp);
231 		(void) vfpClearModified(*r_tmpvfp);
232 
233 		return (1);
234 	}
235 
236 	newserver = pkgopenserver(NULL, pkgadm_dir, B_FALSE);
237 
238 	/* The error has been reported. */
239 	if (newserver == NULL)
240 		return (0);
241 
242 	/* reset return VFP/FILE pointers */
243 
244 	(*r_tmpvfp) = (VFP_T *)NULL;
245 
246 	/* determine path to the primary contents file */
247 	(void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
248 
249 	/*
250 	 * Check and see if there is enough space for the packaging commands
251 	 * to back up the contents file, if there is not, then do not allow
252 	 * execution to continue by failing the ocfile() call.
253 	 */
254 
255 	/* Get the contents file size */
256 
257 	if (stat64(contents, &statb) == -1) {
258 		int	lerrno = errno;
259 
260 		progerr(gettext(ERR_NOCFILE), contents);
261 		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
262 		pkgcloseserver(newserver);
263 		return (0);
264 	}
265 
266 	/* Get the filesystem space */
267 
268 	if (statvfs64(contents, &svfsb) == -1) {
269 		int	lerrno = errno;
270 
271 		progerr(gettext(ERR_NOSTATV), contents);
272 		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
273 		pkgcloseserver(newserver);
274 		return (0);
275 	}
276 
277 	free_blocks = (((fsblkcnt_t)svfsb.f_frsize > 0) ?
278 	    howmany(svfsb.f_frsize, DEV_BSIZE) :
279 	    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
280 
281 	/* determine blocks used by the logfile */
282 	(void) snprintf(logfile, sizeof (logfile), "%s/" PKGLOG, pkgadm_dir);
283 
284 	if (stat64(logfile, &statl) == -1)
285 		log_blocks = 0;
286 	else
287 		log_blocks = nblk(statl.st_size, svfsb.f_bsize, svfsb.f_frsize);
288 
289 	/*
290 	 * Calculate the number of blocks we need to be able to operate on
291 	 * the contents file and the log file.
292 	 * When adding a package (map_blks > 0), we add the size of the
293 	 * pkgmap file times 1.5 as the pkgmap is a bit smaller then the
294 	 * lines added to the contents file.  That data is written both to
295 	 * the new contents file and the log file (2 * 1.5 * map_blks).
296 	 * The new contents file is limited by the size of the current
297 	 * contents file and the increased log file.
298 	 * If we're removing a package, then the log might grow to the size
299 	 * of the full contents file but then the new contents file would
300 	 * be zero and so we only need to add the size of the contents file.
301 	 */
302 	need_blocks = map_blks * 3 +
303 	    /* Current log file */
304 	    log_blocks +
305 	    /* Current contents file */
306 	    nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
307 
308 	if ((need_blocks + 10) > free_blocks) {
309 		progerr(gettext(ERR_CFBACK), contents);
310 		progerr(gettext(ERR_CFBACK1), need_blocks, free_blocks,
311 		    DEV_BSIZE);
312 		pkgcloseserver(newserver);
313 		return (0);
314 	}
315 
316 	/*
317 	 * open the temporary contents file without a path name - this causes
318 	 * the "vfp" to be opened on in-memory storage only, the size of which
319 	 * is set following a successful return - this causes the temporary
320 	 * contents file to be maintained in memory only - if no changes are
321 	 * made as the primary contents file is processed, the in memory data
322 	 * is discarded and not written to the disk.
323 	 */
324 
325 	if (vfpOpen(&tmpvfp, (char *)NULL, "w", VFP_NONE) != 0) {
326 		int	lerrno = errno;
327 
328 		progerr(gettext(ERR_NOTMPOPEN));
329 		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
330 		pkgcloseserver(newserver);
331 		return (0);
332 	}
333 
334 	/*
335 	 * set size of allocation for temporary contents file - this sets the
336 	 * size of the in-memory buffer associated with the open vfp.
337 	 * We only store the new and changed entries.
338 	 * We allocate memory depending on the size of the pkgmap; it's not
339 	 * completely right but <some value + * 1.5 * map_blks * DEV_BSIZE>
340 	 * seems fine (an install adds the size if the name of the package.)
341 	 */
342 
343 	cdiff_alloc = map_blks * DEV_BSIZE;
344 	cdiff_alloc += cdiff_alloc/2;
345 	if (cdiff_alloc < 1000000)
346 		cdiff_alloc += 1000000;
347 
348 	if (vfpSetSize(tmpvfp, cdiff_alloc) != 0) {
349 		int	lerrno = errno;
350 
351 		progerr(gettext(ERR_NOTMPOPEN));
352 		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
353 		(void) vfpClose(&tmpvfp);
354 		pkgcloseserver(newserver);
355 		return (0);
356 	}
357 
358 	/* set return ->s to open server/vfps */
359 
360 	(*r_tmpvfp) = tmpvfp;
361 	*server = newserver;
362 
363 	return (1);	/* All OK */
364 }
365 
366 /*
367  * This is a simple open and lock of the contents file. It doesn't create a
368  * temporary contents file and it doesn't need to do any space checking.
369  * Returns 1 for OK and 0 for "didn't do it".
370  */
371 int
372 socfile(PKGserver *server, boolean_t quiet)
373 {
374 	char		contents[PATH_MAX];
375 	boolean_t 	readonly = B_FALSE;
376 	PKGserver	newserver;
377 
378 	if (pkgadm_dir == NULL) {
379 		if (set_cfdir(NULL) != 0) {
380 			progerr(gettext(ERR_CFDIR));
381 			return (0);
382 		}
383 	}
384 
385 	/*
386 	 * Lock the database for exclusive access, but don't make a fuss if
387 	 * it fails (user may not be root and the .pkg.lock file may not
388 	 * exist yet).
389 	 */
390 
391 	if (!pkgWlock(0)) {
392 		if (!quiet)
393 			logerr(gettext(MSG_NOLOCK));
394 		readonly = B_TRUE;
395 	}
396 
397 	newserver = pkgopenserver(NULL, pkgadm_dir, readonly);
398 	if (newserver == NULL)
399 		return (0);
400 
401 	*server = newserver;
402 	return (1);
403 }
404 
405 /*
406  * Name:	swapcfile
407  * Description: This function closes both the current and temporary contents
408  *		files specified, and conditionally replaces the old transitory
409  *		contents file with the newly updated temporary contents file.
410  *		The "ocfile()" or "socfile()" functions must be called to re-
411  *		open the real contents file for processing.
412  * Arguments:	PKGserver - handle to the package database
413  *		a_cfTmpVfp - (VFP_T **) - [RW, *RW]
414  *			This is the VFP associated which contains all the
415  *			modifications to be written back to the database.
416  *			file that is being written to.
417  *		pkginst - (char) - [RO, *RO]
418  *			This is the name of the package being operated on;
419  *			this is used to write the "last modified by xxx"
420  *			comment at the end of the contents file.
421  *		dbchg - (int) - [RO]
422  *			== 0 - the temporary contents file has NOT been changed
423  *				with respect to the real contents file; do not
424  *				update the real contents file with the contents
425  *				of the temporary contents file.
426  *			!= 0 - the temporary contetns file HAS been changed with
427  *				respect to the real contents file; DO update the
428  *				real contents file with the contents of the
429  *				temporary contents file.
430  * Returns:	int	== RESULT_OK - successful
431  *			== RESULT_WRN - successful with warnings
432  *			== RESULT_ERR - failed with fatal errors - deserves an
433  *				alarming message and a quit()
434  * NOTES: If dbchg != 0, the contents file is always updated. If dbchg == 0,
435  *		the contents file is updated IF the data is modified indication
436  *		is set on the contents file associated with a_cfTmpVfp.
437  */
438 
439 int
440 swapcfile(PKGserver server, VFP_T **a_cfTmpVfp, char *pkginst, int dbchg)
441 {
442 	char	*pe;
443 	char	*pl;
444 	char	*ps;
445 	char	line[256];
446 	char	timeb[BUFSIZ];
447 	int	retval = RESULT_OK;
448 	struct tm	*timep;
449 	time_t	clock;
450 
451 	/* normalize pkginst so its never null */
452 
453 	if (pkginst == (char *)NULL) {
454 		dbchg = 0;
455 		pkginst = "<unknown>";
456 	}
457 
458 	/*
459 	 * If no changes were made to the database, checkpoint the temporary
460 	 * contents file - if this fails, then just close the file which causes
461 	 * the contents file to be reopened and reread if it is needed again
462 	 */
463 
464 	if ((dbchg == 0) && (vfpGetModified(*a_cfTmpVfp) == 0)) {
465 		(void) pkgWunlock();	/* Free the database lock. */
466 		return (retval);
467 	}
468 
469 	/*
470 	 * changes made to the current temporary contents file -
471 	 * remove any trailing comment lines in the temp contents file, then
472 	 * append updated modification info records to temp contents file
473 	 */
474 
475 	pe = vfpGetCurrCharPtr(*a_cfTmpVfp);	/* last char in contents file */
476 	ps = vfpGetFirstCharPtr(*a_cfTmpVfp);	/* 1st char in contents file */
477 	pl = pe;	/* last match is last char in contents file */
478 
479 	/* skip past all trailing newlines and null bytes */
480 
481 	while ((pe > ps) && ((*pe == '\n') || (*pe == '\0'))) {
482 		pe--;
483 	}
484 
485 	/* remove trailing comments as long as there are lines in the file */
486 
487 	while (pe > ps) {
488 		if (*pe != '\n') {
489 			/* curr char is not newline: backup one byte */
490 			pl = pe--;
491 		} else if (*pl != '#') {
492 			/* curr char is newline next char not comment break */
493 			break;
494 		} else {
495 			/* curr char is newline next char is comment - remove */
496 			*pl = '\0';
497 			vfpSetLastCharPtr(*a_cfTmpVfp, pl);
498 			pe--;
499 		}
500 	}
501 
502 	/* create two update comment lines */
503 
504 	(void) time(&clock);
505 	timep = localtime(&clock);
506 
507 	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
508 	(void) snprintf(line, sizeof (line),
509 	    gettext("# Last modified by %s for %s package\n# %s"),
510 	    get_prog_name(), pkginst, timeb);
511 	vfpPuts(*a_cfTmpVfp, line);
512 
513 	/* commit temporary contents file bytes to storage */
514 
515 	if (pkgservercommitfile(*a_cfTmpVfp, server) != 0) {
516 		int	lerrno = errno;
517 
518 		logerr(gettext(ERR_COMMIT));
519 		vfpClose(a_cfTmpVfp);
520 		pkgcloseserver(server);
521 		(void) pkgWunlock();	/* Free the database lock. */
522 		return (RESULT_ERR);
523 	}
524 
525 	return (relslock() == 0 ? RESULT_ERR : retval);
526 }
527 
528 /* This function releases the lock on the package database. */
529 int
530 relslock(void)
531 {
532 	/*
533 	 * This closes the contents file and releases the lock.
534 	 */
535 	if (!pkgWunlock()) {
536 		int	lerrno = errno;
537 
538 		progerr(gettext(ERR_NOUPD));
539 		logerr(gettext(ERR_FCLOSE_FAIL), lerrno);
540 		return (0);
541 	}
542 	return (1);
543 }
544 
545 /*
546  * This function attempts to lock the package database. It returns 1 on
547  * success, 0 on failure. The positive logic verbose flag determines whether
548  * or not the function displays the error message upon failure.
549  */
550 int
551 pkgWlock(int verbose) {
552 	int retry_cnt, retval;
553 	char lockpath[PATH_MAX];
554 
555 	active_lock = 0;
556 
557 	(void) snprintf(lockpath, sizeof (lockpath),
558 			"%s/%s", pkgadm_dir, LOCKFILE);
559 
560 	retry_cnt = LOCKRETRY;
561 
562 	/*
563 	 * If the lock file is not present, create it. The mode is set to
564 	 * allow any process to lock the database, that's because pkgchk may
565 	 * be run by a non-root user.
566 	 */
567 	if (access(lockpath, F_OK) == -1) {
568 		lock_fd = open(lockpath, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0644);
569 		if (lock_fd < 0) {
570 			if (verbose)
571 				progerr(gettext(ERR_MKLOCK), lockpath);
572 			return (0);
573 		} else {
574 			(void) fchmod(lock_fd, 0644);	/* force perms. */
575 		}
576 	} else {
577 		if ((lock_fd = open(lockpath, O_RDWR)) == -1) {
578 			if (verbose)
579 				progerr(gettext(ERR_OPLOCK), lockpath);
580 			return (0);
581 		}
582 	}
583 
584 	(void) signal(SIGALRM, do_alarm);
585 	(void) alarm(LOCKWAIT);
586 
587 	do {
588 		if (lockf(lock_fd, F_LOCK, 0)) {
589 			if (errno == EAGAIN || errno == EINTR)
590 				logerr(gettext(MSG_XWTING));
591 			else if (errno == ECOMM) {
592 				logerr(gettext(ERR_LCKREM));
593 				retval = 0;
594 				break;
595 			} else if (errno == EBADF) {
596 				logerr(gettext(ERR_BADLCK));
597 				retval = 0;
598 				break;
599 			} else if (errno == EDEADLK) {
600 				logerr(gettext(ERR_DEADLCK));
601 				retval = 0;
602 				break;
603 			}
604 		} else {
605 			active_lock = 1;
606 			retval = 1;
607 			break;
608 		}
609 	} while (retry_cnt--);
610 
611 	(void) signal(SIGALRM, SIG_IGN);
612 
613 	if (retval == 0)
614 	{
615 		if (retry_cnt == -1) {
616 			logerr(gettext(ERR_TMOUT));
617 		}
618 
619 		(void) pkgWunlock();	/* close the lockfile. */
620 	}
621 
622 	return (retval);
623 }
624 
625 /*
626  * Release the lock on the package database. Returns 1 on success, 0 on
627  * failure.
628  */
629 static int
630 pkgWunlock(void) {
631 	if (active_lock) {
632 		active_lock = 0;
633 		if (close(lock_fd))
634 			return (0);
635 		else
636 			return (1);
637 	} else
638 		return (1);
639 }
640 
641 /*
642  * This function verifies that the contents file is in place.
643  * returns 1 - if it exists
644  * returns 0 - if it does not exist
645  */
646 int
647 iscfile(void)
648 {
649 	char	contents[PATH_MAX];
650 
651 	(void) snprintf(contents, PATH_MAX, "%s/contents", get_PKGADM());
652 
653 	return (access(contents, F_OK) == 0 ? 1 : 0);
654 }
655 
656 /*
657  * This function verifies that the contents file is in place. If it is - no
658  * change. If it isn't - this creates it.
659  * Returns:	== 0 : failure
660  *		!= 0 : success
661  */
662 
663 int
664 vcfile(void)
665 {
666 	int	lerrno;
667 	int	fd;
668 	char	contents[PATH_MAX];
669 
670 	/*
671 	 * create full path to contents file
672 	 */
673 
674 	(void) snprintf(contents, sizeof (contents),
675 	    "%s/contents", get_PKGADM());
676 
677 	/*
678 	 * Attempt to create the file - will only be successful
679 	 * if the file does not currently exist.
680 	 */
681 
682 	fd = open(contents, O_WRONLY | O_CREAT | O_EXCL, 0644);
683 	if (fd >= 0) {
684 		/*
685 		 * Contents file wasn't there, but is now.
686 		 */
687 
688 		echo(gettext("## Software contents file initialized"));
689 		(void) close(fd);
690 		return (1);	/* success */
691 	}
692 
693 	/*
694 	 * Could not create the file - it may exist or there may be
695 	 * permissions issues - find out and act accordingly.
696 	 */
697 
698 	lerrno = errno;
699 
700 	/* success if error is 'file exists' */
701 
702 	if (lerrno == EEXIST) {
703 		return (1);	/* success */
704 	}
705 
706 	/* success if error is 'permission denied' but file exists */
707 
708 	if (lerrno == EACCES) {
709 		/*
710 		 * Because O_CREAT and O_EXCL are specified in open(),
711 		 * if the contents file already exists, the open will
712 		 * fail with EACCES - determine if this is the case -
713 		 * if so return success.
714 		 */
715 
716 		if (access(contents, F_OK) == 0) {
717 			return (1);	/* success */
718 		}
719 
720 		/*
721 		 * access() failed - if because of permissions failure this
722 		 * means the contents file exists but it cannot be accessed
723 		 * or the path to the contents file cannot be accessed - in
724 		 * either case the contents file cannot be accessed.
725 		 */
726 
727 		if (errno == EACCES) {
728 			progerr(gettext(ERR_ACCESS_CONT), contents,
729 			    strerror(lerrno));
730 			logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
731 			return (0);	/* failure */
732 		}
733 	}
734 
735 	/*
736 	 * the contents file does not exist and it cannot be created.
737 	 */
738 
739 	progerr(gettext(ERR_CREAT_CONT), contents, strerror(lerrno));
740 	logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
741 	return (0);	/* failure */
742 }
743