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