manifest_hash.c revision 11996:91b62f7b8186
1193323Sed/*
2193323Sed * CDDL HEADER START
3193323Sed *
4193323Sed * The contents of this file are subject to the terms of the
5193323Sed * Common Development and Distribution License (the "License").
6193323Sed * You may not use this file except in compliance with the License.
7193323Sed *
8193323Sed * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9193323Sed * or http://www.opensolaris.org/os/licensing.
10193323Sed * See the License for the specific language governing permissions
11193323Sed * and limitations under the License.
12193323Sed *
13193323Sed * When distributing Covered Code, include this CDDL HEADER in each
14193323Sed * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15193323Sed * If applicable, add the following below this CDDL HEADER, with the
16193323Sed * fields enclosed by brackets "[]" replaced with your own identifying
17193323Sed * information: Portions Copyright [yyyy] [name of copyright owner]
18193323Sed *
19193323Sed * CDDL HEADER END
20193323Sed */
21193323Sed/*
22193323Sed * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23193323Sed * Use is subject to license terms.
24193323Sed */
25193323Sed
26193323Sed
27193323Sed#include <sys/stat.h>
28193323Sed#include <sys/types.h>
29193323Sed
30193323Sed#include <assert.h>
31193323Sed#include <ctype.h>
32201360Srdivacky#include <errno.h>
33193323Sed#include <fcntl.h>
34193323Sed#include <libintl.h>
35193323Sed#include <libscf.h>
36193323Sed#include <libuutil.h>
37193323Sed#include <limits.h>
38193323Sed#include <md5.h>
39212904Sdim#include <pthread.h>
40243830Sdim#include <stdio.h>
41243830Sdim#include <stdlib.h>
42243830Sdim#include <string.h>
43243830Sdim#include <strings.h>
44210299Sed#include <unistd.h>
45193323Sed
46218893Sdim#include <manifest_hash.h>
47193323Sed
48193323Sed/*
49239462Sdim * Translate a file name to property name.  Return an allocated string or NULL
50193323Sed * if realpath() fails. If deathrow is true, realpath() is skipped. This
51193323Sed * allows to return the property name even if the file doesn't exist.
52193323Sed */
53193323Sedchar *
54193323Sedmhash_filename_to_propname(const char *in, boolean_t deathrow)
55193323Sed{
56239462Sdim	char *out, *cp, *base;
57193323Sed	size_t len, piece_len;
58193323Sed
59193323Sed	out = uu_zalloc(PATH_MAX + 1);
60226633Sdim	if (deathrow) {
61226633Sdim		/* used only for service deathrow handling */
62226633Sdim		if (strlcpy(out, in, PATH_MAX + 1) >= (PATH_MAX + 1)) {
63198090Srdivacky			uu_free(out);
64198090Srdivacky			return (NULL);
65198090Srdivacky		}
66198090Srdivacky	} else {
67193323Sed		if (realpath(in, out) == NULL) {
68193323Sed			uu_free(out);
69193323Sed			return (NULL);
70193323Sed		}
71193323Sed	}
72193323Sed
73193323Sed	base = getenv("PKG_INSTALL_ROOT");
74198396Srdivacky
75243830Sdim	/*
76193323Sed	 * We copy-shift over the basedir and the leading slash, since it's
77193323Sed	 * not relevant to when we boot with this repository.
78193323Sed	 */
79194612Sed
80243830Sdim	cp = out + ((base != NULL)? strlen(base) : 0);
81208599Srdivacky	if (*cp == '/')
82194612Sed		cp++;
83200581Srdivacky	(void) memmove(out, cp, strlen(cp) + 1);
84218893Sdim
85243830Sdim	len = strlen(out);
86193323Sed	if (len > scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) {
87193323Sed		/* Use the first half and the second half. */
88193323Sed		piece_len = (scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) - 3) / 2;
89193323Sed
90193323Sed		(void) strncpy(out + piece_len, "__", 2);
91193323Sed
92193323Sed		(void) memmove(out + piece_len + 2, out + (len - piece_len),
93193323Sed		    piece_len + 1);
94193323Sed	}
95193323Sed
96193323Sed	/*
97193323Sed	 * Translate non-property characters to '_', first making sure that
98193323Sed	 * we don't begin with '_'.
99223017Sdim	 */
100226633Sdim
101193323Sed	if (!isalpha(*out))
102203954Srdivacky		*out = 'A';
103193323Sed
104193323Sed	for (cp = out + 1; *cp != '\0'; ++cp) {
105193323Sed		if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
106193323Sed			*cp = '_';
107193323Sed	}
108193574Sed
109193574Sed	return (out);
110198090Srdivacky}
111224145Sdim
112234353Sdimint
113243830Sdimmhash_retrieve_entry(scf_handle_t *hndl, const char *name, uchar_t *hash,
114193323Sed    apply_action_t *action)
115193323Sed{
116193323Sed	scf_scope_t *scope;
117193323Sed	scf_service_t *svc;
118193323Sed	scf_propertygroup_t *pg;
119193323Sed	scf_property_t *prop;
120193323Sed	scf_value_t *val;
121193323Sed	scf_error_t err;
122226633Sdim	ssize_t szret;
123226633Sdim	int result = 0;
124226633Sdim
125193323Sed	if (action)
126193574Sed		*action = APPLY_NONE;
127193574Sed
128193323Sed	/*
129198090Srdivacky	 * In this implementation the hash for name is the opaque value of
130193323Sed	 * svc:/MHASH_SVC/:properties/name/MHASH_PROP
131193323Sed	 */
132193323Sed
133193323Sed	if ((scope = scf_scope_create(hndl)) == NULL ||
134193323Sed	    (svc = scf_service_create(hndl)) == NULL ||
135193323Sed	    (pg = scf_pg_create(hndl)) == NULL ||
136226633Sdim	    (prop = scf_property_create(hndl)) == NULL ||
137226633Sdim	    (val = scf_value_create(hndl)) == NULL) {
138234353Sdim		result = -1;
139198892Srdivacky		goto out;
140193323Sed	}
141226633Sdim
142226633Sdim	if (scf_handle_get_local_scope(hndl, scope) < 0) {
143193323Sed		result = -1;
144224145Sdim		goto out;
145198892Srdivacky	}
146193323Sed
147193323Sed	if (scf_scope_get_service(scope, MHASH_SVC, svc) < 0) {
148193323Sed		result = -1;
149193323Sed		goto out;
150193323Sed	}
151193323Sed
152193323Sed	if (scf_service_get_pg(svc, name, pg) != SCF_SUCCESS) {
153193323Sed		result = -1;
154193323Sed		goto out;
155201360Srdivacky	}
156193323Sed
157193323Sed	if (scf_pg_get_property(pg, MHASH_PROP, prop) != SCF_SUCCESS) {
158193323Sed		result = -1;
159193323Sed		goto out;
160193323Sed	}
161193323Sed
162193323Sed	if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
163193323Sed		result = -1;
164193323Sed		goto out;
165193323Sed	}
166193323Sed
167193323Sed	szret = scf_value_get_opaque(val, hash, MHASH_SIZE);
168	if (szret < 0) {
169		result = -1;
170		goto out;
171	}
172
173	/*
174	 * Make sure that the old hash is returned with
175	 * remainder of the bytes zeroed.
176	 */
177	if (szret == MHASH_SIZE_OLD) {
178		(void) memset(hash + MHASH_SIZE_OLD, 0,
179		    MHASH_SIZE - MHASH_SIZE_OLD);
180	} else if (szret != MHASH_SIZE) {
181		scf_value_destroy(val);
182		result = -1;
183		goto out;
184	}
185
186	/*
187	 * If caller has requested the apply_last property, read the
188	 * property if it exists.
189	 */
190	if (action != NULL) {
191		uint8_t apply_value;
192
193		if (scf_pg_get_property(pg, MHASH_APPLY_PROP, prop) !=
194		    SCF_SUCCESS) {
195			err = scf_error();
196			if ((err != SCF_ERROR_DELETED) &&
197			    (err != SCF_ERROR_NOT_FOUND)) {
198				result = -1;
199			}
200			goto out;
201		}
202		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
203			err = scf_error();
204			if ((err != SCF_ERROR_DELETED) &&
205			    (err != SCF_ERROR_NOT_FOUND)) {
206				result = -1;
207			}
208			goto out;
209		}
210		if (scf_value_get_boolean(val, &apply_value) != SCF_SUCCESS) {
211			result = -1;
212			goto out;
213		}
214		if (apply_value)
215			*action = APPLY_LATE;
216	}
217
218out:
219	(void) scf_value_destroy(val);
220	scf_property_destroy(prop);
221	scf_pg_destroy(pg);
222	scf_service_destroy(svc);
223	scf_scope_destroy(scope);
224
225	return (result);
226}
227
228int
229mhash_store_entry(scf_handle_t *hndl, const char *name, const char *fname,
230    uchar_t *hash, apply_action_t apply_late, char **errstr)
231{
232	scf_scope_t *scope = NULL;
233	scf_service_t *svc = NULL;
234	scf_propertygroup_t *pg = NULL;
235	scf_property_t *prop = NULL;
236	scf_value_t *aval = NULL;
237	scf_value_t *val = NULL;
238	scf_value_t *fval = NULL;
239	scf_transaction_t *tx = NULL;
240	scf_transaction_entry_t *ae = NULL;
241	scf_transaction_entry_t *e = NULL;
242	scf_transaction_entry_t *fe = NULL;
243	scf_error_t err;
244	int ret, result = 0;
245
246	int i;
247
248	if ((scope = scf_scope_create(hndl)) == NULL ||
249	    (svc = scf_service_create(hndl)) == NULL ||
250	    (pg = scf_pg_create(hndl)) == NULL ||
251	    (prop = scf_property_create(hndl)) == NULL) {
252		if (errstr != NULL)
253			*errstr = gettext("Could not create scf objects");
254		result = -1;
255		goto out;
256	}
257
258	if (scf_handle_get_local_scope(hndl, scope) != SCF_SUCCESS) {
259		if (errstr != NULL)
260			*errstr = gettext("Could not get local scope");
261		result = -1;
262		goto out;
263	}
264
265	for (i = 0; i < 5; ++i) {
266
267		if (scf_scope_get_service(scope, MHASH_SVC, svc) ==
268		    SCF_SUCCESS)
269			break;
270
271		if (scf_error() != SCF_ERROR_NOT_FOUND) {
272			if (errstr != NULL)
273				*errstr = gettext("Could not get manifest hash "
274				    "service");
275			result = -1;
276			goto out;
277		}
278
279		if (scf_scope_add_service(scope, MHASH_SVC, svc) ==
280		    SCF_SUCCESS)
281			break;
282
283		err = scf_error();
284
285		if (err == SCF_ERROR_EXISTS)
286			/* Try again. */
287			continue;
288		else if (err == SCF_ERROR_PERMISSION_DENIED) {
289			if (errstr != NULL)
290				*errstr = gettext("Could not store file hash: "
291				    "permission denied.\n");
292			result = -1;
293			goto out;
294		}
295
296		if (errstr != NULL)
297			*errstr = gettext("Could not add manifest hash "
298			    "service");
299		result = -1;
300		goto out;
301	}
302
303	if (i == 5) {
304		if (errstr != NULL)
305			*errstr = gettext("Could not store file hash: "
306			    "service addition contention.\n");
307		result = -1;
308		goto out;
309	}
310
311	for (i = 0; i < 5; ++i) {
312		if (scf_service_get_pg(svc, name, pg) == SCF_SUCCESS)
313			break;
314
315		if (scf_error() != SCF_ERROR_NOT_FOUND) {
316			if (errstr != NULL)
317				*errstr = gettext("Could not get service's "
318				    "hash record)");
319			result = -1;
320			goto out;
321		}
322
323		if (scf_service_add_pg(svc, name, MHASH_PG_TYPE,
324		    MHASH_PG_FLAGS, pg) == SCF_SUCCESS)
325			break;
326
327		err = scf_error();
328
329		if (err == SCF_ERROR_EXISTS)
330			/* Try again. */
331			continue;
332		else if (err == SCF_ERROR_PERMISSION_DENIED) {
333			if (errstr != NULL)
334				*errstr = gettext("Could not store file hash: "
335				    "permission denied.\n");
336			result = -1;
337			goto out;
338		}
339
340		if (errstr != NULL)
341			*errstr = gettext("Could not store file hash");
342		result = -1;
343		goto out;
344	}
345	if (i == 5) {
346		if (errstr != NULL)
347			*errstr = gettext("Could not store file hash: "
348			    "property group addition contention.\n");
349		result = -1;
350		goto out;
351	}
352
353	if ((e = scf_entry_create(hndl)) == NULL ||
354	    (val = scf_value_create(hndl)) == NULL ||
355	    (fe = scf_entry_create(hndl)) == NULL ||
356	    (fval = scf_value_create(hndl)) == NULL ||
357	    (ae = scf_entry_create(hndl)) == NULL ||
358	    (aval = scf_value_create(hndl)) == NULL) {
359		if (errstr != NULL)
360			*errstr = gettext("Could not store file hash: "
361			    "permission denied.\n");
362		result = -1;
363		goto out;
364	}
365
366	ret = scf_value_set_opaque(val, hash, MHASH_SIZE);
367	assert(ret == SCF_SUCCESS);
368	ret = scf_value_set_astring(fval, fname);
369	assert(ret == SCF_SUCCESS);
370	if (apply_late == APPLY_LATE) {
371		scf_value_set_boolean(aval, 1);
372	}
373
374	tx = scf_transaction_create(hndl);
375	if (tx == NULL) {
376		if (errstr != NULL)
377			*errstr = gettext("Could not create transaction");
378		result = -1;
379		goto out;
380	}
381
382	do {
383		if (scf_pg_update(pg) == -1) {
384			if (errstr != NULL)
385				*errstr = gettext("Could not update hash "
386				    "entry");
387			result = -1;
388			goto out;
389		}
390		if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
391			if (scf_error() != SCF_ERROR_PERMISSION_DENIED) {
392				if (errstr != NULL)
393					*errstr = gettext("Could not start "
394					    "hash transaction.\n");
395				result = -1;
396				goto out;
397			}
398
399			if (errstr != NULL)
400				*errstr = gettext("Could not store file hash: "
401				    "permission denied.\n");
402			result = -1;
403
404			scf_transaction_destroy(tx);
405			(void) scf_entry_destroy(e);
406			goto out;
407		}
408
409		if (scf_transaction_property_new(tx, e, MHASH_PROP,
410		    SCF_TYPE_OPAQUE) != SCF_SUCCESS &&
411		    scf_transaction_property_change_type(tx, e, MHASH_PROP,
412		    SCF_TYPE_OPAQUE) != SCF_SUCCESS) {
413			if (errstr != NULL)
414				*errstr = gettext("Could not modify hash "
415				    "entry");
416			result = -1;
417			goto out;
418		}
419
420		ret = scf_entry_add_value(e, val);
421		assert(ret == SCF_SUCCESS);
422
423		if (scf_transaction_property_new(tx, fe, MHASH_FILE_PROP,
424		    SCF_TYPE_ASTRING) != SCF_SUCCESS &&
425		    scf_transaction_property_change_type(tx, fe,
426		    MHASH_FILE_PROP, SCF_TYPE_ASTRING) != SCF_SUCCESS) {
427			if (errstr != NULL)
428				*errstr = gettext("Could not modify file "
429				    "entry");
430			result = -1;
431			goto out;
432		}
433
434		ret = scf_entry_add_value(fe, fval);
435		assert(ret == SCF_SUCCESS);
436
437		switch (apply_late) {
438		case APPLY_NONE:
439			if (scf_transaction_property_delete(tx, ae,
440			    MHASH_APPLY_PROP) != 0) {
441				err = scf_error();
442				if ((err != SCF_ERROR_DELETED) &&
443				    (err != SCF_ERROR_NOT_FOUND)) {
444					if (errstr != NULL) {
445						*errstr = gettext("Could not "
446						    "delete apply_late "
447						    "property");
448					}
449					result = -1;
450					goto out;
451				}
452			}
453			break;
454		case APPLY_LATE:
455			if ((scf_transaction_property_new(tx, ae,
456			    MHASH_APPLY_PROP,
457			    SCF_TYPE_BOOLEAN) != SCF_SUCCESS) &&
458			    (scf_transaction_property_change_type(tx, ae,
459			    MHASH_APPLY_PROP, SCF_TYPE_BOOLEAN) !=
460			    SCF_SUCCESS)) {
461				if (errstr != NULL) {
462					*errstr = gettext("Could not modify "
463					    "apply_late property");
464				}
465				result = -1;
466				goto out;
467			}
468
469			ret = scf_entry_add_value(ae, aval);
470			assert(ret == SCF_SUCCESS);
471			break;
472		default:
473			abort();
474		};
475
476		ret = scf_transaction_commit(tx);
477
478		if (ret == 0)
479			scf_transaction_reset(tx);
480	} while (ret == 0);
481
482	if (ret < 0) {
483		if (scf_error() != SCF_ERROR_PERMISSION_DENIED) {
484			if (errstr != NULL)
485				*errstr = gettext("Could not store file hash: "
486				    "permission denied.\n");
487			result = -1;
488			goto out;
489		}
490
491		if (errstr != NULL)
492			*errstr = gettext("Could not commit transaction");
493		result = -1;
494	}
495
496	scf_transaction_destroy(tx);
497	(void) scf_entry_destroy(e);
498	(void) scf_entry_destroy(fe);
499	(void) scf_entry_destroy(ae);
500
501out:
502	(void) scf_value_destroy(val);
503	(void) scf_value_destroy(fval);
504	(void) scf_value_destroy(aval);
505	scf_property_destroy(prop);
506	scf_pg_destroy(pg);
507	scf_service_destroy(svc);
508	scf_scope_destroy(scope);
509
510	return (result);
511}
512
513/*
514 * Generate the md5 hash of a file; manifest files are smallish
515 * so we can read them in one gulp.
516 */
517static int
518md5_hash_file(const char *file, off64_t sz, uchar_t *hash)
519{
520	char *buf;
521	int fd;
522	ssize_t res;
523	int ret;
524
525	fd = open(file, O_RDONLY);
526	if (fd < 0)
527		return (-1);
528
529	buf = malloc(sz);
530	if (buf == NULL) {
531		(void) close(fd);
532		return (-1);
533	}
534
535	res = read(fd, buf, (size_t)sz);
536
537	(void) close(fd);
538
539	if (res == sz) {
540		ret = 0;
541		md5_calc(hash, (uchar_t *)buf, (unsigned int) sz);
542	} else {
543		ret = -1;
544	}
545
546	free(buf);
547	return (ret);
548}
549
550/*
551 * int mhash_test_file(scf_handle_t *, const char *, uint_t, char **, uchar_t *)
552 *   Test the given filename against the hashed metadata in the repository.
553 *   The behaviours for import and apply are slightly different.  For imports,
554 *   if the hash value is absent or different, then the import operation
555 *   continues.  For profile application, the operation continues only if the
556 *   hash value for the file is absent.
557 *
558 *   We keep two hashes: one which can be quickly test: the metadata hash,
559 *   and one which is more expensive to test: the file contents hash.
560 *
561 *   If either hash matches, the file does not need to be re-read.
562 *   If only one of the hashes matches, a side effect of this function
563 *   is to store the newly computed hash.
564 *   If neither hash matches, the hash computed for the new file is returned
565 *   and not stored.
566 *
567 *   Return values:
568 *	MHASH_NEWFILE	- the file no longer matches the hash or no hash existed
569 *			  ONLY in this case we return the new file's hash.
570 *	MHASH_FAILURE	- an internal error occurred, or the file was not found.
571 *	MHASH_RECONCILED- based on the metadata/file hash, the file does
572 *			  not need to be re-read; if necessary,
573 *			  the hash was upgraded or reconciled.
574 *
575 * NOTE: no hash is returned UNLESS MHASH_NEWFILE is returned.
576 */
577int
578mhash_test_file(scf_handle_t *hndl, const char *file, uint_t is_profile,
579    char **pnamep, uchar_t *hashbuf)
580{
581	apply_action_t action;
582	boolean_t do_hash;
583	struct stat64 st;
584	char *cp;
585	char *data;
586	uchar_t stored_hash[MHASH_SIZE];
587	uchar_t hash[MHASH_SIZE];
588	char *pname;
589	int ret;
590	int hashash;
591	int metahashok = 0;
592
593	if (pnamep)
594		*pnamep = NULL;
595
596	/*
597	 * In the case where we are doing automated imports, we reduce the UID,
598	 * the GID, the size, and the mtime into a string (to eliminate
599	 * endianness) which we then make opaque as a single MD5 digest.
600	 *
601	 * The previous hash was composed of the inode number, the UID, the file
602	 * size, and the mtime.  This formulation was found to be insufficiently
603	 * portable for use in highly replicated deployments.  The current
604	 * algorithm will allow matches of this "v1" hash, but always returns
605	 * the effective "v2" hash, such that updates result in the more
606	 * portable hash being used.
607	 *
608	 * An unwanted side effect of a hash based solely on the file
609	 * meta data is the fact that we pay no attention to the contents
610	 * which may remain the same despite meta data changes.  This happens
611	 * with (live) upgrades.  We extend the V2 hash with an additional
612	 * digest of the file contents and the code retrieving the hash
613	 * from the repository zero fills the remainder so we can detect
614	 * it is missing.
615	 *
616	 * If the the V2 digest matches, we check for the presence of
617	 * the contents digest and compute and store it if missing.
618	 *
619	 * If the V2 digest doesn't match but we also have a non-zero
620	 * file hash, we match the file content digest.  If it matches,
621	 * we compute and store the new complete hash so that later
622	 * checks will find the meta data digest correct.
623	 *
624	 * If the above matches fail and the V1 hash doesn't match either,
625	 * we consider the test to have failed, implying that some aspect
626	 * of the manifest has changed.
627	 */
628
629	cp = getenv("SVCCFG_CHECKHASH");
630	do_hash = (cp != NULL && *cp != '\0');
631	if (!do_hash) {
632		return (MHASH_NEWFILE);
633	}
634
635	pname = mhash_filename_to_propname(file, B_FALSE);
636	if (pname == NULL)
637		return (MHASH_FAILURE);
638
639	hashash = mhash_retrieve_entry(hndl, pname, stored_hash, &action) == 0;
640	if (is_profile == 0) {
641		/* Actions other than APPLY_NONE are restricted to profiles. */
642		assert(action == APPLY_NONE);
643	}
644
645	/*
646	 * As a general rule, we do not reread a profile.  The exception to
647	 * this rule is when we are running as part of the manifest import
648	 * service and the apply_late property is set to true.
649	 */
650	if (hashash && is_profile) {
651		cp = getenv("SMF_FMRI");
652		if ((cp == NULL) ||
653		    (strcmp(cp, SCF_INSTANCE_MI) != 0) ||
654		    (action != APPLY_LATE)) {
655			uu_free(pname);
656			return (MHASH_RECONCILED);
657		}
658	}
659
660	/*
661	 * No hash and not interested in one, then don't bother computing it.
662	 * We also skip returning the property name in that case.
663	 */
664	if (!hashash && hashbuf == NULL) {
665		uu_free(pname);
666		return (MHASH_NEWFILE);
667	}
668
669	do {
670		ret = stat64(file, &st);
671	} while (ret < 0 && errno == EINTR);
672	if (ret < 0) {
673		uu_free(pname);
674		return (MHASH_FAILURE);
675	}
676
677	data = uu_msprintf(MHASH_FORMAT_V2, st.st_uid, st.st_gid,
678	    st.st_size, st.st_mtime);
679	if (data == NULL) {
680		uu_free(pname);
681		return (MHASH_FAILURE);
682	}
683
684	(void) memset(hash, 0, MHASH_SIZE);
685	md5_calc(hash, (uchar_t *)data, strlen(data));
686
687	uu_free(data);
688
689	/*
690	 * Verify the meta data hash.
691	 */
692	if (hashash && memcmp(hash, stored_hash, MD5_DIGEST_LENGTH) == 0) {
693		int i;
694
695		metahashok = 1;
696		/*
697		 * The metadata hash matches; now we see if there was a
698		 * content hash; if not, we will continue on and compute and
699		 * store the updated hash.
700		 * If there was no content hash, mhash_retrieve_entry()
701		 * will have zero filled it.
702		 */
703		for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
704			if (stored_hash[MD5_DIGEST_LENGTH+i] != 0) {
705				if (action == APPLY_LATE) {
706					if (pnamep != NULL)
707						*pnamep = pname;
708					ret = MHASH_NEWFILE;
709				} else {
710					uu_free(pname);
711					ret = MHASH_RECONCILED;
712				}
713				return (ret);
714			}
715		}
716	}
717
718	/*
719	 * Compute the file hash as we can no longer avoid having to know it.
720	 * Note: from this point on "hash" contains the full, current, hash.
721	 */
722	if (md5_hash_file(file, st.st_size, &hash[MHASH_SIZE_OLD]) != 0) {
723		uu_free(pname);
724		return (MHASH_FAILURE);
725	}
726	if (hashash) {
727		uchar_t hash_v1[MHASH_SIZE_OLD];
728
729		if (metahashok ||
730		    memcmp(&hash[MHASH_SIZE_OLD], &stored_hash[MHASH_SIZE_OLD],
731		    MD5_DIGEST_LENGTH) == 0) {
732
733			/*
734			 * Reconcile entry: we get here when either the
735			 * meta data hash matches or the content hash matches;
736			 * we then update the database with the complete
737			 * new hash so we can be a bit quicker next time.
738			 */
739			(void) mhash_store_entry(hndl, pname, file, hash,
740			    APPLY_NONE, NULL);
741			if (action == APPLY_LATE) {
742				if (pnamep != NULL)
743					*pnamep = pname;
744				ret = MHASH_NEWFILE;
745			} else {
746				uu_free(pname);
747				ret = MHASH_RECONCILED;
748			}
749			return (ret);
750		}
751
752		/*
753		 * No match on V2 hash or file content; compare V1 hash.
754		 */
755		data = uu_msprintf(MHASH_FORMAT_V1, st.st_ino, st.st_uid,
756		    st.st_size, st.st_mtime);
757		if (data == NULL) {
758			uu_free(pname);
759			return (MHASH_FAILURE);
760		}
761
762		md5_calc(hash_v1, (uchar_t *)data, strlen(data));
763
764		uu_free(data);
765
766		if (memcmp(hash_v1, stored_hash, MD5_DIGEST_LENGTH) == 0) {
767			/*
768			 * Update the new entry so we don't have to go through
769			 * all this trouble next time.
770			 */
771			(void) mhash_store_entry(hndl, pname, file, hash,
772			    APPLY_NONE, NULL);
773			uu_free(pname);
774			return (MHASH_RECONCILED);
775		}
776	}
777
778	if (pnamep != NULL)
779		*pnamep = pname;
780	else
781		uu_free(pname);
782
783	if (hashbuf != NULL)
784		(void) memcpy(hashbuf, hash, MHASH_SIZE);
785
786	return (MHASH_NEWFILE);
787}
788