chiark / gitweb /
rand/rand-x86ish.S: Hoist argument register allocation outside.
[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 -------------------------------------------------*/