chiark / gitweb /
More reliable copying in key saving.
[catacomb] / key-file.c
1 /* -*-c-*-
2  *
3  * $Id$
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
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
60 static int fdcopy(int source, int dest)
61 {
62   char buf[4096];
63   char *p;
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;
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     }
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
103 int 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);
110   if (!f->fp)
111     return (KWRITE_FAIL);
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
209 fail_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
236 fail_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
252 fail_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
264 fail_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
275  *              @unsigned how@ = opening options (@KOPEN_*@).
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
290 int key_lockfile(key_file *f, const char *file, unsigned how)
291 {
292   int of, lf;
293   const char *ff;
294   int fd;
295
296   /* --- Handle the magic no-file option --- */
297
298   if (how & KOPEN_NOFILE) {
299     f->fp = 0;
300     return (0);
301   }
302
303   /* --- Lots of things depend on whether we're writing --- */
304
305   switch (how & KOPEN_MASK) {
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 -------------------------------------------------*/