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