kernel - Reduce lwp_signotify() latency
[dragonfly.git] / usr.sbin / pwd_mkdb / pwd_mkdb.c
1 /*-
2  * Copyright (c) 1991, 1993, 1994
3  *      The Regents of the University of California.  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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1991, 1993, 1994 The Regents of the University of California.  All rights reserved.
30  * @(#)pwd_mkdb.c       8.5 (Berkeley) 4/20/94
31  * $FreeBSD: src/usr.sbin/pwd_mkdb/pwd_mkdb.c,v 1.51 2005/06/15 10:13:04 dd Exp $
32  * $DragonFly: src/usr.sbin/pwd_mkdb/pwd_mkdb.c,v 1.5 2005/12/05 02:40:27 swildner Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/endian.h>
37 #include <sys/stat.h>
38 #include <arpa/inet.h>
39
40 #include <db.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include "pw_scan.h"
53
54 #define INSECURE        1
55 #define SECURE          2
56 #define PERM_INSECURE   (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
57 #define PERM_SECURE     (S_IRUSR|S_IWUSR)
58 #define LEGACY_VERSION(x)  _PW_VERSIONED(x, 3)
59 #define CURRENT_VERSION(x) _PW_VERSIONED(x, 4)
60
61 HASHINFO openinfo = {
62         4096,           /* bsize */
63         32,             /* ffactor */
64         256,            /* nelem */
65         2048 * 1024,    /* cachesize */
66         NULL,           /* hash() */
67         BYTE_ORDER      /* lorder */
68 };
69
70 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
71 static struct passwd pwd;                       /* password structure */
72 static char *pname;                             /* password file name */
73 static char prefix[MAXPATHLEN];
74
75 static int is_comment;  /* flag for comments */
76 static char line[LINE_MAX];
77
78 void    cleanup(void);
79 void    error(const char *);
80 void    cp(char *, char *, mode_t mode);
81 void    mv(char *, char *);
82 int     scan(FILE *, struct passwd *);
83 static void     usage(void);
84
85 int
86 main(int argc, char *argv[])
87 {
88         static char verskey[] = _PWD_VERSION_KEY;
89         char version = _PWD_CURRENT_VERSION;
90         DB *dp, *sdp, *pw_db;
91         DBT data, sdata, key;
92         FILE *fp, *oldfp;
93         sigset_t set;
94         int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
95         unsigned int len;
96         uint32_t store;
97         const char *t;
98         char *p;
99         char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
100         char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
101         char buf2[MAXPATHLEN];
102         char sbuf2[MAXPATHLEN];
103         char *username;
104         u_int method, methoduid;
105         int Cflag, dflag, iflag;
106         int nblock = 0;
107
108         iflag = dflag = Cflag = 0;
109         strcpy(prefix, _PATH_PWD);
110         makeold = 0;
111         username = NULL;
112         oldfp = NULL;
113         while ((ch = getopt(argc, argv, "BCLNd:ips:u:v")) != -1)
114                 switch(ch) {
115                 case 'B':                       /* big-endian output */
116                         openinfo.lorder = BIG_ENDIAN;
117                         break;
118                 case 'C':                       /* verify only */
119                         Cflag = 1;
120                         break;
121                 case 'L':                       /* little-endian output */
122                         openinfo.lorder = LITTLE_ENDIAN;
123                         break;
124                 case 'N':                       /* do not wait for lock */
125                         nblock = LOCK_NB;       /* will fail if locked */
126                         break;
127                 case 'd':
128                         dflag++;
129                         strlcpy(prefix, optarg, sizeof(prefix));
130                         break;
131                 case 'i':
132                         iflag++;
133                         break;
134                 case 'p':                       /* create V7 "file.orig" */
135                         makeold = 1;
136                         break;
137                 case 's':                       /* change default cachesize */
138                         openinfo.cachesize = atoi(optarg) * 1024 * 1024;
139                         break;
140                 case 'u':                       /* only update this record */
141                         username = optarg;
142                         break;
143                 case 'v':                       /* backward compatible */
144                         break;
145                 default:
146                         usage();
147                 }
148         argc -= optind;
149         argv += optind;
150
151         if (argc != 1 || (username && (*username == '+' || *username == '-')))
152                 usage();
153
154         /*
155          * This could be changed to allow the user to interrupt.
156          * Probably not worth the effort.
157          */
158         sigemptyset(&set);
159         sigaddset(&set, SIGTSTP);
160         sigaddset(&set, SIGHUP);
161         sigaddset(&set, SIGINT);
162         sigaddset(&set, SIGQUIT);
163         sigaddset(&set, SIGTERM);
164         sigprocmask(SIG_BLOCK, &set, NULL);
165
166         /* We don't care what the user wants. */
167         umask(0);
168
169         pname = *argv;
170
171         /*
172          * Open and lock the original password file.  We have to check
173          * the hardlink count after we get the lock to handle any potential
174          * unlink/rename race.
175          *
176          * This lock is necessary when someone runs pwd_mkdb manually, directly
177          * on master.passwd, to handle the case where a user might try to
178          * change his password while pwd_mkdb is running.
179          */
180         for (;;) {
181                 struct stat st;
182
183                 if (!(fp = fopen(pname, "r")))
184                         error(pname);
185                 if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag))
186                         error("flock");
187                 if (fstat(fileno(fp), &st) < 0)
188                         error(pname);
189                 if (st.st_nlink != 0)
190                         break;
191                 fclose(fp);
192                 fp = NULL;
193         }
194
195         /* check only if password database is valid */
196         if (Cflag) {
197                 for (cnt = 1; scan(fp, &pwd); ++cnt);
198                 exit(0);
199         }
200
201         /* Open the temporary insecure password database. */
202         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
203         snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
204         if (username) {
205                 int use_version;
206
207                 snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
208                 snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
209
210                 clean = FILE_INSECURE;
211                 cp(buf2, buf, PERM_INSECURE);
212                 dp = dbopen(buf,
213                     O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
214                 if (dp == NULL)
215                         error(buf);
216
217                 clean = FILE_SECURE;
218                 cp(sbuf2, sbuf, PERM_SECURE);
219                 sdp = dbopen(sbuf,
220                     O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
221                 if (sdp == NULL)
222                         error(sbuf);
223
224                 /*
225                  * Do some trouble to check if we should store this users
226                  * uid. Don't use getpwnam/getpwuid as that interferes
227                  * with NIS.
228                  */
229                 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
230                 if (!pw_db)
231                         error(_MP_DB);
232
233                 key.data = verskey;
234                 key.size = sizeof(verskey)-1;
235                 if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
236                         use_version = *(unsigned char *)data.data;
237                 else
238                         use_version = 3;
239                 buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
240                 len = strlen(username);
241
242                 /* Only check that username fits in buffer */
243                 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
244                 key.data = (u_char *)buf;
245                 key.size = len + 1;
246                 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
247                         p = (char *)data.data;
248
249                         /* jump over pw_name and pw_passwd, to get to pw_uid */
250                         while (*p++)
251                                 ;
252                         while (*p++)
253                                 ;
254
255                         buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
256                         memmove(buf + 1, p, sizeof(store));
257                         key.data = (u_char *)buf;
258                         key.size = sizeof(store) + 1;
259
260                         if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
261                                 /* First field of data.data holds pw_pwname */
262                                 if (!strcmp(data.data, username))
263                                         methoduid = 0;
264                                 else
265                                         methoduid = R_NOOVERWRITE;
266                         } else {
267                                 methoduid = R_NOOVERWRITE;
268                         }
269                 } else {
270                         methoduid = R_NOOVERWRITE;
271                 }
272                 if ((pw_db->close)(pw_db))
273                         error("close pw_db");
274                 method = 0;
275         } else {
276                 dp = dbopen(buf,
277                     O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
278                 if (dp == NULL)
279                         error(buf);
280                 clean = FILE_INSECURE;
281
282                 sdp = dbopen(sbuf,
283                     O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
284                 if (sdp == NULL)
285                         error(sbuf);
286                 clean = FILE_SECURE;
287
288                 method = R_NOOVERWRITE;
289                 methoduid = R_NOOVERWRITE;
290         }
291
292         /*
293          * Open file for old password file.  Minor trickiness -- don't want to
294          * chance the file already existing, since someone (stupidly) might
295          * still be using this for permission checking.  So, open it first and
296          * fdopen the resulting fd.  The resulting file should be readable by
297          * everyone.
298          */
299         if (makeold) {
300                 snprintf(buf, sizeof(buf), "%s.orig", pname);
301                 if ((tfd = open(buf,
302                     O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
303                         error(buf);
304                 if ((oldfp = fdopen(tfd, "w")) == NULL)
305                         error(buf);
306                 clean = FILE_ORIG;
307         }
308
309         /*
310          * The databases actually contain three copies of the original data.
311          * Each password file entry is converted into a rough approximation
312          * of a ``struct passwd'', with the strings placed inline.  This
313          * object is then stored as the data for three separate keys.  The
314          * first key * is the pw_name field prepended by the _PW_KEYBYNAME
315          * character.  The second key is the pw_uid field prepended by the
316          * _PW_KEYBYUID character.  The third key is the line number in the
317          * original file prepended by the _PW_KEYBYNUM character.  (The special
318          * characters are prepended to ensure that the keys do not collide.)
319          */
320         /* In order to transition this file into a machine-independent
321          * form, we have to change the format of entries.  However, since
322          * older binaries will still expect the old MD format entries, we
323          * create those as usual and use versioned tags for the new entries.
324          */
325         if (username == NULL) {
326                 /* Do not add the VERSION tag when updating a single
327                  * user.  When operating on `old format' databases, this
328                  * would result in applications `seeing' only the updated
329                  * entries.
330                  */
331                 key.data = verskey;
332                 key.size = sizeof(verskey)-1;
333                 data.data = &version;
334                 data.size = 1;
335                 if ((dp->put)(dp, &key, &data, 0) == -1)
336                         error("put");
337                 if ((dp->put)(sdp, &key, &data, 0) == -1)
338                         error("put");
339         }
340         ypcnt = 1;
341         data.data = (u_char *)buf;
342         sdata.data = (u_char *)sbuf;
343         key.data = (u_char *)tbuf;
344         for (cnt = 1; scan(fp, &pwd); ++cnt) {
345                 if (!is_comment && 
346                     (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
347                         yp_enabled = 1;
348                 if (is_comment)
349                         --cnt;
350 #define COMPACT(e)      t = e; while ((*p++ = *t++));
351 #define SCALAR(e)       store = htonl((uint32_t)(e));      \
352                         memmove(p, &store, sizeof(store)); \
353                         p += sizeof(store);
354 #define LSCALAR(e)      store = HTOL((uint32_t)(e));       \
355                         memmove(p, &store, sizeof(store)); \
356                         p += sizeof(store);
357 #define HTOL(e)         (openinfo.lorder == BYTE_ORDER ? \
358                         (uint32_t)(e) : \
359                         bswap32((uint32_t)(e)))
360                 if (!is_comment && 
361                     (!username || (strcmp(username, pwd.pw_name) == 0))) {
362                         /* Create insecure data. */
363                         p = buf;
364                         COMPACT(pwd.pw_name);
365                         COMPACT("*");
366                         SCALAR(pwd.pw_uid);
367                         SCALAR(pwd.pw_gid);
368                         SCALAR(pwd.pw_change);
369                         COMPACT(pwd.pw_class);
370                         COMPACT(pwd.pw_gecos);
371                         COMPACT(pwd.pw_dir);
372                         COMPACT(pwd.pw_shell);
373                         SCALAR(pwd.pw_expire);
374                         SCALAR(pwd.pw_fields);
375                         data.size = p - buf;
376
377                         /* Create secure data. */
378                         p = sbuf;
379                         COMPACT(pwd.pw_name);
380                         COMPACT(pwd.pw_passwd);
381                         SCALAR(pwd.pw_uid);
382                         SCALAR(pwd.pw_gid);
383                         SCALAR(pwd.pw_change);
384                         COMPACT(pwd.pw_class);
385                         COMPACT(pwd.pw_gecos);
386                         COMPACT(pwd.pw_dir);
387                         COMPACT(pwd.pw_shell);
388                         SCALAR(pwd.pw_expire);
389                         SCALAR(pwd.pw_fields);
390                         sdata.size = p - sbuf;
391
392                         /* Store insecure by name. */
393                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
394                         len = strlen(pwd.pw_name);
395                         memmove(tbuf + 1, pwd.pw_name, len);
396                         key.size = len + 1;
397                         if ((dp->put)(dp, &key, &data, method) == -1)
398                                 error("put");
399
400                         /* Store insecure by number. */
401                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
402                         store = htonl(cnt);
403                         memmove(tbuf + 1, &store, sizeof(store));
404                         key.size = sizeof(store) + 1;
405                         if ((dp->put)(dp, &key, &data, method) == -1)
406                                 error("put");
407
408                         /* Store insecure by uid. */
409                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
410                         store = htonl(pwd.pw_uid);
411                         memmove(tbuf + 1, &store, sizeof(store));
412                         key.size = sizeof(store) + 1;
413                         if ((dp->put)(dp, &key, &data, methoduid) == -1)
414                                 error("put");
415
416                         /* Store secure by name. */
417                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
418                         len = strlen(pwd.pw_name);
419                         memmove(tbuf + 1, pwd.pw_name, len);
420                         key.size = len + 1;
421                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
422                                 error("put");
423
424                         /* Store secure by number. */
425                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
426                         store = htonl(cnt);
427                         memmove(tbuf + 1, &store, sizeof(store));
428                         key.size = sizeof(store) + 1;
429                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
430                                 error("put");
431
432                         /* Store secure by uid. */
433                         tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
434                         store = htonl(pwd.pw_uid);
435                         memmove(tbuf + 1, &store, sizeof(store));
436                         key.size = sizeof(store) + 1;
437                         if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
438                                 error("put");
439
440                         /* Store insecure and secure special plus and special minus */
441                         if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
442                                 tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
443                                 store = htonl(ypcnt);
444                                 memmove(tbuf + 1, &store, sizeof(store));
445                                 ypcnt++;
446                                 key.size = sizeof(store) + 1;
447                                 if ((dp->put)(dp, &key, &data, method) == -1)
448                                         error("put");
449                                 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
450                                         error("put");
451                         }
452
453                         /* Create insecure data. (legacy version) */
454                         p = buf;
455                         COMPACT(pwd.pw_name);
456                         COMPACT("*");
457                         LSCALAR(pwd.pw_uid);
458                         LSCALAR(pwd.pw_gid);
459                         LSCALAR(pwd.pw_change);
460                         COMPACT(pwd.pw_class);
461                         COMPACT(pwd.pw_gecos);
462                         COMPACT(pwd.pw_dir);
463                         COMPACT(pwd.pw_shell);
464                         LSCALAR(pwd.pw_expire);
465                         LSCALAR(pwd.pw_fields);
466                         data.size = p - buf;
467
468                         /* Create secure data. (legacy version) */
469                         p = sbuf;
470                         COMPACT(pwd.pw_name);
471                         COMPACT(pwd.pw_passwd);
472                         LSCALAR(pwd.pw_uid);
473                         LSCALAR(pwd.pw_gid);
474                         LSCALAR(pwd.pw_change);
475                         COMPACT(pwd.pw_class);
476                         COMPACT(pwd.pw_gecos);
477                         COMPACT(pwd.pw_dir);
478                         COMPACT(pwd.pw_shell);
479                         LSCALAR(pwd.pw_expire);
480                         LSCALAR(pwd.pw_fields);
481                         sdata.size = p - sbuf;
482
483                         /* Store insecure by name. */
484                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
485                         len = strlen(pwd.pw_name);
486                         memmove(tbuf + 1, pwd.pw_name, len);
487                         key.size = len + 1;
488                         if ((dp->put)(dp, &key, &data, method) == -1)
489                                 error("put");
490
491                         /* Store insecure by number. */
492                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
493                         store = HTOL(cnt);
494                         memmove(tbuf + 1, &store, sizeof(store));
495                         key.size = sizeof(store) + 1;
496                         if ((dp->put)(dp, &key, &data, method) == -1)
497                                 error("put");
498
499                         /* Store insecure by uid. */
500                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
501                         store = HTOL(pwd.pw_uid);
502                         memmove(tbuf + 1, &store, sizeof(store));
503                         key.size = sizeof(store) + 1;
504                         if ((dp->put)(dp, &key, &data, methoduid) == -1)
505                                 error("put");
506
507                         /* Store secure by name. */
508                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
509                         len = strlen(pwd.pw_name);
510                         memmove(tbuf + 1, pwd.pw_name, len);
511                         key.size = len + 1;
512                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
513                                 error("put");
514
515                         /* Store secure by number. */
516                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
517                         store = HTOL(cnt);
518                         memmove(tbuf + 1, &store, sizeof(store));
519                         key.size = sizeof(store) + 1;
520                         if ((sdp->put)(sdp, &key, &sdata, method) == -1)
521                                 error("put");
522
523                         /* Store secure by uid. */
524                         tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
525                         store = HTOL(pwd.pw_uid);
526                         memmove(tbuf + 1, &store, sizeof(store));
527                         key.size = sizeof(store) + 1;
528                         if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
529                                 error("put");
530
531                         /* Store insecure and secure special plus and special minus */
532                         if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
533                                 tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
534                                 store = HTOL(ypcnt);
535                                 memmove(tbuf + 1, &store, sizeof(store));
536                                 ypcnt++;
537                                 key.size = sizeof(store) + 1;
538                                 if ((dp->put)(dp, &key, &data, method) == -1)
539                                         error("put");
540                                 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
541                                         error("put");
542                         }
543                 }
544                 /* Create original format password file entry */
545                 if (is_comment && makeold){     /* copy comments */
546                         if (fprintf(oldfp, "%s\n", line) < 0)
547                                 error("write old");
548                 } else if (makeold) {
549                         char uidstr[20];
550                         char gidstr[20];
551
552                         snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
553                         snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
554
555                         if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
556                             pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
557                             pwd.pw_fields & _PWF_GID ? gidstr : "",
558                             pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
559                                 error("write old");
560                 }
561         }
562         /* If YP enabled, set flag. */
563         if (yp_enabled) {
564                 buf[0] = yp_enabled + 2;
565                 data.size = 1;
566                 key.size = 1;
567                 tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
568                 if ((dp->put)(dp, &key, &data, method) == -1)
569                         error("put");
570                 if ((sdp->put)(sdp, &key, &data, method) == -1)
571                         error("put");
572                 tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
573                 key.size = 1;
574                 if ((dp->put)(dp, &key, &data, method) == -1)
575                         error("put");
576                 if ((sdp->put)(sdp, &key, &data, method) == -1)
577                         error("put");
578         }
579
580         if ((dp->close)(dp) == -1)
581                 error("close");
582         if ((sdp->close)(sdp) == -1)
583                 error("close");
584         if (makeold) {
585                 fflush(oldfp);
586                 if (fclose(oldfp) == EOF)
587                         error("close old");
588         }
589
590         /* Set master.passwd permissions, in case caller forgot. */
591         fchmod(fileno(fp), S_IRUSR|S_IWUSR);
592
593         /* Install as the real password files. */
594         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
595         snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
596         mv(buf, buf2);
597         snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
598         snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
599         mv(buf, buf2);
600         if (makeold) {
601                 snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
602                 snprintf(buf, sizeof(buf), "%s.orig", pname);
603                 mv(buf, buf2);
604         }
605         /*
606          * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
607          * all use flock(2) on it to block other incarnations of themselves.
608          * The rename means that everything is unlocked, as the original file
609          * can no longer be accessed.
610          */
611         snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
612         mv(pname, buf);
613
614         /*
615          * Close locked password file after rename()
616          */
617         if (fclose(fp) == EOF)
618                 error("close fp");
619
620         exit(0);
621 }
622
623 int
624 scan(FILE *fp, struct passwd *pw)
625 {
626         static int lcnt;
627         size_t len;
628         char *p;
629
630         p = fgetln(fp, &len);
631         if (p == NULL)
632                 return (0);
633         ++lcnt;
634         /*
635          * ``... if I swallow anything evil, put your fingers down my
636          * throat...''
637          *      -- The Who
638          */
639         if (len > 0 && p[len - 1] == '\n')
640                 len--;
641         if (len >= sizeof(line) - 1) {
642                 warnx("line #%d too long", lcnt);
643                 goto fmt;
644         }
645         memcpy(line, p, len);
646         line[len] = '\0';
647
648         /* 
649          * Ignore comments: ^[ \t]*#
650          */
651         for (p = line; *p != '\0'; p++)
652                 if (*p != ' ' && *p != '\t')
653                         break;
654         if (*p == '#' || *p == '\0') {
655                 is_comment = 1;
656                 return(1);
657         } else
658                 is_comment = 0;
659
660         if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
661                 warnx("at line #%d", lcnt);
662 fmt:            errno = EFTYPE; /* XXX */
663                 error(pname);
664         }
665
666         return (1);
667 }
668
669 void
670 cp(char *from, char *to, mode_t mode)
671 {
672         static char buf[MAXBSIZE];
673         int from_fd, rcount, to_fd, wcount;
674
675         if ((from_fd = open(from, O_RDONLY, 0)) < 0)
676                 error(from);
677         if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
678                 error(to);
679         while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
680                 wcount = write(to_fd, buf, rcount);
681                 if (rcount != wcount || wcount == -1) {
682                         int sverrno = errno;
683
684                         snprintf(buf, sizeof(buf), "%s to %s", from, to);
685                         errno = sverrno;
686                         error(buf);
687                 }
688         }
689         if (rcount < 0) {
690                 int sverrno = errno;
691
692                 snprintf(buf, sizeof(buf), "%s to %s", from, to);
693                 errno = sverrno;
694                 error(buf);
695         }
696 }
697
698
699 void
700 mv(char *from, char *to)
701 {
702         char buf[MAXPATHLEN];
703
704         if (rename(from, to)) {
705                 int sverrno = errno;
706                 snprintf(buf, sizeof(buf), "%s to %s", from, to);
707                 errno = sverrno;
708                 error(buf);
709         }
710 }
711
712 void
713 error(const char *name)
714 {
715
716         warn("%s", name);
717         cleanup();
718         exit(1);
719 }
720
721 void
722 cleanup(void)
723 {
724         char buf[MAXPATHLEN];
725
726         switch(clean) {
727         case FILE_ORIG:
728                 snprintf(buf, sizeof(buf), "%s.orig", pname);
729                 unlink(buf);
730                 /* FALLTHROUGH */
731         case FILE_SECURE:
732                 snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
733                 unlink(buf);
734                 /* FALLTHROUGH */
735         case FILE_INSECURE:
736                 snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
737                 unlink(buf);
738         }
739 }
740
741 static void
742 usage(void)
743 {
744
745         fprintf(stderr,
746 "usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n");
747         exit(1);
748 }