usbdi.9: Some small fixes.
[dragonfly.git] / usr.sbin / mtree / compare.c
1 /*-
2  * Copyright (c) 1989, 1993
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  * @(#)compare.c        8.1 (Berkeley) 6/6/93
30  * $FreeBSD: src/usr.sbin/mtree/compare.c,v 1.15.2.4 2003/05/07 17:55:17 tobez Exp $
31  * $DragonFly: src/usr.sbin/mtree/compare.c,v 1.5 2004/03/15 16:24:22 dillon Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <fts.h>
40 #ifdef MD5
41 #include <md5.h>
42 #endif
43 #ifdef SHA1
44 #include <sha.h>
45 #endif
46 #ifdef RMD160
47 #include <ripemd.h>
48 #endif
49 #include <stdio.h>
50 #include <time.h>
51 #include <unistd.h>
52 #include "mtree.h"
53 #include "extern.h"
54
55 static const char *ftype(u_int);
56
57 #define INDENTNAMELEN   8
58 #define LABEL \
59         if (!label++) { \
60                 len = printf("%s changed\n", RP(p)); \
61                 tab = "\t"; \
62         }
63
64 int
65 compare(NODE *s, FTSENT *p)
66 {
67         u_long len, val;
68         int fd, label;
69         char *cp, *fflags;
70         const char *tab = "";
71
72         label = 0;
73         switch(s->type) {
74         case F_BLOCK:
75                 if (!S_ISBLK(p->fts_statp->st_mode))
76                         goto typeerr;
77                 break;
78         case F_CHAR:
79                 if (!S_ISCHR(p->fts_statp->st_mode))
80                         goto typeerr;
81                 break;
82         case F_DIR:
83                 if (!S_ISDIR(p->fts_statp->st_mode))
84                         goto typeerr;
85                 break;
86         case F_FIFO:
87                 if (!S_ISFIFO(p->fts_statp->st_mode))
88                         goto typeerr;
89                 break;
90         case F_FILE:
91                 if (!S_ISREG(p->fts_statp->st_mode))
92                         goto typeerr;
93                 break;
94         case F_LINK:
95                 if (!S_ISLNK(p->fts_statp->st_mode))
96                         goto typeerr;
97                 break;
98         case F_SOCK:
99                 if (!S_ISSOCK(p->fts_statp->st_mode)) {
100 typeerr:                LABEL;
101                         printf("\ttype expected %s found %s\n",
102                             ftype(s->type), inotype(p->fts_statp->st_mode));
103                         return (label);
104                 }
105                 break;
106         }
107         /* Set the uid/gid first, then set the mode. */
108         if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
109                 LABEL;
110                 printf("%suser expected %lu found %lu",
111                     tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
112                 if (uflag)
113                         if (chown(p->fts_accpath, s->st_uid, -1))
114                                 printf(" not modified: %s\n",
115                                     strerror(errno));
116                         else
117                                 printf(" modified\n");
118                 else
119                         printf("\n");
120                 tab = "\t";
121         }
122         if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
123                 LABEL;
124                 printf("%sgid expected %lu found %lu",
125                     tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
126                 if (uflag)
127                         if (chown(p->fts_accpath, -1, s->st_gid))
128                                 printf(" not modified: %s\n",
129                                     strerror(errno));
130                         else
131                                 printf(" modified\n");
132                 else
133                         printf("\n");
134                 tab = "\t";
135         }
136         if (s->flags & F_MODE &&
137             !S_ISLNK(p->fts_statp->st_mode) &&
138             s->st_mode != (p->fts_statp->st_mode & MBITS)) {
139                 LABEL;
140                 printf("%spermissions expected %#o found %#o",
141                     tab, s->st_mode, p->fts_statp->st_mode & MBITS);
142                 if (uflag)
143                         if (chmod(p->fts_accpath, s->st_mode))
144                                 printf(" not modified: %s\n",
145                                     strerror(errno));
146                         else
147                                 printf(" modified\n");
148                 else
149                         printf("\n");
150                 tab = "\t";
151         }
152         if (s->flags & F_NLINK && s->type != F_DIR &&
153             s->st_nlink != p->fts_statp->st_nlink) {
154                 LABEL;
155                 printf("%slink_count expected %u found %u\n",
156                     tab, s->st_nlink, p->fts_statp->st_nlink);
157                 tab = "\t";
158         }
159         if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
160                 !S_ISDIR(p->fts_statp->st_mode)) {
161                 LABEL;
162                 printf("%ssize expected %jd found %jd\n", tab,
163                     (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
164                 tab = "\t";
165         }
166         /*
167          * XXX
168          * Catches nano-second differences, but doesn't display them.
169          */
170         if ((s->flags & F_TIME) &&
171              ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
172              (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
173                 LABEL;
174                 printf("%smodification time expected %.24s ",
175                     tab, ctime(&s->st_mtimespec.tv_sec));
176                 printf("found %.24s\n",
177                     ctime(&p->fts_statp->st_mtimespec.tv_sec));
178                 tab = "\t";
179         }
180         if (s->flags & F_CKSUM) {
181                 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
182                         LABEL;
183                         printf("%scksum: %s: %s\n",
184                             tab, p->fts_accpath, strerror(errno));
185                         tab = "\t";
186                 } else if (crc(fd, &val, &len)) {
187                         close(fd);
188                         LABEL;
189                         printf("%scksum: %s: %s\n",
190                             tab, p->fts_accpath, strerror(errno));
191                         tab = "\t";
192                 } else {
193                         close(fd);
194                         if (s->cksum != val) {
195                                 LABEL;
196                                 printf("%scksum expected %lu found %lu\n",
197                                     tab, s->cksum, val);
198                                 tab = "\t";
199                         }
200                 }
201         }
202         /*
203          * XXX
204          * since chflags(2) will reset file times, the utimes() above
205          * may have been useless!  oh well, we'd rather have correct
206          * flags, rather than times?
207          */
208         if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) {
209                 LABEL;
210                 fflags = flags_to_string(s->st_flags);
211                 printf("%sflags expected \"%s\"", tab, fflags);
212                 free(fflags);
213
214                 fflags = flags_to_string(p->fts_statp->st_flags);
215                 printf(" found \"%s\"", fflags);
216                 free(fflags);
217
218                 if (uflag)
219                         if (chflags(p->fts_accpath, s->st_flags))
220                                 printf(" not modified: %s\n",
221                                     strerror(errno));
222                         else
223                                 printf(" modified\n");
224                 else
225                         printf("\n");
226                 tab = "\t";
227         }
228 #ifdef MD5
229         if (s->flags & F_MD5) {
230                 char *new_digest, buf[33];
231
232                 new_digest = MD5File(p->fts_accpath, buf);
233                 if (!new_digest) {
234                         LABEL;
235                         printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
236                                strerror(errno));
237                         tab = "\t";
238                 } else if (strcmp(new_digest, s->md5digest)) {
239                         LABEL;
240                         printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
241                                new_digest);
242                         tab = "\t";
243                 }
244         }
245 #endif /* MD5 */
246 #ifdef SHA1
247         if (s->flags & F_SHA1) {
248                 char *new_digest, buf[41];
249
250                 new_digest = SHA1_File(p->fts_accpath, buf);
251                 if (!new_digest) {
252                         LABEL;
253                         printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
254                                strerror(errno));
255                         tab = "\t";
256                 } else if (strcmp(new_digest, s->sha1digest)) {
257                         LABEL;
258                         printf("%sSHA-1 expected %s found %s\n", 
259                                tab, s->sha1digest, new_digest);
260                         tab = "\t";
261                 }
262         }
263 #endif /* SHA1 */
264 #ifdef RMD160
265         if (s->flags & F_RMD160) {
266                 char *new_digest, buf[41];
267
268                 new_digest = RIPEMD160_File(p->fts_accpath, buf);
269                 if (!new_digest) {
270                         LABEL;
271                         printf("%sRIPEMD160: %s: %s\n", tab,
272                                p->fts_accpath, strerror(errno));
273                         tab = "\t";
274                 } else if (strcmp(new_digest, s->rmd160digest)) {
275                         LABEL;
276                         printf("%sRIPEMD160 expected %s found %s\n",
277                                tab, s->rmd160digest, new_digest);
278                         tab = "\t";
279                 }
280         }
281 #endif /* RMD160 */
282
283         if (s->flags & F_SLINK &&
284             strcmp(cp = rlink(p->fts_accpath), s->slink)) {
285                 LABEL;
286                 printf("%slink_ref expected %s found %s\n",
287                       tab, s->slink, cp);
288         }
289         return (label);
290 }
291
292 const char *
293 inotype(u_int type)
294 {
295         switch(type & S_IFMT) {
296         case S_IFBLK:
297                 return ("block");
298         case S_IFCHR:
299                 return ("char");
300         case S_IFDIR:
301                 return ("dir");
302         case S_IFIFO:
303                 return ("fifo");
304         case S_IFREG:
305                 return ("file");
306         case S_IFLNK:
307                 return ("link");
308         case S_IFSOCK:
309                 return ("socket");
310         default:
311                 return ("unknown");
312         }
313         /* NOTREACHED */
314 }
315
316 static const char *
317 ftype(u_int type)
318 {
319         switch(type) {
320         case F_BLOCK:
321                 return ("block");
322         case F_CHAR:
323                 return ("char");
324         case F_DIR:
325                 return ("dir");
326         case F_FIFO:
327                 return ("fifo");
328         case F_FILE:
329                 return ("file");
330         case F_LINK:
331                 return ("link");
332         case F_SOCK:
333                 return ("socket");
334         default:
335                 return ("unknown");
336         }
337         /* NOTREACHED */
338 }
339
340 char *
341 rlink(char *name)
342 {
343         static char lbuf[MAXPATHLEN];
344         int len;
345
346         if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
347                 err(1, "line %d: %s", lineno, name);
348         lbuf[len] = '\0';
349         return (lbuf);
350 }