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 +pefs_name_pton(char const *src, size_t srclen, u_char *target, size_t targsize)
3043 +{
3044 +	int tarindex, state, ch;
3045 +	char *pos;
3046 +
3047 +	state = 0;
3048 +	tarindex = 0;
3049 +
3050 +	while ((ch = *src++) != '\0' && srclen-- > 0) {
3051 +		if (target && (size_t)tarindex >= targsize)
3052 +			return (-1);
3053 +
3054 +		pos = strchr(Base64, ch);
3055 +		if (pos == 0)		/* A non-base64 character. */
3056 +			return (-1);
3057 +
3058 +		switch (state) {
3059 +		case 0:
3060 +			if (target) {
3061 +				target[tarindex] = (pos - Base64) << 2;
3062 +			}
3063 +			state = 1;
3064 +			break;
3065 +		case 1:
3066 +			if (target) {
3067 +				target[tarindex]   |=  (pos - Base64) >> 4;
3068 +				if ((size_t)tarindex + 1 < targsize)
3069 +					target[tarindex+1] =
3070 +					    ((pos - Base64) & 0x0f) << 4 ;
3071 +			}
3072 +			tarindex++;
3073 +			state = 2;
3074 +			break;
3075 +		case 2:
3076 +			if (target) {
3077 +				target[tarindex]   |=  (pos - Base64) >> 2;
3078 +				if ((size_t)tarindex + 1 < targsize)
3079 +					target[tarindex+1] =
3080 +					    ((pos - Base64) & 0x03) << 6;
3081 +			}
3082 +			tarindex++;
3083 +			state = 3;
3084 +			break;
3085 +		case 3:
3086 +			if (target) {
3087 +				target[tarindex] |= (pos - Base64);
3088 +			}
3089 +			tarindex++;
3090 +			state = 0;
3091 +			break;
3092 +		default:
3093 +			return (-1);
3094 +		}
3095 +	}
3096 +
3097 +	if (tarindex == 0)
3098 +		return (-1);
3099 +	return (tarindex);
3100 +}
3101 Index: pefs_kmod/sbin/pefs/Makefile
3102 ===================================================================
3103 --- pefs_kmod/sbin/pefs/Makefile	(revision 235719)
3104 +++ pefs_kmod/sbin/pefs/Makefile	(revision 240588)
3105 @@ -5,7 +5,7 @@
3106  .PATH:	${SYS}/crypto/hmac ${SYS}/crypto/rijndael ${SYS}/crypto/sha2
3107  
3108  PROG=	pefs
3109 -SRCS=	pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c
3110 +SRCS=	pefs_ctl.c pefs_key.c pefs_keychain.c pefs_subr.c pefs_checksum.c
3111  SRCS+=	hmac_sha512.c sha2.c
3112  SRCS+=	rijndael-api.c rijndael-api-fst.c rijndael-alg-fst.c
3113  SRCS+=	pkcs5v2.c
3114 @@ -17,7 +17,7 @@
3115  DEBUG_FLAGS+= -g
3116  
3117  DPADD=  ${LIBUTIL}
3118 -LDADD=  -lutil
3119 +LDADD=  -lutil -lcrypto
3120  
3121  BINDIR?= /sbin
3122  
3123 Index: pefs_kmod/sys/fs/pefs/pefs.h
3124 ===================================================================
3125 --- pefs_kmod/sys/fs/pefs/pefs.h	(revision 235719)
3126 +++ pefs_kmod/sys/fs/pefs/pefs.h	(revision 240588)
3127 @@ -48,6 +48,25 @@
3128  	char			pxk_key[PEFS_KEY_SIZE];
3129  };
3130  
3131 +struct pefs_xnamecsum {
3132 +	uint32_t		pxnc_namelen;
3133 +	char			pxnc_csum[PEFS_NAME_CSUM_SIZE];
3134 +	char			pxnc_filename[MAXNAMLEN + 1];
3135 +};
3136 +
3137 +struct pefs_xsector_ctext {
3138 +	off_t			pxsct_offset;
3139 +	uint32_t		pxsct_ctext_len;
3140 +	char			pxsct_ctext[PEFS_SECTOR_SIZE];
3141 +};
3142 +
3143 +struct pefs_xslink_ctext {
3144 +	uint32_t		pxsl_namelen;
3145 +	uint32_t		pxsl_target_len;
3146 +	char			pxsl_filename[MAXPATHLEN + 1];
3147 +	char			pxsl_target[PEFS_SECTOR_SIZE];
3148 +};
3149 +
3150  #ifdef _IO
3151  #define	PEFS_GETKEY			_IOWR('p', 0, struct pefs_xkey)
3152  #define	PEFS_ADDKEY			_IOWR('p', 1, struct pefs_xkey)
3153 @@ -55,6 +74,9 @@
3154  #define	PEFS_DELKEY			_IOWR('p', 3, struct pefs_xkey)
3155  #define	PEFS_FLUSHKEYS			_IO('p', 4)
3156  #define	PEFS_GETNODEKEY			_IOWR('p', 5, struct pefs_xkey)
3157 +#define PEFS_GETNAMECSUM		_IOWR('p', 6, struct pefs_xnamecsum)
3158 +#define PEFS_GETSECTORCTEXT		_IOWR('p', 7, struct pefs_xsector_ctext)
3159 +#define PEFS_GETSLINKCTEXT		_IOWR('p', 8, struct pefs_xslink_ctext)
3160  #endif
3161  
3162  #ifdef _KERNEL
3163 @@ -98,6 +120,8 @@
3164  #define	PN_WANTRECYCLE			0x000100
3165  #define	PN_LOCKBUF_SMALL		0x001000
3166  #define	PN_LOCKBUF_LARGE		0x002000
3167 +#define	PN_NO_CHECKSUM			0x000010
3168 +#define	PN_WRONG_CHECKSUM		0x000020
3169  
3170  struct pefs_node {
3171  	LIST_ENTRY(pefs_node)	pn_listentry;
3172 @@ -109,17 +133,32 @@
3173  	void			*pn_buf_large;
3174  	int			pn_flags;
3175  	struct pefs_tkey	pn_tkey;
3176 +	char				*pn_checksum_index_entry;
3177  };
3178  
3179  #define	PM_ROOT_CANRECURSE		0x01
3180  #define	PM_DIRCACHE			0x02
3181  #define	PM_ASYNCRECLAIM			0x04
3182 +#define PM_CHECKSUM				0x08
3183  
3184 +struct pefs_checksum {
3185 +	uint8_t pcs_version;
3186 +	uint8_t pcs_reserved;
3187 +	uint8_t pcs_hash_len;
3188 +	uint8_t pcs_hash_algo;
3189 +	uint8_t pcs_hash_algo_name[8];
3190 +	uint8_t pcs_offset_to_hash_table;
3191 +	uint32_t pcs_hash_table_size;
3192 +	char *pcs_table1, *pcs_table2;
3193 +	struct vnode *pcs_checksumvp;
3194 +};
3195 +
3196  struct pefs_mount {
3197  	struct mount		*pm_lowervfs;
3198  	struct vnode		*pm_rootvp;
3199  	struct mtx		pm_keys_lock;
3200  	struct pefs_key_head	pm_keys;
3201 +	struct pefs_checksum	pm_checksum;
3202  	int			pm_flags;
3203  };
3204  
3205 Index: pefs_kmod/sys/fs/pefs/pefs_mac.c
3206 ===================================================================
3207 --- pefs_kmod/sys/fs/pefs/pefs_mac.c	(revision 0)
3208 +++ pefs_kmod/sys/fs/pefs/pefs_mac.c	(revision 240588)
3209 @@ -0,0 +1,218 @@
3210 +/*-
3211 + * Copyright (c) 2012 Efstratios Karatzas
3212 + * All rights reserved.
3213 + *
3214 + * Redistribution and use in source and binary forms, with or without
3215 + * modification, are permitted provided that the following conditions
3216 + * are met:
3217 + * 1. Redistributions of source code must retain the above copyright
3218 + *    notice, this list of conditions and the following disclaimer.
3219 + * 2. Redistributions in binary form must reproduce the above copyright
3220 + *    notice, this list of conditions and the following disclaimer in the
3221 + *    documentation and/or other materials provided with the distribution.
3222 + *
3223 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
3224 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3225 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3226 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
3227 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3228 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3229 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3230 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3231 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3232 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3233 + * SUCH DAMAGE.
3234 + *
3235 + * $FreeBSD$
3236 + */
3237 +
3238 +#include <sys/cdefs.h>
3239 +__FBSDID("$FreeBSD$");
3240 +
3241 +#include <sys/param.h>
3242 +#include <sys/systm.h>
3243 +#include <sys/kernel.h>
3244 +#include <sys/dirent.h>
3245 +#include <sys/mman.h>
3246 +#include <sys/module.h>
3247 +#include <sys/mount.h>
3248 +#include <sys/sysctl.h>
3249 +#include <sys/stat.h>
3250 +#include <sys/vnode.h>
3251 +#include <sys/imgact.h>
3252 +
3253 +#include <vm/vm.h>
3254 +
3255 +#include <fs/pefs/pefs.h>
3256 +
3257 +#include <security/mac/mac_policy.h>
3258 +
3259 +/*
3260 + * XXXgpf:
3261 + * The problem with this MAC hook is that the hook is called before
3262 + * do_execve() checks if our executable requires an interpreter.
3263 + * Therefore, the script file will itself be checked for the schg flag.
3264 + *
3265 + * We could:
3266 + *
3267 + * a) allow this because it's a feature! During development of a script,
3268 + * user will have to pass it as an argument to the interpreter and when it's
3269 + * complete, continue calling it like that or add the schg flag.
3270 + *
3271 + * b) add a brand new MAC hook that will be called at the precise point
3272 + * in do_execve() where only the interpreter or the regular executable
3273 + * will be checked for the schg flag. [don't seem the other devs will go
3274 + * for us modifying MAC framework though]
3275 + *
3276 + * c) duplicate code from do_execve() and perform the check ourselves. It
3277 + * could be done I guess but I'm not sure since image activators seem to have
3278 + * their own custom functions that are called in order to figure out whether
3279 + * the interpreted flag should be turned on. Don't know how much they are
3280 + * allowed to tamper with imgp, besides that flag.
3281 + *
3282 + */
3283 +static int
3284 +pefs_vnode_check_exec(struct ucred *cred, struct vnode *vp,
3285 +	struct label *vplabel, struct image_params *imgp,
3286 +	struct label *execlabel)
3287 +{
3288 +	int enabled, rval;
3289 +	size_t enabled_len;
3290 +
3291 +	rval = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3292 +				&enabled, &enabled_len, NULL, 0, NULL, 0);
3293 +
3294 +	if (rval == 0 && enabled != 0) {
3295 +		if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0) {
3296 +			return (EPERM);
3297 +		}
3298 +	}
3299 +
3300 +	return (0);
3301 +}
3302 +
3303 +/*
3304 + * XXXgpf:
3305 + *
3306 + * This new hook is placed in do_execve(), found in sys/kern/kern_exec.c.
3307 + * Its purpose is to only check the interpreter for the schg flag in case
3308 + * there's an executable that requires an interpreter.
3309 + *
3310 + * It is placed after exec_check_permissions() and it will be called after
3311 + * we've looped back for the interpreter. Therefore, only either the interpeter
3312 + * or the regular executable itself will ever be checked by this hook; we'll
3313 + * never check the script file itself.
3314 + *
3315 + * It uses a different dbg sysctl var than the above hook for obvious reasons.
3316 + */
3317 +static int
3318 +pefs_vnode_check_exec_noscript(struct ucred *cred, struct vnode *vp,
3319 +	struct label *vplabel, struct image_params *imgp,
3320 +	struct label *execlabel)
3321 +{
3322 +	int enabled, rval;
3323 +	size_t enabled_len;
3324 +
3325 +	rval = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3326 +				&enabled, &enabled_len, NULL, 0, NULL, 0);
3327 +
3328 +	if (rval == 0 && enabled != 0) {
3329 +		if ((imgp->attr->va_flags & SF_IMMUTABLE) == 0)
3330 +			return (EPERM);
3331 +	}
3332 +
3333 +	return (0);
3334 +}
3335 +
3336 +
3337 +/*
3338 + * XXXgpf: Check if schg is turned on when we mmap() a vnode with
3339 + * the PROT_EXEC flag.
3340 + */
3341 +static int
3342 +pefs_vnode_check_mmap(struct ucred *cred, struct vnode *vp,
3343 +	struct label *vplabel, int prot, int flags)
3344 +{
3345 +	struct vattr va;
3346 +	int enabled1, enabled2, error, rval1, rval2;
3347 +	size_t enabled_len;
3348 +
3349 +	if ((prot & PROT_EXEC) == 0)
3350 +		return (0);
3351 +
3352 +	rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3353 +				&enabled1, &enabled_len, NULL, 0, NULL, 0);
3354 +
3355 +	rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3356 +				&enabled2, &enabled_len, NULL, 0, NULL, 0);
3357 +
3358 +	if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0)) {
3359 +		error = VOP_GETATTR(vp, &va, cred);
3360 +		if (error != 0)
3361 +			return (EACCES);
3362 +
3363 +		if ((va.va_flags & SF_IMMUTABLE) == 0)
3364 +			return (EACCES);
3365 +	}
3366 +
3367 +	return (0);
3368 +}
3369 +
3370 +/*
3371 + * XXXgpf:
3372 + *
3373 + * Checking mmap(2) with the above MAC hook is not enough if we wish to
3374 + * prevent user from mmaping a file and then executing those pages due to
3375 + * mprotect(2).
3376 + *
3377 + * I did notice the existance of mac_vnode_check_mprotect(), but unfortunately
3378 + * it's not used anywhere in the kernel for some reason(?)! If it ever comes
3379 + * back into action, I believe it would be preferable to the following solution.
3380 + *
3381 + * My alternative solution was to set the MAXPROT flag of the mapped area
3382 + * during mmap(). If we are mapping a file and we need schg protection, we
3383 + * remove VM_PROT_EXECUTE from MAXPROT which in turn causes following attempts
3384 + * to mprotect() with PROT_EXEC to fail.
3385 + */
3386 +static void
3387 +pefs_vnode_set_mmap_maxprot(struct ucred *cred, struct vnode *vp,
3388 +	struct label *vplabel, vm_prot_t *maxprotp, int flags)
3389 +{
3390 +	int enabled1, enabled2, rval1, rval2;
3391 +	size_t enabled_len;
3392 +
3393 +	if ((*maxprotp & VM_PROT_EXECUTE) == 0)
3394 +		return;
3395 +
3396 +	rval1 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable",
3397 +				&enabled1, &enabled_len, NULL, 0, NULL, 0);
3398 +
3399 +	rval2 = kernel_sysctlbyname(curthread, "vfs.pefs.exec.enable_noscript",
3400 +				&enabled2, &enabled_len, NULL, 0, NULL, 0);
3401 +
3402 +	if ((rval1 == 0 && enabled1 != 0) || (rval2 == 0 && enabled2 != 0))
3403 +		*maxprotp &= ~VM_PROT_EXECUTE;
3404 +}
3405 +
3406 +static struct mac_policy_ops pefs_ops =
3407 +{
3408 +	.mpo_vnode_check_exec = pefs_vnode_check_exec,
3409 +	.mpo_vnode_check_exec_noscript = pefs_vnode_check_exec_noscript,
3410 +	.mpo_vnode_check_mmap = pefs_vnode_check_mmap,
3411 +	.mpo_vnode_set_mmap_maxprot = pefs_vnode_set_mmap_maxprot,
3412 +};
3413 +
3414 +MAC_POLICY_SET(&pefs_ops, mac_pefs, "pefs exec protection",
3415 +	MPC_LOADTIME_FLAG_UNLOADOK, NULL);
3416 +
3417 +/* XXXgpf: declare our debugging sysctls for schg execution control */
3418 +SYSCTL_NODE(_vfs_pefs, OID_AUTO, exec, CTLFLAG_RW, 0,
3419 +		"PEFS kern_exec.c stuff");
3420 +
3421 +int	pefs_exec_enable = 0;
3422 +SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable, CTLFLAG_RW,
3423 +		&pefs_exec_enable, 0, "Enable exec protection");
3424 +
3425 +int	pefs_exec_enable_noscript = 0;
3426 +SYSCTL_INT(_vfs_pefs_exec, OID_AUTO, enable_noscript, CTLFLAG_RW,
3427 +		&pefs_exec_enable_noscript, 0, "Enable exec protection [no scripts]");
3428 Index: pefs_kmod/sys/fs/pefs/pefs_checksum.c
3429 ===================================================================
3430 --- pefs_kmod/sys/fs/pefs/pefs_checksum.c	(revision 0)
3431 +++ pefs_kmod/sys/fs/pefs/pefs_checksum.c	(revision 240588)
3432 @@ -0,0 +1,409 @@
3433 +/*-
3434 + * Copyright (c) 2012 Efstratios Karatzas
3435 + * All rights reserved.
3436 + *
3437 + * Redistribution and use in source and binary forms, with or without
3438 + * modification, are permitted provided that the following conditions
3439 + * are met:
3440 + * 1. Redistributions of source code must retain the above copyright
3441 + *    notice, this list of conditions and the following disclaimer.
3442 + * 2. Redistributions in binary form must reproduce the above copyright
3443 + *    notice, this list of conditions and the following disclaimer in the
3444 + *    documentation and/or other materials provided with the distribution.
3445 + *
3446 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
3447 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3448 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3449 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
3450 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3451 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3452 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3453 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3454 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3455 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3456 + * SUCH DAMAGE.
3457 + *
3458 + * $FreeBSD$
3459 + */
3460 +
3461 +#include <sys/cdefs.h>
3462 +__FBSDID("$FreeBSD$");
3463 +
3464 +#include <sys/param.h>
3465 +#include <sys/systm.h>
3466 +#include <sys/malloc.h>
3467 +#include <sys/mount.h>
3468 +#include <sys/namei.h>
3469 +#include <sys/stat.h>
3470 +#include <sys/vnode.h>
3471 +#include <sys/dirent.h>
3472 +#include <sys/endian.h>
3473 +#include <sys/fnv_hash.h>
3474 +
3475 +#include <crypto/sha2/sha2.h>
3476 +
3477 +#include <fs/pefs/pefs.h>
3478 +#include <fs/pefs/pefs_checksum.h>
3479 +
3480 +const char *pefs_checksum_supported_digests[] = {"sha256","sha512"};
3481 +uint8_t pefs_checksum_supported_hash_lengths[] = {32, 64};
3482 +
3483 +/* sanitize .pefs.checkum's global file header that's read during VFS_MOUNT() */
3484 +int
3485 +pefs_sanitize_checksum_header(struct pefs_checksum *pcs)
3486 +{
3487 +	int error, i;
3488 +
3489 +	error = 0;
3490 +	for (i=0; i < PEFS_CHECKSUM_SUPPORTED_DIGESTS; i++)
3491 +		if (strncmp(pefs_checksum_supported_digests[i], pcs->pcs_hash_algo_name,
3492 +			sizeof(pcs->pcs_hash_algo_name)) == 0)
3493 +			break;
3494 +
3495 +	pcs->pcs_hash_algo = i;
3496 +	switch(pcs->pcs_hash_algo) {
3497 +	/* FALLTHROUGH */
3498 +	case (PEFS_SHA256):
3499 +	case (PEFS_SHA512):
3500 +		dprintf(("digest: %s\n", pcs->pcs_hash_algo_name));
3501 +		if (pcs->pcs_hash_len != pefs_checksum_supported_hash_lengths[i]) {
3502 +			dprintf(("pefs_sanitize invalid algo len %u\n", pcs->pcs_hash_len));
3503 +			error = EINVAL;
3504 +		}
3505 +		break;
3506 +	default:
3507 +		dprintf(("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name));
3508 +		error = ENODEV;
3509 +		break;
3510 +	}
3511 +
3512 +	return (error);
3513 +}
3514 +
3515 +static uint32_t
3516 +pefs_checksum_hash1(struct pefs_checksum *pc,
3517 +	struct pefs_checksum_index_entry *pcie)
3518 +{
3519 +	uint32_t nbucket;
3520 +
3521 +	nbucket = pcie->pcie_fid.pcfi_num % pc->pcs_hash_table_size;
3522 +	dprintf(("hash1: goto bucket %d\n", nbucket));
3523 +	return (nbucket);
3524 +}
3525 +
3526 +static uint32_t
3527 +pefs_checksum_hash2(struct pefs_checksum *pc,
3528 +	struct pefs_checksum_index_entry *pcie)
3529 +{
3530 +	uint32_t nbucket;
3531 +
3532 +	nbucket = fnv_64_buf(&(pcie->pcie_fid.pcfi_num),
3533 +		sizeof(pcie->pcie_fid.pcfi_num), FNV1_64_INIT) %
3534 +		pc->pcs_hash_table_size;
3535 +	dprintf(("hash2: goto bucket %d\n", nbucket));
3536 +
3537 +	return (nbucket);
3538 +}
3539 +
3540 +/* fill out pcie from data pointed to by p */
3541 +static void
3542 +pefs_get_index_entry(char *p, struct pefs_checksum_index_entry *pcie)
3543 +{
3544 +	MPASS(p != NULL);
3545 +
3546 +	memcpy(&(pcie->pcie_nhashes), p, sizeof(pcie->pcie_nhashes));
3547 +	pcie->pcie_nhashes = le32toh(pcie->pcie_nhashes);
3548 +	if (pcie->pcie_nhashes != 0) {
3549 +		p+=sizeof(pcie->pcie_nhashes);
3550 +
3551 +		memcpy(&(pcie->pcie_offset), p, sizeof(pcie->pcie_offset));
3552 +		pcie->pcie_offset = le32toh(pcie->pcie_offset);
3553 +		p+=sizeof(pcie->pcie_offset);
3554 +
3555 +		memcpy(pcie->pcie_fid.pcfi_str, p, sizeof(pcie->pcie_fid.pcfi_str));
3556 +	}
3557 +}
3558 +
3559 +static int
3560 +pefs_checksum_index_lookup(struct pefs_checksum_index_entry *pcie,
3561 +	struct vnode *vp)
3562 +{
3563 +	struct pefs_checksum_index_entry target_pcie;
3564 +	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3565 +	struct pefs_checksum *pcs = &(pm->pm_checksum);
3566 +	struct pefs_node *pn = VP_TO_PN(vp);
3567 +	char *start;
3568 +	uint32_t pos;
3569 +
3570 +	pos = pefs_checksum_hash1(pcs, pcie);
3571 +	start = &(pcs->pcs_table1[pos * PEFS_HT_CELL_SIZE]);
3572 +	pefs_get_index_entry(start, &target_pcie);
3573 +
3574 +	if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
3575 +		dprintf(("cell %d:\n", pos));
3576 +		dprintf(("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
3577 +			target_pcie.pcie_nhashes, target_pcie.pcie_offset,
3578 +			target_pcie.pcie_fid.pcfi_num));
3579 +
3580 +		if (target_pcie.pcie_fid.pcfi_num == pcie->pcie_fid.pcfi_num) {
3581 +			pn->pn_checksum_index_entry = start;
3582 +			dprintf(("checksum lookup: found1!\n"));
3583 +			return (0);
3584 +		}
3585 +	}
3586 +
3587 +	pos = pefs_checksum_hash2(pcs, pcie);
3588 +	start = &(pcs->pcs_table2[pos * PEFS_HT_CELL_SIZE]);
3589 +	pefs_get_index_entry(start, &target_pcie);
3590 +
3591 +	if (!PEFS_EMPTY_INDEX_ENTRY(&target_pcie)) {
3592 +		dprintf(("cell %d:\n", pos));
3593 +		dprintf(("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
3594 +			target_pcie.pcie_nhashes, target_pcie.pcie_offset,
3595 +			target_pcie.pcie_fid.pcfi_num));
3596 +
3597 +		if (target_pcie.pcie_fid.pcfi_num == pcie->pcie_fid.pcfi_num) {
3598 +			pn->pn_checksum_index_entry = start;
3599 +			dprintf(("checksum lookup: found2!\n"));
3600 +			return (0);
3601 +		}
3602 +	}
3603 +
3604 +	pn->pn_checksum_index_entry = NULL;
3605 +	dprintf(("checksum lookup: not found!\n"));
3606 +	return (ENOENT);
3607 +}
3608 +
3609 +void
3610 +pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
3611 +	struct componentname *cnp, struct vnode *vp)
3612 +{
3613 +	struct vattr va;
3614 +	struct pefs_checksum_index_entry pcie;
3615 +	struct pefs_node *pn = VP_TO_PN(vp);
3616 +	struct ucred *cred = vp->v_mount->mnt_cred;
3617 +	char *buf;
3618 +	size_t buf_len;
3619 +	int error, r;
3620 +
3621 +	dprintf(("gpf: checksum code @ lookup\n"));
3622 +	if ((cnp != NULL && cnp->cn_nameiop != LOOKUP) || (vp->v_type != VREG &&
3623 +		vp->v_type != VLNK)
3624 +		|| ((pn->pn_flags & PN_NO_CHECKSUM) != 0))
3625 +		goto not_found;
3626 +
3627 +	if (strncmp(enc_name, ".pefs.db", enc_name_len) == 0 ||
3628 +		strncmp(enc_name, ".pefs.conf", enc_name_len) == 0 ||
3629 +		strncmp(enc_name, ".pefs.checksum", enc_name_len) == 0)
3630 +		goto not_found;
3631 +
3632 +	enc_name++;
3633 +	enc_name_len--;
3634 +	buf_len = MAXNAMLEN + 1;
3635 +	buf = malloc(buf_len, M_TEMP, M_WAITOK);
3636 +
3637 +	r = pefs_name_pton(enc_name, enc_name_len, buf, buf_len);
3638 +	if (r <= 0) {
3639 +		/* XXXgpf: I sincerely doubt an error can occur here */
3640 +		error = EINVAL;
3641 +		dprintf(("name_pton error: %d\n", error));
3642 +	}
3643 +	else {
3644 +		memcpy(pcie.pcie_fid.pcfi_str, buf, sizeof(pcie.pcie_fid.pcfi_str));
3645 +		dprintf(("id to lookup: %llu\n", pcie.pcie_fid.pcfi_num));
3646 +		error = pefs_checksum_index_lookup(&pcie, vp);
3647 +		if (error != 0) {
3648 +			free(buf, M_TEMP);
3649 +			goto not_found;
3650 +		}
3651 +	}
3652 +	/*
3653 +	 * Check to see if schg flag is set, if not mark the vnode so that all
3654 +	 * read access is denied.
3655 +	 *
3656 +	 * XXXgpf: [TODO] I should make sure that the PN_WRONG_CHECKSUM flag is
3657 +	 * used in other parts of the pefs codebase to deny access to the file
3658 +	 * data.
3659 +	 */
3660 +	error = VOP_GETATTR(vp, &va, cred);
3661 +	if (error != 0) {
3662 +		dprintf(("unable to retrieve attributes of %llu\n",
3663 +			pcie.pcie_fid.pcfi_num));
3664 +		pn->pn_flags|= PN_WRONG_CHECKSUM;
3665 +	}
3666 +	else {
3667 +		if ((va.va_flags & SF_IMMUTABLE) == 0) {
3668 +			dprintf(("schg not set for %llu\n", pcie.pcie_fid.pcfi_num));
3669 +			pn->pn_flags|= PN_WRONG_CHECKSUM;
3670 +		}
3671 +	}
3672 +
3673 +	free(buf, M_TEMP);
3674 +	return;
3675 +
3676 +not_found:
3677 +	pn->pn_flags|= PN_NO_CHECKSUM;
3678 +}
3679 +
3680 +static int
3681 +pefs_generate_checksum(struct vnode *vp, char *data, ssize_t data_len,
3682 +	unsigned char **digest, ssize_t *digest_len)
3683 +{
3684 +	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3685 +	struct pefs_checksum *pcs = &(pm->pm_checksum);
3686 +	unsigned char *dig;
3687 +
3688 +	switch(pcs->pcs_hash_algo) {
3689 +	case (PEFS_SHA256):
3690 +		*digest_len = SHA256_DIGEST_STRING_LENGTH;
3691 +		dig = malloc(*digest_len, M_TEMP, M_WAITOK);
3692 +		/*
3693 +		 * XXXgpf: Does this interface work for any length input?
3694 +		 * [TODO] Also, I should either use a different interface or store the
3695 +		 * checksums in hex during .pefs.checksum creation because turning
3696 +		 * them to hex at this point every single time we have a read is
3697 +		 * just silly.
3698 +		 */
3699 +		SHA256_Data(data, data_len, dig);
3700 +		break;
3701 +	case (PEFS_SHA512):
3702 +		*digest_len = SHA512_DIGEST_STRING_LENGTH;
3703 +		dig = malloc(*digest_len, M_TEMP, M_WAITOK);
3704 +		SHA512_Data(data, data_len, dig);
3705 +		break;
3706 +	default:
3707 +		dprintf(("pefs_sanitize invalid algo %s\n", pcs->pcs_hash_algo_name));
3708 +		return (ENODEV);
3709 +	}
3710 +
3711 +	*digest = dig;
3712 +	return (0);
3713 +}
3714 +
3715 +static int
3716 +pefs_retrieve_checksum(struct vnode *vp, struct pefs_checksum_index_entry *pcie,
3717 +	off_t block_offset, unsigned char **digest, ssize_t digest_len)
3718 +{
3719 +	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3720 +	struct pefs_checksum *pcs = &(pm->pm_checksum);
3721 +	struct ucred *cred = vp->v_mount->mnt_cred;
3722 +	struct uio *puio;
3723 +	struct pefs_chunk pc;
3724 +	unsigned char *dig;
3725 +	off_t checksum_offset;
3726 +	int error, i;
3727 +
3728 +	pefs_chunk_create(&pc, NULL, pcs->pcs_hash_len);
3729 +	checksum_offset = pcie->pcie_offset + pcs->pcs_hash_len *
3730 +		(block_offset / PEFS_SECTOR_SIZE);
3731 +	puio = pefs_chunk_uio(&pc, checksum_offset, UIO_READ);
3732 +
3733 +	/* XXXgpf: gleb says  I should use vn_rdwr instead of VOP_READ */
3734 +	error = VOP_READ(pcs->pcs_checksumvp, puio, IO_UNIT, cred);
3735 +	if (error != 0) {
3736 +		dprintf(("pefs_retrieve_checksum read error %d\n", error));
3737 +		return (error);
3738 +	}
3739 +
3740 +	dig = malloc(digest_len, M_TEMP, M_WAITOK);
3741 +	for (i=0; i < pcs->pcs_hash_len; i++)
3742 +		sprintf(&dig[i*2],"%02x", ((unsigned char *)pc.pc_base)[i]);
3743 +	dig[i*2] = '\0';
3744 +
3745 +	pefs_chunk_free(&pc, NULL);
3746 +	*digest = dig;
3747 +
3748 +	return (0);
3749 +}
3750 +
3751 +static int
3752 +pefs_compare_checksums(unsigned char *digest1, unsigned char *digest2,
3753 +	ssize_t digest_len)
3754 +{
3755 +	int error;
3756 +
3757 +	dprintf(("compare dig1: %s\n", digest1));
3758 +	dprintf(("compare dig2: %s\n", digest2));
3759 +
3760 +	error = memcmp(digest1, digest2, digest_len);
3761 +	if (error != 0) {
3762 +		dprintf(("checksum mismatch!\n"));
3763 +		error = EAUTH;
3764 +	}
3765 +
3766 +	return (error);
3767 +}
3768 +
3769 +int
3770 +pefs_integrity_check(struct vnode *vp, off_t offset, u_quad_t fsize,
3771 +	struct pefs_chunk *pc)
3772 +{
3773 +	struct pefs_checksum_index_entry pcie;
3774 +	struct pefs_node *pn = VP_TO_PN(vp);
3775 +	ssize_t digest_len, resid;
3776 +	unsigned char *digest1, *digest2;
3777 +	char *buf, *end;
3778 +	long *p;
3779 +	int error;
3780 +
3781 +	dprintf(("integrity checking!\noffset %llu\n", offset));
3782 +
3783 +	if ((pn->pn_flags & PN_WRONG_CHECKSUM) != 0)
3784 +		return (EAUTH);
3785 +
3786 +	pefs_get_index_entry(pn->pn_checksum_index_entry, &pcie);
3787 +
3788 +	dprintf(("id: %llu\n", pcie.pcie_fid.pcfi_num));
3789 +
3790 +	buf = (char *)pc->pc_base;
3791 +	end = buf + pc->pc_size;
3792 +
3793 +	if ((fsize > pcie.pcie_nhashes * PEFS_SECTOR_SIZE) ||
3794 +		(fsize < (pcie.pcie_nhashes - 1) * PEFS_SECTOR_SIZE)) {
3795 +		dprintf(("file size differs from the one in .pefs.checksum\n"));
3796 +		pn->pn_flags|= PN_WRONG_CHECKSUM;
3797 +		return (EAUTH);
3798 +	}
3799 +
3800 +	while (buf < end) {
3801 +		if ((end - buf) >= PEFS_SECTOR_SIZE) {
3802 +			p = (long *)buf;
3803 +			resid = PEFS_SECTOR_SIZE / sizeof(long);
3804 +			for (; resid > 0; resid--)
3805 +				if (*(p++) != 0)
3806 +					break;
3807 +			if (resid == 0) {
3808 +				bzero(buf, PEFS_SECTOR_SIZE);
3809 +				offset += PEFS_SECTOR_SIZE;
3810 +				buf += PEFS_SECTOR_SIZE;
3811 +				continue;
3812 +			}
3813 +			resid = PEFS_SECTOR_SIZE;
3814 +		}
3815 +		else
3816 +			resid = end - buf;
3817 +
3818 +		error = pefs_generate_checksum(vp, buf, resid, &digest1, &digest_len);
3819 +		if (error != 0)
3820 +			return (error);
3821 +
3822 +		error = pefs_retrieve_checksum(vp, &pcie, offset, &digest2, digest_len);
3823 +		if (error != 0) {
3824 +			free(digest1, M_TEMP);
3825 +			return (error);
3826 +		}
3827 +
3828 +		error = pefs_compare_checksums(digest1, digest2, digest_len);
3829 +		free(digest1, M_TEMP);
3830 +		free(digest2, M_TEMP);
3831 +		if (error != 0) {
3832 +			pn->pn_flags|= PN_WRONG_CHECKSUM;
3833 +			return (error);
3834 +		}
3835 +
3836 +		buf += resid;
3837 +		offset += resid;
3838 +	}
3839 +
3840 +	return (0);
3841 +}
3842 Index: pefs_kmod/sys/fs/pefs/pefs_vnops.c
3843 ===================================================================
3844 --- pefs_kmod/sys/fs/pefs/pefs_vnops.c	(revision 235719)
3845 +++ pefs_kmod/sys/fs/pefs/pefs_vnops.c	(revision 240588)
3846 @@ -64,6 +64,7 @@
3847  #include <vm/vnode_pager.h>
3848  
3849  #include <fs/pefs/pefs.h>
3850 +#include <fs/pefs/pefs_checksum.h>
3851  #include <fs/pefs/pefs_dircache.h>
3852  
3853  #define	DIRENT_MINSIZE		(sizeof(struct dirent) - (MAXNAMLEN + 1))
3854 @@ -474,6 +475,7 @@
3855  pefs_lookup(struct vop_cachedlookup_args *ap)
3856  {
3857  	struct componentname *cnp = ap->a_cnp;
3858 +	struct pefs_mount *pm;
3859  	struct vnode *vp = NULL;
3860  	struct vnode *lvp = NULL;
3861  	struct vnode *dvp = ap->a_dvp;
3862 @@ -484,8 +486,8 @@
3863  	int nokey_lookup, skip_lookup;
3864  	int error;
3865  
3866 -	PEFSDEBUG("pefs_lookup: op=%lx, name=%.*s\n",
3867 -	    cnp->cn_nameiop, (int)cnp->cn_namelen, cnp->cn_nameptr);
3868 +	dprintf(("pefs_lookup: op=%lx, name=%.*s\n",
3869 +	    cnp->cn_nameiop, (int)cnp->cn_namelen, cnp->cn_nameptr));
3870  
3871  	pefs_enccn_init(&enccn);
3872  
3873 @@ -560,6 +562,22 @@
3874  			if (error != 0) {
3875  				vput(lvp);
3876  			} else {
3877 +				pm = VFS_TO_PEFS(vp->v_mount);
3878 +				if ((pm->pm_flags & PM_CHECKSUM) != 0) {
3879 +					if (nokey_lookup) {
3880 +						dprintf(("cnp name=%.*s\n",(int)cnp->cn_namelen,
3881 +							cnp->cn_nameptr));
3882 +						pefs_checksum_lookup(cnp->cn_nameptr,
3883 +							cnp->cn_namelen, cnp, vp);
3884 +					}
3885 +					else {
3886 +						dprintf(("enccnp name=%.*s\n",
3887 +							(int)enccn.pec_cn.cn_namelen,
3888 +							enccn.pec_cn.cn_nameptr));
3889 +						pefs_checksum_lookup(enccn.pec_cn.cn_nameptr,
3890 +							enccn.pec_cn.cn_namelen, cnp, vp);
3891 +					}
3892 +				}
3893  				*ap->a_vpp = vp;
3894  				if ((cnp->cn_flags & MAKEENTRY) &&
3895  				    cnp->cn_nameiop != CREATE)
3896 @@ -1901,6 +1919,7 @@
3897  		return (error);
3898  
3899  	error = pefs_read_int(vp, uio, ioflag, cred, fsize);
3900 +
3901  	return (error);
3902  }
3903  
3904 @@ -1910,6 +1929,7 @@
3905  {
3906  	struct vnode *lvp = PEFS_LOWERVP(vp);
3907  	struct uio *puio;
3908 +	struct pefs_mount *pm = VFS_TO_PEFS(vp->v_mount);
3909  	struct pefs_node *pn = VP_TO_PN(vp);
3910  	struct pefs_chunk pc;
3911  	struct sf_buf *sf;
3912 @@ -1966,6 +1986,11 @@
3913  
3914  		/* XXX assert full buffer is read */
3915  		pefs_chunk_setsize(&pc, done);
3916 +		if ((pm->pm_flags & PM_CHECKSUM) != 0 && PEFS_NEEDS_CHECKING(pn)) {
3917 +			error = pefs_integrity_check(vp, poffset, fsize, &pc);
3918 +			if (error != 0)
3919 +				break;
3920 +		}
3921  		pefs_data_decrypt(&pn->pn_tkey, poffset, &pc);
3922  		if (nocopy == 0) {
3923  			error = pefs_chunk_copy(&pc, bskip, uio);
3924 @@ -2355,15 +2380,28 @@
3925  static int
3926  pefs_ioctl(struct vop_ioctl_args *ap)
3927  {
3928 +	struct pefs_enccn enccn;
3929 +	struct componentname cn;
3930 +	struct pefs_chunk pc;
3931 +	struct nameidata nd, *ndp = &nd;
3932 +	u_quad_t fsize;
3933  	struct vnode *vp = ap->a_vp;
3934 +	struct vnode *lvp = PEFS_LOWERVP(vp);
3935 +	struct vnode *svp, *slvp;
3936  	struct pefs_xkey *xk = ap->a_data;
3937 +	struct pefs_xnamecsum *xncs = ap->a_data;
3938 +	struct pefs_xsector_ctext *xsct = ap->a_data;
3939 +	struct pefs_xslink_ctext *xsl = ap->a_data;
3940  	struct ucred *cred = ap->a_cred;
3941  	struct thread *td = ap->a_td;
3942  	struct mount *mp = vp->v_mount;
3943  	struct pefs_mount *pm = VFS_TO_PEFS(mp);
3944  	struct pefs_node *pn;
3945  	struct pefs_key *pk;
3946 -	int error = 0, i;
3947 +	struct uio *puio;
3948 +	char *enc, *buf;
3949 +	size_t enc_len, buf_len;
3950 +	int error = 0, i, r;
3951  
3952  	if (mp->mnt_cred->cr_uid != cred->cr_uid) {
3953  		error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
3954 @@ -2465,6 +2503,179 @@
3955  		if (pefs_key_remove_all(pm))
3956  			pefs_flushkey(mp, td, PEFS_FLUSHKEY_ALL, NULL);
3957  		break;
3958 +	case PEFS_GETSECTORCTEXT:
3959 +		vn_lock(vp, LK_EXCLUSIVE);
3960 +
3961 +		if (vp->v_type != VREG) {
3962 +			dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT vp is not a reg file\n"));
3963 +			VOP_UNLOCK(vp, 0);
3964 +			return (EOPNOTSUPP);
3965 +		}
3966 +
3967 +		error = pefs_getsize(vp, &fsize, cred);
3968 +		if (error != 0) {
3969 +			VOP_UNLOCK(vp, 0);
3970 +			return (error);
3971 +		}
3972 +
3973 +		if (xsct->pxsct_ctext_len > PEFS_SECTOR_SIZE ||
3974 +			xsct->pxsct_ctext_len == 0 ||
3975 +			xsct->pxsct_ctext_len > fsize) {
3976 +			dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT invalid len: %d\n",
3977 +						xsct->pxsct_ctext_len));
3978 +			VOP_UNLOCK(vp, 0);
3979 +			return (EINVAL);
3980 +		}
3981 +
3982 +		if (xsct->pxsct_offset > (fsize - xsct->pxsct_ctext_len)) {
3983 +			dprintf(("pefs_ioctl: PEFS_GETSECTORCTEXT invalid offset: %llu\n",
3984 +						xsct->pxsct_offset));
3985 +			VOP_UNLOCK(vp, 0);
3986 +			return (EINVAL);
3987 +		}
3988 +
3989 +		pn = VP_TO_PN(vp);
3990 +		pefs_chunk_create(&pc, pn, xsct->pxsct_ctext_len);
3991 +		puio = pefs_chunk_uio(&pc, xsct->pxsct_offset, UIO_READ);
3992 +
3993 +		error = VOP_READ(lvp, puio, IO_UNIT, cred);
3994 +
3995 +		if (error == 0)
3996 +			memcpy(xsct->pxsct_ctext, pc.pc_base, xsct->pxsct_ctext_len);
3997 +
3998 +		pefs_chunk_free(&pc, pn);
3999 +		VOP_UNLOCK(vp, 0);
4000 +		break;
4001 +	case PEFS_GETNAMECSUM:
4002 +		vn_lock(vp, LK_EXCLUSIVE);
4003 +		if (vp->v_type != VDIR) {
4004 +			dprintf(("pefs_ioctl: PEFS_GETNAMEMAC vp is not a directory\n"));
4005 +			VOP_UNLOCK(vp, 0);
4006 +			return (ENOTDIR);
4007 +		}
4008 +
4009 +		if (strnlen(xncs->pxnc_filename, sizeof(xncs->pxnc_filename)) !=
4010 +			xncs->pxnc_namelen) {
4011 +			dprintf(("pefs_ioctl: PEFS_GETNAMEMAC incorrect pxnc_namelen %d\n",
4012 +				xncs->pxnc_namelen));
4013 +			VOP_UNLOCK(vp, 0);
4014 +			return (EINVAL);
4015 +		}
4016 +
4017 +		if (strchr(xncs->pxnc_filename, '/') != NULL) {
4018 +			dprintf(("pefs_ioctl: PEFS_GETNAMEMAC pxnc_filename contains '/'\n");
4019 +			VOP_UNLOCK(vp, 0));
4020 +			return (EINVAL);
4021 +		}
4022 +
4023 +		pefs_enccn_init(&enccn);
4024 +		if (pefs_no_keys(vp)) {
4025 +			enc = xncs->pxnc_filename;
4026 +			enc_len = xncs->pxnc_namelen;
4027 +			error = 0;
4028 +		}
4029 +		else {
4030 +			cn.cn_nameiop = LOOKUP;
4031 +			cn.cn_thread = td;
4032 +			cn.cn_cred = cred;
4033 +			cn.cn_lkflags = 0;
4034 +			cn.cn_flags = 0;
4035 +			cn.cn_nameptr = xncs->pxnc_filename;
4036 +			cn.cn_namelen = xncs->pxnc_namelen;
4037 +
4038 +			/* XXXgpf: does this lookup rely solely on present cache data? */
4039 +			error = pefs_enccn_lookup(&enccn, vp, &cn);
4040 +			if (error == 0) {
4041 +				enc = enccn.pec_cn.cn_nameptr;
4042 +				enc_len = enccn.pec_cn.cn_namelen;
4043 +			}
4044 +		}
4045 +		VOP_UNLOCK(vp, 0);
4046 +
4047 +		if (error == 0) {
4048 +			if (enc[0] != '.' || enc_len <= 1) {
4049 +				pefs_enccn_free(&enccn);
4050 +				error = EINVAL;
4051 +				break;
4052 +			}
4053 +			enc++;
4054 +			enc_len--;
4055 +			buf_len = MAXNAMLEN + 1;
4056 +			buf = malloc(buf_len, M_TEMP, M_WAITOK);
4057 +
4058 +			r = pefs_name_pton(enc, enc_len, buf, buf_len);
4059 +			if (r <= 0)
4060 +				error = EINVAL;
4061 +			else
4062 +				memcpy(xncs->pxnc_csum, buf, sizeof(xncs->pxnc_csum));
4063 +
4064 +			pefs_enccn_free(&enccn);
4065 +			free(buf, M_TEMP);
4066 +		}
4067 +		break;
4068 +	case PEFS_GETSLINKCTEXT:
4069 +		if (vp->v_type != VDIR) {
4070 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT vp is not a directory\n"));
4071 +			VOP_UNLOCK(vp, 0);
4072 +			return (ENOTDIR);
4073 +		}
4074 +
4075 +		if (strnlen(xsl->pxsl_filename, sizeof(xsl->pxsl_filename)) !=
4076 +			xsl->pxsl_namelen) {
4077 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT incorrect namelen %d\n",
4078 +				xsl->pxsl_namelen));
4079 +			VOP_UNLOCK(vp, 0);
4080 +			return (EINVAL);
4081 +		}
4082 +
4083 +		if (strchr(xsl->pxsl_filename, '/') != NULL) {
4084 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT filename contains '/'\n"));
4085 +			VOP_UNLOCK(vp, 0);
4086 +			return (EINVAL);
4087 +		}
4088 +
4089 +		/* grab a vnodep for our symlink */
4090 +		NDINIT_ATVP(ndp, LOOKUP, MPSAFE | LOCKPARENT | LOCKLEAF | NOFOLLOW,
4091 +			UIO_SYSSPACE, xsl->pxsl_filename, vp, td);
4092 +		error = namei(ndp);
4093 +		if (error != 0) {
4094 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT namei error %d\n", error));
4095 +			return (error);
4096 +		}
4097 +		NDFREE(ndp, NDF_ONLY_PNBUF);
4098 +		svp = ndp->ni_vp;
4099 +
4100 +		if (ndp->ni_dvp != vp) {
4101 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT namei wrong parent\n"));
4102 +			vput(svp);
4103 +			VOP_UNLOCK(vp, 0);
4104 +			return (ENOENT);
4105 +		}
4106 +
4107 +		if (svp->v_type != VLNK) {
4108 +			dprintf(("pefs_ioctl: PEFS_GETSLINKCTEXT svp is not a symlink\n"));
4109 +			vput(svp);
4110 +			VOP_UNLOCK(vp, 0);
4111 +			return (EFTYPE);
4112 +		}
4113 +
4114 +		/* VOP_READLINK our slvp */
4115 +		slvp = PEFS_LOWERVP(svp);
4116 +
4117 +		pn = VP_TO_PN(svp);
4118 +		pefs_chunk_create(&pc, pn, MAXPATHLEN + 1);
4119 +		puio = pefs_chunk_uio(&pc, 0, UIO_READ);
4120 +
4121 +		error = VOP_READLINK(slvp, puio, cred);
4122 +
4123 +		xsl->pxsl_target_len = pc.pc_size - pc.pc_uio.uio_resid;
4124 +		if (error == 0)
4125 +			memcpy(xsl->pxsl_target, pc.pc_base, xsl->pxsl_target_len);
4126 +
4127 +		pefs_chunk_free(&pc, pn);
4128 +		vput(svp);
4129 +		VOP_UNLOCK(vp, 0);
4130 +		break;
4131  	default:
4132  		error = ENOTTY;
4133  		break;
4134 Index: pefs_kmod/sys/fs/pefs/pefs_checksum.h
4135 ===================================================================
4136 --- pefs_kmod/sys/fs/pefs/pefs_checksum.h	(revision 0)
4137 +++ pefs_kmod/sys/fs/pefs/pefs_checksum.h	(revision 240588)
4138 @@ -0,0 +1,81 @@
4139 +/*-
4140 + * Copyright (c) 2012 Efstratios Karatzas
4141 + * All rights reserved.
4142 + *
4143 + * Redistribution and use in source and binary forms, with or without
4144 + * modification, are permitted provided that the following conditions
4145 + * are met:
4146 + * 1. Redistributions of source code must retain the above copyright
4147 + *    notice, this list of conditions and the following disclaimer.
4148 + * 2. Redistributions in binary form must reproduce the above copyright
4149 + *    notice, this list of conditions and the following disclaimer in the
4150 + *    documentation and/or other materials provided with the distribution.
4151 + *
4152 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4153 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4154 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4155 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4156 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4157 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4158 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4159 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4160 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
4161 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4162 + * SUCH DAMAGE.
4163 + *
4164 + * $FreeBSD$
4165 + */
4166 +
4167 +#define PEFS_FILE_CHECKSUM		".pefs.checksum"
4168 +
4169 +#define PEFS_CFH_SIZE 16	/* file header of .pefs.checksum file */
4170 +#define PEFS_HT_CELL_SIZE 16 /* hash table cell(bucket) size */
4171 +
4172 +#define PEFS_SIGNATURE_MAX_LENGTH 512
4173 +
4174 +#define PEFS_CHECKSUM_SUPPORTED_DIGESTS	2
4175 +
4176 +#define PEFS_SHA256 0
4177 +#define PEFS_SHA512 1
4178 +
4179 +/*
4180 + * XXXgpf: use this instead of PEFS_DEBUG so as to lower the number of debugging
4181 + * messages during code development.
4182 + */
4183 +//#define PEFS_INTEGRITY_DEBUG
4184 +#if defined (PEFS_INTEGRITY_DEBUG)
4185 +#define dprintf(a)		printf a
4186 +#else
4187 +#define dprintf(a)      (void)0
4188 +#endif
4189 +
4190 +union pefs_checksum_file_id {
4191 +	uint64_t pcfi_num;
4192 +	uint8_t	pcfi_str[8];
4193 +};
4194 +
4195 +struct pefs_checksum_index_entry {
4196 +	uint32_t pcie_nhashes;
4197 +	uint32_t pcie_offset;
4198 +	union pefs_checksum_file_id pcie_fid;
4199 +};
4200 +
4201 +static __inline int
4202 +PEFS_EMPTY_INDEX_ENTRY(struct pefs_checksum_index_entry *pcie)
4203 +{
4204 +	MPASS(pcie != NULL);
4205 +	return ((pcie->pcie_nhashes == 0) ? 1 : 0);
4206 +}
4207 +
4208 +static __inline int
4209 +PEFS_NEEDS_CHECKING(struct pefs_node *pn)
4210 +{
4211 +	MPASS(pn != NULL);
4212 +	return ((pn->pn_checksum_index_entry != NULL) ? 1 : 0);
4213 +}
4214 +
4215 +void	pefs_checksum_lookup(char *enc_name, size_t enc_name_len,
4216 +			struct componentname *cnp, struct vnode *vp);
4217 +int		pefs_integrity_check(struct vnode *vp, off_t offset,
4218 +			u_quad_t fsize,	struct pefs_chunk *pc);
4219 +int		pefs_sanitize_checksum_header(struct pefs_checksum *pcs);
4220 Index: pefs_kmod/sys/fs/pefs/pefs_subr.c
4221 ===================================================================
4222 --- pefs_kmod/sys/fs/pefs/pefs_subr.c	(revision 235719)
4223 +++ pefs_kmod/sys/fs/pefs/pefs_subr.c	(revision 240588)
4224 @@ -52,6 +52,7 @@
4225  #include <sys/vnode.h>
4226  
4227  #include <fs/pefs/pefs.h>
4228 +#include <fs/pefs/pefs_checksum.h>
4229  #include <fs/pefs/pefs_dircache.h>
4230  
4231  typedef int	pefs_node_init_fn(struct mount *mp, struct pefs_node *pn,
4232 @@ -400,6 +401,7 @@
4233  		PEFSDEBUG("pefs_node_get: creating node without key: %p\n", pn);
4234  
4235  	pn->pn_vnode = vp;
4236 +	pn->pn_checksum_index_entry = NULL;
4237  	vp->v_type = lvp->v_type;
4238  	vp->v_data = pn;
4239  	vp->v_vnlock = lvp->v_vnlock;
4240 @@ -454,8 +456,24 @@
4241  pefs_node_get_lookupkey(struct mount *mp, struct vnode *lvp, struct vnode **vpp,
4242      struct ucred *cred)
4243  {
4244 +	struct pefs_mount *pm = VFS_TO_PEFS(mp);
4245 +	char *encname;
4246 +	int encname_len, error;
4247 +
4248  	MPASS(cred != NULL);
4249 -	return (pefs_node_get(mp, lvp, vpp, pefs_node_init_lookupkey, cred));
4250 +	error = pefs_node_get(mp, lvp, vpp, pefs_node_init_lookupkey, cred);
4251 +	if ((error == 0) && (pm->pm_flags & PM_CHECKSUM) != 0) {		
4252 +		encname_len = MAXNAMLEN + 1;
4253 +		encname = malloc(encname_len, M_TEMP, M_WAITOK);
4254 +
4255 +		error = pefs_node_lookup_name(lvp, NULL, cred, encname, &encname_len);
4256 +		if (error == 0) {
4257 +			printf("lookupkey encname=%.*s\n",(int)encname_len, encname);
4258 +			pefs_checksum_lookup(encname, encname_len, NULL, *vpp);
4259 +		}
4260 +		free(encname, M_TEMP);
4261 +	}
4262 +	return (error);
4263  }
4264  
4265  static __inline void
4266 Index: pefs_kmod/sys/fs/pefs/pefs_xbase64.c
4267 ===================================================================
4268 --- pefs_kmod/sys/fs/pefs/pefs_xbase64.c	(revision 235719)
4269 +++ pefs_kmod/sys/fs/pefs/pefs_xbase64.c	(revision 240588)
4270 @@ -48,6 +48,7 @@
4271  #include <sys/libkern.h>
4272  #include <sys/mount.h>
4273  #include <sys/vnode.h>
4274 +#include <sys/dirent.h>
4275  
4276  #include <fs/pefs/pefs.h>
4277  
4278 Index: pefs_kmod/sys/fs/pefs/pefs_vfsops.c
4279 ===================================================================
4280 --- pefs_kmod/sys/fs/pefs/pefs_vfsops.c	(revision 235719)
4281 +++ pefs_kmod/sys/fs/pefs/pefs_vfsops.c	(revision 240588)
4282 @@ -34,6 +34,7 @@
4283  #include <sys/cdefs.h>
4284  __FBSDID("$FreeBSD$");
4285  
4286 +#include <sys/endian.h>
4287  #include <sys/param.h>
4288  #include <sys/systm.h>
4289  #include <sys/fcntl.h>
4290 @@ -44,8 +45,10 @@
4291  #include <sys/namei.h>
4292  #include <sys/proc.h>
4293  #include <sys/vnode.h>
4294 +#include <sys/dirent.h>
4295  
4296  #include <fs/pefs/pefs.h>
4297 +#include <fs/pefs/pefs_checksum.h>
4298  
4299  struct pefs_opt_descr {
4300  	char	*fs;
4301 @@ -72,10 +75,12 @@
4302  	"dircache",
4303  	"nodircache",
4304  	"asyncreclaim",
4305 +	"checksum",
4306  	NULL
4307  };
4308  
4309  static MALLOC_DEFINE(M_PEFSMNT, "pefs_mount", "PEFS mount structure");
4310 +static MALLOC_DEFINE(M_PEFSCSTABLE, "pefs_checksum_table", "PEFS checksum table");
4311  
4312  static void
4313  pefs_opt_set(struct mount *mp, int opt, struct pefs_mount *pm,
4314 @@ -119,6 +124,136 @@
4315  	return (0);
4316  }
4317  
4318 +/* XXXgpf: tmp 4 dbg purposes */
4319 +static void
4320 +pefs_dbg_vnode(struct vnode *vp, char *str)
4321 +{
4322 +	if (vp == NULL)
4323 +		return;
4324 +	dprintf(("%s is locked: %d\n", str, VOP_ISLOCKED(vp)));
4325 +	dprintf(("%s usecount: %d\n", str, vp->v_usecount));
4326 +	dprintf(("%s holdcnt: %d\n", str, vp->v_holdcnt));
4327 +	dprintf(("%s writecount: %d\n", str, vp->v_writecount));
4328 +}
4329 +
4330 +static int
4331 +pefs_checksum_load(struct mount *mp)
4332 +{
4333 +	char *bufp, *path;
4334 +	struct nameidata nd, *ndp = &nd;
4335 +	struct pefs_chunk pc;
4336 +	struct pefs_mount *pm = VFS_TO_PEFS(mp);
4337 +	struct pefs_checksum *pcs = &(pm->pm_checksum);
4338 +	struct ucred *cred = mp->mnt_cred;
4339 +	struct uio *puio;
4340 +	struct vnode *checksumvp;
4341 +	uint32_t buflen, pathlen;
4342 +	int error;
4343 +
4344 +	pcs->pcs_checksumvp = NULL;
4345 +	pcs->pcs_table1 = NULL;
4346 +	pcs->pcs_table2 = NULL;
4347 +
4348 +	pathlen = MAXPATHLEN + 1;
4349 +	path = malloc(pathlen, M_TEMP, M_WAITOK);
4350 +	snprintf(path, pathlen, "%s/%s", mp->mnt_stat.f_mntfromname,
4351 +		PEFS_FILE_CHECKSUM);
4352 +
4353 +	/* grab a vp for our checksum file */
4354 +	NDINIT(ndp, LOOKUP, LOCKLEAF, UIO_SYSSPACE, path, curthread);
4355 +	error = namei(ndp);
4356 +	free(path, M_TEMP);
4357 +	if (error != 0) {
4358 +		dprintf(("pefs_checksum_load: namei error %d\n", error));
4359 +		return (error);
4360 +	}
4361 +
4362 +	checksumvp = ndp->ni_vp;
4363 +	NDFREE(ndp, NDF_NO_VP_PUT);
4364 +
4365 +	/* read checksum file header info */
4366 +	buflen = PEFS_CFH_SIZE;
4367 +	pefs_chunk_create(&pc, NULL, buflen);
4368 +	puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH, UIO_READ);
4369 +
4370 +	/* XXXgpf: gleb says  I should use vn_rdwr instead of VOP_READ */
4371 +	error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4372 +	if (error != 0) {
4373 +		dprintf(("pefs_checksum_load: vop_read1 error %d\n", error));
4374 +		pefs_chunk_free(&pc, NULL);
4375 +		vput(checksumvp);
4376 +		return (error);
4377 +	}
4378 +
4379 +	bufp = pc.pc_base;
4380 +	memcpy(&(pcs->pcs_version), bufp, sizeof(pcs->pcs_version));
4381 +	bufp+=sizeof(pcs->pcs_version);
4382 +	memcpy(&(pcs->pcs_reserved), bufp, sizeof(pcs->pcs_reserved));
4383 +	bufp+=sizeof(pcs->pcs_reserved);
4384 +	memcpy(&(pcs->pcs_hash_len), bufp, sizeof(pcs->pcs_hash_len));
4385 +	bufp+=sizeof(pcs->pcs_hash_len);
4386 +	memcpy(&(pcs->pcs_hash_algo_name), bufp, sizeof(pcs->pcs_hash_algo_name));
4387 +	bufp+=sizeof(pcs->pcs_hash_algo_name);
4388 +	memcpy(&(pcs->pcs_offset_to_hash_table), bufp,
4389 +		sizeof(pcs->pcs_offset_to_hash_table));
4390 +	bufp+=sizeof(pcs->pcs_offset_to_hash_table);
4391 +	memcpy(&(pcs->pcs_hash_table_size), bufp, sizeof(pcs->pcs_hash_table_size));
4392 +	pcs->pcs_hash_table_size = le32toh(pcs->pcs_hash_table_size);
4393 +
4394 +	error = pefs_sanitize_checksum_header(pcs);
4395 +	if (error != 0) {
4396 +		dprintf(("pefs_checksum_load: sanitize error %d\n", error));
4397 +		pefs_chunk_free(&pc, NULL);
4398 +		vput(checksumvp);
4399 +		return (error);
4400 +	}
4401 +
4402 +	pefs_chunk_free(&pc, NULL);
4403 +
4404 +	/* load and keep the 2 hash tables in kernel heap */
4405 +	buflen = pcs->pcs_hash_table_size * PEFS_HT_CELL_SIZE;
4406 +	pefs_chunk_create(&pc, NULL, buflen);
4407 +	puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH +
4408 +		pcs->pcs_offset_to_hash_table, UIO_READ);
4409 +
4410 +	error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4411 +	if (error != 0) {
4412 +		dprintf(("pefs_checksum_load: vop_read2 error %d\n", error));
4413 +		pefs_chunk_free(&pc, NULL);
4414 +		vput(checksumvp);
4415 +		return (error);
4416 +	}
4417 +
4418 +	pcs->pcs_table1 = malloc(buflen, M_PEFSCSTABLE, M_WAITOK);
4419 +	memcpy(pcs->pcs_table1, pc.pc_base, buflen);
4420 +	pefs_chunk_free(&pc, NULL);
4421 +
4422 +	pefs_chunk_create(&pc, NULL, buflen);
4423 +	puio = pefs_chunk_uio(&pc, PEFS_SIGNATURE_MAX_LENGTH +
4424 +		pcs->pcs_offset_to_hash_table +	buflen, UIO_READ);
4425 +
4426 +	error = VOP_READ(checksumvp, puio, IO_UNIT, cred);
4427 +	if (error != 0) {
4428 +		dprintf(("pefs_checksum_load: vop_read3 error %d\n", error));
4429 +		pefs_chunk_free(&pc, NULL);
4430 +		free(pcs->pcs_table1, M_PEFSCSTABLE);
4431 +		vput(checksumvp);
4432 +		return (error);
4433 +	}
4434 +
4435 +	pcs->pcs_table2 = malloc(buflen, M_PEFSCSTABLE, M_WAITOK);
4436 +	memcpy(pcs->pcs_table2, pc.pc_base, buflen);
4437 +	pefs_chunk_free(&pc, NULL);
4438 +
4439 +	pefs_dbg_vnode(checksumvp, "before VOP_UNLOCK checksumvp");
4440 +	/* keep the reference for checksumvp */
4441 +	VOP_UNLOCK(checksumvp, 0);
4442 +	pefs_dbg_vnode(checksumvp, "after VOP_UNLOCK checksumvp");
4443 +	pcs->pcs_checksumvp = checksumvp;
4444 +
4445 +	return (0);
4446 +}
4447 +
4448  /*
4449   * Mount null layer
4450   */
4451 @@ -131,7 +266,7 @@
4452  	struct pefs_mount *pm;
4453  	char *from, *from_free;
4454  	int isvnunlocked = 0, len;
4455 -	int opt_dircache, opt_asyncreclaim;
4456 +	int opt_checksum, opt_dircache, opt_asyncreclaim;
4457  	int error = 0;
4458  
4459  	PEFSDEBUG("pefs_mount(mp = %p)\n", (void *)mp);
4460 @@ -155,6 +290,12 @@
4461  		vfs_deleteopt(mp->mnt_optnew, "asyncreclaim");
4462  		opt_asyncreclaim = 1;
4463  	}
4464 +	opt_checksum = -1;
4465 +	if (vfs_flagopt(mp->mnt_optnew, "checksum", NULL, 0)) {
4466 +		vfs_deleteopt(mp->mnt_optnew, "checksum");
4467 +		dprintf(("checksum!\n"));
4468 +		opt_checksum = 1;
4469 +	}
4470  
4471  	if (mp->mnt_flag & MNT_UPDATE) {
4472  		error = EOPNOTSUPP;
4473 @@ -170,6 +311,9 @@
4474  			    PM_ASYNCRECLAIM, "asyncreclaim");
4475  			error = 0;
4476  		}
4477 +		/*
4478 +		 * XXXgpf: should we allow opt_checksum with MNT_UPDATE?
4479 +		 */
4480  		return (error);
4481  	}
4482  
4483 @@ -262,6 +406,7 @@
4484  		pm->pm_flags |= PM_ROOT_CANRECURSE;
4485  	pefs_opt_set(mp, opt_dircache, pm, PM_DIRCACHE, "dircache");
4486  	pefs_opt_set(mp, opt_asyncreclaim, pm, PM_ASYNCRECLAIM, "asyncreclaim");
4487 +	pefs_opt_set(mp, opt_checksum, pm, PM_CHECKSUM, "checksum");
4488  
4489  	/*
4490  	 * Save reference.  Each mount also holds
4491 @@ -302,6 +447,17 @@
4492  	mp->mnt_data =  pm;
4493  	vfs_getnewfsid(mp);
4494  
4495 +	if ((pm->pm_flags & PM_CHECKSUM) != 0) {
4496 +		pefs_dbg_vnode(lowerrootvp, "BEFORE lowerrootvp");
4497 +		error = pefs_checksum_load(mp);
4498 +		pefs_dbg_vnode(lowerrootvp, "AFTER lowerrootvp");
4499 +		if (error != 0) {
4500 +			vrele(vp);
4501 +			free(pm, M_PEFSMNT);
4502 +			return (error);
4503 +		}
4504 +	}
4505 +
4506  	PEFSDEBUG("pefs_mount: lower %s, alias at %s\n",
4507  		mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
4508  	return (0);
4509 @@ -331,6 +487,16 @@
4510  	 * Finally, throw away the pefs_mount structure
4511  	 */
4512  	pm = VFS_TO_PEFS(mp);
4513 +	if ((pm->pm_flags & PM_CHECKSUM) != 0) {
4514 +		/* free the hash tables from the heap */
4515 +		if (pm->pm_checksum.pcs_table1 != NULL)
4516 +			free(pm->pm_checksum.pcs_table1, M_PEFSCSTABLE);
4517 +		if (pm->pm_checksum.pcs_table2 != NULL)
4518 +			free(pm->pm_checksum.pcs_table2, M_PEFSCSTABLE);
4519 +		/* vrele the checksum file's vnode */
4520 +		if	(pm->pm_checksum.pcs_checksumvp != NULL)
4521 +			vrele(pm->pm_checksum.pcs_checksumvp);
4522 +	}
4523  	mp->mnt_data = 0;
4524  	pefs_key_remove_all(pm);
4525  	mtx_destroy(&pm->pm_keys_lock);
4526 Index: pefs_kmod/sys/modules/pefs/Makefile
4527 ===================================================================
4528 --- pefs_kmod/sys/modules/pefs/Makefile	(revision 235719)
4529 +++ pefs_kmod/sys/modules/pefs/Makefile	(revision 240588)
4530 @@ -6,7 +6,9 @@
4531  SRCS=	vnode_if.h \
4532  	pefs_subr.c pefs_vfsops.c pefs_vnops.c pefs_xbase64.c pefs_crypto.c \
4533  	pefs_dircache.c \
4534 -	pefs_xts.c vmac.c
4535 +	pefs_xts.c vmac.c \
4536 +	pefs_checksum.c \
4537 +	pefs_mac.c
4538  
4539  .if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
4540  SRCS+=	pefs_aesni.c
4541 @@ -17,12 +19,9 @@
4542  #DEBUG_FLAGS+= -DPEFS_DEBUG
4543  #DEBUG_FLAGS+= -DPEFS_DEBUG_EXTRA
4544  
4545 -CFLAGS+= -I${.CURDIR}/../../
4546 -
4547 -# Temporally build crypto/hmac into pefs module
4548 +CFLAGS+= -I${.CURDIR}/../../# Temporally build crypto/hmac into pefs module
4549  .PATH:	${.CURDIR}/../../crypto/hmac
4550  
4551  SRCS+=	hmac_sha512.c
4552  
4553  .include <bsd.kmod.mk>
4554 -
4555 

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2012-08-20T16:31:46+0000, 8.1 KB) [[attachment:head.diff]]
  • [get | view] (2012-08-20T16:32:06+0000, 124.0 KB) [[attachment:pefs.diff]]
  • [get | view] (2012-04-30T18:52:11+0000, 73.7 KB) [[attachment:pefs_design.pdf]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.