fileupd.c revision 21673
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 *	$FreeBSD: head/usr.sbin/pw/fileupd.c 21673 1997-01-14 07:20:47Z jkh $
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
42extendline(char **buf, int * buflen, int needed)
43{
44	if (needed > *buflen) {
45		char	*tmp = realloc(*buf, needed);
46		if (tmp == NULL)
47			return -1;
48		*buf = tmp;
49		*buflen = needed;
50	}
51	return *buflen;
52}
53
54int
55extendarray(char ***buf, int * buflen, int needed)
56{
57	if (needed > *buflen) {
58		char	**tmp = realloc(*buf, needed * sizeof(char *));
59		if (tmp == NULL)
60			return -1;
61		*buf = tmp;
62		*buflen = needed;
63	}
64	return *buflen;
65}
66
67
68int
69fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
70{
71	int             rc = 0;
72
73	if (pfxlen <= 1)
74		errno = EINVAL;
75	else {
76		int             infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode);
77
78		if (infd != -1) {
79			FILE           *infp = fdopen(infd, "r+");
80
81			if (infp == NULL)
82				close(infd);
83			else {
84				int             outfd;
85				char            file[MAXPATHLEN];
86
87				strcpy(file, filename);
88				strcat(file, ".new");
89				outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
90				if (outfd != -1) {
91					FILE           *outfp = fdopen(outfd, "w+");
92
93					if (outfp == NULL)
94						close(outfd);
95					else {
96						int             updated = UPD_CREATE;
97						int		linesize = PWBUFSZ;
98						char           *line = malloc(linesize);
99
100					nextline:
101						while (fgets(line, linesize, infp) != NULL) {
102							char           *p = strchr(line, '\n');
103
104							while ((p = strchr(line, '\n')) == NULL) {
105								int	l;
106								if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) {
107									int	ch;
108									fputs(line, outfp);
109									while ((ch = fgetc(infp)) != EOF) {
110										fputc(ch, outfp);
111										if (ch == '\n')
112											break;
113									}
114									goto nextline;
115								}
116								l = strlen(line);
117								if (fgets(line + l, linesize - l, infp) == NULL)
118									break;
119							}
120							if (*line != '#' && *line != '\n') {
121								if (!updated && strncmp(line, prefix, pfxlen) == 0) {
122									updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
123
124									/*
125									 * Only actually write changes if updating
126									 */
127									if (updmode == UPD_REPLACE)
128										strcpy(line, newline);
129									else if (updmode == UPD_DELETE)
130										continue;
131								}
132							}
133							fputs(line, outfp);
134						}
135
136						/*
137						 * Now, we need to decide what to do: If we are in
138						 * update mode, and no record was updated, then error If
139						 * we are in insert mode, and record already exists,
140						 * then error
141						 */
142						if (updmode != updated)
143							errno = (updmode == UPD_CREATE) ? EEXIST : ENOENT;
144						else {
145
146							/*
147							 * If adding a new record, append it to the end
148							 */
149							if (updmode == UPD_CREATE)
150								fputs(newline, outfp);
151
152							/*
153							 * Flush the file and check for the result
154							 */
155							rc = fflush(outfp) != EOF;
156							if (rc) {
157
158								/*
159								 * Copy data back into the
160								 * original file and truncate
161								 */
162								rewind(infp);
163								rewind(outfp);
164								while (fgets(line, linesize, outfp) != NULL)
165									fputs(line, infp);
166
167								/*
168								 * This is a gross hack, but we may have
169								 * corrupted the original file
170								 * Unfortunately, it will lose the inode.
171								 */
172								if (fflush(infp) == EOF || ferror(infp))
173									rc = rename(file, filename) == 0;
174								else
175									ftruncate(infd, ftell(infp));
176							}
177						}
178						free(line);
179						fclose(outfp);
180					}
181					remove(file);
182				}
183				fclose(infp);
184			}
185		}
186	}
187	return rc;
188}
189