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