Properly handle an error return from udev2dev().
[dragonfly.git] / usr.sbin / ctm / ctm / ctm_pass3.c
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  *
9  * $FreeBSD: src/usr.sbin/ctm/ctm/ctm_pass3.c,v 1.19 1999/08/28 01:16:00 peter Exp $
10  * $DragonFly: src/usr.sbin/ctm/ctm/Attic/ctm_pass3.c,v 1.2 2003/06/17 04:29:53 dillon Exp $
11  *
12  */
13
14 #include "ctm.h"
15 #define BADREAD 32
16
17 /*---------------------------------------------------------------------------*/
18 /* Pass3 -- Validate the incoming CTM-file.
19  */
20
21 int
22 settime(const char *name, const struct timeval *times)
23 {
24         if (SetTime)
25             if (utimes(name,times)) {
26                 warn("utimes(): %s", name);
27                 return -1;
28             }
29         return 0;
30 }
31
32 int
33 Pass3(FILE *fd)
34 {
35     u_char *p,*q,buf[BUFSIZ];
36     MD5_CTX ctx;
37     int i,j,sep,cnt;
38     u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
39     struct CTM_Syntax *sp;
40     FILE *ed=0;
41     struct stat st;
42     char md5_1[33];
43     int match=0;
44     struct timeval times[2];
45     struct CTM_Filter *filter = NULL;
46     if(Verbose>3)
47         printf("Pass3 -- Applying the CTM-patch\n");
48     MD5Init (&ctx);
49
50     GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
51     GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
52     GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
53     GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
54     GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
55     GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
56
57     /*
58      * This would be cleaner if mktime() worked in UTC rather than
59      * local time.
60      */
61     if (SetTime) {
62         struct tm tm;
63         char *tz;
64         char buf[5];
65         int i;
66
67 #define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
68 #define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
69         TimeStamp); WRONG}
70
71         if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
72         for (i = 0; i < 14; i++)
73             if (!isdigit(TimeStamp[i])) WRONGDATE
74
75         tz = getenv("TZ");
76         if (setenv("TZ", "UTC", 1) < 0) WRONG
77         tzset();
78
79         tm.tm_isdst = tm.tm_gmtoff = 0;
80
81         SUBSTR(0, 4);
82         tm.tm_year = atoi(buf) - 1900;
83         SUBSTR(4, 2);
84         tm.tm_mon = atoi(buf) - 1;
85         if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
86         SUBSTR(6, 2);
87         tm.tm_mday = atoi(buf);
88         if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
89         SUBSTR(8, 2);
90         tm.tm_hour = atoi(buf);
91         if (tm.tm_hour > 24) WRONGDATE
92         SUBSTR(10, 2);
93         tm.tm_min = atoi(buf);
94         if (tm.tm_min > 59) WRONGDATE
95         SUBSTR(12, 2);
96         tm.tm_sec = atoi(buf);
97         if (tm.tm_min > 62) WRONGDATE   /* allow leap seconds */
98     
99         times[0].tv_sec = times[1].tv_sec = mktime(&tm);
100         if (times[0].tv_sec == -1) WRONGDATE
101         times[0].tv_usec = times[1].tv_usec = 0;
102
103         if (tz) {
104             if (setenv("TZ", tz, 1) < 0) WRONGDATE
105          } else {
106             unsetenv("TZ");
107         }
108     }
109
110     for(;;) {
111         Delete(md5);
112         Delete(uid);
113         Delete(gid);
114         Delete(mode);
115         Delete(md5before);
116         Delete(trash);
117         Delete(name);
118         cnt = -1;
119
120         GETFIELD(p,' ');
121
122         if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
123
124         if(!strcmp(p+3,"_END"))
125             break;
126
127         for(sp=Syntax;sp->Key;sp++)
128             if(!strcmp(p+3,sp->Key))
129                 goto found;
130         WRONG
131     found:
132         for(i=0;(j = sp->List[i]);i++) {
133             if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
134                 sep = ' ';
135             else
136                 sep = '\n';
137
138             switch (j & CTM_F_MASK) {
139                 case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
140                 case CTM_F_Uid:  GETFIELDCOPY(uid,sep); break;
141                 case CTM_F_Gid:  GETFIELDCOPY(gid,sep); break;
142                 case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
143                 case CTM_F_MD5:
144                     if(j & CTM_Q_MD5_Before)
145                         GETFIELDCOPY(md5before,sep);
146                     else
147                         GETFIELDCOPY(md5,sep);
148                     break;
149                 case CTM_F_Count: GETBYTECNT(cnt,sep); break;
150                 case CTM_F_Bytes: GETDATA(trash,cnt); break;
151                 default: WRONG
152                 }
153             }
154         /* XXX This should go away.  Disallow trailing '/' */
155         j = strlen(name)-1;
156         if(name[j] == '/') name[j] = '\0';
157
158         /*
159          * If a filter list is specified, run thru the filter list and
160          * match `name' against filters.  If the name matches, set the
161          * required action to that specified in the filter.
162          * The default action if no filterlist is given is to match
163          * everything.  
164          */
165
166         match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
167         for (filter = FilterList; filter; filter = filter->Next) {
168             if (0 == regexec(&filter->CompiledRegex, name,
169                 0, 0, 0)) {
170                 match = filter->Action;
171             }
172         }
173
174         if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
175                 continue;
176
177         if (Verbose > 0)
178                 fprintf(stderr,"> %s %s\n",sp->Key,name);
179         if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
180             i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
181             if(i < 0) {
182                 warn("%s", name);
183                 WRONG
184             }
185             if(cnt != write(i,trash,cnt)) {
186                 warn("%s", name);
187                 WRONG
188             }
189             close(i);
190             if(strcmp(md5,MD5File(name,md5_1))) {
191                 fprintf(stderr,"  %s %s MD5 didn't come out right\n",
192                    sp->Key,name);
193                 WRONG
194             }
195             if (settime(name,times)) WRONG
196             continue;
197         }
198         if(!strcmp(sp->Key,"FE")) {
199             ed = popen("ed","w");
200             if(!ed) {
201                 WRONG
202             }
203             fprintf(ed,"e %s\n",name);
204             if(cnt != fwrite(trash,1,cnt,ed)) {
205                 warn("%s", name);
206                 pclose(ed);
207                 WRONG
208             }
209             fprintf(ed,"w %s\n",name);
210             if(pclose(ed)) {
211                 warn("ed");
212                 WRONG
213             }
214             if(strcmp(md5,MD5File(name,md5_1))) {
215                 fprintf(stderr,"  %s %s MD5 didn't come out right\n",
216                    sp->Key,name);
217                 WRONG
218             }
219             if (settime(name,times)) WRONG
220             continue;
221         }
222         if(!strcmp(sp->Key,"FN")) {
223             strcpy(buf,name);
224             strcat(buf,TMPSUFF);
225             i = ctm_edit(trash,cnt,name,buf);
226             if(i) {
227                 fprintf(stderr," %s %s Edit failed with code %d.\n",
228                     sp->Key,name,i);
229                 WRONG
230             }
231             if(strcmp(md5,MD5File(buf,md5_1))) {
232                 fprintf(stderr," %s %s Edit failed MD5 check.\n",
233                     sp->Key,name);
234                 WRONG
235             }
236             if (rename(buf,name) == -1)
237                 WRONG
238             if (settime(name,times)) WRONG
239             continue;
240         }
241         if(!strcmp(sp->Key,"DM")) {
242             if(0 > mkdir(name,0777)) {
243                 sprintf(buf,"mkdir -p %s",name);
244                 system(buf);
245             }
246             if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
247                 fprintf(stderr,"<%s> mkdir failed\n",name);
248                 WRONG
249             }
250             if (settime(name,times)) WRONG
251             continue;
252         }
253         if(!strcmp(sp->Key,"FR")) {
254             if (KeepIt) { 
255                 if (Verbose > 1) 
256                         printf("<%s> not removed\n", name);
257             }
258             else if (0 != unlink(name)) {
259                 fprintf(stderr,"<%s> unlink failed\n",name);
260                 if (!Force)
261                     WRONG
262             }
263             continue;
264         }
265         if(!strcmp(sp->Key,"DR")) {
266             /*
267              * We cannot use rmdir() because we do not get the directories
268              * in '-depth' order (cvs-cur.0018.gz for examples)
269              */
270             if (KeepIt) {
271                 if (Verbose > 1) {
272                         printf("<%s> not removed\n", name);
273                 }
274             } else {
275                     sprintf(buf,"rm -rf %s",name);
276                     system(buf);
277             }
278             continue;
279         }
280         WRONG
281     }
282
283     Delete(md5);
284     Delete(uid);
285     Delete(gid);
286     Delete(mode);
287     Delete(md5before);
288     Delete(trash);
289     Delete(name);
290
291     q = MD5End (&ctx,md5_1);
292     GETFIELD(p,'\n');
293     if(strcmp(q,p)) WRONG
294     if (-1 != getc(fd)) WRONG
295     return 0;
296 }