patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / scripts / sumversion.c
1 #include <netinet/in.h>
2 #include <stdint.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include <string.h>
6 #include "modpost.h"
7
8 /* Parse tag=value strings from .modinfo section */
9 static char *next_string(char *string, unsigned long *secsize)
10 {
11         /* Skip non-zero chars */
12         while (string[0]) {
13                 string++;
14                 if ((*secsize)-- <= 1)
15                         return NULL;
16         }
17
18         /* Skip any zero padding. */
19         while (!string[0]) {
20                 string++;
21                 if ((*secsize)-- <= 1)
22                         return NULL;
23         }
24         return string;
25 }
26
27 static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
28                          const char *tag)
29 {
30         char *p;
31         unsigned int taglen = strlen(tag);
32         unsigned long size = modinfo_len;
33
34         for (p = modinfo; p; p = next_string(p, &size)) {
35                 if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
36                         return p + taglen + 1;
37         }
38         return NULL;
39 }
40
41 /*
42  * Stolen form Cryptographic API.
43  *
44  * MD4 Message Digest Algorithm (RFC1320).
45  *
46  * Implementation derived from Andrew Tridgell and Steve French's
47  * CIFS MD4 implementation, and the cryptoapi implementation
48  * originally based on the public domain implementation written
49  * by Colin Plumb in 1993.
50  *
51  * Copyright (c) Andrew Tridgell 1997-1998.
52  * Modified by Steve French (sfrench@us.ibm.com) 2002
53  * Copyright (c) Cryptoapi developers.
54  * Copyright (c) 2002 David S. Miller (davem@redhat.com)
55  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
56  *
57  * This program is free software; you can redistribute it and/or modify
58  * it under the terms of the GNU General Public License as published by
59  * the Free Software Foundation; either version 2 of the License, or
60  * (at your option) any later version.
61  *
62  */
63 #define MD4_DIGEST_SIZE         16
64 #define MD4_HMAC_BLOCK_SIZE     64
65 #define MD4_BLOCK_WORDS         16
66 #define MD4_HASH_WORDS          4
67
68 struct md4_ctx {
69         uint32_t hash[MD4_HASH_WORDS];
70         uint32_t block[MD4_BLOCK_WORDS];
71         uint64_t byte_count;
72 };
73
74 static inline uint32_t lshift(uint32_t x, unsigned int s)
75 {
76         x &= 0xFFFFFFFF;
77         return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
78 }
79
80 static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z)
81 {
82         return (x & y) | ((~x) & z);
83 }
84
85 static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z)
86 {
87         return (x & y) | (x & z) | (y & z);
88 }
89
90 static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z)
91 {
92         return x ^ y ^ z;
93 }
94
95 #define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
96 #define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (uint32_t)0x5A827999,s))
97 #define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (uint32_t)0x6ED9EBA1,s))
98
99 /* XXX: this stuff can be optimized */
100 static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
101 {
102         while (words--) {
103                 *buf = ntohl(*buf);
104                 buf++;
105         }
106 }
107
108 static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
109 {
110         while (words--) {
111                 *buf = htonl(*buf);
112                 buf++;
113         }
114 }
115
116 static void md4_transform(uint32_t *hash, uint32_t const *in)
117 {
118         uint32_t a, b, c, d;
119
120         a = hash[0];
121         b = hash[1];
122         c = hash[2];
123         d = hash[3];
124
125         ROUND1(a, b, c, d, in[0], 3);
126         ROUND1(d, a, b, c, in[1], 7);
127         ROUND1(c, d, a, b, in[2], 11);
128         ROUND1(b, c, d, a, in[3], 19);
129         ROUND1(a, b, c, d, in[4], 3);
130         ROUND1(d, a, b, c, in[5], 7);
131         ROUND1(c, d, a, b, in[6], 11);
132         ROUND1(b, c, d, a, in[7], 19);
133         ROUND1(a, b, c, d, in[8], 3);
134         ROUND1(d, a, b, c, in[9], 7);
135         ROUND1(c, d, a, b, in[10], 11);
136         ROUND1(b, c, d, a, in[11], 19);
137         ROUND1(a, b, c, d, in[12], 3);
138         ROUND1(d, a, b, c, in[13], 7);
139         ROUND1(c, d, a, b, in[14], 11);
140         ROUND1(b, c, d, a, in[15], 19);
141
142         ROUND2(a, b, c, d,in[ 0], 3);
143         ROUND2(d, a, b, c, in[4], 5);
144         ROUND2(c, d, a, b, in[8], 9);
145         ROUND2(b, c, d, a, in[12], 13);
146         ROUND2(a, b, c, d, in[1], 3);
147         ROUND2(d, a, b, c, in[5], 5);
148         ROUND2(c, d, a, b, in[9], 9);
149         ROUND2(b, c, d, a, in[13], 13);
150         ROUND2(a, b, c, d, in[2], 3);
151         ROUND2(d, a, b, c, in[6], 5);
152         ROUND2(c, d, a, b, in[10], 9);
153         ROUND2(b, c, d, a, in[14], 13);
154         ROUND2(a, b, c, d, in[3], 3);
155         ROUND2(d, a, b, c, in[7], 5);
156         ROUND2(c, d, a, b, in[11], 9);
157         ROUND2(b, c, d, a, in[15], 13);
158
159         ROUND3(a, b, c, d,in[ 0], 3);
160         ROUND3(d, a, b, c, in[8], 9);
161         ROUND3(c, d, a, b, in[4], 11);
162         ROUND3(b, c, d, a, in[12], 15);
163         ROUND3(a, b, c, d, in[2], 3);
164         ROUND3(d, a, b, c, in[10], 9);
165         ROUND3(c, d, a, b, in[6], 11);
166         ROUND3(b, c, d, a, in[14], 15);
167         ROUND3(a, b, c, d, in[1], 3);
168         ROUND3(d, a, b, c, in[9], 9);
169         ROUND3(c, d, a, b, in[5], 11);
170         ROUND3(b, c, d, a, in[13], 15);
171         ROUND3(a, b, c, d, in[3], 3);
172         ROUND3(d, a, b, c, in[11], 9);
173         ROUND3(c, d, a, b, in[7], 11);
174         ROUND3(b, c, d, a, in[15], 15);
175
176         hash[0] += a;
177         hash[1] += b;
178         hash[2] += c;
179         hash[3] += d;
180 }
181
182 static inline void md4_transform_helper(struct md4_ctx *ctx)
183 {
184         le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(uint32_t));
185         md4_transform(ctx->hash, ctx->block);
186 }
187
188 static void md4_init(struct md4_ctx *mctx)
189 {
190         mctx->hash[0] = 0x67452301;
191         mctx->hash[1] = 0xefcdab89;
192         mctx->hash[2] = 0x98badcfe;
193         mctx->hash[3] = 0x10325476;
194         mctx->byte_count = 0;
195 }
196
197 static void md4_update(struct md4_ctx *mctx,
198                        const unsigned char *data, unsigned int len)
199 {
200         const uint32_t avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
201
202         mctx->byte_count += len;
203
204         if (avail > len) {
205                 memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
206                        data, len);
207                 return;
208         }
209
210         memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
211                data, avail);
212
213         md4_transform_helper(mctx);
214         data += avail;
215         len -= avail;
216
217         while (len >= sizeof(mctx->block)) {
218                 memcpy(mctx->block, data, sizeof(mctx->block));
219                 md4_transform_helper(mctx);
220                 data += sizeof(mctx->block);
221                 len -= sizeof(mctx->block);
222         }
223
224         memcpy(mctx->block, data, len);
225 }
226
227 static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
228 {
229         const unsigned int offset = mctx->byte_count & 0x3f;
230         char *p = (char *)mctx->block + offset;
231         int padding = 56 - (offset + 1);
232
233         *p++ = 0x80;
234         if (padding < 0) {
235                 memset(p, 0x00, padding + sizeof (uint64_t));
236                 md4_transform_helper(mctx);
237                 p = (char *)mctx->block;
238                 padding = 56;
239         }
240
241         memset(p, 0, padding);
242         mctx->block[14] = mctx->byte_count << 3;
243         mctx->block[15] = mctx->byte_count >> 29;
244         le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
245                           sizeof(uint64_t)) / sizeof(uint32_t));
246         md4_transform(mctx->hash, mctx->block);
247         cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t));
248
249         snprintf(out, len, "%08X%08X%08X%08X",
250                  mctx->hash[0], mctx->hash[1], mctx->hash[2], mctx->hash[3]);
251 }
252
253 static inline void add_char(unsigned char c, struct md4_ctx *md)
254 {
255         md4_update(md, &c, 1);
256 }
257
258 static int parse_string(const char *file, unsigned long len,
259                         struct md4_ctx *md)
260 {
261         unsigned long i;
262
263         add_char(file[0], md);
264         for (i = 1; i < len; i++) {
265                 add_char(file[i], md);
266                 if (file[i] == '"' && file[i-1] != '\\')
267                         break;
268         }
269         return i;
270 }
271
272 static int parse_comment(const char *file, unsigned long len)
273 {
274         unsigned long i;
275
276         for (i = 2; i < len; i++) {
277                 if (file[i-1] == '*' && file[i] == '/')
278                         break;
279         }
280         return i;
281 }
282
283 /* FIXME: Handle .s files differently (eg. # starts comments) --RR */
284 static int parse_file(const char *fname, struct md4_ctx *md)
285 {
286         char *file;
287         unsigned long i, len;
288
289         file = grab_file(fname, &len);
290         if (!file)
291                 return 0;
292
293         for (i = 0; i < len; i++) {
294                 /* Collapse and ignore \ and CR. */
295                 if (file[i] == '\\' && (i+1 < len) && file[i+1] == '\n') {
296                         i++;
297                         continue;
298                 }
299
300                 /* Ignore whitespace */
301                 if (isspace(file[i]))
302                         continue;
303
304                 /* Handle strings as whole units */
305                 if (file[i] == '"') {
306                         i += parse_string(file+i, len - i, md);
307                         continue;
308                 }
309
310                 /* Comments: ignore */
311                 if (file[i] == '/' && file[i+1] == '*') {
312                         i += parse_comment(file+i, len - i);
313                         continue;
314                 }
315
316                 add_char(file[i], md);
317         }
318         release_file(file, len);
319         return 1;
320 }
321
322 /* We have dir/file.o.  Open dir/.file.o.cmd, look for deps_ line to
323  * figure out source file. */
324 static int parse_source_files(const char *objfile, struct md4_ctx *md)
325 {
326         char *cmd, *file, *line, *dir;
327         const char *base;
328         unsigned long flen, pos = 0;
329         int dirlen, ret = 0, check_files = 0;
330
331         cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
332
333         base = strrchr(objfile, '/');
334         if (base) {
335                 base++;
336                 dirlen = base - objfile;
337                 sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base);
338         } else {
339                 dirlen = 0;
340                 sprintf(cmd, ".%s.cmd", objfile);
341         }
342         dir = NOFAIL(malloc(dirlen + 1));
343         strncpy(dir, objfile, dirlen);
344         dir[dirlen] = '\0';
345
346         file = grab_file(cmd, &flen);
347         if (!file) {
348                 fprintf(stderr, "Warning: could not find %s for %s\n",
349                         cmd, objfile);
350                 goto out;
351         }
352
353         /* There will be a line like so:
354                 deps_drivers/net/dummy.o := \
355                   drivers/net/dummy.c \
356                     $(wildcard include/config/net/fastroute.h) \
357                   include/linux/config.h \
358                     $(wildcard include/config/h.h) \
359                   include/linux/module.h \
360
361            Sum all files in the same dir or subdirs.
362         */
363         while ((line = get_next_line(&pos, file, flen)) != NULL) {
364                 char* p = line;
365                 if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) {
366                         check_files = 1;
367                         continue;
368                 }
369                 if (!check_files)
370                         continue;
371
372                 /* Continue until line does not end with '\' */
373                 if ( *(p + strlen(p)-1) != '\\')
374                         break;
375                 /* Terminate line at first space, to get rid of final ' \' */
376                 while (*p) {
377                        if (isspace(*p)) {
378                                 *p = '\0';
379                                 break;
380                         }
381                         p++;
382                 }
383
384                 /* Check if this file is in same dir as objfile */
385                 if ((strstr(line, dir)+strlen(dir)-1) == strrchr(line, '/')) {
386                         if (!parse_file(line, md)) {
387                                 fprintf(stderr,
388                                         "Warning: could not open %s: %s\n",
389                                         line, strerror(errno));
390                                 goto out_file;
391                         }
392
393                 }
394
395         }
396
397         /* Everyone parsed OK */
398         ret = 1;
399 out_file:
400         release_file(file, flen);
401 out:
402         free(dir);
403         free(cmd);
404         return ret;
405 }
406
407 static int get_version(const char *modname, char sum[])
408 {
409         void *file;
410         unsigned long len;
411         int ret = 0;
412         struct md4_ctx md;
413         char *sources, *end, *fname;
414         const char *basename;
415         char filelist[sizeof(".tmp_versions/%s.mod") + strlen(modname)];
416
417         /* Source files for module are in .tmp_versions/modname.mod,
418            after the first line. */
419         if (strrchr(modname, '/'))
420                 basename = strrchr(modname, '/') + 1;
421         else
422                 basename = modname;
423         sprintf(filelist, ".tmp_versions/%s", basename);
424         /* Truncate .o, add .mod */
425         strcpy(filelist + strlen(filelist)-2, ".mod");
426
427         file = grab_file(filelist, &len);
428         if (!file) {
429                 fprintf(stderr, "Warning: could not find versions for %s\n",
430                         filelist);
431                 return 0;
432         }
433
434         sources = strchr(file, '\n');
435         if (!sources) {
436                 fprintf(stderr, "Warning: malformed versions file for %s\n",
437                         modname);
438                 goto release;
439         }
440
441         sources++;
442         end = strchr(sources, '\n');
443         if (!end) {
444                 fprintf(stderr, "Warning: bad ending versions file for %s\n",
445                         modname);
446                 goto release;
447         }
448         *end = '\0';
449
450         md4_init(&md);
451         for (fname = strtok(sources, " "); fname; fname = strtok(NULL, " ")) {
452                 if (!parse_source_files(fname, &md))
453                         goto release;
454         }
455
456         /* sum is of form \0<padding>. */
457         md4_final_ascii(&md, sum, 1 + strlen(sum+1));
458         ret = 1;
459 release:
460         release_file(file, len);
461         return ret;
462 }
463
464 static void write_version(const char *filename, const char *sum,
465                           unsigned long offset)
466 {
467         int fd;
468
469         fd = open(filename, O_RDWR);
470         if (fd < 0) {
471                 fprintf(stderr, "Warning: changing sum in %s failed: %s\n",
472                         filename, strerror(errno));
473                 return;
474         }
475
476         if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
477                 fprintf(stderr, "Warning: changing sum in %s:%lu failed: %s\n",
478                         filename, offset, strerror(errno));
479                 goto out;
480         }
481
482         if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) {
483                 fprintf(stderr, "Warning: writing sum in %s failed: %s\n",
484                         filename, strerror(errno));
485                 goto out;
486         }
487 out:
488         close(fd);
489 }
490
491 void strip_rcs_crap(char *version)
492 {
493         unsigned int len, full_len;
494
495         if (strncmp(version, "$Revision", strlen("$Revision")) != 0)
496                 return;
497
498         /* Space for version string follows. */
499         full_len = strlen(version) + strlen(version + strlen(version) + 1) + 2;
500
501         /* Move string to start with version number: prefix will be
502          * $Revision$ or $Revision: */
503         len = strlen("$Revision");
504         if (version[len] == ':' || version[len] == '$')
505                 len++;
506         while (isspace(version[len]))
507                 len++;
508         memmove(version, version+len, full_len-len);
509         full_len -= len;
510
511         /* Preserve up to next whitespace. */
512         len = 0;
513         while (version[len] && !isspace(version[len]))
514                 len++;
515         memmove(version + len, version + strlen(version),
516                 full_len - strlen(version));
517 }
518
519 /* If the modinfo contains a "version" value, then set this. */
520 void maybe_frob_version(const char *modfilename,
521                         void *modinfo,
522                         unsigned long modinfo_len,
523                         unsigned long modinfo_offset)
524 {
525         char *version, *csum;
526
527         version = get_modinfo(modinfo, modinfo_len, "version");
528         if (!version)
529                 return;
530
531         /* RCS $Revision gets stripped out. */
532         strip_rcs_crap(version);
533
534         /* Check against double sumversion */
535         if (strchr(version, ' '))
536                 return;
537
538         /* Version contains embedded NUL: second half has space for checksum */
539         csum = version + strlen(version);
540         *(csum++) = ' ';
541         if (get_version(modfilename, csum))
542                 write_version(modfilename, version,
543                               modinfo_offset + (version - (char *)modinfo));
544 }