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