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