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