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