add .gbp.conf for gbp service
[tools/isomd5sum.git] / libimplantisomd5.c
1 /*
2  * Copyright (C) 2001-2007 Red Hat, Inc.
3  *
4  * Michael Fulbright <msf@redhat.com>
5  * Dustin Kirkland  <dustin.dirkland@gmail.com>
6  *      Added support for checkpoint fragment sums;                
7  *      Exits media check as soon as bad fragment md5sum'ed        
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "md5.h"
33 #include "libimplantisomd5.h"
34
35 #define APPDATA_OFFSET 883
36 #define SIZE_OFFSET 84
37
38 /* Length in characters of string used for fragment md5sum checking  */
39 #define FRAGMENT_SUM_LENGTH 60
40 /* FRAGMENT_COUNT must be an integral divisor or FRAGMENT_SUM_LENGTH */
41 /* 60 => 2, 3, 4, 5, 6, 10, 12, 15, 20, or 30 */
42 #define FRAGMENT_COUNT 20
43
44 /* number of sectors to ignore at end of iso when computing sum */
45 #define SKIPSECTORS 15
46
47 #define MAX(x, y)  ((x > y) ? x : y)
48 #define MIN(x, y)  ((x < y) ? x : y)
49
50 /* finds primary volume descriptor and returns info from it */
51 /* mediasum must be a preallocated buffer at least 33 bytes long */
52 static int parsepvd(int isofd, char *mediasum, long long *isosize) {
53     unsigned char buf[2048];
54     long long offset;
55     unsigned char *p __attribute__((unused));
56
57     if (lseek(isofd, 16*2048, SEEK_SET) == -1)
58         return ((long long)-1);
59     
60     offset = (16 * 2048);
61     for (;1;) {
62         if (read(isofd, buf, 2048L) == -1)
63             return ((long long)-1);
64
65         if (buf[0] == 1)
66             /* found primary volume descriptor */
67             break;
68         else if (buf[0] == 255)
69             /* hit end and didn't find primary volume descriptor */
70             return ((long long)-1);
71         offset += 2048L;
72     }
73     
74     /* read out md5sum */
75 #if 0
76     memcpy(mediasum, buf + APPDATA_OFFSET + 13, 32);
77     mediasum[32] = '\0';
78
79     for (p=mediasum; *p; p++)
80         if (*p != ' ')
81             break;
82
83     /* if the md5sum was all spaces, we didn't find md5sum */
84     if (!*p)
85         return -1;
86 #endif
87
88     /* get isosize */
89     *isosize = (buf[SIZE_OFFSET]*0x1000000+buf[SIZE_OFFSET+1]*0x10000 +
90                 buf[SIZE_OFFSET+2]*0x100 + buf[SIZE_OFFSET+3]) * 2048LL;
91
92     return offset;
93 }
94
95
96 static unsigned int writeAppData(unsigned char *appdata, char *valstr, unsigned int loc) {
97     if (loc + strlen(valstr) > 511) {
98         printf("Attempted to write too much appdata, exiting...\n");
99         exit(-1);
100     }
101
102     memcpy(appdata + loc, valstr, strlen(valstr));
103
104     return loc+strlen(valstr);
105 }
106
107
108
109
110 int implantISOFile(char *fname, int supported, int forceit, int quiet, char **errstr) {
111     int i;
112     int isofd;
113     int nread;
114     int dirty;
115     int pvd_offset;
116     int current_fragment = 0;
117     int previous_fragment = 0;
118     int nattempt;
119     long long isosize, total;
120     unsigned char md5sum[16];
121     unsigned char fragmd5sum[16];
122     unsigned int loc;
123     unsigned int bufsize = 32768;
124     unsigned char *buf;
125     unsigned char orig_appdata[512];
126     unsigned char new_appdata[512];
127     char mediasum[33];
128     char md5str[40];
129     char fragstr[FRAGMENT_SUM_LENGTH+1];
130     MD5_CTX md5ctx, fragmd5ctx;
131
132     isofd = open(fname, O_RDWR);
133
134     if (isofd < 0) {
135         *errstr = "Error - Unable to open file %s\n\n";
136         return -1;
137     }
138
139     pvd_offset = parsepvd(isofd, mediasum, &isosize);
140     if (pvd_offset < 0) {
141         *errstr = "Could not find primary volumne!\n\n";
142         return -1;
143     }
144
145     lseek(isofd, pvd_offset + APPDATA_OFFSET, SEEK_SET);
146     nread = read(isofd, orig_appdata, 512);
147
148     if (!forceit) {
149         dirty = 0;
150         for (i=0; i < 512; i++)
151             if (orig_appdata[i] != ' ')
152                 dirty = 1;
153
154         if (dirty) {
155             *errstr = "Application data has been used - not implanting md5sum!\n";
156             return -1;
157         }
158     } else {
159         /* write out blanks to erase old app data */
160         lseek(isofd, pvd_offset + APPDATA_OFFSET, SEEK_SET);
161         memset(new_appdata, ' ', 512);
162         i = write(isofd, new_appdata, 512);
163         if (i<0) {
164             printf("write failed %d\n", i);
165             perror("");
166         }
167     }
168
169     /* now do md5sum */
170     lseek(isofd, 0L, SEEK_SET);
171
172     MD5_Init(&md5ctx);
173     *fragstr = '\0';
174     buf = malloc(bufsize * sizeof(unsigned char));
175
176     total = 0;
177     /* read up to 15 sectors from end, due to problems reading last few */
178     /* sectors on burned CDs                                            */
179     while (total < isosize - SKIPSECTORS*2048) {
180         nattempt = MIN(isosize - SKIPSECTORS*2048 - total, bufsize);
181         nread = read(isofd, buf, nattempt);
182
183         if (nread <= 0)
184             break;
185         
186         MD5_Update(&md5ctx, buf, nread);
187
188         /* if we're onto the next fragment, calculate the previous sum and write */
189         current_fragment = total * (FRAGMENT_COUNT+1) / (isosize - SKIPSECTORS*2048);
190         if ( current_fragment != previous_fragment ) {
191             memcpy(&fragmd5ctx, &md5ctx, sizeof(MD5_CTX));
192             MD5_Final(fragmd5sum, &fragmd5ctx);
193             for (i=0; i<FRAGMENT_SUM_LENGTH/FRAGMENT_COUNT; i++) {
194                 char tmpstr[2];
195                 snprintf(tmpstr, 2, "%01x", fragmd5sum[i]);
196                 strncat(fragstr, tmpstr, 2);
197             }
198             /*  printf("\nFragment [%i]: %s\n", previous_fragment, fragstr);  */
199             previous_fragment = current_fragment;
200         }
201
202         total = total + nread;
203     }
204     free(buf);
205
206     MD5_Final(md5sum, &md5ctx);
207
208     *md5str = '\0';
209     for (i=0; i<16; i++) {
210         char tmpstr[4];
211         snprintf (tmpstr, 4, "%02x", md5sum[i]);
212         strncat(md5str, tmpstr, 2);
213     }
214
215     if (!quiet) {
216         printf("Inserting md5sum into iso image...\n");
217         printf("md5 = %s\n", md5str);
218         printf("Inserting fragment md5sums into iso image...\n");
219         printf("fragmd5 = %s\n", fragstr);
220         printf("frags = %d\n", FRAGMENT_COUNT);
221     }
222     /*    memcpy(new_appdata, orig_appdata, 512); */
223     memset(new_appdata, ' ', 512);
224
225     loc = 0;
226     loc = writeAppData(new_appdata, "ISO MD5SUM = ", loc);
227     loc = writeAppData(new_appdata, md5str, loc);
228     loc = writeAppData(new_appdata, ";", loc);
229
230     buf = malloc(512 * sizeof(unsigned char));
231     snprintf((char *)buf, 512, "SKIPSECTORS = %d", SKIPSECTORS);
232
233     loc = writeAppData(new_appdata, (char *)buf, loc);
234     loc = writeAppData(new_appdata, ";", loc);
235     free(buf);
236
237     if (supported) {
238         if (!quiet)
239             printf("Setting supported flag to 1\n");
240         loc = writeAppData(new_appdata, "RHLISOSTATUS=1", loc);
241     } else {
242         if (!quiet)
243             printf("Setting supported flag to 0\n");
244         loc = writeAppData(new_appdata, "RHLISOSTATUS=0", loc);
245     }
246         
247     loc = writeAppData(new_appdata, ";", loc);
248
249     loc = writeAppData(new_appdata, "FRAGMENT SUMS = ", loc);
250     loc = writeAppData(new_appdata, fragstr, loc);
251     loc = writeAppData(new_appdata, ";", loc);
252
253     buf = malloc(512 * sizeof(unsigned char));
254     snprintf((char *)buf, 512, "FRAGMENT COUNT = %d", FRAGMENT_COUNT);
255     loc = writeAppData(new_appdata, (char *)buf, loc);
256     loc = writeAppData(new_appdata, ";", loc);
257     free(buf);
258
259     loc = writeAppData(new_appdata, "THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!", loc);
260     
261     i = lseek(isofd, pvd_offset + APPDATA_OFFSET, SEEK_SET);
262     if (i<0)
263         printf("seek failed\n");
264
265     i = write(isofd, new_appdata, 512);
266     if (i<0) {
267         printf("write failed %d\n", i);
268         perror("");
269     }
270
271     close(isofd);
272     errstr = NULL;
273     return 0;
274 }