4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
32 #include <sys/types.h>
35 #include "bblk_einfo.h"
36 #include "boot_utils.h"
38 bblk_hash_t bblk_no_hash
= {BBLK_NO_HASH
, 0, "(no hash)", NULL
};
39 bblk_hash_t bblk_md5_hash
= {BBLK_HASH_MD5
, 0x10, "MD5", md5_calc
};
41 bblk_hash_t
*bblk_hash_list
[BBLK_HASH_TOT
] = {
47 * einfo_compare_dotted_version()
48 * Compares two strings with an arbitrary long number of dot-separated numbers.
49 * Returns: 0 - if the version numbers are equal
50 * 1 - if str1 version number is more recent than str2
51 * 2 - if str2 version number is more recent than str1
52 * -1 - if an error occurred
54 * Comparison is done field by field, by retrieving an unsigned integer value,
55 * (missing fields are assumed as 0, but explict zeroes take precedence) so:
56 * 4.1.2.11 > 4.1.2.2 > 4.1.2.0 > 4.1.2
58 * where ">" means "more recent than".
61 einfo_compare_dotted_version(const char *str1
, const char *str2
)
64 char *verstr1
, *verstr2
, *freeptr1
, *freeptr2
;
65 char *parsep1
, *parsep2
;
66 unsigned int val_str1
, val_str2
;
68 freeptr1
= verstr1
= strdup(str1
);
69 freeptr2
= verstr2
= strdup(str2
);
70 if (verstr1
== NULL
|| verstr2
== NULL
) {
75 while (verstr1
!= NULL
&& verstr2
!= NULL
) {
76 parsep1
= strsep(&verstr1
, ".");
77 parsep2
= strsep(&verstr2
, ".");
79 val_str1
= atoi(parsep1
);
80 val_str2
= atoi(parsep2
);
82 if (val_str1
> val_str2
) {
87 if (val_str2
> val_str1
) {
93 /* Common portion of the version string is equal. */
94 if (verstr1
== NULL
&& verstr2
!= NULL
)
96 if (verstr2
== NULL
&& verstr1
!= NULL
)
106 * einfo_compare_timestamps()
107 * Currently, timestamp is in %Y%m%dT%H%M%SZ format in UTC, which means that
108 * we can simply do a lexicographic comparison to know which one is the most
111 * Returns: 0 - if timestamps coincide
112 * 1 - if the timestamp in str1 is more recent
113 * 2 - if the timestamp in str2 is more recent
116 einfo_compare_timestamps(const char *str1
, const char *str2
)
120 retval
= strcmp(str1
, str2
);
130 * einfo_compare_version()
131 * Given two extended versions, compare the two and returns which one is more
132 * "recent". Comparison is based on dotted version number fields and a
135 * Returns: -1 - on error
136 * 0 - if the two versions coincide
137 * 1 - if the version in str1 is more recent
138 * 2 - if the version in str2 is more recent
140 * The version string generally uses following form:
141 * self_release,build_release:timestamp
142 * The release numbers are compared as dotted versions.
144 * While comparing, if the self releases are identical but the build
145 * release is missing, this version string is considered older.
147 * If the release strings are identical, and one of the timestamps is missing,
148 * we return an error. Otherwise, return the result from comparing the
152 einfo_compare_version(const char *str1
, const char *str2
)
155 char *verstr1
, *verstr2
, *freeptr1
, *freeptr2
;
156 char *parsep1
, *parsep2
;
157 char *timep1
, *timep2
;
159 freeptr1
= verstr1
= strdup(str1
);
160 freeptr2
= verstr2
= strdup(str2
);
161 if (verstr1
== NULL
|| verstr2
== NULL
) {
166 /* Extract the time part from the version string. */
169 parsep1
= strsep(&timep1
, ":");
170 parsep2
= strsep(&timep2
, ":");
172 while (parsep1
!= NULL
&& parsep2
!= NULL
) {
173 parsep1
= strsep(&verstr1
, ",-");
174 parsep2
= strsep(&verstr2
, ",-");
176 /* If both are NULL, compare timestamps */
177 if (parsep1
== NULL
&& parsep2
== NULL
)
180 if (parsep1
== NULL
) {
184 if (parsep2
== NULL
) {
189 retval
= einfo_compare_dotted_version(parsep1
, parsep2
);
196 /* The dotted versions are identical, check timestamps. */
197 if (timep1
== NULL
|| timep2
== NULL
) {
201 retval
= einfo_compare_timestamps(timep1
, timep2
);
211 * Print the extended information contained into the pointed structure.
212 * 'bufsize' specifies the real size of the structure, since str_off and
213 * hash_off need to point somewhere past the header.
216 print_einfo(uint8_t flags
, bblk_einfo_t
*einfo
, unsigned long bufsize
)
220 boolean_t has_hash
= B_FALSE
;
221 unsigned char *hash
= NULL
;
223 if (einfo
->str_off
+ einfo
->str_size
> bufsize
) {
224 (void) fprintf(stdout
, gettext("String offset %d is beyond the "
225 "buffer size\n"), einfo
->str_off
);
229 version
= (char *)einfo
+ einfo
->str_off
;
230 if (einfo
->hash_type
!= BBLK_NO_HASH
&&
231 einfo
->hash_type
< BBLK_HASH_TOT
) {
232 if (einfo
->hash_off
+ einfo
->hash_size
> bufsize
) {
233 (void) fprintf(stdout
, gettext("Warning: hashing "
234 "present but hash offset %d is beyond the buffer "
235 "size\n"), einfo
->hash_off
);
238 hash
= (unsigned char *)einfo
+ einfo
->hash_off
;
243 if (flags
& EINFO_PRINT_HEADER
) {
244 (void) fprintf(stdout
, "Boot Block Extended Info Header:\n");
245 (void) fprintf(stdout
, "\tmagic: ");
246 for (i
= 0; i
< EINFO_MAGIC_SIZE
; i
++)
247 (void) fprintf(stdout
, "%c", einfo
->magic
[i
]);
248 (void) fprintf(stdout
, "\n");
249 (void) fprintf(stdout
, "\tversion: %d\n", einfo
->version
);
250 (void) fprintf(stdout
, "\tflags: %x\n", einfo
->flags
);
251 (void) fprintf(stdout
, "\textended version string offset: %d\n",
253 (void) fprintf(stdout
, "\textended version string size: %d\n",
255 (void) fprintf(stdout
, "\thashing type: %d (%s)\n",
256 einfo
->hash_type
, has_hash
?
257 bblk_hash_list
[einfo
->hash_type
]->name
: "nil");
258 (void) fprintf(stdout
, "\thash offset: %d\n", einfo
->hash_off
);
259 (void) fprintf(stdout
, "\thash size: %d\n", einfo
->hash_size
);
262 if (flags
& EINFO_EASY_PARSE
) {
263 (void) fprintf(stdout
, "%s\n", version
);
265 (void) fprintf(stdout
, "Extended version string: %s\n",
268 (void) fprintf(stdout
, "%s hash: ",
269 bblk_hash_list
[einfo
->hash_type
]->name
);
271 (void) fprintf(stdout
, "No hashing available\n");
276 for (i
= 0; i
< einfo
->hash_size
; i
++) {
277 (void) fprintf(stdout
, "%02x", hash
[i
]);
279 (void) fprintf(stdout
, "\n");
284 compute_hash(bblk_hs_t
*hs
, unsigned char *dest
, bblk_hash_t
*hash
)
286 if (hs
== NULL
|| dest
== NULL
|| hash
== NULL
)
289 hash
->compute_hash(dest
, hs
->src_buf
, hs
->src_size
);
294 prepare_and_write_einfo(unsigned char *dest
, char *infostr
, bblk_hs_t
*hs
,
295 uint32_t maxsize
, uint32_t *used_space
)
300 bblk_einfo_t
*einfo
= (bblk_einfo_t
*)dest
;
301 bblk_hash_t
*hashinfo
= bblk_hash_list
[BBLK_DEFAULT_HASH
];
304 * 'dest' might be both containing the buffer we want to hash and
305 * containing our einfo structure: delay any update of it after the
306 * hashing has been calculated.
308 hash_size
= hashinfo
->size
;
309 hash_off
= sizeof (bblk_einfo_t
);
311 if (hash_off
+ hash_size
> maxsize
) {
312 (void) fprintf(stderr
, gettext("Unable to add extended info, "
313 "not enough space\n"));
317 data
= dest
+ hash_off
;
318 if (compute_hash(hs
, data
, hashinfo
) < 0) {
319 (void) fprintf(stderr
, gettext("%s hash operation failed\n"),
321 einfo
->hash_type
= bblk_no_hash
.type
;
322 einfo
->hash_size
= bblk_no_hash
.size
;
324 einfo
->hash_type
= hashinfo
->type
;
325 einfo
->hash_size
= hashinfo
->size
;
328 (void) memcpy(einfo
->magic
, EINFO_MAGIC
, EINFO_MAGIC_SIZE
);
329 einfo
->version
= BBLK_EINFO_VERSION
;
331 einfo
->hash_off
= hash_off
;
332 einfo
->hash_size
= hash_size
;
333 einfo
->str_off
= einfo
->hash_off
+ einfo
->hash_size
+ 1;
335 if (infostr
== NULL
) {
336 (void) fprintf(stderr
, gettext("Unable to add extended info, "
337 "string is empty\n"));
340 einfo
->str_size
= strlen(infostr
);
342 if (einfo
->str_off
+ einfo
->str_size
> maxsize
) {
343 (void) fprintf(stderr
, gettext("Unable to add extended info, "
344 "not enough space\n"));
348 data
= dest
+ einfo
->str_off
;
349 (void) memcpy(data
, infostr
, einfo
->str_size
);
350 *used_space
= einfo
->str_off
+ einfo
->str_size
;
356 * einfo_should_update()
357 * Given information on the boot block currently on disk (disk_einfo) and
358 * information on the supplied boot block (hs for hashing, verstr as the
359 * associated version string) decide if an update of the on-disk boot block
360 * is necessary or not.
363 einfo_should_update(bblk_einfo_t
*disk_einfo
, bblk_hs_t
*hs
, char *verstr
)
365 bblk_hash_t
*hashing
;
366 unsigned char *disk_hash
;
367 unsigned char *local_hash
;
371 if (disk_einfo
== NULL
)
374 if (memcmp(disk_einfo
->magic
, EINFO_MAGIC
, EINFO_MAGIC_SIZE
) != 0)
377 if (disk_einfo
->version
< BBLK_EINFO_VERSION
)
380 disk_version
= einfo_get_string(disk_einfo
);
381 retval
= einfo_compare_version(verstr
, disk_version
);
383 * If something goes wrong or if the on-disk version is more recent
384 * do not update the bootblock.
386 if (retval
== -1 || retval
== 2)
390 * If we got here it means that the two version strings are either
391 * equal or the new bootblk binary is more recent. In order to save
392 * some needless writes let's use the hash to determine if an update
393 * is really necessary.
395 if (disk_einfo
->hash_type
== bblk_no_hash
.type
)
398 if (disk_einfo
->hash_type
>= BBLK_HASH_TOT
)
401 hashing
= bblk_hash_list
[disk_einfo
->hash_type
];
403 local_hash
= malloc(hashing
->size
);
404 if (local_hash
== NULL
)
408 * Failure in computing the hash may mean something wrong
409 * with the boot block file. Better be conservative here.
411 if (compute_hash(hs
, local_hash
, hashing
) < 0) {
416 disk_hash
= (unsigned char *)einfo_get_hash(disk_einfo
);
418 if (memcmp(local_hash
, disk_hash
, disk_einfo
->hash_size
) == 0) {
428 einfo_get_string(bblk_einfo_t
*einfo
)
433 return ((char *)einfo
+ einfo
->str_off
);
437 einfo_get_hash(bblk_einfo_t
*einfo
)
442 return ((char *)einfo
+ einfo
->hash_off
);