fileupd.c revision 20253
1/*-
2 * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer as
10 *    the first lines of this file unmodified.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by David L. Nugent.
17 * 4. The name of the author may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	$Id$
33 */
34
35#include <stdio.h>
36#include <fcntl.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <sys/param.h>
42#include <errno.h>
43#include <unistd.h>
44
45#include "pwupd.h"
46
47int
48fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
49{
50	int             rc = 0;
51
52	if (pfxlen <= 1)
53		errno = EINVAL;
54	else {
55		int             infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode);
56
57		if (infd != -1) {
58			FILE           *infp = fdopen(infd, "r+");
59
60			if (infp == NULL)
61				close(infd);
62			else {
63				int             outfd;
64				char            file[MAXPATHLEN];
65
66				strcpy(file, filename);
67				strcat(file, ".new");
68				outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
69				if (outfd != -1) {
70					FILE           *outfp = fdopen(outfd, "w+");
71
72					if (outfp == NULL)
73						close(outfd);
74					else {
75						int             updated = UPD_CREATE;
76						char            line[2048];
77
78						while (fgets(line, sizeof(line), infp) != NULL) {
79							char           *p = strchr(line, '\n');
80
81							if (p == NULL) {	/* Line too long */
82								int             ch;
83
84								fputs(line, outfp);
85								while ((ch = fgetc(infp)) != EOF) {
86									fputc(ch, outfp);
87									if (ch == '\n')
88										break;
89								}
90								continue;
91							}
92							if (*line != '#' && *line != '\n') {
93								if (!updated && strncmp(line, prefix, pfxlen) == 0) {
94									updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
95
96									/*
97									 * Only actually write changes if updating
98									 */
99									if (updmode == UPD_REPLACE)
100										strcpy(line, newline);
101									else if (updmode == UPD_DELETE)
102										continue;
103								}
104							}
105							fputs(line, outfp);
106						}
107
108						/*
109						 * Now, we need to decide what to do: If we are in
110						 * update mode, and no record was updated, then error If
111						 * we are in insert mode, and record already exists,
112						 * then error
113						 */
114						if (updmode != updated)
115							errno = (updmode == UPD_CREATE) ? EEXIST : ENOENT;
116						else {
117
118							/*
119							 * If adding a new record, append it to the end
120							 */
121							if (updmode == UPD_CREATE)
122								fputs(newline, outfp);
123
124							/*
125							 * Flush the file and check for the result
126							 */
127							rc = fflush(outfp) != EOF;
128							if (rc) {
129
130								/*
131								 * Copy data back into the
132								 * original file and truncate
133								 */
134								rewind(infp);
135								rewind(outfp);
136								while (fgets(line, sizeof(line), outfp) != NULL)
137									fputs(line, infp);
138
139								/*
140								 * This is a gross hack, but we may have
141								 * corrupted the original file
142								 * Unfortunately, it will lose the inode.
143								 */
144								if (fflush(infp) == EOF || ferror(infp))
145									rc = rename(file, filename) == 0;
146								else
147									ftruncate(infd, ftell(infp));
148							}
149						}
150						fclose(outfp);
151					}
152					remove(file);
153				}
154				fclose(infp);
155			}
156		}
157	}
158	return rc;
159}
160