1 /* Part of publib.
2 
3    Copyright (c) 1994-2006 Lars Wirzenius.  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 
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11 
12    2. Redistributions in binary form must reproduce the above
13       copyright notice, this list of conditions and the following
14       disclaimer in the documentation and/or other materials provided
15       with the distribution.
16 
17    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*
30  * File:	file_io.c
31  * Purpose:	File I/O.
32  * Author:	Lars Wirzenius
33  * Version:	"@(#)publib:$Id: file_io.c,v 1.1 1996/11/05 21:15:46 liw Exp $"
34  */
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <time.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <errno.h>
44 
45 #include "publib/alloc.h"
46 #include "publib/files.h"
47 #include "publib/errormsg.h"
48 
49 
50 #define MAX_TRIES 1024
51 
52 static int create_save_temp_file(const char *, char *, int *);
53 static int copy_modes(const char *, const char *);
54 static int make_backup_name(char *, const char *, size_t);
55 
56 
file_read_open(FILE * f,void ** data,size_t * size)57 int file_read_open(FILE *f, void **data, size_t *size) {
58 	struct dynarr da;
59 	int c;
60 
61 	dynarr_init(&da, 1);
62 	while ((c = getc(f)) != EOF && !ferror(stdout)) {
63 		if (dynarr_resize(&da, da.used + 2) == -1) {
64 			__publib_error("out of memory");
65 			dynarr_free(&da);
66 			(void) fclose(f);
67 			return -1;
68 		}
69 		((char *) da.data)[da.used++] = c;
70 	}
71 	if (ferror(f)) {
72 		__publib_error("error reading from file");
73 		return -1;
74 	}
75 
76 	*data = da.data;
77 	*size = da.used;
78 
79 	return 0;
80 }
81 
82 
file_read(const char * pathname,void ** data,size_t * size)83 int file_read(const char *pathname, void **data, size_t *size) {
84 	FILE *f;
85 	int ret;
86 
87 	f = fopen(pathname, "r");
88 	if (f == NULL) {
89 		__publib_error("error opening file for reading");
90 		return -1;
91 	}
92 
93 	ret = file_read_open(f, data, size);
94 	if (fclose(f) == EOF) {
95 		__publib_error("error closing file after reading");
96 		ret = -1;
97 	}
98 	return ret;
99 }
100 
101 
file_write(const char * pathname,void * data,size_t n)102 int file_write(const char *pathname, void *data, size_t n) {
103 	FILE *f;
104 	int c;
105 
106 	f = fopen(pathname, "w");
107 	if (f == NULL) {
108 		__publib_error("couldn't open file for writing");
109 		return -1;
110 	}
111 	(void) fwrite(data ? data : "", 1, n, f);
112 	c = ferror(f);
113 	if (fclose(f) == EOF || c) {
114 		__publib_error("error writing to file");
115 		return -1;
116 	}
117 	return 0;
118 }
119 
120 
file_save(const char * name,void * data,size_t size,int keep_backup)121 int file_save(const char *name, void *data, size_t size, int keep_backup) {
122 	FILE *f;
123 	int c, fd;
124 	char temp_name[FILENAME_MAX];
125 	char back_name[sizeof(temp_name) + 4];
126 
127 	if (strlen(name) + 64 > sizeof(temp_name)) {
128 		__publib_error("filename is too long (can't save)");
129 		return -1;
130 	}
131 
132 	if (make_backup_name(back_name, name, sizeof(back_name)) == -1)
133 		return -1;
134 
135 	if (create_save_temp_file(name, temp_name, &fd) == -1)
136 		return -1;
137 
138 	f = fdopen(fd, "w");
139 	if (f == NULL) {
140 		__publib_error("error opening file");
141 		return -1;
142 	}
143 
144 	(void) fwrite(data ? data : "", 1, size, f);
145 
146 	c = ferror(f);
147 	if (fclose(f) == EOF || c) {
148 		__publib_error("error closing file");
149 		return -1;
150 	}
151 
152 	if (rename(name, back_name) == -1 && errno != ENOENT) {
153 		__publib_error("rename of original failed (can't save)");
154 		return -1;
155 	}
156 	if (rename(temp_name, name) == -1) {
157 		__publib_error("rename of new failed (can't save)");
158 		return -1;
159 	}
160 	if (copy_modes(back_name, name) == -1)
161 		return -1;
162 
163 	if (!keep_backup) {
164 		if (remove(back_name) == -1 && errno != ENOENT) {
165 			__publib_error("remove of backup failed (can't save)");
166 			return -1;
167 		}
168 	}
169 
170 	return 0;
171 }
172 
173 
174 /**********************************************************************/
175 
176 
177 /*
178  * Function:	create_save_temp_file
179  * Purpose:	Create a new file in same directory as the file to be saved.
180  * Arguments:	original	the name of the original file
181  *		buf		where the new file's name is stored
182  *		fd		where file descriptor of new file is stored
183  * Return:	-1 for failure, 0 for success.
184  * Note:	buf should be big enough; strlen(original) + 64 should
185  *		be enough (unless you have _really_ big unsigned longs).
186  */
create_save_temp_file(const char * original,char * buf,int * fd)187 static int create_save_temp_file(const char *original, char *buf, int *fd) {
188 	char *p;
189 	unsigned long i;
190 	time_t random;
191 
192 	strcpy(buf, original);
193 	p = strrchr(buf, '/');
194 	if (p != NULL)
195 		p[1] = '\0';
196 	else
197 		buf[0] = '\0';
198 
199 	time(&random);
200 	*fd = -1;
201 	p = strchr(buf, '\0');
202 	for (i = 0; i < MAX_TRIES && *fd == -1; ++i) {
203 		sprintf(p, "#%lu-%lu#", (unsigned long) random, i);
204 		*fd = open(buf, O_WRONLY | O_CREAT | O_EXCL, 0600);
205 		if (*fd == -1 && errno != EEXIST) {
206 			__publib_error("can't create temporary file");
207 			return -1;
208 		}
209 	}
210 
211 	if (*fd == -1)
212 		return -1;
213 	return 0;
214 }
215 
216 
217 
218 /*
219  * Function:	copy_modes
220  * Purpose:	Copy the permissions from one file to another.
221  * Arguments:	src	original file
222  *		tgt	target file
223  * Return:	-1 for error, 0 for OK.
224  */
copy_modes(const char * src,const char * tgt)225 static int copy_modes(const char *src, const char *tgt) {
226 	struct stat st;
227 
228 	if (stat(src, &st) == -1) {
229 		if (errno == ENOENT)
230 			return 0;
231 		__publib_error("Can't stat file (can't complete save)");
232 		return -1;
233 	}
234 	if (chmod(tgt, st.st_mode) == -1) {
235 		__publib_error("Can't chmod file (can't complete save)");
236 		return -1;
237 	}
238 	return 0;
239 }
240 
241 
242 
243 /*
244  * Function:	make_backup_name
245  * Purpose:	Create name of backup file from original file.
246  * Note:	The target buffer must be big enough.
247  */
make_backup_name(char * back,const char * orig,size_t max)248 static int make_backup_name(char *back, const char *orig, size_t max) {
249 	if (strlen(orig) + 2 > max)
250 		return -1;
251 
252 	sprintf(back, "%s~", orig);
253 	return 0;
254 }
255