Attachment 'pefs.diff'

Download

   1 Index: pefs_kmod/sbin/pefs/pefs_checksum.c
   2 ===================================================================
   3 --- pefs_kmod/sbin/pefs/pefs_checksum.c	(revision 0)
   4 +++ pefs_kmod/sbin/pefs/pefs_checksum.c	(revision 240588)
   5 @@ -0,0 +1,2348 @@
   6 +/*-
   7 + * Copyright (c) 2012 Efstratios Karatzas
   8 + * All rights reserved.
   9 + *
  10 + * Redistribution and use in source and binary forms, with or without
  11 + * modification, are permitted provided that the following conditions
  12 + * are met:
  13 + * 1. Redistributions of source code must retain the above copyright
  14 + *    notice, this list of conditions and the following disclaimer.
  15 + * 2. Redistributions in binary form must reproduce the above copyright
  16 + *    notice, this list of conditions and the following disclaimer in the
  17 + *    documentation and/or other materials provided with the distribution.
  18 + *
  19 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  20 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  23 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  25 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  28 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29 + * SUCH DAMAGE.
  30 + *
  31 + */
  32 +
  33 +#include <sys/cdefs.h>
  34 +__FBSDID("$FreeBSD$");
  35 +
  36 +#include <sys/dirent.h>
  37 +#include <sys/endian.h>
  38 +#include <sys/ioctl.h>
  39 +#include <sys/mount.h>
  40 +#include <sys/queue.h>
  41 +#include <sys/tree.h>
  42 +#include <sys/types.h>
  43 +#include <sys/stat.h>
  44 +#include <sys/fnv_hash.h>
  45 +
  46 +#include <ctype.h>
  47 +#include <dirent.h>
  48 +#include <inttypes.h>
  49 +#include <libgen.h>
  50 +#include <math.h>
  51 +#include <stdio.h>
  52 +#include <stdlib.h>
  53 +#include <string.h>
  54 +#include <errno.h>
  55 +#include <unistd.h>
  56 +#include <fcntl.h>
  57 +#include <err.h>
  58 +
  59 +#include <fs/pefs/pefs.h>
  60 +
  61 +#include <openssl/evp.h>
  62 +#include <openssl/pem.h>
  63 +
  64 +#include "pefs_ctl.h"
  65 +
  66 +//#define PEFS_INTEGRITY_DEBUG
  67 +#if defined (PEFS_INTEGRITY_DEBUG)
  68 +#define dprintf(a)		printf a
  69 +#else
  70 +#define dprintf(a)      (void)0
  71 +#endif
  72 +
  73 +#define PEFS_EXTEND 1
  74 +#define PEFS_NOEXTEND 2
  75 +#define PEFS_REALLOC 3
  76 +
  77 +#define PEFS_SIGNATURE_MAX_LENGTH 512
  78 +#define PEFS_CHECKSUM_FILE_VERSION 0xDD
  79 +#define PEFS_HASH_BYTE_ALIGNMENT 512
  80 +#define PEFS_EXTRA_TABLE_SIZE 15
  81 +
  82 +#define	PEFS_PLEN 1024
  83 +#define	PEFS_SEED_LEN 20
  84 +
  85 +#define PEFS_BUFISZE 512
  86 +
  87 +/* tail that contains a single file's checksums */
  88 +TAILQ_HEAD(checksum_head, checksum);
  89 +/* tail that contains all file headers that require integrity checking */
  90 +TAILQ_HEAD(file_header_head, file_header);
  91 +/* tail for all the file_headers that refer to the same inode */
  92 +TAILQ_HEAD(hardlink_fh_head, file_header);
  93 +
  94 +/* RB tree for hardlink counters */
  95 +RB_HEAD(hardlink_head, hardlink_counter);
  96 +RB_PROTOTYPE(hardlink_head, hardlink_counter, hardlink_entries, pefs_rb_cmp);
  97 +
  98 +/* on disk size of .pefs.checksum's unique file header */
  99 +#define PEFS_CFH_SIZE 16
 100 +/* on disk size of a single file header (also a bucket in cuckoo hashing) */
 101 +#define PEFS_FH_SIZE 16
 102 +
 103 +/*
 104 + * This struct is used to check if all hardlinks for a given inode are
 105 + * supplied by the user.
 106 + */
 107 +struct hardlink_counter {
 108 +	/* inode number for the file in question */
 109 +	ino_t inode;
 110 +	/* total hardlinks of the file */
 111 +	uint32_t total_links;
 112 +	/* how many links are found in user supplied list */
 113 +	uint32_t links_found;
 114 +	/* file headers of the links we have found */
 115 +	struct hardlink_fh_head file_headers;
 116 +	/* entry in hardlink RB tree */
 117 +	RB_ENTRY(hardlink_counter) hardlink_entries;
 118 +};
 119 +
 120 +/*
 121 + * This is the unique file header of the .pefs.checksum file, found in
 122 + * the beginning of the file.
 123 + */
 124 +struct checksum_file_header {
 125 +	uint8_t version;
 126 +	uint8_t reserved;
 127 +	uint8_t hash_len;
 128 +	uint8_t hash_algo[8];
 129 +	uint8_t offset_to_hash_table;
 130 +	uint32_t hash_table_size;
 131 +};
 132 +
 133 +struct checksum {
 134 +	unsigned char *hash;
 135 +	TAILQ_ENTRY(checksum) checksum_entries;
 136 +};
 137 +
 138 +union file_id {
 139 +	uint64_t fid_num;
 140 +	uint8_t	fid_str[8];
 141 +};
 142 +
 143 +/* XXXgpf: [TODO] turns offsets to uint64_t? */
 144 +struct file_header {
 145 +	/*
 146 +	 * on disk information
 147 +	 */
 148 +
 149 +	/* the number of hashes for the file */
 150 +	uint32_t nhashes;
 151 +	/* in file offset to start of checksums */
 152 +	uint32_t offset_to_checksums;
 153 +	/* id is MAC tweak from filename (first 64 bits) */
 154 +	union file_id fid;
 155 +
 156 +	/*
 157 +	 * in memory information
 158 +	 */
 159 +
 160 +	/* fullpath for this file */
 161 +	char path[MAXPATHLEN + 1];
 162 +	/* fullpath for this file's parent dir */
 163 +	char dirpath[MAXPATHLEN + 1];
 164 +	/* filename */
 165 +	char filename[MAXNAMLEN + 1];
 166 +	/*fullpath to this symlink's immediate next target */
 167 +	char *target_path;
 168 +	/* file descriptors for the file and its parent dir */
 169 +	int fd, pfd;
 170 +	/* mark that this entry was found during "verify" action */
 171 +	int found;
 172 +	/* this file's checksums */
 173 +	struct checksum_head checksums;
 174 +	/* entry in global file header tail */
 175 +	TAILQ_ENTRY(file_header) file_header_entries;
 176 +	/* entry in hardlink counter */
 177 +	TAILQ_ENTRY(file_header) fh_hardlink_entries;
 178 +};
 179 +
 180 +struct bucket {
 181 +	struct file_header * fhp;
 182 +};
 183 +
 184 +/*
 185 + * This cuckoo hashing implementation requires 2 tables, each
 186 + * with his own hash function: pefs_hash1() & pefs_hash2()
 187 + */
 188 +struct cuckoo_hash_table {
 189 +	struct bucket *buckets1;	/* table1 */
 190 +	struct bucket *buckets2;	/* table2 */
 191 +	uint32_t size; /* how many buckets in each table */
 192 +	uint32_t nelements;	/* total number of elements <= size */
 193 +};
 194 +
 195 +static int
 196 +pefs_is_prime(uint32_t num)
 197 +{
 198 +	uint32_t i, sq;
 199 +
 200 +	if ((num % 2 == 0) || (num % 3 == 0))
 201 +		return 0;
 202 +
 203 +	sq = sqrt(num);
 204 +	/* All other primes are in the form of 6k+-1 */
 205 +	for (i = 5; i <= sq; i+=6)  {
 206 +		if ((num % i == 0) || (num % (i+2) == 0))
 207 +			return 0;
 208 +	}
 209 +
 210 +	return 1;
 211 +}
 212 +
 213 +static int
 214 +pefs_next_prime(uint32_t num)
 215 +{
 216 +	uint32_t i;
 217 +
 218 +	if (num == 2 || num == 3)
 219 +		return num;
 220 +
 221 +	if (num % 2 == 0)
 222 +		num+=1;
 223 +
 224 +	for (i = num;; i+=2) {
 225 +		if (pefs_is_prime(i))
 226 +			return i;
 227 +	}
 228 +
 229 +	return 0;
 230 +}
 231 +
 232 +static struct file_header *
 233 +pefs_allocate_file_header(void)
 234 +{
 235 +	struct file_header *fhp;
 236 +
 237 +	fhp = malloc(sizeof(struct file_header));
 238 +	if (fhp == NULL) {
 239 +		pefs_warn("memory allocation error");
 240 +		return (NULL);
 241 +	}
 242 +
 243 +	fhp->nhashes = 0;
 244 +	fhp->offset_to_checksums = 0;
 245 +	fhp->fid.fid_num = 0;
 246 +	fhp->fd = -1;
 247 +	fhp->pfd = -1;
 248 +	fhp->found = 0;
 249 +
 250 +	fhp->target_path = NULL;
 251 +
 252 +	fhp->path[0] = '\0';
 253 +	fhp->dirpath[0] = '\0';
 254 +	fhp->filename[0] = '\0';
 255 +
 256 +	TAILQ_INIT(&(fhp->checksums));
 257 +
 258 +	return (fhp);
 259 +}
 260 +
 261 +static void
 262 +pefs_close_files(struct file_header *fhp)
 263 +{
 264 +	if (fhp->fd >= 0) {
 265 +		close(fhp->fd);
 266 +		fhp->fd = -1;
 267 +	}
 268 +	if (fhp->pfd >= 0) {
 269 +		close(fhp->pfd);
 270 +		fhp->pfd = -1;
 271 +	}
 272 +}
 273 +
 274 +static void
 275 +pefs_free_file_header(struct file_header *fhp)
 276 +{
 277 +	struct checksum *csp, *tcsp;
 278 +	if (fhp != NULL) {
 279 +		pefs_close_files(fhp);
 280 +		TAILQ_FOREACH_SAFE(csp, &(fhp->checksums), checksum_entries, tcsp) {
 281 +			TAILQ_REMOVE(&(fhp->checksums), csp, checksum_entries);
 282 +			if (csp->hash != NULL)
 283 +				free(csp->hash);
 284 +			free(csp);
 285 +		}
 286 +		if (fhp->target_path != NULL)
 287 +			free(fhp->target_path);
 288 +		free(fhp);
 289 +	}
 290 +}
 291 +
 292 +static int
 293 +pefs_compute_symlink_checksum(struct file_header *fhp, const EVP_MD *md,
 294 +	uint8_t hash_len, int flags)
 295 +{
 296 +	struct pefs_xslink_ctext xsl;
 297 +	EVP_MD_CTX mdctx;
 298 +	char *buf;
 299 +	int error, i, md_len;
 300 +	size_t buf_len;
 301 +	struct checksum *csp;
 302 +
 303 +	if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
 304 +		buf = fhp->target_path;
 305 +		buf_len = strnlen(fhp->target_path, MAXPATHLEN + 1);
 306 +	}
 307 +	else {
 308 +		/* feed parent directory to ioctl() */
 309 +		strlcpy(xsl.pxsl_filename, fhp->filename, sizeof(xsl.pxsl_filename));
 310 +		xsl.pxsl_namelen = strnlen(xsl.pxsl_filename,
 311 +			sizeof(xsl.pxsl_filename));
 312 +
 313 +		error = ioctl(fhp->pfd, PEFS_GETSLINKCTEXT, &xsl);
 314 +		if (error != 0) {
 315 +			pefs_warn("error retrieving symlink's ciphertext of %s", fhp->path);
 316 +			return (PEFS_ERR_IO);
 317 +		}
 318 +
 319 +		dprintf(("read %d bytes from kernel\n\n", xsl.pxsl_target_len));
 320 +		dprintf(("printing contents of buf:"));
 321 +		for (i=0; i < (int)xsl.pxsl_target_len; i++)
 322 +			dprintf(("%c", xsl.pxsl_target[i]));
 323 +		dprintf(("!\n"));
 324 +		buf = xsl.pxsl_target;
 325 +		buf_len = xsl.pxsl_target_len;
 326 +	}
 327 +	EVP_MD_CTX_init(&mdctx);
 328 +	EVP_DigestInit_ex(&mdctx, md, NULL);
 329 +	EVP_DigestUpdate(&mdctx, buf, buf_len);
 330 +
 331 +	csp = malloc(sizeof(struct checksum));
 332 +	if (csp == NULL) {
 333 +		pefs_warn("memory allocation error");
 334 +		return (PEFS_ERR_SYS);
 335 +	}
 336 +	csp->hash = malloc(hash_len);
 337 +	if (csp->hash == NULL) {
 338 +		pefs_warn("memory allocation error");
 339 +		free(csp);
 340 +		return (PEFS_ERR_SYS);
 341 +	}
 342 +
 343 +	EVP_DigestFinal_ex(&mdctx, csp->hash, &md_len);
 344 +
 345 +	dprintf(("Digest is: "));
 346 +	for (i = 0; i < md_len; i++) dprintf(("%02x", csp->hash[i]));
 347 +	dprintf(("\n"));
 348 +
 349 +	EVP_MD_CTX_cleanup(&mdctx);
 350 +	TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
 351 +	fhp->nhashes++;
 352 +
 353 +	return (0);
 354 +}
 355 +
 356 +static int
 357 +pefs_compute_file_checksums(struct file_header *fhp, const EVP_MD *md,
 358 +	uint8_t hash_len, int flags)
 359 +{
 360 +	struct pefs_xsector_ctext xsct;
 361 +	char sector_buf[PEFS_SECTOR_SIZE];
 362 +	EVP_MD_CTX mdctx;
 363 +	struct stat sb;
 364 +	off_t offset, resid;
 365 +	FILE * fp;
 366 +	char *buf;
 367 +	uint32_t bytes_to_read;
 368 +	size_t buf_len;
 369 +	int error, i, md_len;
 370 +	struct checksum *csp;
 371 +
 372 +	if (fhp->target_path != NULL)
 373 +		return (pefs_compute_symlink_checksum(fhp, md, hash_len, flags));
 374 +
 375 +	/* XXXgpf: what happens if file size > 2^64? */
 376 +	if (fstat(fhp->fd, &sb) != 0) {
 377 +		warn("cannot stat file %s", fhp->path);
 378 +		return (PEFS_ERR_SYS);
 379 +	}
 380 +
 381 +	resid = sb.st_size;
 382 +	if (resid == 0) {
 383 +		pefs_warn("empty files are not allowed: %s", fhp->path);
 384 +		return (PEFS_ERR_GENERIC);
 385 +	}
 386 +
 387 +	fp = NULL;
 388 +	if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
 389 +		fp = fdopen(fhp->fd, "r");
 390 +		if (fp == NULL) {
 391 +			pefs_warn("could not open file for reading: %s", fhp->path);
 392 +			return (PEFS_ERR_IO);
 393 +		}
 394 +	}
 395 +
 396 +	offset = 0;
 397 +	xsct.pxsct_offset = 0;
 398 +	while (resid > 0) {
 399 +		if (resid > PEFS_SECTOR_SIZE)
 400 +			bytes_to_read = PEFS_SECTOR_SIZE;
 401 +		else
 402 +			bytes_to_read = resid;
 403 +
 404 +		if ((flags & PEFS_UNMOUNTED) != 0 || (flags & PEFS_NOKEY) != 0) {
 405 +			fread(sector_buf, bytes_to_read, sizeof(char), fp);
 406 +			if (ferror(fp)) {
 407 +				pefs_warn("error reading from file: %s", fhp->path);
 408 +				return (PEFS_ERR_IO);
 409 +			}
 410 +			buf = sector_buf;
 411 +			buf_len = bytes_to_read;
 412 +			resid-=bytes_to_read;
 413 +		}
 414 +		else {
 415 +			resid-=bytes_to_read;
 416 +			xsct.pxsct_ctext_len = bytes_to_read;
 417 +			error = ioctl(fhp->fd, PEFS_GETSECTORCTEXT, &xsct);
 418 +			if (error != 0) {
 419 +				pefs_warn("error retrieving ciphertext of %s", fhp->path);
 420 +				return (PEFS_ERR_IO);
 421 +			}
 422 +			xsct.pxsct_offset+= xsct.pxsct_ctext_len;
 423 +			buf = xsct.pxsct_ctext;
 424 +			buf_len = xsct.pxsct_ctext_len;
 425 +		}
 426 +
 427 +		EVP_MD_CTX_init(&mdctx);
 428 +		EVP_DigestInit_ex(&mdctx, md, NULL);
 429 +		EVP_DigestUpdate(&mdctx, buf, buf_len);
 430 +
 431 +		dprintf(("read %d bytes\n\n", buf_len));
 432 +		dprintf(("printing contents of buffer:"));
 433 +		for (i=0; i < (int)buf_len; i++) dprintf(("%c", buf[i]));
 434 +			dprintf(("!\n"));
 435 +
 436 +		csp = malloc(sizeof(struct checksum));
 437 +		if (csp == NULL) {
 438 +			pefs_warn("memory allocation error");
 439 +			EVP_MD_CTX_cleanup(&mdctx);
 440 +			return (PEFS_ERR_SYS);
 441 +		}
 442 +		csp->hash = malloc(hash_len);
 443 +		if (csp->hash == NULL) {
 444 +			pefs_warn("memory allocation error");
 445 +			free(csp);
 446 +			EVP_MD_CTX_cleanup(&mdctx);
 447 +			return (PEFS_ERR_SYS);
 448 +		}
 449 +
 450 +		EVP_DigestFinal_ex(&mdctx, csp->hash, &md_len);
 451 +
 452 +		dprintf(("Digest is: "));
 453 +		for (i = 0; i < md_len; i++) dprintf(("%02x", csp->hash[i]));
 454 +		dprintf(("\n"));
 455 +
 456 +		EVP_MD_CTX_cleanup(&mdctx);
 457 +
 458 +		TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
 459 +		fhp->nhashes++;
 460 +	}
 461 +
 462 +	/*
 463 +	 * XXXgpf: [TODO] better move the fp into file_header struct and deal with
 464 +	 * closing it during pefs_file_close.
 465 +	 */
 466 +	if (fp != NULL) {
 467 +		fclose(fp);
 468 +		fhp->fd = -1;
 469 +	}
 470 +
 471 +	return (0);
 472 +}
 473 +
 474 +static void
 475 +pefs_init_hash_table(struct cuckoo_hash_table *chtp)
 476 +{
 477 +	chtp->size = 0;
 478 +	chtp->nelements = 0;
 479 +	chtp->buckets1 = NULL;
 480 +	chtp->buckets2 = NULL;
 481 +}
 482 +
 483 +static int
 484 +pefs_allocate_hash_table(struct cuckoo_hash_table *chtp, uint32_t nelements,
 485 +	int alloc_flag)
 486 +{
 487 +	uint32_t i;
 488 +
 489 +	if (alloc_flag == PEFS_EXTEND) {
 490 +		/*
 491 +		 * spending 15% more space for each table lowers the chance to fall into
 492 +		 * an infinite loop during cuckoo insertion to about 1.5%.
 493 +		 */
 494 +		chtp->size = pefs_next_prime(nelements +
 495 +				((nelements * PEFS_EXTRA_TABLE_SIZE)/100));
 496 +		chtp->nelements = nelements;
 497 +		if (chtp->size < chtp->nelements) {
 498 +			pefs_warn("numeric overflow while computing hash table size");
 499 +			return (PEFS_ERR_GENERIC);
 500 +		}
 501 +	}
 502 +	else if (alloc_flag == PEFS_NOEXTEND) {
 503 +		chtp->size = nelements;
 504 +		chtp->nelements = nelements;
 505 +	}
 506 +	/* re-alloc the hash tables in case of infinite loop during cuckoo insert */
 507 +	else if (alloc_flag == PEFS_REALLOC) {
 508 +		chtp->size = pefs_next_prime(chtp->size + 1);
 509 +			if (chtp->size < chtp->nelements) {
 510 +			pefs_warn("numeric overflow while computing new hash table size");
 511 +			return (PEFS_ERR_GENERIC);
 512 +		}
 513 +		free(chtp->buckets1);
 514 +		free(chtp->buckets2);
 515 +	}
 516 +
 517 +	dprintf(("hash table elem:%u\tsize: %u\n", chtp->nelements, chtp->size));
 518 +
 519 +	chtp->buckets1 = malloc (chtp->size * sizeof(struct bucket));
 520 +	if (chtp->buckets1 == NULL) {
 521 +		pefs_warn("memory allocation error");
 522 +		return (PEFS_ERR_SYS);
 523 +	}
 524 +
 525 +	for (i = 0; i < chtp->size; i++)
 526 +		chtp->buckets1[i].fhp = NULL;
 527 +
 528 +	chtp->buckets2 = malloc (chtp->size * sizeof(struct bucket));
 529 +	if (chtp->buckets2 == NULL) {
 530 +		pefs_warn("memory allocation error");
 531 +		return (PEFS_ERR_SYS);
 532 +	}
 533 +
 534 +	for (i = 0; i < chtp->size; i++)
 535 +		chtp->buckets2[i].fhp = NULL;
 536 +
 537 +	return (0);
 538 +}
 539 +
 540 +static void
 541 +pefs_free_hash_table(struct cuckoo_hash_table *chtp)
 542 +{
 543 +	struct bucket *bp;
 544 +	uint32_t i;
 545 +
 546 +	if (chtp->buckets1 != NULL) {
 547 +		for (i = 0; i < chtp->size; i++) {
 548 +			bp = &chtp->buckets1[i];
 549 +			pefs_free_file_header(bp->fhp);
 550 +		}
 551 +		free(chtp->buckets1);
 552 +	}
 553 +
 554 +	if (chtp->buckets2 != NULL) {
 555 +		for (i = 0; i < chtp->size; i++) {
 556 +			bp = &chtp->buckets2[i];
 557 +			pefs_free_file_header(bp->fhp);
 558 +		}
 559 +		free(chtp->buckets2);
 560 +	}
 561 +}
 562 +
 563 +static void
 564 +pefs_free_file_header_tail(struct file_header_head *fh_headp)
 565 +{
 566 +	struct file_header *fhp, *tfhp;
 567 +
 568 +	TAILQ_FOREACH_SAFE(fhp, fh_headp, file_header_entries, tfhp) {
 569 +		TAILQ_REMOVE(fh_headp, fhp, file_header_entries);
 570 +		pefs_free_file_header(fhp);
 571 +	}
 572 +}
 573 +
 574 +static uint32_t
 575 +pefs_hash1(struct cuckoo_hash_table *chtp, struct file_header *fhp)
 576 +{
 577 +	uint32_t nbucket;
 578 +
 579 +	nbucket = fhp->fid.fid_num % chtp->size;
 580 +	dprintf(("hash1: goto bucket %d\n", nbucket));
 581 +	return (nbucket);
 582 +}
 583 +
 584 +static uint32_t
 585 +pefs_hash2(struct cuckoo_hash_table *chtp, struct file_header *fhp)
 586 +{
 587 +	uint32_t nbucket;
 588 +
 589 +	nbucket = fnv_64_buf(&(fhp->fid.fid_num), sizeof(fhp->fid.fid_num),
 590 +		FNV1_64_INIT) %	chtp->size;
 591 +	dprintf(("hash2: goto bucket %d\n", nbucket));
 592 +
 593 +	return (nbucket);
 594 +}
 595 +
 596 +static struct file_header *
 597 +pefs_cuckoo_lookup(struct cuckoo_hash_table *chtp, struct file_header *fhp)
 598 +{
 599 +	struct file_header *elem, *elem1, *elem2;
 600 +	int pos1, pos2;
 601 +
 602 +	elem = fhp;
 603 +	pos1 = pefs_hash1(chtp, elem);
 604 +	elem1 = chtp->buckets1[pos1].fhp;
 605 +	if (elem1 != NULL) {
 606 +		if (elem1->fid.fid_num == elem->fid.fid_num) {
 607 +			return (elem1);
 608 +		}
 609 +	}
 610 +
 611 +	pos2 = pefs_hash2(chtp, elem);
 612 +	elem2 = chtp->buckets2[pos2].fhp;
 613 +	if (elem2 != NULL) {
 614 +		if (elem2->fid.fid_num == elem->fid.fid_num) {
 615 +			return (elem2);
 616 +		}
 617 +	}
 618 +
 619 +	return (NULL);
 620 +}
 621 +
 622 +static int
 623 +pefs_cuckoo_insert(struct cuckoo_hash_table *chtp,
 624 +	struct file_header *fhp)
 625 +{
 626 +	struct file_header *elem, *elem1, *elem2, *res;
 627 +	uint32_t i, max_tries, pos1, pos2;
 628 +
 629 +	max_tries = chtp->size;
 630 +	elem = fhp;
 631 +
 632 +	/* file_id collision check */
 633 +	res = pefs_cuckoo_lookup(chtp, elem);
 634 +	if (res != NULL) {
 635 +		pefs_warn("file identifier collision detected between files: %s & %s",
 636 +			res->path, elem->path);
 637 +		return (PEFS_ERR_EXIST);
 638 +	}
 639 +
 640 +	for (i = 0; i < max_tries; i++) {
 641 +		pos1 = pefs_hash1(chtp, elem);
 642 +		elem1 = chtp->buckets1[pos1].fhp;
 643 +		/* do the cuckoo! */
 644 +		chtp->buckets1[pos1].fhp = elem;
 645 +		if (elem1 == NULL)
 646 +			return 0;
 647 +		pos2 = pefs_hash2(chtp, elem1);
 648 +		elem2 = chtp->buckets2[pos2].fhp;
 649 +		/* do the cuckoo! */
 650 +		chtp->buckets2[pos2].fhp = elem1;
 651 +		if (elem2 == NULL)
 652 +			return 0;
 653 +		else
 654 +			elem = elem2;
 655 +	}
 656 +
 657 +	/* XXXgpf: should be left as a warning during development phase */
 658 +	pefs_warn("cuckoo_insert resulted in infinite loop!");
 659 +	return (PEFS_ERR_GENERIC);
 660 +}
 661 +
 662 +static int
 663 +pefs_add_to_hash_table(struct cuckoo_hash_table *chtp,
 664 +	struct file_header *fhp)
 665 +{
 666 +	return (pefs_cuckoo_insert(chtp, fhp));
 667 +}
 668 +
 669 +/* XXXgpf: for debugging purposes */
 670 +static void
 671 +pefs_print_hash_tables(struct cuckoo_hash_table *chtp, uint8_t hash_len)
 672 +{
 673 +	struct file_header *fhp;
 674 +	struct checksum *csp;
 675 +	uint32_t i,j;
 676 +
 677 +	dprintf(("\n+++Printing Hash Table 1+++\n\n"));
 678 +	for (i = 0; i < chtp->size; i++) {
 679 +		fhp = chtp->buckets1[i].fhp;
 680 +		dprintf(("\nbucket %d with element: %d\n", i, fhp == NULL ? 0 : 1));
 681 +		if (fhp != NULL) {
 682 +			dprintf(("\tid = %llu\tnhashes = %d\n", fhp->fid.fid_num,
 683 +				fhp->nhashes));
 684 +			if (fhp->path[0] == '/')
 685 +				dprintf(("\tpath = %s\n", fhp->path));
 686 +			TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
 687 +				dprintf(("\t\tdigest="));
 688 +				for (j = 0; j < hash_len; j++)
 689 +					dprintf(("%02x", csp->hash[j]));
 690 +				dprintf(("\n"));
 691 +			}
 692 +		}
 693 +	}
 694 +
 695 +	dprintf(("\n+++Printing Hash Table 2+++\n\n"));
 696 +	for (i = 0; i < chtp->size; i++) {
 697 +		fhp = chtp->buckets2[i].fhp;
 698 +		dprintf(("\nbucket %d with element: %d\n", i, fhp == NULL ? 0 : 1));
 699 +		if (fhp != NULL) {
 700 +			dprintf(("\tid = %llu\tnhashes = %d\n", fhp->fid.fid_num,
 701 +				fhp->nhashes));
 702 +			if (fhp->path[0] == '/')
 703 +				dprintf(("\tpath = %s\n", fhp->path));
 704 +			TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
 705 +				dprintf(("\t\tdigest="));
 706 +				for (j = 0; j < hash_len; j++)
 707 +					dprintf(("%02x", csp->hash[j]));
 708 +				dprintf(("\n"));
 709 +			}
 710 +		}
 711 +	}
 712 +}
 713 +
 714 +/*
 715 + * pefs encrypted filename = XBase64(checksum || E(tweak || filename))
 716 + * We use filename mac (checksum) as file_id. This way, should a filesystem
 717 + * be dump/restored, there will be no need to recreate .pefs.checksum because
 718 + * filenames remain the same.
 719 + *
 720 + * file id used is checksum = VMAC(E(tweak || filename))
 721 + */
 722 +static int
 723 +pefs_get_file_id(struct file_header *fhp, int flags)
 724 +{
 725 +	struct pefs_xnamecsum xncs;
 726 +	uint64_t temp;
 727 +	char *enc, *buf;
 728 +	int error, r;
 729 +	size_t buf_len, enc_len;
 730 +
 731 +	if ((flags & PEFS_NOKEY) != 0 || (flags & PEFS_UNMOUNTED) != 0) {
 732 +		/* in this case, we already have the encrypted filename */
 733 +		enc = fhp->filename;
 734 +		enc_len = strnlen(fhp->filename, sizeof(fhp->filename));
 735 +		enc++;
 736 +		enc_len--;
 737 +		buf_len = MAXNAMLEN + 1;
 738 +		buf = malloc(buf_len);
 739 +		if (buf == NULL) {
 740 +			pefs_warn("memory allocation error");
 741 +			return (PEFS_ERR_SYS);
 742 +		}
 743 +
 744 +		r = pefs_name_pton(enc, enc_len, buf, buf_len);
 745 +		if (r <= 0) {
 746 +			pefs_warn("failed to extract file id from encrypted filename: %s",
 747 +				fhp->filename);
 748 +			error = PEFS_ERR_GENERIC;
 749 +		}
 750 +		else {
 751 +			memcpy(fhp->fid.fid_str, buf, sizeof(temp));
 752 +			error = 0;
 753 +		}
 754 +
 755 +		free(buf);
 756 +		return (error);
 757 +	}
 758 +
 759 +	strlcpy(xncs.pxnc_filename, fhp->filename, sizeof(xncs.pxnc_filename));
 760 +	xncs.pxnc_namelen = strnlen(xncs.pxnc_filename, sizeof(xncs.pxnc_filename));
 761 +
 762 +	/* feed parent directory to ioctl() */
 763 +	error = ioctl(fhp->pfd, PEFS_GETNAMECSUM, &xncs);
 764 +
 765 +	if (error == 0)
 766 +		memcpy(fhp->fid.fid_str, xncs.pxnc_csum, sizeof(xncs.pxnc_csum));
 767 +	else
 768 +		pefs_warn("failed to fetch file id from kernel for filename: %s",
 769 +			fhp->filename);
 770 +
 771 +	return (error);
 772 +}
 773 +
 774 +static void
 775 +pefs_symlink_warn(struct cuckoo_hash_table *chtp, struct file_header_head *fhhp)
 776 +{
 777 +	struct stat sb;
 778 +	struct file_header targetfh;
 779 +	char dirbuf[MAXPATHLEN + 1], namebuf[MAXNAMLEN + 1];
 780 +	struct file_header *fhp, *res;
 781 +	char *dirnamep, *namep;
 782 +	int error;
 783 +
 784 +	TAILQ_FOREACH(fhp, fhhp, file_header_entries) {
 785 +		/*
 786 +		 * If fhp == symlink and target file == regular file || symlink,
 787 +		 * then grab target's filename MAC and look it up in our hash table.
 788 +		 * Print a warning message if it is not found.
 789 +		 * symlink target referes to the file that is immediately pointed to by
 790 +		 * our symlink. therefore in a syml1->syml2->file example, we only check
 791 +		 * syml2 if we are supplied syml1. This is by choise so that user will
 792 +		 * receive warning for intermediate parts of a symlink chain.
 793 +		 *
 794 +		 * XXXgpf: perhaps relax restrictions on warning messages
 795 +		 */
 796 +		if (fhp->target_path != NULL) {
 797 +			if (lstat(fhp->target_path, &sb) != 0) {
 798 +				warn("cannot stat file %s", fhp->target_path);
 799 +				continue;
 800 +			}
 801 +
 802 +			if (S_ISLNK(sb.st_mode) == 0 && S_ISREG(sb.st_mode) == 0)
 803 +				continue;
 804 +
 805 +			/* retrieve dirpath & filaname */
 806 +			strlcpy(targetfh.path, fhp->target_path, sizeof(targetfh.path));
 807 +			strlcpy(dirbuf, targetfh.path, sizeof(dirbuf));
 808 +			strlcpy(namebuf, targetfh.path, sizeof(namebuf));
 809 +
 810 +			dirnamep = dirname(dirbuf);
 811 +			if (dirnamep == NULL) {
 812 +				pefs_warn("failed to extract dirname of %s", targetfh.path);
 813 +				continue;
 814 +			}
 815 +			strlcpy(targetfh.dirpath, dirnamep, sizeof(targetfh.dirpath));
 816 +
 817 +			namep = basename(namebuf);
 818 +			if (namep == NULL) {
 819 +				pefs_warn("failed to extract filename of %s", targetfh.path);
 820 +				continue;
 821 +			}
 822 +			strlcpy(targetfh.filename, namep, sizeof(targetfh.filename));
 823 +
 824 +			targetfh.pfd = -1;
 825 +			targetfh.pfd = open(targetfh.dirpath, O_RDONLY);
 826 +			if (targetfh.pfd < 0) {
 827 +				warn("cannot open %s", targetfh.dirpath);
 828 +				continue;
 829 +			}
 830 +
 831 +			error = pefs_get_file_id(&targetfh, 0);
 832 +			if (error == 0) {
 833 +				res = pefs_cuckoo_lookup(chtp, &targetfh);
 834 +				if (res == NULL)
 835 +					pefs_warn("target file %s of symlink %s was not "
 836 +						"found in inputlist", targetfh.path, fhp->path);
 837 +			}
 838 +			pefs_close_files(&targetfh);
 839 +		}
 840 +	}
 841 +}
 842 +
 843 +/* XXXgpf: for debugging purposes */
 844 +static void
 845 +pefs_hardlink_print(struct hardlink_head *hlc_headp)
 846 +{
 847 +	struct hardlink_counter *hlcp;
 848 +	struct file_header *fhp;
 849 +
 850 +	dprintf(("\n+++Printing RB tree+++\n\n"));
 851 +	RB_FOREACH(hlcp, hardlink_head, hlc_headp) {
 852 +		dprintf(("inode %d\ttotal links %d\tlinks found %d\n",
 853 +			hlcp->inode, hlcp->total_links, hlcp->links_found));
 854 +		TAILQ_FOREACH(fhp, &(hlcp->file_headers), fh_hardlink_entries) {
 855 +			dprintf(("\tpath: %s\n", fhp->path));
 856 +		}
 857 +	}
 858 +}
 859 +
 860 +static void
 861 +pefs_hardlink_warn(struct hardlink_head *hlc_headp)
 862 +{
 863 +	struct hardlink_counter *hlcp;
 864 +	struct file_header *fhp;
 865 +	int i;
 866 +
 867 +	RB_FOREACH(hlcp, hardlink_head, hlc_headp) {
 868 +		if (hlcp->total_links > hlcp->links_found) {
 869 +			pefs_warn("%d hard link(s) of total %d were found in input list "
 870 +				"for file with inode: %d",
 871 +				hlcp->links_found, hlcp->total_links, hlcp->inode);
 872 +			i = 1;
 873 +			TAILQ_FOREACH(fhp, &(hlcp->file_headers), fh_hardlink_entries) {
 874 +				pefs_warn("link %d: %s", i++, fhp->path);
 875 +			}
 876 +		}
 877 +	}
 878 +}
 879 +
 880 +static int
 881 +pefs_hardlink_insert(struct hardlink_head *hlc_headp, struct file_header *fhp,
 882 +		struct stat *sbp)
 883 +{
 884 +	struct hardlink_counter find, *res, *new_hlcp;
 885 +
 886 +	find.inode = sbp->st_ino;
 887 +	res = RB_FIND(hardlink_head, hlc_headp, &find);
 888 +
 889 +	if (res != NULL) {
 890 +		res->links_found++;
 891 +		TAILQ_INSERT_TAIL(&(res->file_headers), fhp, fh_hardlink_entries);
 892 +	}
 893 +	else {
 894 +		new_hlcp = malloc(sizeof(struct hardlink_counter));
 895 +		if (new_hlcp == NULL) {
 896 +			warn("memory allocation error");
 897 +			return (PEFS_ERR_SYS);
 898 +		}
 899 +
 900 +		new_hlcp->inode = sbp->st_ino;
 901 +		new_hlcp->total_links = sbp->st_nlink;
 902 +		new_hlcp->links_found = 1;
 903 +		TAILQ_INIT(&(new_hlcp->file_headers));
 904 +		TAILQ_INSERT_TAIL(&(new_hlcp->file_headers), fhp, fh_hardlink_entries);
 905 +
 906 +		RB_INSERT(hardlink_head, hlc_headp, new_hlcp);
 907 +	}
 908 +
 909 +	return (0);
 910 +}
 911 +
 912 +static int
 913 +pefs_hardlink_cmp(struct hardlink_counter *hlcp1, struct hardlink_counter *hlcp2)
 914 +{
 915 +	if (hlcp1->inode < hlcp2->inode)
 916 +		return -1;
 917 +	else if (hlcp1->inode > hlcp2->inode)
 918 +		return 1;
 919 +	else
 920 +		return 0;
 921 +}
 922 +
 923 +static void
 924 +pefs_hardlink_free(struct hardlink_head *hlc_headp)
 925 +{
 926 +	struct hardlink_counter *cur, *next;
 927 +
 928 +	for (cur = RB_MIN(hardlink_head, hlc_headp); cur != NULL; cur = next) {
 929 +		next = RB_NEXT(hardlink_head, hlc_headp, cur);
 930 +		RB_REMOVE(hardlink_head, hlc_headp, cur);
 931 +		free(cur);
 932 +	}
 933 +}
 934 +
 935 +/* open a file and perform various semantic checks on it */
 936 +static int
 937 +pefs_open_semantic_checks(struct file_header *fhp, struct statfs *fsp,
 938 +	struct hardlink_head *hlc_headp, int flags)
 939 +{
 940 +	char dirbuf[MAXPATHLEN + 1], namebuf[MAXNAMLEN + 1];
 941 +	char sbuf[MAXPATHLEN + 1];
 942 +	struct stat sb;
 943 +	struct statfs this_fs;
 944 +	char *dirnamep, *namep;
 945 +	size_t target_path_size;
 946 +	int error, nchars;
 947 +
 948 +	/* retrieve dirpath & filename */
 949 +	strlcpy(dirbuf, fhp->path, sizeof(dirbuf));
 950 +	strlcpy(namebuf, fhp->path, sizeof(namebuf));
 951 +
 952 +	dirnamep = dirname(dirbuf);
 953 +	if (dirnamep == NULL) {
 954 +		pefs_warn("failed to extract dirname of %s", fhp->path);
 955 +		return (PEFS_ERR_GENERIC);
 956 +	}
 957 +	strlcpy(fhp->dirpath, dirnamep, sizeof(fhp->dirpath));
 958 +
 959 +	namep = basename(namebuf);
 960 +		if (namep == NULL) {
 961 +		pefs_warn("failed to extract filename of %s", fhp->path);
 962 +		return (PEFS_ERR_GENERIC);
 963 +	}
 964 +	strlcpy(fhp->filename, namep, sizeof(fhp->filename));
 965 +
 966 +	dprintf(("path = %s!\ndirname = %s!\nbasename = %s!\n", fhp->path,
 967 +			fhp->dirpath, fhp->filename));
 968 +
 969 +	if (lstat(fhp->path, &sb) != 0) {
 970 +		warn("cannot stat file %s", fhp->path);
 971 +		return (PEFS_ERR_SYS);
 972 +	}
 973 +
 974 +	if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
 975 +		(flags & PEFS_SETIMMUTABLE) == 0 &&
 976 +		(flags & PEFS_VERIFY) == 0 &&
 977 +		(flags & PEFS_GETID) == 0) {
 978 +		pefs_warn("file %s does not have schg flag", fhp->path);
 979 +		return (PEFS_ERR_SYS);
 980 +	}
 981 +
 982 +	if (S_ISLNK(sb.st_mode) != 0) {
 983 +		fhp->pfd = open(fhp->dirpath, O_RDONLY);
 984 +		if (fhp->pfd < 0) {
 985 +			warn("cannot open %s", fhp->dirpath);
 986 +			return (PEFS_ERR_IO);
 987 +		}
 988 +
 989 +		if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
 990 +			(flags & PEFS_SETIMMUTABLE) != 0) {
 991 +			dprintf(("setting immutable flag %s\n", fhp->path));
 992 +			error = lchflags(fhp->path, SF_IMMUTABLE);
 993 +			if (error != 0) {
 994 +				warn("cannot set schg flag to file %s", fhp->path);
 995 +				return (PEFS_ERR_SYS);
 996 +			}
 997 +		}
 998 +
 999 +		nchars = readlink(fhp->path, sbuf, sizeof(sbuf));
1000 +		if (nchars == -1) {
1001 +			warn("readlink failed: %s", fhp->path);
1002 +			return (PEFS_ERR_SYS);
1003 +		}
1004 +
1005 +		/*
1006 +		 * Target_path can be used to tell if user has supplied target_file
1007 +		 * in input file-list, since symlinks are not traversed. User will have
1008 +		 * to provide fullpaths for both symlink & target file if he wants
1009 +		 * integrity checking for both. However, we will print warning messages
1010 +		 * in case target file is not provided in user supplied input list.
1011 +		 *
1012 +		 * Target referes to the file immediately pointed to by our symlink, not
1013 +		 * the final target of a possible symlink chain.
1014 +		 */
1015 +		target_path_size = MAXPATHLEN + 1;
1016 +		fhp->target_path = malloc(target_path_size);
1017 +		if (fhp->target_path == NULL) {
1018 +			warn("memory allocation error");
1019 +			return (PEFS_ERR_SYS);
1020 +		}
1021 +		sbuf[nchars] = '\0';
1022 +
1023 +		/* turn relative paths to absolute paths */
1024 +		if (sbuf[0] != '/' && (flags & PEFS_UNMOUNTED) == 0 &&
1025 +			(flags & PEFS_NOKEY) == 0)
1026 +			snprintf(fhp->target_path, target_path_size, "%s/%s",
1027 +				fhp->dirpath, sbuf);
1028 +		else
1029 +			strlcpy(fhp->target_path, sbuf, target_path_size);
1030 +
1031 +		/*
1032 +		 * The only semantic check that's performed on target file is an attempt
1033 +		 * to lstat() the file, in order to make sure the file exists. This is
1034 +		 * intentional since target file is allowed to reside on a different
1035 +		 * filesystem or in the same filesystem, but not be a regular file or a
1036 +		 * symlink.
1037 +		 * e.g. a directory
1038 +		 */
1039 +		if ((flags & PEFS_NOKEY) == 0 && (flags & PEFS_UNMOUNTED) == 0) {
1040 +			/*
1041 +			 * If target_path is encrypted due to no key or unmounted fs,
1042 +			 * lstat will always fail.
1043 +			 */
1044 +			if (lstat(fhp->target_path, &sb) != 0) {
1045 +				warn("cannot stat symlink's target file %s", fhp->target_path);
1046 +				return (PEFS_ERR_SYS);
1047 +			}
1048 +		}
1049 +		return (0);
1050 +	}
1051 +
1052 +	fhp->fd = open(fhp->path, O_RDONLY | O_NOFOLLOW);
1053 +	if (fhp->fd < 0) {
1054 +		warn("cannot open %s", fhp->path);
1055 +		return (PEFS_ERR_IO);
1056 +	}
1057 +
1058 +	fhp->pfd = open(fhp->dirpath, O_RDONLY);
1059 +	if (fhp->pfd < 0) {
1060 +		warn("cannot open %s", fhp->dirpath);
1061 +		return (PEFS_ERR_IO);
1062 +	}
1063 +
1064 +	if (fstat(fhp->fd, &sb) != 0) {
1065 +		warn("cannot stat file %s", fhp->path);
1066 +		return (PEFS_ERR_SYS);
1067 +	}
1068 +
1069 +	if (S_ISREG(sb.st_mode) == 0 && (flags & PEFS_GETID) == 0) {
1070 +		pefs_warn("filename: %s is not a regular file", fhp->path);
1071 +		return (PEFS_ERR_INVALID);
1072 +	}
1073 +
1074 +	if ((sb.st_flags & SF_IMMUTABLE) == 0 &&
1075 +		(flags & PEFS_SETIMMUTABLE) != 0) {
1076 +		dprintf(("setting immutable flag %s\n", fhp->path));
1077 +		error = fchflags(fhp->fd, SF_IMMUTABLE);
1078 +		if (error != 0) {
1079 +			warn("cannot set schg flag to file %s", fhp->path);
1080 +			return (PEFS_ERR_SYS);
1081 +		}
1082 +	}
1083 +
1084 +	if ((flags & PEFS_UNMOUNTED) == 0 && (fsp != NULL)) {
1085 +		if (fstatfs(fhp->fd, &this_fs) == -1) {
1086 +			pefs_warn("statfs failed: %s: %s", fhp->path, strerror(errno));
1087 +			return (PEFS_ERR_SYS);
1088 +		}
1089 +
1090 +		if ((fsp->f_fsid.val[0] != this_fs.f_fsid.val[0]) ||
1091 +			(fsp->f_fsid.val[1] != this_fs.f_fsid.val[1])) {
1092 +			pefs_warn("filename: %s does not reside in filesystem %s",
1093 +				fhp->path, fsp->f_mntonname);
1094 +			return (PEFS_ERR_INVALID);
1095 +		}
1096 +	}
1097 +
1098 +	/* Keep all hardlink file headers in a rb tree */
1099 +	if (sb.st_nlink > 1 && hlc_headp != NULL)
1100 +		return (pefs_hardlink_insert(hlc_headp, fhp, &sb));
1101 +
1102 +	return (0);
1103 +}
1104 +
1105 +static struct file_header *
1106 +pefs_next_file(FILE *fpin, int *error, int *nfiles)
1107 +{
1108 +	char buf[MAXPATHLEN + 1];
1109 +	struct file_header *fhp;
1110 +
1111 +	if (fgets(buf, sizeof(buf), fpin) == NULL) {
1112 +		if (feof(fpin))
1113 +			*error = 0;
1114 +		else {
1115 +			*error = PEFS_ERR_IO;
1116 +			warn("error reading input");
1117 +		}
1118 +		return (NULL);
1119 +	}
1120 +
1121 +	if (buf[strnlen(buf, sizeof(buf)) - 1] == '\n')
1122 +		buf[strnlen(buf, sizeof(buf)) - 1] = '\0';
1123 +	dprintf(("\nnext file entry=%s!\n", buf));
1124 +
1125 +	fhp = pefs_allocate_file_header();
1126 +	if (fhp == NULL) {
1127 +		*error = PEFS_ERR_SYS;
1128 +		return (NULL);
1129 +	}
1130 +
1131 +	strlcpy(fhp->path, buf, sizeof(fhp->path));
1132 +	(*nfiles)++;
1133 +
1134 +	return (fhp);
1135 +}
1136 +
1137 +/*
1138 + * This function creates the in memory database that will be later written to
1139 + * the checksum file.
1140 + * A) For each file entry:
1141 + * 		A1) semantic checks:
1142 + * 			A1a) file should reside in pefs filesystem & file should be
1143 + * 				 regular file or symbolic link.
1144 + * 			A1b) if symlink, acquire and save the absolute path of the symlink's
1145 + * 				 target. Try to lstat() the target but don't do anything else.
1146 + * 			A1c) If hardlink, save a reference to this file entry in our
1147 + * 				 rb tree that uses i-node numbers as keys and is used in
1148 + * 				 part 'C' to print warnings.
1149 + * 			A1d) Open and store file descriptors to file & parent_directory.
1150 + * 		A2) the file_id is retrieved. (filename MAC)
1151 + * 		A3) list of checksums is computed for the file's 4k blocks.
1152 + * 		A4) file entry is added to the universal fh_head.
1153 + * B) Print warnings for hardlinks if the number of links found in inputlist
1154 + * isn't equal to the number of total inode links.
1155 + * C) Hash tables are allocated.
1156 + * D) Cuckoo insertion:
1157 + * We try to populate our hash tables using the cuckoo algorithm. Should we fall
1158 + * into an infinite loop during insertion, we re-allocate larger hash tables
1159 + * and try again until we succeed. The possibility to fail twice in a row is
1160 + * 1.5% * 1.5% = 0.0225%
1161 + * E) For each symlink found in input list, print warnings if its target file
1162 + * was not found in input list since symlinks are not traversed.
1163 + */
1164 +static int
1165 +pefs_create_in_memory_db(FILE *fpin, const EVP_MD *md, uint8_t hash_len,
1166 +	struct cuckoo_hash_table *chtp, char *fsroot, int flags)
1167 +{
1168 +	struct statfs fs;
1169 +	struct file_header_head fh_head;
1170 +	struct hardlink_head hlc_head;
1171 +	struct file_header *fhp;
1172 +	int error;
1173 +	uint32_t nfiles;
1174 +
1175 +	nfiles = 0;
1176 +	if (statfs(fsroot, &fs) == -1) {
1177 +		pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
1178 +		return (PEFS_ERR_SYS);
1179 +	}
1180 +
1181 +	TAILQ_INIT(&fh_head);
1182 +	RB_INIT(&hlc_head);
1183 +	while((fhp = pefs_next_file(fpin, &error, &nfiles)) != NULL) {
1184 +		error = pefs_open_semantic_checks(fhp, &fs, &hlc_head, flags);
1185 +		if (error != 0) {
1186 +			pefs_free_file_header(fhp);
1187 +			return (error);
1188 +		}
1189 +
1190 +		error = pefs_get_file_id(fhp, flags);
1191 +		if (error != 0) {
1192 +			pefs_free_file_header(fhp);
1193 +			return (error);
1194 +		}
1195 +
1196 +		error = pefs_compute_file_checksums(fhp, md, hash_len, flags);
1197 +		if (error != 0) {
1198 +			pefs_free_file_header(fhp);
1199 +			return (error);
1200 +		}
1201 +
1202 +		TAILQ_INSERT_TAIL(&fh_head, fhp, file_header_entries);
1203 +		pefs_close_files(fhp);
1204 +	}
1205 +
1206 +	/* checking I/O error from pefs_next_file() */
1207 +	if (error != 0)
1208 +		return (error);
1209 +
1210 +	pefs_hardlink_print(&hlc_head);
1211 +	pefs_hardlink_warn(&hlc_head);
1212 +	pefs_hardlink_free(&hlc_head);
1213 +
1214 +	error = pefs_allocate_hash_table(chtp, nfiles, PEFS_EXTEND);
1215 +	if (error != 0)
1216 +		return (error);
1217 +
1218 +cuckoo_insert:
1219 +	TAILQ_FOREACH(fhp, &fh_head, file_header_entries) {
1220 +		error = pefs_add_to_hash_table(chtp, fhp);
1221 +		/* collision error */
1222 +		if (error == PEFS_ERR_EXIST)
1223 +			return (error);
1224 +		/*
1225 +		 * cuckoo insertion algorithm fell into an infinite loop!
1226 +		 * Create new, larger hash tables where size = next_prime(old_size)
1227 +		 * and try again.
1228 +		 */
1229 +		else if (error != 0) {
1230 +			dprintf(("fell into an infinite loop!\n"));
1231 +			error = pefs_allocate_hash_table(chtp, nfiles, PEFS_REALLOC);
1232 +			if (error != 0)
1233 +				return (error);
1234 +			goto cuckoo_insert;
1235 +		}
1236 +	}
1237 +	pefs_print_hash_tables(chtp, hash_len);
1238 +	pefs_symlink_warn(chtp, &fh_head);
1239 +
1240 +	return (error);
1241 +}
1242 +
1243 +static int
1244 +pefs_write_checksum_file_header(int fdout, struct checksum_file_header *cfhp)
1245 +{
1246 +	int rval;
1247 +	uint32_t bytes, hash_table_size;
1248 +
1249 +	rval = lseek(fdout, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1250 +	if (rval == -1) {
1251 +		warn("lseek error while writing to .pefs.checksum");
1252 +		return (PEFS_ERR_SYS);
1253 +	}
1254 +
1255 +	bytes = write(fdout, &(cfhp->version), sizeof(cfhp->version));
1256 +	if (bytes != sizeof(cfhp->version)) {
1257 +		warn("error writing to .pefs.checksum");
1258 +		return (PEFS_ERR_IO);
1259 +	}
1260 +
1261 +	bytes = write(fdout, &(cfhp->reserved), sizeof(cfhp->reserved));
1262 +	if (bytes != sizeof(cfhp->reserved)) {
1263 +		warn("error writing to .pefs.checksum");
1264 +		return (PEFS_ERR_IO);
1265 +	}
1266 +
1267 +	bytes = write(fdout, &(cfhp->hash_len), sizeof(cfhp->hash_len));
1268 +	if (bytes != sizeof(cfhp->hash_len)) {
1269 +		warn("error writing to .pefs.checksum");
1270 +		return (PEFS_ERR_IO);
1271 +	}
1272 +
1273 +	bytes = write(fdout, cfhp->hash_algo, sizeof(cfhp->hash_algo));
1274 +	if (bytes != sizeof(cfhp->hash_algo)) {
1275 +		warn("error writing to .pefs.checksum");
1276 +		return (PEFS_ERR_IO);
1277 +	}
1278 +
1279 +	bytes = write(fdout, &(cfhp->offset_to_hash_table),
1280 +			sizeof(cfhp->offset_to_hash_table));
1281 +	if (bytes != sizeof(cfhp->offset_to_hash_table)) {
1282 +		warn("error writing to .pefs.checksum");
1283 +		return (PEFS_ERR_IO);
1284 +	}
1285 +
1286 +	hash_table_size = htole32(cfhp->hash_table_size);
1287 +	bytes = write(fdout, &hash_table_size, sizeof(hash_table_size));
1288 +	if (bytes != sizeof(hash_table_size)) {
1289 +		warn("error writing to .pefs.checksum");
1290 +		return (PEFS_ERR_IO);
1291 +	}
1292 +
1293 +	return (0);
1294 +}
1295 +
1296 +static int
1297 +pefs_write_file_header(int fdout, struct file_header *fhp,
1298 +	uint32_t *buckets_offset)
1299 +{
1300 +	uint32_t nhashes, offset_to_checksums;
1301 +	int bytes;
1302 +
1303 +	nhashes = htole32(fhp->nhashes);
1304 +	bytes = pwrite(fdout, &nhashes, sizeof(nhashes), *buckets_offset);
1305 +	if (bytes != sizeof(nhashes)) {
1306 +		warn("error writing to .pefs.checksum");
1307 +		return (PEFS_ERR_IO);
1308 +	}
1309 +	(*buckets_offset)+= sizeof(nhashes);
1310 +
1311 +	offset_to_checksums = htole32(fhp->offset_to_checksums);
1312 +	bytes = pwrite(fdout, &offset_to_checksums,
1313 +			sizeof(offset_to_checksums), *buckets_offset);
1314 +	if (bytes != sizeof(offset_to_checksums)) {
1315 +		warn("error writing to .pefs.checksum");
1316 +		return (PEFS_ERR_IO);
1317 +	}
1318 +	(*buckets_offset)+= sizeof(offset_to_checksums);
1319 +
1320 +	bytes = pwrite(fdout, fhp->fid.fid_str, sizeof(fhp->fid.fid_str),
1321 +		*buckets_offset);
1322 +	if (bytes != sizeof(fhp->fid.fid_str)) {
1323 +		warn("error writing to .pefs.checksum");
1324 +		return (PEFS_ERR_IO);
1325 +	}
1326 +	(*buckets_offset)+= sizeof(fhp->fid.fid_str);
1327 +
1328 +	return (0);
1329 +}
1330 +
1331 +static int
1332 +pefs_write_bucket(int fdout, struct bucket *bp, uint32_t *buckets_offset)
1333 +{
1334 +	struct file_header emptyfh;
1335 +	struct file_header *fhp;
1336 +
1337 +	fhp = bp->fhp;
1338 +	/* Empty files aren't allowed so nhashes == 0 symbolizes an empty bucket */
1339 +	if (fhp == NULL) {
1340 +		emptyfh.nhashes = 0;
1341 +		emptyfh.fid.fid_num = 0;
1342 +		emptyfh.offset_to_checksums = 0;
1343 +		fhp = &emptyfh;
1344 +	}
1345 +
1346 +	return (pefs_write_file_header(fdout, fhp, buckets_offset));
1347 +}
1348 +
1349 +static int
1350 +pefs_write_hash(int fdout, struct checksum *csp, uint32_t *hashes_offset,
1351 +	uint8_t hash_len)
1352 +{
1353 +	int bytes;
1354 +
1355 +	bytes = pwrite(fdout, csp->hash, hash_len, *hashes_offset);
1356 +	if (bytes != hash_len) {
1357 +		warn("error writing to .pefs.checksum");
1358 +		return (PEFS_ERR_IO);
1359 +	}
1360 +	(*hashes_offset)+= hash_len;
1361 +
1362 +	return (0);
1363 +}
1364 +
1365 +/*
1366 + * All data member writes are done separately so as to avoid alignment problems.
1367 + * Writes are always in little endian byte order, except file_id which is
1368 + * always treated as big endian.
1369 + *
1370 + * First 512 bytes of .pefs.checksum are reserved for the file's digital
1371 + * signature.
1372 + *
1373 + * After that, the next 16 bytes of .pefs.checksum are filled with
1374 + * .pefs.checksum's global file header. Right after this header lies the
1375 + * 'index' part of our database.
1376 + * This index is later kept in kernel memory.
1377 + *
1378 + * Index:
1379 + * Both hash tables of cuckoo algorithm are written to the file sequentially.
1380 + * The first hash table corresponds to hash1() and the second hash table to
1381 + * hash2(). Each bucket (cell) of a hash table contains at most one
1382 + * entry(=file_header).
1383 + * The size of an entry is 16 bytes.
1384 + *
1385 + * hash table entries end at the following offset:
1386 + * 16 + hash_table_size * 2 * 16
1387 + *
1388 + * Checksums:
1389 + * The last part of .pefs.checksum is filled with the actual checksums.
1390 + * The offset where the first checksum starts is a 512 aligned address.
1391 + * Each hash table file header entry contains an offset that points to the
1392 + * beginning of a chain of checksums for that particular file's 4k blocks.
1393 + */
1394 +static int
1395 +pefs_write_checksum_file(int fdout, struct checksum_file_header *cfhp,
1396 +	struct cuckoo_hash_table *chtp)
1397 +{
1398 +	struct bucket *bp;
1399 +	struct checksum *csp;
1400 +	struct file_header *fhp;
1401 +	uint32_t i, buckets_offset, hashes_offset;
1402 +	int error;
1403 +
1404 +	error = pefs_write_checksum_file_header(fdout, cfhp);
1405 +	if (error != 0)
1406 +		return (error);
1407 +
1408 +	/* this points to where the buckets start */
1409 +	buckets_offset = PEFS_SIGNATURE_MAX_LENGTH + cfhp->offset_to_hash_table;
1410 +
1411 +	/* this points to where the buckets stop and the checksums start */
1412 +	hashes_offset = buckets_offset;
1413 +	hashes_offset+= chtp->size * PEFS_FH_SIZE * 2;
1414 +	if (hashes_offset % PEFS_HASH_BYTE_ALIGNMENT != 0)
1415 +		hashes_offset+= PEFS_HASH_BYTE_ALIGNMENT - (hashes_offset %
1416 +			PEFS_HASH_BYTE_ALIGNMENT);
1417 +
1418 +	for (i = 0; i < chtp->size; i++) {
1419 +		bp = &chtp->buckets1[i];
1420 +		if (bp->fhp != NULL)
1421 +			bp->fhp->offset_to_checksums = hashes_offset;
1422 +		error = pefs_write_bucket(fdout, bp, &buckets_offset);
1423 +		if (error != 0)
1424 +			return (error);
1425 +
1426 +		fhp = bp->fhp;
1427 +		if (fhp != NULL) {
1428 +			TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
1429 +				error = pefs_write_hash(fdout, csp, &hashes_offset,
1430 +					cfhp->hash_len);
1431 +				if (error != 0)
1432 +					return (error);
1433 +			}
1434 +		}
1435 +	}
1436 +
1437 +	for (i = 0; i < chtp->size; i++) {
1438 +		bp = &chtp->buckets2[i];
1439 +		if (bp->fhp != NULL)
1440 +			bp->fhp->offset_to_checksums = hashes_offset;
1441 +		error = pefs_write_bucket(fdout, bp, &buckets_offset);
1442 +		if (error != 0)
1443 +			return (error);
1444 +
1445 +		fhp = bp->fhp;
1446 +		if (fhp != NULL) {
1447 +			TAILQ_FOREACH(csp, &(fhp->checksums), checksum_entries) {
1448 +				error = pefs_write_hash(fdout, csp, &hashes_offset,
1449 +					cfhp->hash_len);
1450 +				if (error != 0)
1451 +					return (error);
1452 +			}
1453 +		}
1454 +	}
1455 +
1456 +	return (0);
1457 +}
1458 +
1459 +static void
1460 +pefs_init_checksum_file_header(struct checksum_file_header *cfhp,
1461 +	const char *algo, uint8_t hash_len, struct cuckoo_hash_table *chtp)
1462 +{
1463 +	cfhp->hash_len = hash_len;
1464 +	cfhp->hash_table_size = chtp->size;
1465 +	cfhp->version = PEFS_CHECKSUM_FILE_VERSION;
1466 +	strlcpy(cfhp->hash_algo, algo, sizeof(cfhp->hash_algo));
1467 +	cfhp->offset_to_hash_table = PEFS_CFH_SIZE;
1468 +}
1469 +
1470 +/* read dsa privkey from file */
1471 +static EVP_PKEY *
1472 +pefs_read_dsa_privkey(FILE *pk_fp)
1473 +{
1474 +	DSA	*dsa;
1475 +	EVP_PKEY *pkey;
1476 +	int rval;
1477 +
1478 +	dsa = PEM_read_DSAPrivateKey(pk_fp, NULL, NULL, NULL);
1479 +	if (dsa == NULL) {
1480 +		pefs_warn("error reading dsa private key");
1481 +		return (NULL);
1482 +	}
1483 +
1484 +	pkey = EVP_PKEY_new();
1485 +	if (pkey == NULL) {
1486 +		pefs_warn("error allocating a pkey");
1487 +		DSA_free(dsa);
1488 +		return (NULL);
1489 +	}
1490 +
1491 +	rval = EVP_PKEY_assign_DSA(pkey, dsa);
1492 +	if (rval != 1) {
1493 +		pefs_warn("error assigning dsa key");
1494 +		EVP_PKEY_free(pkey);
1495 +		DSA_free(dsa);
1496 +		return (NULL);
1497 +	}
1498 +
1499 +	return (pkey);
1500 +}
1501 +
1502 +/* Sign .pefs.checksum. Signature is placed at the beginning of the file. */
1503 +static int
1504 +pefs_sign_file(int fd, FILE *pkfp)
1505 +{
1506 +	unsigned char buf[PEFS_BUFISZE];
1507 +	EVP_MD_CTX ctx;
1508 +	const EVP_MD *md;
1509 +	EVP_PKEY *pkey;
1510 +	unsigned char *sign;
1511 +	unsigned int sign_len;
1512 +	int bytes, error, rval;
1513 +
1514 +	pkey = pefs_read_dsa_privkey(pkfp);
1515 +	if (pkey == NULL)
1516 +		return (PEFS_ERR_SYS);
1517 +
1518 +	md = EVP_dss1();
1519 +	if (md == NULL) {
1520 +		pefs_warn("error acquiring digest type");
1521 +		EVP_PKEY_free(pkey);
1522 +		return (PEFS_ERR_GENERIC);
1523 +	}
1524 +
1525 +	/* generate digital signature */
1526 +	EVP_SignInit(&ctx, md);
1527 +
1528 +	error = lseek(fd, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1529 +	if (error == -1) {
1530 +		warn("lseek error while signing file");
1531 +		EVP_PKEY_free(pkey);
1532 +		return (PEFS_ERR_SYS);
1533 +	}
1534 +
1535 +	while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
1536 +		rval = EVP_SignUpdate(&ctx, buf, bytes);
1537 +		if (rval != 1) {
1538 +			pefs_warn("error updating sign data");
1539 +			EVP_PKEY_free(pkey);
1540 +			return (PEFS_ERR_SYS);
1541 +		}
1542 +	}
1543 +
1544 +	if (bytes == -1) {
1545 +		warn("read error");
1546 +		EVP_PKEY_free(pkey);
1547 +		return (PEFS_ERR_IO);
1548 +	}
1549 +
1550 +	sign = malloc(EVP_PKEY_size(pkey));
1551 +	if (sign == NULL) {
1552 +		pefs_warn("memory allocation error");
1553 +		EVP_PKEY_free(pkey);
1554 +		return (PEFS_ERR_SYS);
1555 +	}
1556 +
1557 +	rval = EVP_SignFinal(&ctx, sign, &sign_len, pkey);
1558 +	if (rval != 1) {
1559 +		pefs_warn("error generating signature");
1560 +		free(sign);
1561 +		EVP_PKEY_free(pkey);
1562 +		return (PEFS_ERR_SYS);
1563 +	}
1564 +
1565 +	/* write digital signature to beginning of .pefs.checksum */
1566 +	bytes = pwrite(fd, sign, sign_len, 0);
1567 +	if (bytes == -1) {
1568 +		pefs_warn("error writing signature");
1569 +		free(sign);
1570 +		EVP_PKEY_free(pkey);
1571 +		return (PEFS_ERR_IO);
1572 +	}
1573 +
1574 +	free(sign);
1575 +	EVP_PKEY_free(pkey);
1576 +
1577 +	return (0);
1578 +}
1579 +
1580 +/* read dsa pubkey from file */
1581 +static EVP_PKEY *
1582 +pefs_read_dsa_pubkey(FILE *pk_fp)
1583 +{
1584 +	DSA	*dsa;
1585 +	EVP_PKEY *pkey;
1586 +	int rval;
1587 +
1588 +	dsa = PEM_read_DSA_PUBKEY(pk_fp, NULL, NULL, NULL);
1589 +	if (dsa == NULL) {
1590 +		pefs_warn("error reading dsa pubkey");
1591 +		return (NULL);
1592 +	}
1593 +
1594 +	pkey = EVP_PKEY_new();
1595 +	if (pkey == NULL) {
1596 +		pefs_warn("error allocating a pkey");
1597 +		DSA_free(dsa);
1598 +		return (NULL);
1599 +	}
1600 +
1601 +	rval = EVP_PKEY_assign_DSA(pkey, dsa);
1602 +	if (rval != 1) {
1603 +		pefs_warn("error assigning dsa key");
1604 +		EVP_PKEY_free(pkey);
1605 +		DSA_free(dsa);
1606 +		return (NULL);
1607 +	}
1608 +
1609 +	return (pkey);
1610 +}
1611 +
1612 +/* verify digital signature of .pefs.checksum */
1613 +static int
1614 +pefs_verify_signature(int fd, FILE *pk_fp)
1615 +{
1616 +	unsigned char buf[PEFS_BUFISZE];
1617 +	EVP_MD_CTX ctx;
1618 +	const EVP_MD *md;
1619 +	EVP_PKEY *pkey;
1620 +	unsigned char *sign;
1621 +	int bytes, error, rval, sign_len;
1622 +
1623 +	pkey = pefs_read_dsa_pubkey(pk_fp);
1624 +	if (pkey == NULL)
1625 +		return (PEFS_ERR_SYS);
1626 +
1627 +	sign = malloc(EVP_PKEY_size(pkey));
1628 +	if (sign == NULL) {
1629 +		pefs_warn("memory allocation error");
1630 +		EVP_PKEY_free(pkey);
1631 +		return (PEFS_ERR_SYS);
1632 +	}
1633 +
1634 +	/* read signature from .pefs.checksum */
1635 +	sign_len = pread(fd, sign, EVP_PKEY_size(pkey), 0);
1636 +	if (sign_len == -1) {
1637 +		warn("error reading signature");
1638 +		free(sign);
1639 +		return (PEFS_ERR_IO);
1640 +	}
1641 +
1642 +	md = EVP_dss1();
1643 +	if (md == NULL) {
1644 +		pefs_warn("error acquiring digest type");
1645 +		free(sign);
1646 +		EVP_PKEY_free(pkey);
1647 +		return (PEFS_ERR_GENERIC);
1648 +	}
1649 +
1650 +	/* process .pefs.checksum & verify the signature */
1651 +	EVP_VerifyInit(&ctx, md);
1652 +
1653 +	error = lseek(fd, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1654 +	if (error == -1) {
1655 +		warn("lseek error while verifying file");
1656 +		free(sign);
1657 +		EVP_PKEY_free(pkey);
1658 +		return (PEFS_ERR_SYS);
1659 +	}
1660 +
1661 +	while ((bytes = read(fd, buf, sizeof(buf))) > 0) {
1662 +		rval = EVP_VerifyUpdate(&ctx, buf, bytes);
1663 +		if (rval != 1) {
1664 +			pefs_warn("error updating sign data");
1665 +			free(sign);
1666 +			EVP_PKEY_free(pkey);
1667 +			return (PEFS_ERR_SYS);
1668 +		}
1669 +	}
1670 +
1671 +	if (bytes == -1) {
1672 +		warn("read error");
1673 +		free(sign);
1674 +		EVP_PKEY_free(pkey);
1675 +		return (PEFS_ERR_IO);
1676 +	}
1677 +
1678 +	rval = EVP_VerifyFinal(&ctx, sign, sign_len, pkey);
1679 +	if (rval == 0) {
1680 +		pefs_warn("file signature is not verified by public key");
1681 +		free(sign);
1682 +		EVP_PKEY_free(pkey);
1683 +		return (PEFS_ERR_GENERIC);
1684 +	}
1685 +	else if (rval == -1) {
1686 +		pefs_warn("error verifying signature");
1687 +		free(sign);
1688 +		EVP_PKEY_free(pkey);
1689 +		return (PEFS_ERR_GENERIC);
1690 +	}
1691 +
1692 +	free(sign);
1693 +	EVP_PKEY_free(pkey);
1694 +
1695 +	return (0);
1696 +}
1697 +
1698 +/*
1699 + * If .pefs.checksum is created inside pefs mounted fs, then it will obtain an
1700 + * encrypted filename & encrypted data, which is unacceptable. User should
1701 + * create checksum file outside of filesystem and then copy it by hand.
1702 + */
1703 +static int
1704 +pefs_open_checksum_file(int *fdp, char *fsroot, char *csm_path)
1705 +{
1706 +	struct statfs pefs_fs, checksum_fs;
1707 +	int fd;
1708 +
1709 +	*fdp = -1;
1710 +
1711 +	/* create checksum file */
1712 +	fd = open(csm_path, O_RDWR | O_CREAT | O_EXCL,  S_IRUSR | S_IWUSR);
1713 +	if (fd == -1) {
1714 +		warn("cannot open %s", csm_path);
1715 +		return (PEFS_ERR_IO);
1716 +	}
1717 +
1718 +	*fdp = fd;
1719 +
1720 +	if (statfs(fsroot, &pefs_fs) == -1) {
1721 +		pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
1722 +		return (PEFS_ERR_SYS);
1723 +	}
1724 +
1725 +	if (statfs(csm_path, &checksum_fs) == -1) {
1726 +		pefs_warn("statfs failed: %s: %s", csm_path, strerror(errno));
1727 +		return (PEFS_ERR_SYS);
1728 +	}
1729 +
1730 +	if ((pefs_fs.f_fsid.val[0] == checksum_fs.f_fsid.val[0]) &&
1731 +		(pefs_fs.f_fsid.val[1] == checksum_fs.f_fsid.val[1])) {
1732 +		pefs_warn("%s must be created outside of pefs mounted filesystem %s",
1733 +			csm_path, pefs_fs.f_mntonname);
1734 +		return (PEFS_ERR_INVALID);
1735 +	}
1736 +
1737 +	return (0);
1738 +}
1739 +
1740 +/*
1741 + * An in memory database is created from entries in fpin. This database is
1742 + * later written to file ".pefs.checksum" which is created under csm_path.
1743 + * algo is used as a cryptographic hash function that produces checksums
1744 + * for 4k blocks of each file. When we are done with .pefs.checksum, we
1745 + * sign it and place the signature at the beginning of .pefs.checksum.
1746 + */
1747 +int
1748 +pefs_create_checksum_file(FILE *fpin, char *fsroot, char *csm_path,
1749 +	FILE *pk_fp, const char *algo, int flags)
1750 +{
1751 +	struct cuckoo_hash_table checksum_hash_table;
1752 +	struct checksum_file_header cfh;
1753 +	const EVP_MD *md;
1754 +	int error, fdout;
1755 +	uint8_t hash_len;
1756 +
1757 +	OpenSSL_add_all_digests();
1758 +	md = EVP_get_digestbyname(algo);
1759 +
1760 +	if(md == NULL) {
1761 +		pefs_warn("Unknown message digest %s\n", algo);
1762 +		return (PEFS_ERR_INVALID);
1763 +	}
1764 +	hash_len = EVP_MD_size(md);
1765 +
1766 +	pefs_init_hash_table(&checksum_hash_table);
1767 +
1768 +	error = pefs_open_checksum_file(&fdout, fsroot, csm_path);
1769 +	if (error != 0)
1770 +		goto out;
1771 +
1772 +	error = pefs_create_in_memory_db(fpin, md, hash_len,
1773 +		&checksum_hash_table, fsroot, flags);
1774 +	if (error != 0)
1775 +		goto out;
1776 +
1777 +	pefs_init_checksum_file_header(&cfh, algo, hash_len, &checksum_hash_table);
1778 +
1779 +	error = pefs_write_checksum_file(fdout, &cfh, &checksum_hash_table);
1780 +	if (error != 0)
1781 +		goto out;
1782 +
1783 +	error = pefs_sign_file(fdout, pk_fp);
1784 +
1785 +out:
1786 +	if (fdout >= 0) {
1787 +		close(fdout);
1788 +		if (error != 0)
1789 +			unlink(csm_path);
1790 +	}
1791 +	pefs_free_hash_table(&checksum_hash_table);
1792 +
1793 +	return (error);
1794 +}
1795 +
1796 +static int
1797 +pefs_read_checksum_file_header(int fdin, struct checksum_file_header *cfhp)
1798 +{
1799 +	int rval;
1800 +	uint32_t bytes, hash_table_size;
1801 +
1802 +	rval = lseek(fdin, PEFS_SIGNATURE_MAX_LENGTH, SEEK_SET);
1803 +	if (rval == -1) {
1804 +		warn("lseek error while reading checksum file header");
1805 +		return (PEFS_ERR_SYS);
1806 +	}
1807 +
1808 +	bytes = read(fdin, &(cfhp->version), sizeof(cfhp->version));
1809 +	if (bytes != sizeof(cfhp->version)) {
1810 +		warn("error reading from .pefs.checksum");
1811 +		return (PEFS_ERR_IO);
1812 +	}
1813 +
1814 +	bytes = read(fdin, &(cfhp->reserved), sizeof(cfhp->reserved));
1815 +	if (bytes != sizeof(cfhp->reserved)) {
1816 +		warn("error reading from .pefs.checksum");
1817 +		return (PEFS_ERR_IO);
1818 +	}
1819 +
1820 +	bytes = read(fdin, &(cfhp->hash_len), sizeof(cfhp->hash_len));
1821 +	if (bytes != sizeof(cfhp->hash_len)) {
1822 +		warn("error reading from .pefs.checksum");
1823 +		return (PEFS_ERR_IO);
1824 +	}
1825 +
1826 +	bytes = read(fdin, cfhp->hash_algo, sizeof(cfhp->hash_algo));
1827 +	if (bytes != sizeof(cfhp->hash_algo)) {
1828 +		warn("error reading from .pefs.checksum");
1829 +		return (PEFS_ERR_IO);
1830 +	}
1831 +
1832 +	bytes = read(fdin, &(cfhp->offset_to_hash_table),
1833 +			sizeof(cfhp->offset_to_hash_table));
1834 +	if (bytes != sizeof(cfhp->offset_to_hash_table)) {
1835 +		warn("error reading from .pefs.checksum");
1836 +		return (PEFS_ERR_IO);
1837 +	}
1838 +
1839 +	bytes = read(fdin, &hash_table_size, sizeof(hash_table_size));
1840 +	if (bytes != sizeof(hash_table_size)) {
1841 +		warn("error reading from .pefs.checksum");
1842 +		return (PEFS_ERR_IO);
1843 +	}
1844 +	cfhp->hash_table_size = le32toh(hash_table_size);
1845 +
1846 +	dprintf(("+++printing checksum file header info+++\n"));
1847 +	dprintf(("version %X\nhash_len %d\nhash_algo %s\n",
1848 +		cfhp->version, cfhp->hash_len, cfhp->hash_algo));
1849 +	dprintf(("offset to hash table %d\nhash table size %d\n",
1850 +		cfhp->offset_to_hash_table, cfhp->hash_table_size));
1851 +
1852 +	return (0);
1853 +}
1854 +
1855 +static int
1856 +pefs_read_file_header(int fdin, struct file_header *fhp,
1857 +	uint32_t *buckets_offset)
1858 +{
1859 +	uint32_t nhashes, offset_to_checksums;
1860 +	int bytes;
1861 +
1862 +	bytes = pread(fdin, &nhashes, sizeof(nhashes), *buckets_offset);
1863 +	if (bytes != sizeof(nhashes)) {
1864 +		warn("error reading from .pefs.checksum");
1865 +		return (PEFS_ERR_IO);
1866 +	}
1867 +	fhp->nhashes = le32toh(nhashes);
1868 +	(*buckets_offset)+= sizeof(nhashes);
1869 +
1870 +	bytes = pread(fdin, &offset_to_checksums, sizeof(offset_to_checksums),
1871 +		*buckets_offset);
1872 +	if (bytes != sizeof(offset_to_checksums)) {
1873 +		warn("error reading from .pefs.checksum");
1874 +		return (PEFS_ERR_IO);
1875 +	}
1876 +	fhp->offset_to_checksums = le32toh(offset_to_checksums);
1877 +	(*buckets_offset)+= sizeof(offset_to_checksums);
1878 +
1879 +	bytes = pread(fdin, fhp->fid.fid_str, sizeof(fhp->fid.fid_str),
1880 +		*buckets_offset);
1881 +	if (bytes != sizeof(fhp->fid.fid_str)) {
1882 +		warn("error reading from .pefs.checksum");
1883 +		return (PEFS_ERR_IO);
1884 +	}
1885 +	(*buckets_offset)+= sizeof(fhp->fid.fid_str);
1886 +
1887 +	dprintf(("\nfile header offset = %d\n", *fh_offset));
1888 +	dprintf(("\n++priting file header info++\n"));
1889 +	dprintf(("nhashes %d\noffset_to_checksums %u\n",
1890 +			fhp->nhashes, fhp->offset_to_checksums));
1891 +	dprintf(("file id %llu\n", fhp->file_id));
1892 +
1893 +	return (0);
1894 +}
1895 +
1896 +static int
1897 +pefs_read_bucket(int fdin, struct bucket *bp, uint32_t *buckets_offset)
1898 +{
1899 +	struct file_header *fhp;
1900 +	int error;
1901 +
1902 +	dprintf(("bucket offset = %d\n", *buckets_offset));
1903 +	fhp = pefs_allocate_file_header();
1904 +	if (fhp == NULL)
1905 +		return (PEFS_ERR_SYS);
1906 +
1907 +	error = pefs_read_file_header(fdin, fhp, buckets_offset);
1908 +	if (error != 0)
1909 +		return (error);
1910 +
1911 +	if (fhp->nhashes == 0) {
1912 +		pefs_free_file_header(fhp);
1913 +		fhp = NULL;
1914 +	}
1915 +	bp->fhp = fhp;
1916 +
1917 +	dprintf(("\n++priting bucket info++\n"));
1918 +
1919 +	return (0);
1920 +}
1921 +
1922 +static int
1923 +pefs_read_hash(int fdin, struct checksum *csp, uint32_t *hashes_offset,
1924 +	uint8_t hash_len)
1925 +{
1926 +	int bytes;
1927 +
1928 +	bytes = pread(fdin, csp->hash, hash_len, *hashes_offset);
1929 +	if (bytes != hash_len) {
1930 +		warn("error reading from .pefs.checksum");
1931 +		return (PEFS_ERR_IO);
1932 +	}
1933 +	(*hashes_offset)+= hash_len;
1934 +
1935 +	dprintf(("hashes offset = %d\n", *hashes_offset));
1936 +	dprintf(("hash %s\n", csp->hash));
1937 +
1938 +	return (0);
1939 +}
1940 +
1941 +static void
1942 +pefs_add_to_file_header(struct file_header *fhp, struct checksum *csp)
1943 +{
1944 +	TAILQ_INSERT_TAIL(&(fhp->checksums), csp, checksum_entries);
1945 +}
1946 +
1947 +/* Create in memory checksum database by reading .pefs.checksum file. */
1948 +static int
1949 +pefs_read_checksum_file(int fdin, struct checksum_file_header *cfhp,
1950 +	struct cuckoo_hash_table *chtp)
1951 +{
1952 +	struct bucket *bp;
1953 +	struct checksum *csp;
1954 +	struct file_header *fhp;
1955 +	uint32_t i, k, buckets_offset, hashes_offset;
1956 +	int error;
1957 +
1958 +	/* this points to where the buckets start */
1959 +	buckets_offset = PEFS_SIGNATURE_MAX_LENGTH + cfhp->offset_to_hash_table;
1960 +
1961 +	for (i = 0; i < chtp->size; i++) {
1962 +		bp = &chtp->buckets1[i];
1963 +		error = pefs_read_bucket(fdin, bp, &buckets_offset);
1964 +		if (error != 0)
1965 +			return (error);
1966 +
1967 +		fhp = bp->fhp;
1968 +		if (fhp != NULL) {
1969 +			hashes_offset = fhp->offset_to_checksums;
1970 +
1971 +			for (k = 0; k < fhp->nhashes; k++) {
1972 +				csp = malloc(sizeof(struct checksum));
1973 +				if (csp == NULL) {
1974 +					pefs_warn("memory allocation error");
1975 +					return (PEFS_ERR_SYS);
1976 +				}
1977 +				csp->hash = malloc(cfhp->hash_len);
1978 +				if (csp->hash == NULL) {
1979 +					pefs_warn("memory allocation error");
1980 +					return (PEFS_ERR_SYS);
1981 +				}
1982 +
1983 +				error = pefs_read_hash(fdin, csp, &hashes_offset,
1984 +					cfhp->hash_len);
1985 +				if (error != 0)
1986 +					return (error);
1987 +
1988 +				pefs_add_to_file_header(fhp, csp);
1989 +			}
1990 +		}
1991 +	}
1992 +
1993 +	for (i = 0; i < chtp->size; i++) {
1994 +		bp = &chtp->buckets2[i];
1995 +		error = pefs_read_bucket(fdin, bp, &buckets_offset);
1996 +		if (error != 0)
1997 +			return (error);
1998 +
1999 +		fhp = bp->fhp;
2000 +		if (fhp != NULL) {
2001 +			hashes_offset = fhp->offset_to_checksums;
2002 +
2003 +			for (k = 0; k < fhp->nhashes; k++) {
2004 +				csp = malloc(sizeof(struct checksum));
2005 +				if (csp == NULL) {
2006 +					pefs_warn("memory allocation error");
2007 +					return (PEFS_ERR_SYS);
2008 +				}
2009 +				csp->hash = malloc(cfhp->hash_len);
2010 +				if (csp->hash == NULL) {
2011 +					pefs_warn("memory allocation error");
2012 +					return (PEFS_ERR_SYS);
2013 +				}
2014 +
2015 +				error = pefs_read_hash(fdin, csp, &hashes_offset,
2016 +					cfhp->hash_len);
2017 +				if (error != 0)
2018 +					return (error);
2019 +
2020 +				pefs_add_to_file_header(fhp, csp);
2021 +			}
2022 +		}
2023 +	}
2024 +
2025 +	return (0);
2026 +}
2027 +
2028 +static int
2029 +pefs_compare_checksums(struct file_header *fhp, struct file_header *indexfhp,
2030 +	uint8_t hash_len)
2031 +{
2032 +	struct checksum *csp1, *csp2;
2033 +	uint32_t i;
2034 +	int error, cmp;
2035 +
2036 +	dprintf(("comparing hashes for file with fid: %llu\n",
2037 +		fhp->fid.fid_num));
2038 +
2039 +	error = 0;
2040 +	if (fhp->nhashes != indexfhp->nhashes) {
2041 +		pefs_warn("number of hashes differ between on disk file and %s values "
2042 +			"for file %s: %u vs %u",
2043 +			PEFS_FILE_CHECKSUM, fhp->path, fhp->nhashes, indexfhp->nhashes);
2044 +		error = PEFS_ERR_CHECKSUM;
2045 +	}
2046 +
2047 +	csp1 = TAILQ_FIRST(&fhp->checksums);
2048 +	csp2 = TAILQ_FIRST(&indexfhp->checksums);
2049 +	i = 1;
2050 +	while (csp1 != NULL && csp2 != NULL) {
2051 +		cmp = memcmp(csp1->hash, csp2->hash, hash_len);
2052 +		if (cmp != 0) {
2053 +			pefs_warn("checksum no: %u differs between on disk file and %s "
2054 +				"values for file %s",
2055 +				i, PEFS_FILE_CHECKSUM, fhp->path);
2056 +			error = PEFS_ERR_CHECKSUM;
2057 +		}
2058 +		csp1 = TAILQ_NEXT(csp1, checksum_entries);
2059 +		csp2 = TAILQ_NEXT(csp2, checksum_entries);
2060 +		i++;
2061 +	}
2062 +
2063 +	return (error);
2064 +}
2065 +
2066 +/*
2067 + * Traverse the entire filesystem and for every regular file or symbolic link,
2068 + * look it up in .pefs.checksum index and verify its checksums.
2069 + *
2070 + * This function will try to avoid returning due to errors encountered when
2071 + * checksums mismatch or immutable flags are missing so as to print as many
2072 + * warnings as possible.
2073 + */
2074 +static int
2075 +pefs_traverse_fs(struct cuckoo_hash_table *chtp, const EVP_MD *md,
2076 +	uint8_t hash_len, DIR *dirp, char *path, struct statfs *fsp,
2077 +	struct hardlink_head *hlc_headp, struct file_header_head *fh_headp,
2078 +	int flags, int *checksum_error)
2079 +{
2080 +	char tmpath[MAXPATHLEN];
2081 +	struct stat sb;
2082 +	DIR *dirtmp;
2083 +	struct dirent *sdp;
2084 +	struct file_header *fhp, *indexfhp;
2085 +	int error;
2086 +
2087 +	while (dirp) {
2088 +		sdp = readdir(dirp);
2089 +		if (sdp != NULL) {
2090 +			if (strcmp(sdp->d_name, "..") == 0 ||
2091 +				strcmp(sdp->d_name, ".") == 0 ||
2092 +				strcmp(sdp->d_name, ".pefs.db") == 0 ||
2093 +				strcmp(sdp->d_name, ".pefs.conf") == 0 ||
2094 +				strcmp(sdp->d_name, ".pefs.checksum") == 0)
2095 +				continue;
2096 +
2097 +			dprintf(("dirent: %s\n", sdp->d_name));
2098 +			snprintf(tmpath, sizeof(tmpath), "%s/%s", path, sdp->d_name);
2099 +			switch (sdp->d_type) {
2100 +			case DT_DIR:
2101 +				dirtmp = opendir(tmpath);
2102 +				if (dirtmp == NULL) {
2103 +					pefs_warn("failed to open dir: %s", tmpath);
2104 +					closedir(dirp);
2105 +					return (PEFS_ERR_SYS);
2106 +				}
2107 +				error = pefs_traverse_fs(chtp, md, hash_len, dirtmp, tmpath,
2108 +					fsp, hlc_headp, fh_headp, flags, checksum_error);
2109 +				if (error != 0) {
2110 +					closedir(dirp);
2111 +					return (PEFS_ERR_SYS);
2112 +				}
2113 +				break;
2114 +			/*
2115 +			 * Look up the file and verify its checksums.
2116 +			 * Total number of checksums should be the same and checksums
2117 +			 * should match.
2118 +			 * Also, take care of hardlinks & symlink warnings.
2119 +			 * After the traversal is done, we should have found all of the
2120 +			 * entries in the checksum file.
2121 +			 */
2122 +			/* FALLTHROUGH */
2123 +			case DT_REG:
2124 +			case DT_LNK:
2125 +				fhp = pefs_allocate_file_header();
2126 +				if (fhp == NULL) {
2127 +					closedir(dirp);
2128 +					return (PEFS_ERR_SYS);
2129 +				}
2130 +				strlcpy(fhp->path, tmpath, sizeof(fhp->path));
2131 +
2132 +				error = pefs_open_semantic_checks(fhp, fsp, NULL, flags);
2133 +				if (error != 0) {
2134 +					closedir(dirp);
2135 +					pefs_free_file_header(fhp);
2136 +					return (error);
2137 +				}
2138 +
2139 +				error = pefs_get_file_id(fhp, flags);
2140 +				if (error != 0) {
2141 +					closedir(dirp);
2142 +					pefs_free_file_header(fhp);
2143 +					return (error);
2144 +				}
2145 +
2146 +				indexfhp = pefs_cuckoo_lookup(chtp, fhp);
2147 +				if (indexfhp == NULL) {
2148 +					pefs_free_file_header(fhp);
2149 +					break;
2150 +				}
2151 +				indexfhp->found = 1;
2152 +
2153 +				error = pefs_compute_file_checksums(fhp, md, hash_len, flags);
2154 +				if (error != 0) {
2155 +					closedir(dirp);
2156 +					pefs_free_file_header(fhp);
2157 +					return (error);
2158 +				}
2159 +
2160 +				error = lstat(fhp->path, &sb);
2161 +				if (error != 0) {
2162 +					warn("cannot stat file %s", fhp->path);
2163 +					closedir(dirp);
2164 +					pefs_free_file_header(fhp);
2165 +					return (PEFS_ERR_SYS);
2166 +				}
2167 +
2168 +				if ((sb.st_flags & SF_IMMUTABLE) == 0) {
2169 +					pefs_warn("file %s does not have schg flag", fhp->path);
2170 +					*checksum_error = PEFS_ERR_CHECKSUM;
2171 +				}
2172 +
2173 +				error = pefs_hardlink_insert(hlc_headp, fhp, &sb);
2174 +				if (error != 0) {
2175 +					closedir(dirp);
2176 +					pefs_free_file_header(fhp);
2177 +					return (error);
2178 +				}
2179 +
2180 +				/*
2181 +				 * if error encountered during pefs_compare_checksums,
2182 +				 * keep on traversing the fs to find other errors as well.
2183 +				 */
2184 +				error = pefs_compare_checksums(fhp, indexfhp, hash_len);
2185 +				if (error != 0)
2186 +					*checksum_error = error;
2187 +
2188 +				TAILQ_INSERT_TAIL(fh_headp, fhp, file_header_entries);
2189 +				pefs_close_files(fhp);
2190 +				break;
2191 +			default:
2192 +				break;
2193 +			}
2194 +		}
2195 +		else {
2196 +			closedir(dirp);
2197 +			return (0);
2198 +		}
2199 +	}
2200 +
2201 +	/* unreachable, just to silence compiler */
2202 +	pefs_warn("invalid dirp argument for dir: %s", path);
2203 +	return (PEFS_ERR_SYS);
2204 +}
2205 +
2206 +static int
2207 +pefs_found_all_entries(struct cuckoo_hash_table *chtp)
2208 +{
2209 +	struct file_header *fhp;
2210 +	uint32_t i;
2211 +	int error;
2212 +
2213 +	error = 0;
2214 +	for (i = 0; i < chtp->size; i++) {
2215 +		fhp = chtp->buckets1[i].fhp;
2216 +		if (fhp != NULL)
2217 +			if (fhp->found != 1) {
2218 +				pefs_warn("file with file id %llu was not found in "
2219 +					"filesystem but exists in %s",
2220 +					fhp->fid.fid_num, PEFS_FILE_CHECKSUM);
2221 +				error = PEFS_ERR_NOENT;
2222 +			}
2223 +	}
2224 +
2225 +	for (i = 0; i < chtp->size; i++) {
2226 +		fhp = chtp->buckets2[i].fhp;
2227 +		if (fhp != NULL)
2228 +			if (fhp->found != 1) {
2229 +				pefs_warn("file with file id %llu was not found in "
2230 +					"filesystem but exists in %s",
2231 +					fhp->fid.fid_num, PEFS_FILE_CHECKSUM);
2232 +				error = PEFS_ERR_NOENT;
2233 +			}
2234 +	}
2235 +
2236 +	return (error);
2237 +}
2238 +
2239 +/*
2240 + * Verify the contents of a .pefs.checksum file.
2241 + * A) .pefs.checksum is read into memory.
2242 + * B) The entire filesystem is traversed in order to check each and every file.
2243 + * C) warning messages are produced for hardlinks and symbolic links.
2244 + * D) check that every file in .pefs.checksum was actually found in filesystem.
2245 + * E) verify the file's signature with the user supplied public key
2246 + */
2247 +int
2248 +pefs_verify_checksum(int fdin, FILE *pk_fp, char *fsroot, int flags)
2249 +{
2250 +	struct statfs fs;
2251 +	struct checksum_file_header cfh;
2252 +	struct cuckoo_hash_table cht;
2253 +	struct file_header_head fh_head;
2254 +	struct hardlink_head hlc_head;
2255 +	const EVP_MD *md;
2256 +	DIR *dirp;
2257 +	int error, checksum_error;
2258 +	uint8_t hash_len;
2259 +
2260 +	RB_INIT(&hlc_head);
2261 +	TAILQ_INIT(&fh_head);
2262 +	checksum_error = 0;
2263 +
2264 +	if (statfs(fsroot, &fs) == -1) {
2265 +		pefs_warn("statfs failed: %s: %s", fsroot, strerror(errno));
2266 +		return (PEFS_ERR_SYS);
2267 +	}
2268 +
2269 +	error = pefs_read_checksum_file_header(fdin, &cfh);
2270 +	if (error != 0)
2271 +		return (error);
2272 +
2273 +	OpenSSL_add_all_digests();
2274 +	md = EVP_get_digestbyname(cfh.hash_algo);
2275 +
2276 +	if(md == NULL) {
2277 +		pefs_warn("Unknown message digest %s", cfh.hash_algo);
2278 +		return (PEFS_ERR_INVALID);
2279 +	}
2280 +	hash_len = EVP_MD_size(md);
2281 +
2282 +	error = pefs_allocate_hash_table(&cht, cfh.hash_table_size, PEFS_NOEXTEND);
2283 +	if (error != 0)
2284 +		return (error);
2285 +
2286 +	error = pefs_read_checksum_file(fdin, &cfh, &cht);
2287 +	if (error != 0)
2288 +		goto out;
2289 +
2290 +	/* pefs_print_hash_tables(&cht, hash_len); */
2291 +
2292 +	dirp = opendir(fsroot);
2293 +	if (dirp == NULL) {
2294 +		pefs_warn("failed to open dir %s", fsroot);
2295 +		goto out;
2296 +	}
2297 +
2298 +	error = pefs_traverse_fs(&cht, md, hash_len, dirp, fsroot, &fs, &hlc_head,
2299 +		&fh_head, flags, &checksum_error);
2300 +	if (error != 0)
2301 +		goto out;
2302 +
2303 +	/* pefs_hardlink_print(&hlc_head); */
2304 +	pefs_hardlink_warn(&hlc_head);
2305 +	if ((flags & PEFS_UNMOUNTED) == 0 && (flags & PEFS_NOKEY) == 0)
2306 +		pefs_symlink_warn(&cht, &fh_head);
2307 +
2308 +	error = pefs_found_all_entries(&cht);
2309 +	if (error == 0 && checksum_error != 0)
2310 +		error = checksum_error;
2311 +
2312 +	error = pefs_verify_signature(fdin, pk_fp);
2313 +
2314 +out:
2315 +	pefs_free_hash_table(&cht);
2316 +	pefs_hardlink_free(&hlc_head);
2317 +	pefs_free_file_header_tail(&fh_head);
2318 +
2319 +	return (error);
2320 +}
2321 +
2322 +/* retrieve and then print the name checksum ID for a given filename */
2323 +int
2324 +pefs_filename_to_id(char *file_path, int flags)
2325 +{
2326 +	struct file_header *fhp;
2327 +	int error;
2328 +
2329 +	fhp = pefs_allocate_file_header();
2330 +	if (fhp == NULL) {
2331 +		error = PEFS_ERR_SYS;
2332 +		goto out;
2333 +	}
2334 +
2335 +	strlcpy(fhp->path, file_path, sizeof(fhp->path));
2336 +
2337 +	error = pefs_open_semantic_checks(fhp, NULL, NULL, flags);
2338 +	if (error != 0)
2339 +		goto out;
2340 +
2341 +	error = pefs_get_file_id(fhp, flags);
2342 +	if (error != 0)
2343 +		goto out;
2344 +
2345 +	printf("id: %llu\n", fhp->fid.fid_num);
2346 +
2347 +out:
2348 +	pefs_free_file_header(fhp);
2349 +	return (error);
2350 +}
2351 +
2352 +RB_GENERATE(hardlink_head, hardlink_counter, hardlink_entries,
2353 +	pefs_hardlink_cmp);
2354 Index: pefs_kmod/sbin/pefs/pefs_ctl.c
2355 ===================================================================
2356 --- pefs_kmod/sbin/pefs/pefs_ctl.c	(revision 235719)
2357 +++ pefs_kmod/sbin/pefs/pefs_ctl.c	(revision 240588)
2358 @@ -32,6 +32,8 @@
2359  #include <sys/ioccom.h>
2360  #include <sys/module.h>
2361  #include <sys/mount.h>
2362 +#include <sys/stat.h>
2363 +#include <sys/dirent.h>
2364  
2365  #include <assert.h>
2366  #include <ctype.h>
2367 @@ -74,6 +76,9 @@
2368  static int	pefs_getkey(int argc, char *argv[]);
2369  static int	pefs_showchains(int argc, char *argv[]);
2370  static int	pefs_showalgs(int argc, char *argv[]);
2371 +static int	pefs_addchecksum(int argc, char *argv[]);
2372 +static int	pefs_verify(int argc, char *argv[]);
2373 +static int	pefs_nameid(int argc, char *argv[]);
2374  
2375  typedef int (*command_func_t)(int argc, char **argv);
2376  typedef int (*keyop_func_t)(struct pefs_keychain_head *kch, int fd,
2377 @@ -100,9 +105,16 @@
2378  	{ "delchain",	pefs_delchain },
2379  	{ "showchains",	pefs_showchains },
2380  	{ "showalgs",	pefs_showalgs },
2381 +	{ "addchecksum", pefs_addchecksum},
2382 +	{ "verify", pefs_verify},
2383 +	{ "nameid", pefs_nameid},
2384  	{ NULL, NULL },
2385  };
2386  
2387 +
2388 +/* XXXgpf: [TODO] should probably add more at a later point */
2389 +const char *supported_digests[] = {"sha256","sha512"};
2390 +
2391  void
2392  pefs_warn(const char *fmt, ...)
2393  {
2394 @@ -991,6 +1003,334 @@
2395  	return (0);
2396  }
2397  
2398 +/*
2399 + * XXXgpf: Instead of a man page entry:
2400 + *
2401 + * pefs addchecksum [-s] [-a alg] [-i inputfile] [-k pkey_file] [-d dirpath] \
2402 + * filesystem
2403 + *
2404 + * $command creates .pefs.checksum db file for filesystem.
2405 + * This file will contain all checksums necessary to check integrity
2406 + * of files upon access.
2407 + *
2408 + * alg is the name of the algorithm to be used as a cryptographic
2409 + * hash function; supported algorithms: sha256, sha512. sha256 is
2410 + * used by default.
2411 + *
2412 + * inputfile contains list of files that need integrity checking. If
2413 + * the argument is not supplied, input is read from stdin by default.
2414 + * These files should be either regular files or symbolic links.
2415 + * Symlinks are not traversed.
2416 + *
2417 + * dirpath defines where .pefs.checksum should be created. By default,
2418 + * .pefs.checksum is created under $PWD. dirpath should be a directory,
2419 + * outside of target pefs filesystem.
2420 + *
2421 + * pkey_file is the file that contains the private key that will be used
2422 + * by the DSA signing algorithm. Key should be in PEM format.
2423 + *
2424 + * -f symbolizes that $command should set immutable flag schg for every file
2425 + * in inputlist if the flag is not already set.
2426 + *
2427 + * When $command is run, filesystem must be mounted with pefs, and
2428 + * user must have supplied the necessary pefs key(s).
2429 + *
2430 + */
2431 +static int
2432 +pefs_addchecksum(int argc, char *argv[])
2433 +{
2434 +	char fsroot[MAXPATHLEN + 1];
2435 +	char csm_path[MAXPATHLEN + 1];
2436 +	struct stat sb;
2437 +	FILE *fpin, *pk_fp;
2438 +	int error, flags, i, j;
2439 +	const char *algo;
2440 +
2441 +	flags = 0;
2442 +	fpin = stdin;
2443 +	pk_fp = NULL;
2444 +	/* by default use sha256 */
2445 +	algo = supported_digests[0];
2446 +	/* by default create checksum file under $PWD */
2447 +	snprintf(csm_path, sizeof(csm_path), "./%s", PEFS_FILE_CHECKSUM);
2448 +
2449 +	while ((i = getopt(argc, argv, "sa:i:k:d:")) != -1)
2450 +		switch(i) {
2451 +		case 'a':
2452 +			for (j=0; j < PEFS_SUPPORTED_DIGESTS; j++)
2453 +				if (strcmp(supported_digests[j], optarg) == 0) {
2454 +					algo = supported_digests[j];
2455 +					break;
2456 +				}
2457 +
2458 +			if (j == PEFS_SUPPORTED_DIGESTS) {
2459 +				pefs_warn("invalid digestname: %s", optarg);
2460 +				error = PEFS_ERR_INVALID;
2461 +				goto out;
2462 +			}
2463 +			break;
2464 +		case 's':
2465 +			flags|= PEFS_SETIMMUTABLE;
2466 +			break;
2467 +		case 'i':
2468 +			fpin = fopen(optarg, "r");
2469 +			if (fpin == NULL) {
2470 +				warn("cannot open inputfile: %s", optarg);
2471 +				error = PEFS_ERR_INVALID;
2472 +				goto out;
2473 +			}
2474 +			break;
2475 +		case 'k':
2476 +			pk_fp = fopen(optarg, "r");
2477 +			if (pk_fp == NULL) {
2478 +				warn("error opening privkey file %s", optarg);
2479 +				error = PEFS_ERR_SYS;
2480 +				goto out;
2481 +			}
2482 +			break;
2483 +		case 'd':
2484 +			if (stat(optarg, &sb) != 0) {
2485 +				warn("cannot stat file %s", optarg);
2486 +				error = PEFS_ERR_INVALID;
2487 +				goto out;
2488 +			}
2489 +
2490 +			if (S_ISDIR(sb.st_mode) == 0) {
2491 +				pefs_warn("filename: %s is not a directory", optarg);
2492 +				error = PEFS_ERR_INVALID;
2493 +				goto out;
2494 +			}
2495 +
2496 +			snprintf(csm_path, sizeof(csm_path), "%s/%s", optarg,
2497 +					PEFS_FILE_CHECKSUM);
2498 +			break;
2499 +		default:
2500 +			if (fpin != NULL)
2501 +				fclose(fpin);
2502 +			pefs_usage();
2503 +		}
2504 +	argc -= optind;
2505 +	argv += optind;
2506 +
2507 +	if (pk_fp == NULL) {
2508 +		pefs_warn("user must provide a file containing the private key");
2509 +		return (PEFS_ERR_INVALID);
2510 +	}
2511 +
2512 +	initfsroot(argc, argv, 0, fsroot, sizeof(fsroot));
2513 +
2514 +	error = pefs_create_checksum_file(fpin, fsroot, csm_path, pk_fp,
2515 +		algo, flags);
2516 +
2517 +out:
2518 +	if (fpin != NULL)
2519 +		fclose(fpin);
2520 +	if (pk_fp != NULL)
2521 +		fclose(pk_fp);
2522 +
2523 +	return (error);
2524 +}
2525 +
2526 +/*
2527 + * XXXgpf: Instead of a man page entry:
2528 + *
2529 + * pefs verify [-u/-n] [-k pkey_file] checksumpath filesystem
2530 + *
2531 + * $command verifies the contents of a .pefs.checksum file. It scans the
2532 + * entire filesystem and checks that every entry in .pefs.checksum is
2533 + * found in the filesystem with the same checksums.
2534 + *
2535 + * $command will try to produce the same warning messages as addchecksum
2536 + * concerning hardlinks and symbolic links.
2537 + *
2538 + * -n flag should be used if filesystem is mounted but key has not
2539 + * been provided yet.
2540 + *
2541 + * -u flag should be used if filesystem is unmounted.
2542 + *
2543 + * flags -u and -n are mutually exclusive.
2544 + *
2545 + * pkey_file is the file containing the public key that is used to verify
2546 + * .pefs.checksum's signature by the DSA algorithm.
2547 + *
2548 + * By default, pefs will assume that filesystem is mounted and user
2549 + * has provided key.
2550 + */
2551 +static int
2552 +pefs_verify(int argc, char *argv[])
2553 +{
2554 +	struct stat sb;
2555 +	char fsroot[MAXPATHLEN + 1];
2556 +	FILE *pk_fp;
2557 +	int error, fdin, flags, i;
2558 +
2559 +	fdin = -1;
2560 +	flags = PEFS_VERIFY;
2561 +	pk_fp = NULL;
2562 +	while ((i = getopt(argc, argv, "k:nu")) != -1)
2563 +		switch(i) {
2564 +		case 'k':
2565 +			pk_fp = fopen(optarg, "r");
2566 +			if (pk_fp == NULL) {
2567 +				warn("error opening pubkey file %s", optarg);
2568 +				error = PEFS_ERR_SYS;
2569 +				goto out;
2570 +			}
2571 +			break;
2572 +		case 'n':
2573 +			flags|= PEFS_NOKEY;
2574 +			if ((flags & PEFS_UNMOUNTED) != 0) {
2575 +				pefs_warn("flags -u and -n are mutually exclusive");
2576 +				return (PEFS_ERR_INVALID);
2577 +			}
2578 +			break;
2579 +		case 'u':
2580 +			flags|= PEFS_UNMOUNTED;
2581 +			if ((flags & PEFS_NOKEY) != 0) {
2582 +				pefs_warn("flags -u and -n are mutually exclusive");
2583 +				return (PEFS_ERR_INVALID);
2584 +			}
2585 +			break;
2586 +		default:
2587 +			pefs_usage();
2588 +		}
2589 +	argc -= optind;
2590 +	argv += optind;
2591 +
2592 +	if (pk_fp == NULL) {
2593 +		pefs_warn("user must provide a file containing the public key");
2594 +		return (PEFS_ERR_INVALID);
2595 +	}
2596 +
2597 +	if (argc != 2) {
2598 +		if (argc < 2)
2599 +			warnx("too few arguments");
2600 +		else
2601 +			warnx("too many arguments");
2602 +		pefs_usage();
2603 +	}
2604 +
2605 +	fdin = open(argv[0], O_RDONLY);
2606 +	if (fdin == -1) {
2607 +		warn("cannot open %s file: %s", PEFS_FILE_CHECKSUM, argv[0]);
2608 +		error = PEFS_ERR_INVALID;
2609 +		goto out;
2610 +	}
2611 +
2612 +	argc -=1;
2613 +	argv +=1;
2614 +
2615 +	if ((flags & PEFS_UNMOUNTED) == 0)
2616 +		initfsroot(argc, argv, 0, fsroot, sizeof(fsroot));
2617 +	else {
2618 +		/*
2619 +		 * XXXgpf: should i also check that fsroot path belongs to
2620 +		 * a non pefs filesystem?
2621 +		 */
2622 +		strlcpy(fsroot, argv[0], sizeof(fsroot));
2623 +		if (stat(fsroot, &sb) != 0) {
2624 +			warn("cannot stat fs root: %s", fsroot);
2625 +			error = PEFS_ERR_NOENT;
2626 +			goto out;
2627 +		}
2628 +
2629 +		if (S_ISDIR(sb.st_mode) == 0) {
2630 +			pefs_warn("fs root is not a directory: %s", fsroot);
2631 +			error = PEFS_ERR_SYS;
2632 +			goto out;
2633 +		}
2634 +	}
2635 +
2636 +	error = pefs_verify_checksum(fdin, pk_fp, fsroot, flags);
2637 +	if (error == 0)
2638 +		printf("integrity verification ok!\n");
2639 +	else
2640 +		pefs_warn("integrity verification encountered error(s)");
2641 +
2642 +out:
2643 +	if (fdin >= 0)
2644 +		close(fdin);
2645 +	if (pk_fp != NULL)
2646 +		fclose(pk_fp);
2647 +	return (error);
2648 +}
2649 +
2650 +/*
2651 + * XXXgpf: Instead of a man page entry:
2652 + *
2653 + * pefs nameid [-u/-n] filepath
2654 + *
2655 + * $command prints out the identifier for an encrypted pefs filename where
2656 + * pefs encrypted filename = XBase64(checksum || E(tweak || filename)).
2657 + *
2658 + * The id is the name checksum, meaning VMAC(E(tweak || filename)).
2659 + *
2660 + * This identifier is used as a primary key when a specific filename is handled
2661 + * by pefs for integrity checking purposes.
2662 + *
2663 + * Some warning messages produced by /sbin/pefs refer to files by their internal
2664 + * ID and not their unencrypted fullpath; e.g. when verifying an unmounted pefs
2665 + * filesystem. Therefore this command can be used to map fullpaths to internal
2666 + * IDs.
2667 + *
2668 + * -n flag should be used if filesystem is mounted but key has not been
2669 + * provided yet.
2670 + *
2671 + * -u flag should be used if filesystem is unmounted.
2672 + *
2673 + * In both of these scenarios the "filepath" that is provided by the user should
2674 + * be the encrypted filepath.
2675 + *
2676 + * flags -u and -n are mutually exclusive.
2677 + *
2678 + * filepath may refer to any kind of file that is encrypted by pefs filesystem,
2679 + * such as directories, regular files, symlinks, etc.
2680 + *
2681 + * Symlinks are not traversed.
2682 + */
2683 +static int
2684 +pefs_nameid(int argc, char *argv[])
2685 +{
2686 +	char file_path[MAXPATHLEN + 1];
2687 +	int error, flags, i;
2688 +
2689 +	flags = PEFS_GETID;
2690 +	while ((i = getopt(argc, argv, "nu")) != -1)
2691 +		switch(i) {
2692 +		case 'n':
2693 +			flags|= PEFS_NOKEY;
2694 +			if ((flags & PEFS_UNMOUNTED) != 0) {
2695 +				pefs_warn("flags -u and -n are mutually exclusive");
2696 +				return (PEFS_ERR_INVALID);
2697 +			}
2698 +			break;
2699 +		case 'u':
2700 +			flags|= PEFS_UNMOUNTED;
2701 +			if ((flags & PEFS_NOKEY) != 0) {
2702 +				pefs_warn("flags -u and -n are mutually exclusive");
2703 +				return (PEFS_ERR_INVALID);
2704 +			}
2705 +			break;
2706 +		default:
2707 +			pefs_usage();
2708 +		}
2709 +	argc -= optind;
2710 +	argv += optind;
2711 +
2712 +	if (argc != 1) {
2713 +		if (argc < 1)
2714 +			warnx("too few arguments");
2715 +		else
2716 +			warnx("too many arguments");
2717 +		pefs_usage();
2718 +	}
2719 +
2720 +	strlcpy(file_path, argv[0], sizeof(file_path));
2721 +	error = pefs_filename_to_id(file_path, flags);
2722 +
2723 +	return (error);
2724 +}
2725 +
2726  static void
2727  pefs_usage_alg(void)
2728  {
2729 @@ -1016,6 +1356,9 @@
2730  "	pefs randomchain [-fv] [-n min] [-N max] filesystem\n"
2731  "	pefs showchains [-fp] [-i iterations] [-k keyfile] filesystem\n"
2732  "	pefs showalgs\n"
2733 +"	pefs addchecksum [-s] [-a algo] [-i inputfile] [-k pkey_file] [-d dirpath] filesystem\n"
2734 +"	pefs verify [-n/u] [-k pkey_file] [checksumpath filesystem]\n"
2735 +"	pefs nameid [-u/-n] [filepath]"
2736  );
2737  	exit(PEFS_ERR_USAGE);
2738  }
2739 Index: pefs_kmod/sbin/pefs/pefs.8
2740 ===================================================================
2741 --- pefs_kmod/sbin/pefs/pefs.8	(revision 235719)
2742 +++ pefs_kmod/sbin/pefs/pefs.8	(revision 240588)
2743 @@ -104,6 +104,25 @@
2744  .Pp
2745  .Nm
2746  .Cm showalgs
2747 +.Pp
2748 +.Nm
2749 +.Cm addchecksum
2750 +.Op Fl s
2751 +.Op Fl a Ar alg
2752 +.Op Fl i Ar input_file
2753 +.Op Fl k Ar privatekey_file
2754 +.Op Fl d Ar dirpath
2755 +.Ar filesystem
2756 +.Nm
2757 +.Cm verify
2758 +.Op Fl u|n
2759 +.Op Fl k Ar publickey_file
2760 +.Ar checksum_file
2761 +.Ar filesystem
2762 +.Nm
2763 +.Cm nameid
2764 +.Op Fl u|n
2765 +.Ar filepath
2766  .Sh DESCRIPTION
2767  The
2768  .Nm
2769 @@ -227,6 +246,76 @@
2770  Print all elements of the key chain staring with given parent key.
2771  .It Cm showalgs
2772  Print list of all supported algorithms.
2773 +.It Cm addchecksum Ar filesystem
2774 +Create 
2775 +.Em .pefs.checksum
2776 +db file for
2777 +.Ar filesystem.
2778 +The algorithm that will be used as a hash function (sha256 by default) is
2779 +set by
2780 +.Fl a Ar alg .
2781 +The file that contains the private key in PEM format for the DSA signing
2782 +algorithm must be provided using
2783 +.Fl k Ar privatekey_file .
2784 +The list of files is read from stdin unless
2785 +.Fl i Ar input_file
2786 +is used. Files should be either regular files or symbolic links. Symlinks 
2787 +are not traversed.
2788 +All files that need integrity checking must have the immutable flag (schg) set;
2789 +.Fl s
2790 +can be used to let
2791 +.Nm
2792 +turn it on for files that do not.
2793 +.Fl d Ar dirpath
2794 +can be used to specify under which directory the resulting
2795 +.Em .pefs.checksum
2796 +file should be placed. Otherwise, it is created under $PWD.
2797 +.It Cm verify Ar checksumpath filesystem
2798 +Verify the contents of a
2799 +.Em .pefs.checksum
2800 +file. This command scans the entire 
2801 +.Ar filesystem
2802 +and checks that every entry in
2803 +.Em .pefs.checkum
2804 +is found and produces the same checksums. The command will try to produce
2805 +the same warning messages as
2806 +.Cm addchecksum
2807 +concerning hardlinks and symbolic links. It will also try to produce as many
2808 +warning messages as possible before failing. If
2809 +.Ar filesystem
2810 +is mounted but the key has not been supplied yet,
2811 +.Fl n
2812 +flag should be used. If the pefs
2813 +.Ar filesystem
2814 +is unmounted, the
2815 +.Fl u
2816 +flag should be used instead. By default, 
2817 +.Nm
2818 +will assume that the filesystem is mounted and user has provided the
2819 +necessary key(s) using
2820 +.Cm addkey .
2821 +The file that contains the public key in PEM format must be provided using
2822 +.Fl k Ar privatekey_file . 
2823 +.It Cm nameid Ar filepath
2824 +Print the identifier for an encrypted pefs filename where filename = 
2825 +XBase64(checksum || E(tweak || filename)). The id is the name checksum,
2826 +meaning VMAC(E(tweak || filename)). This identifier is used as a primary key
2827 +when a filename is handled by
2828 +.Nm
2829 +for integrity checking purposes. Some warning messages produced by
2830 +.Nm
2831 +refer to files by their internal ID and not their decrypted fullpath; e.g.
2832 +when verifying an unmounted pefs filesystem. Therefore, this command can be
2833 +used to map fullpaths to internal IDs. If the pefs
2834 +.Ar filesystem
2835 +is unmounted, the
2836 +.Fl u
2837 +flag should be used instead. By default, 
2838 +.Nm
2839 +will assume that the filesystem is mounted and user has provided the
2840 +necessary key(s) using
2841 +.Cm addkey .
2842 +Symlinks are not traversed.
2843  .El
2844  .Pp
2845  .Ss COMMAND OPTIONS
2846 @@ -248,11 +337,18 @@
2847  .It Fl C
2848  Disables key chain lookup.
2849  By default if chain is found, keys it consists of are also used for operation.
2850 +.It Fl d Ar dirpath
2851 +specifies under which directory the resulting
2852 +.Em .pefs.checksum
2853 +file should be placed.
2854  .It Fl i Ar iterations
2855  Number of
2856  .Ar iterations
2857  to use with PKCS#5v2.
2858 -If this option is not specified default value of 50000 is used.
2859 +If this option is not specified default value of 50000 is used. In case of
2860 +.Cm addchecksum
2861 +, it may be used to specify the file that contains the list of full filenames
2862 +that require integrity checking.
2863  .It Fl I Ar iterations
2864  Specifies number of
2865  .Ar iterations
2866 @@ -270,9 +366,15 @@
2867  Specifies a file which contains part of the key.
2868  If
2869  .Ar keyfile
2870 -is given as -, standard input will be used.
2871 +is given as -, standard input will be used. In case of integrity 
2872 +checking actions, this specifies either the public or the private key 
2873 +that is used by the signing algorithm.
2874  .It Fl K Ar keyfile
2875  Specifies a file which contains part of the secondary/child key.
2876 +.It Fl n
2877 +Specifies that the pefs
2878 +.Ar filesystem
2879 +is mounted but user has not provided the necessary key(s) yet.
2880  .It Fl o Ar options
2881  Mount options passed to
2882  .Xr mount 8
2883 @@ -281,10 +383,19 @@
2884  Do not ask for passphrase.
2885  .It Fl P
2886  Do not ask for passphrase for secondary/child key.
2887 +.It Fl s
2888 +Is used to let
2889 +.Cm addchecksum
2890 +turn on the schg immutable flag for files that need integrity checking but
2891 +lack the schg flag.
2892  .It Fl t
2893  Test-only mode.
2894  Do not perform actual operation but check if it can be performed.
2895  Usable for scripting.
2896 +.It Fl u
2897 +Specifies that the pefs
2898 +.Ar filesystem
2899 +is unmounted.
2900  .It Fl v
2901  Verbose mode.
2902  .It Fl x
2903 @@ -369,6 +480,14 @@
2904  before loading
2905  .Nm
2906  kernel module.
2907 +.It Va vfs.pefs.exec.enable
2908 +If this flag is set to 1, the system allows execution of code that derives
2909 +solely from files with the immutable flag (schg) set. This flag is temporary
2910 +as this functionality should be controlled by securelevel.
2911 +.It Va vfs.pefs.exec.enable.noscript
2912 +Same as the above except for when user is trying to execute a script. In
2913 +that case, only the interpreter will be checked for the schg flag, not the
2914 +script file.
2915  .El
2916  .Sh EXAMPLES
2917  Encrypting a directory:
2918 Index: pefs_kmod/sbin/pefs/pefs_ctl.h
2919 ===================================================================
2920 --- pefs_kmod/sbin/pefs/pefs_ctl.h	(revision 235719)
2921 +++ pefs_kmod/sbin/pefs/pefs_ctl.h	(revision 240588)
2922 @@ -28,6 +28,8 @@
2923  
2924  #include <inttypes.h>
2925  
2926 +struct EVP_MD;
2927 +
2928  #define	PEFS_FSTYPE			"pefs"
2929  #define	PEFS_KLD			PEFS_FSTYPE
2930  
2931 @@ -36,9 +38,18 @@
2932  
2933  #define	PEFS_KDF_ITERATIONS		50000
2934  
2935 +#define PEFS_BLOCKSIZE			4096
2936 +
2937  #define	PEFS_FILE_KEYCHAIN		".pefs.db"
2938  #define	PEFS_FILE_KEYCONF		".pefs.conf"
2939 +#define PEFS_FILE_CHECKSUM		".pefs.checksum"
2940  
2941 +#define PEFS_NOKEY				0x0001
2942 +#define PEFS_UNMOUNTED			0x0002
2943 +#define PEFS_SETIMMUTABLE		0x0004
2944 +#define PEFS_VERIFY				0x0010
2945 +#define PEFS_GETID				0x0020
2946 +
2947  #define	PEFS_KEYCONF_ALG_IND		0
2948  #define	PEFS_KEYCONF_ITERATIONS_IND	1
2949  
2950 @@ -47,6 +58,8 @@
2951  
2952  #define	PEFS_KEYENC_MAC_SIZE		(PEFS_KEY_SIZE / 2)
2953  
2954 +#define PEFS_SUPPORTED_DIGESTS	2
2955 +
2956  #define	PEFS_ERR_GENERIC		1
2957  #define	PEFS_ERR_USAGE			2
2958  #define	PEFS_ERR_IO			3
2959 @@ -54,6 +67,7 @@
2960  #define	PEFS_ERR_NOENT			5
2961  #define	PEFS_ERR_EXIST			6
2962  #define	PEFS_ERR_INVALID		7
2963 +#define PEFS_ERR_CHECKSUM		8
2964  
2965  #define	PEFS_FS_IGNORE_TYPE		0x0001
2966  
2967 @@ -77,6 +91,8 @@
2968  void	pefs_warn(const char *, ...) __printf0like(1, 2);
2969  
2970  int	pefs_getfsroot(const char *path, int flags, char *fsroot, size_t size);
2971 +int pefs_getfsroots(const char *path, int flags, char *fsroot,
2972 +		char * fromfsroot, size_t size);
2973  
2974  int	pefs_key_generate(struct pefs_xkey *xk, const char *passphrase,
2975  	    struct pefs_keyparam *kp);
2976 @@ -85,7 +101,14 @@
2977  int	pefs_key_decrypt(struct pefs_xkeyenc *xe,
2978  	    const struct pefs_xkey *xk_parent);
2979  uintmax_t	pefs_keyid_as_int(char *keyid);
2980 +int pefs_create_checksum_file(FILE *fpin, char *fsroot, char *csm_path,
2981 +		FILE *pk_fp, const char *algo, int flags);
2982 +int pefs_verify_checksum(int fdin, FILE *pk_fp, char *fsroot, int flags);
2983 +int pefs_filename_to_id(char *file_path, int flags);
2984  
2985 +int	pefs_name_pton(char const *src, size_t srclen, u_char *target,
2986 +		size_t targsize);
2987 +
2988  const char *	pefs_alg_name(struct pefs_xkey *xk);
2989  void	pefs_alg_list(FILE *stream);
2990  
2991 Index: pefs_kmod/sbin/pefs/pefs_key.c
2992 ===================================================================
2993 --- pefs_kmod/sbin/pefs/pefs_key.c	(revision 235719)
2994 +++ pefs_kmod/sbin/pefs/pefs_key.c	(revision 240588)
2995 @@ -31,6 +31,7 @@
2996  #include <sys/param.h>
2997  #include <sys/types.h>
2998  #include <sys/errno.h>
2999 +#include <sys/dirent.h>
3000  #include <assert.h>
3001  #include <inttypes.h>
3002  #include <stdio.h>
3003 Index: pefs_kmod/sbin/pefs/pefs_keychain.c
3004 ===================================================================
3005 --- pefs_kmod/sbin/pefs/pefs_keychain.c	(revision 235719)
3006 +++ pefs_kmod/sbin/pefs/pefs_keychain.c	(revision 240588)
3007 @@ -30,6 +30,7 @@
3008  #include <sys/param.h>
3009  #include <sys/endian.h>
3010  #include <sys/stat.h>
3011 +#include <sys/dirent.h>
3012  #include <assert.h>
3013  #include <inttypes.h>
3014  #include <stdio.h>
3015 Index: pefs_kmod/sbin/pefs/pefs_subr.c
3016 ===================================================================
3017 --- pefs_kmod/sbin/pefs/pefs_subr.c	(revision 235719)
3018 +++ pefs_kmod/sbin/pefs/pefs_subr.c	(revision 240588)
3019 @@ -32,6 +32,7 @@
3020  #include <sys/ioccom.h>
3021  #include <sys/module.h>
3022  #include <sys/mount.h>
3023 +#include <sys/dirent.h>
3024  
3025  #include <assert.h>
3026  #include <ctype.h>
3027 @@ -73,3 +74,73 @@
3028  
3029  	return (0);
3030  }
3031 +
3032 +#define	Assert(Cond)		(void)0
3033 +
3034 +/*
3035 + * Algorithm is standard base64 with few exceptions:
3036 + *  - file system friendly alphabet
3037 + *  - no paddings and whitespace skip
3038 + */
3039 +static const char Base64[] =
3040 +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
3041 +int
3042 <