math/mpreduce.h: Missing include files.
[u/mdw/catacomb] / key / key-file.c
1 /* -*-c-*-
2 *
3 * System-dependent key filing operations
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40
41 #include <mLib/dstr.h>
42 #include <mLib/lock.h>
43
44 #include "key.h"
45
46 /*----- Main code ---------------------------------------------------------*/
47
48 /* --- @fdcopy@ --- *
49 *
50 * Arguments: @int source@ = source file descriptor
51 * @int dest@ = destination file descriptor
52 *
53 * Returns: Zero if OK, nonzero otherwise.
54 *
55 * Use: Copies data from one file descriptor to another.
56 */
57
58 static int fdcopy(int source, int dest)
59 {
60 char buf[4096];
61 char *p;
62
63 if (lseek(source, 0, SEEK_SET) < 0||
64 lseek(dest, 0, SEEK_SET) < 0 ||
65 ftruncate(dest, 0) < 0)
66 return (-1);
67 for (;;) {
68 int n = read(source, buf, sizeof(buf));
69 if (n < 0)
70 return (-1);
71 else if (n == 0)
72 break;
73 p = buf;
74 while (n) {
75 int nn = write(dest, p, n);
76 if (nn < 0)
77 return (-1);
78 p += nn;
79 n -= nn;
80 }
81 }
82 return (0);
83 }
84
85 /* --- @key_save@ --- *
86 *
87 * Arguments: @key_file *f@ = pointer to key file block
88 *
89 * Returns: A @KWRITE_@ code indicating how well it worked.
90 *
91 * Use: Writes a key file's data back to the actual file. This code
92 * is extremely careful about error handling. It should usually
93 * be able to back out somewhere sensible, but it can tell when
94 * it's got itself into a real pickle and starts leaving well
95 * alone.
96 *
97 * Callers, please make sure that you ring alarm bells when this
98 * function returns @KWRITE_BROKEN@.
99 */
100
101 int key_save(key_file *f)
102 {
103 dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
104 int rc = KWRITE_FAIL;
105
106 if (!(f->f & KF_MODIFIED))
107 return (KWRITE_OK);
108 if (!f->fp)
109 return (KWRITE_FAIL);
110
111 /* --- Write a new key file out --- *
112 *
113 * Check for an error after each key line. This ought to be enough.
114 * Checking after each individual byte write and @fprintf@ isn't much fun.
115 */
116
117 dstr_putf(&n_new, "%s.new", f->name);
118
119 {
120 key *k;
121 key_iter i;
122 FILE *fp;
123
124 if ((fp = fopen(n_new.buf, "w")) == 0)
125 goto fail_open;
126
127 for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
128 if (key_extract(f, k, fp, 0)) {
129 fclose(fp);
130 goto fail_write;
131 }
132 }
133
134 if (fclose(fp))
135 goto fail_write;
136 }
137
138 /* --- Set up the other filenames --- */
139
140 dstr_putf(&n_older, "%s.older", f->name);
141 dstr_putf(&n_old, "%s.old", f->name);
142
143 /* --- Move the current backup on one --- *
144 *
145 * If the `older' file exists, then we're in need of attention.
146 */
147
148 {
149 struct stat st;
150 if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
151 errno = EEXIST;
152 rc = KWRITE_BROKEN;
153 goto fail_shift;
154 }
155 if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
156 goto fail_shift;
157 }
158
159 /* --- Copy the current file to the backup --- */
160
161 {
162 int fd;
163 if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
164 goto fail_backup;
165 if (fdcopy(fileno(f->fp), fd)) {
166 close(fd);
167 goto fail_backup;
168 }
169 if (close(fd))
170 goto fail_backup;
171 }
172
173 /* --- Copy the newly created file to the current one --- *
174 *
175 * This is the dangerous bit.
176 */
177
178 {
179 int fd;
180 if ((fd = open(n_new.buf, O_RDONLY)) < 0)
181 goto fail_update;
182 if (fdcopy(fd, fileno(f->fp))) {
183 close(fd);
184 goto fail_update;
185 }
186 close(fd);
187 if (fsync(fileno(f->fp)))
188 goto fail_update;
189 }
190
191 /* --- Clean up --- *
192 *
193 * Remove the `new' file and the `older' backup. Then we're done.
194 */
195
196 unlink(n_new.buf);
197 unlink(n_older.buf);
198 dstr_destroy(&n_new);
199 dstr_destroy(&n_old);
200 dstr_destroy(&n_older);
201 return (KWRITE_OK);
202
203 /* --- Failure while writing the new key file --- *
204 *
205 * I need to copy the backup back. If that fails then I'm really stuffed.
206 * If not, then I might as well try to get the backups sorted back out
207 * again.
208 */
209
210 fail_update:
211 {
212 int fd;
213 int e = errno;
214
215 if ((fd = open(n_old.buf, O_RDONLY)) < 0)
216 rc = KWRITE_BROKEN;
217 else if (fdcopy(fd, fileno(f->fp))) {
218 close(fd);
219 rc = KWRITE_BROKEN;
220 } else {
221 close(fd);
222 if (fsync(fileno(f->fp)))
223 rc = KWRITE_BROKEN;
224 }
225
226 errno = e;
227 if (rc == KWRITE_BROKEN)
228 goto fail_shift;
229 }
230 /* Now drop through */
231
232 /* --- Failure while writing the new backup --- *
233 *
234 * The new backup isn't any use. Try to recover the old one.
235 */
236
237 fail_backup:
238 {
239 int e = errno;
240 unlink(n_old.buf);
241 if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
242 rc = KWRITE_BROKEN;
243 errno = e;
244 }
245 /* Now drop through */
246
247 /* --- Failure while demoting the current backup --- *
248 *
249 * Leave the completed output file there for the operator in case he wants
250 * to clean up.
251 */
252
253 fail_shift:
254 dstr_destroy(&n_new);
255 dstr_destroy(&n_old);
256 dstr_destroy(&n_older);
257 return (rc);
258
259 /* --- Failure during write of new data --- *
260 *
261 * Clean up the new file and return. These errors can never cause
262 * breakage.
263 */
264
265 fail_write:
266 unlink(n_new.buf);
267 fail_open:
268 dstr_destroy(&n_new);
269 return (rc);
270 }
271
272 /* --- @key_lockfile@ --- *
273 *
274 * Arguments: @key_file *f@ = pointer to file structure to initialize
275 * @const char *file@ = pointer to the file name
276 * @unsigned how@ = opening options (@KOPEN_*@).
277 *
278 * Returns: Zero if it worked, nonzero otherwise.
279 *
280 * Use: Opens a keyfile and stores the information needed for
281 * continued access in the structure.
282 *
283 * If the file is opened with @KOPEN_WRITE@, it's created if
284 * necessary with read and write permissions for owner only, and
285 * locked for update while it's open.
286 *
287 * This is a system-dependent routine, and only really intended
288 * for the private use of @key_open@.
289 */
290
291 int key_lockfile(key_file *f, const char *file, unsigned how)
292 {
293 int of, lf;
294 const char *ff;
295 int fd;
296
297 /* --- Handle the magic no-file option --- */
298
299 if (how & KOPEN_NOFILE) {
300 f->fp = 0;
301 return (0);
302 }
303
304 /* --- Lots of things depend on whether we're writing --- */
305
306 switch (how & KOPEN_MASK) {
307 case KOPEN_READ:
308 of = O_RDONLY;
309 lf = LOCK_NONEXCL;
310 ff = "r";
311 break;
312 case KOPEN_WRITE:
313 of = O_RDWR | O_CREAT;
314 lf = LOCK_EXCL;
315 ff = "r+";
316 break;
317 default:
318 errno = EINVAL;
319 return (-1);
320 }
321
322 /* --- Open and lock the file --- */
323
324 if ((fd = open(file, of, 0600)) < 0)
325 return (-1);
326 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
327 lock_file(fd, lf) < 0 ||
328 (f->fp = fdopen(fd, ff)) == 0) {
329 close(fd);
330 return (-1);
331 }
332
333 return (0);
334 }
335
336 /*----- That's all, folks -------------------------------------------------*/