Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 14 Aug 2012 17:40:56 +0000
From:      gpf@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r240351 - in soc2012/gpf/pefs_kmod: sbin/pefs sys/fs/pefs
Message-ID:  <20120814174056.A5A21106564A@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gpf
Date: Tue Aug 14 17:40:56 2012
New Revision: 240351
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=240351

Log:
  minor fixes and some cosmetic changes
  

Modified:
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c
  soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h
  soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Tue Aug 14 14:07:34 2012	(r240350)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_checksum.c	Tue Aug 14 17:40:56 2012	(r240351)
@@ -53,11 +53,8 @@
 
 #include <fs/pefs/pefs.h>
 
-#include <openssl/dsa.h>
-#include <openssl/engine.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
-#include <openssl/rand.h>
 
 #include "pefs_ctl.h"
 
@@ -86,7 +83,7 @@
 TAILQ_HEAD(checksum_head, checksum);
 /* tail that contains all file headers that require integrity checking */
 TAILQ_HEAD(file_header_head, file_header);
-/* tail for all the file_headers that refere to the same inode */
+/* tail for all the file_headers that refer to the same inode */
 TAILQ_HEAD(hardlink_fh_head, file_header);
 
 /* RB tree for hardlink counters */
@@ -99,20 +96,26 @@
 #define PEFS_FH_SIZE 16
 
 /*
- * This struct is used to check if all hardlinks for a given inode are supplied
- * by the user
+ * This struct is used to check if all hardlinks for a given inode are
+ * supplied by the user.
  */
 struct hardlink_counter {
-	ino_t inode;	/* inode number for the file in question */
-	uint32_t total_links;	/* total hardlinks of the file */
-	uint32_t links_found;	/* how many links are found in user supplied list */
-	struct hardlink_fh_head file_headers;	/* file headers of the links we have found */
-	RB_ENTRY(hardlink_counter) hardlink_entries;	/* entry in hardlink RB tree */
+	/* inode number for the file in question */
+	ino_t inode;
+	/* total hardlinks of the file */
+	uint32_t total_links;
+	/* how many links are found in user supplied list */
+	uint32_t links_found;
+	/* file headers of the links we have found */
+	struct hardlink_fh_head file_headers;
+	/* entry in hardlink RB tree */
+	RB_ENTRY(hardlink_counter) hardlink_entries;
 };
 
-/* XXXgpf: unions for on disk structs and move to a different header? */
-
-/* this is the unique file header of the .pefs.checksum file, found in the beginning of the file */
+/*
+ * This is the unique file header of the .pefs.checksum file, found in
+ * the beginning of the file.
+ */
 struct checksum_file_header {
 	uint8_t version;
 	uint8_t reserved;
@@ -134,21 +137,39 @@
 
 /* XXXgpf: [TODO] turns offsets to uint64_t? */
 struct file_header {
-	/* on disk information */
-	uint32_t nhashes;	/* the number of hashes for the file */
-	uint32_t offset_to_checksums;	/* in file offset to start of checksums */
-	union file_id fid;	/* id is MAC tweak from filename (first 64 bits) */
-
-	/* in memory information */
-	char path[MAXPATHLEN + 1];	/* fullpath for this file */
-	char dirpath[MAXPATHLEN + 1]; /* fullpath for this file's parent dir */
-	char filename[MAXNAMLEN + 1]; /* filename */
-	char *target_path; /*fullpath to this symlink's immediate next target */
-	int fd, pfd;	/* file descriptors for the file and its parent dir */
-	int found;		/* mark that this entry was found during "verify" action */
-	struct checksum_head checksums;		/* this file's checksums */
-	TAILQ_ENTRY(file_header) file_header_entries;	/* entry in global file header tail */
-	TAILQ_ENTRY(file_header) fh_hardlink_entries;	/* entry in hardlink counter */
+	/*
+	 * on disk information
+	 */
+
+	/* the number of hashes for the file */
+	uint32_t nhashes;
+	/* in file offset to start of checksums */
+	uint32_t offset_to_checksums;
+	/* id is MAC tweak from filename (first 64 bits) */
+	union file_id fid;
+
+	/*
+	 * in memory information
+	 */
+
+	/* fullpath for this file */
+	char path[MAXPATHLEN + 1];
+	/* fullpath for this file's parent dir */
+	char dirpath[MAXPATHLEN + 1];
+	/* filename */
+	char filename[MAXNAMLEN + 1];
+	/*fullpath to this symlink's immediate next target */
+	char *target_path;
+	/* file descriptors for the file and its parent dir */
+	int fd, pfd;
+	/* mark that this entry was found during "verify" action */
+	int found;
+	/* this file's checksums */
+	struct checksum_head checksums;
+	/* entry in global file header tail */
+	TAILQ_ENTRY(file_header) file_header_entries;
+	/* entry in hardlink counter */
+	TAILQ_ENTRY(file_header) fh_hardlink_entries;
 };
 
 struct bucket {
@@ -203,8 +224,37 @@
 	return 0;
 }
 
+static struct file_header *
+pefs_allocate_file_header(void)
+{
+	struct file_header *fhp;
+
+	fhp = malloc(sizeof(struct file_header));
+	if (fhp == NULL) {
+		pefs_warn("memory allocation error");
+		return (NULL);
+	}
+
+	fhp->nhashes = 0;
+	fhp->offset_to_checksums = 0;
+	fhp->fid.fid_num = 0;
+	fhp->fd = -1;
+	fhp->pfd = -1;
+	fhp->found = 0;
+
+	fhp->target_path = NULL;
+
+	fhp->path[0] = '\0';
+	fhp->dirpath[0] = '\0';
+	fhp->filename[0] = '\0';
+
+	TAILQ_INIT(&(fhp->checksums));
+
+	return (fhp);
+}
+
 static void
-pefs_close_file(struct file_header *fhp)
+pefs_close_files(struct file_header *fhp)
 {
 	if (fhp->fd >= 0) {
 		close(fhp->fd);
@@ -216,6 +266,24 @@
 	}
 }
 
+static void
+pefs_free_file_header(struct file_header *fhp)
+{
+	struct checksum *csp, *tcsp;
+	if (fhp != NULL) {
+		pefs_close_files(fhp);
+		TAILQ_FOREACH_SAFE(csp, &(fhp->checksums), checksum_entries, tcsp) {
+			TAILQ_REMOVE(&(fhp->checksums), csp, checksum_entries);
+			if (csp->hash != NULL)
+				free(csp->hash);
+			free(csp);
+		}
+		if (fhp->target_path != NULL)
+			free(fhp->target_path);
+		free(fhp);
+	}
+}
+
 static int
 pefs_compute_symlink_checksum(struct file_header *fhp, const EVP_MD *md,
 	uint8_t hash_len, int flags)
@@ -355,10 +423,10 @@
 		EVP_DigestInit_ex(&mdctx, md, NULL);
 		EVP_DigestUpdate(&mdctx, buf, buf_len);
 
-		//dprintf(("read %d bytes\n\n", buf_len));
-		//dprintf(("printing contents of buffer:"));
-		//for (i=0; i < (int)buf_len; i++) dprintf(("%c", buf[i]));
-		//dprintf(("!\n"));
+		dprintf(("read %d bytes\n\n", buf_len));
+		dprintf(("printing contents of buffer:"));
+		for (i=0; i < (int)buf_len; i++) dprintf(("%c", buf[i]));
+			dprintf(("!\n"));
 
 		csp = malloc(sizeof(struct checksum));
 		if (csp == NULL) {
@@ -464,53 +532,6 @@
 	return (0);
 }
 
-static struct file_header *
-pefs_allocate_file_header(void)
-{
-	struct file_header *fhp;
-
-	fhp = malloc(sizeof(struct file_header));
-	if (fhp == NULL) {
-		pefs_warn("memory allocation error");
-		return (NULL);
-	}
-
-	fhp->nhashes = 0;
-	fhp->offset_to_checksums = 0;
-	fhp->fid.fid_num = 0;
-	fhp->fd = -1;
-	fhp->pfd = -1;
-	fhp->found = 0;
-
-	fhp->target_path = NULL;
-
-	fhp->path[0] = '\0';
-	fhp->dirpath[0] = '\0';
-	fhp->filename[0] = '\0';
-
-	TAILQ_INIT(&(fhp->checksums));
-
-	return (fhp);
-}
-
-static void
-pefs_free_file_header(struct file_header *fhp)
-{
-	struct checksum *csp, *tcsp;
-	if (fhp != NULL) {
-		pefs_close_file(fhp);
-		TAILQ_FOREACH_SAFE(csp, &(fhp->checksums), checksum_entries, tcsp) {
-			TAILQ_REMOVE(&(fhp->checksums), csp, checksum_entries);
-			if (csp->hash != NULL)
-				free(csp->hash);
-			free(csp);
-		}
-		if (fhp->target_path != NULL)
-			free(fhp->target_path);
-		free(fhp);
-	}
-}
-
 static void
 pefs_free_hash_table(struct cuckoo_hash_table *chtp)
 {
@@ -809,7 +830,7 @@
 					pefs_warn("target file %s of symlink %s was not "
 						"found in inputlist", targetfh.path, fhp->path);
 			}
-			pefs_close_file(&targetfh);
+			pefs_close_files(&targetfh);
 		}
 	}
 }
@@ -1123,7 +1144,7 @@
  * 			A1d) Open and store file descriptors to file & parent_directory.
  * 		A2) the file_id is retrieved. (filename MAC)
  * 		A3) list of checksums is computed for the file's 4k blocks.
- * 		A4) file entry is added to universal fh_head.
+ * 		A4) file entry is added to the universal fh_head.
  * B) Print warnings for hardlinks if the number of links found in inputlist
  * isn't equal to the number of total inode links.
  * C) Hash tables are allocated.
@@ -1133,7 +1154,7 @@
  * and try again until we succeed. The possibility to fail twice in a row is
  * 1.5% * 1.5% = 0.0225%
  * E) For each symlink found in input list, print warnings if its target file
- * was not found in input list as well since symlinks are not traversed.
+ * was not found in input list since symlinks are not traversed.
  */
 static int
 pefs_create_in_memory_db(FILE *fpin, const EVP_MD *md, uint8_t hash_len,
@@ -1174,7 +1195,7 @@
 		}
 
 		TAILQ_INSERT_TAIL(&fh_head, fhp, file_header_entries);
-		pefs_close_file(fhp);
+		pefs_close_files(fhp);
 	}
 
 	/* checking I/O error from pefs_next_file() */
@@ -1594,7 +1615,6 @@
 	unsigned char *sign;
 	int bytes, error, rval, sign_len;
 
-	/* read public key from .pefs.pkey */
 	pkey = pefs_read_dsa_pubkey(pk_fp);
 	if (pkey == NULL)
 		return (PEFS_ERR_SYS);
@@ -1859,11 +1879,11 @@
 	}
 	(*buckets_offset)+= sizeof(fhp->fid.fid_str);
 
-	//dprintf(("\nfile header offset = %d\n", *fh_offset));
-	//dprintf(("\n++priting file header info++\n"));
-	//dprintf(("nhashes %d\noffset_to_checksums %u\n",
-			//fhp->nhashes, fhp->offset_to_checksums));
-	//dprintf(("file id %llu\n", fhp->file_id));
+	dprintf(("\nfile header offset = %d\n", *fh_offset));
+	dprintf(("\n++priting file header info++\n"));
+	dprintf(("nhashes %d\noffset_to_checksums %u\n",
+			fhp->nhashes, fhp->offset_to_checksums));
+	dprintf(("file id %llu\n", fhp->file_id));
 
 	return (0);
 }
@@ -1874,7 +1894,7 @@
 	struct file_header *fhp;
 	int error;
 
-	//dprintf(("bucket offset = %d\n", *buckets_offset));
+	dprintf(("bucket offset = %d\n", *buckets_offset));
 	fhp = pefs_allocate_file_header();
 	if (fhp == NULL)
 		return (PEFS_ERR_SYS);
@@ -1889,7 +1909,7 @@
 	}
 	bp->fhp = fhp;
 
-	//dprintf(("\n++priting bucket info++\n"));
+	dprintf(("\n++priting bucket info++\n"));
 
 	return (0);
 }
@@ -1907,8 +1927,8 @@
 	}
 	(*hashes_offset)+= hash_len;
 
-	//dprintf(("hashes offset = %d\n", *hashes_offset));
-	//dprintf(("hash %s\n", csp->hash));
+	dprintf(("hashes offset = %d\n", *hashes_offset));
+	dprintf(("hash %s\n", csp->hash));
 
 	return (0);
 }
@@ -2041,6 +2061,10 @@
 /*
  * Traverse the entire filesystem and for every regular file or symbolic link,
  * look it up in .pefs.checksum index and verify its checksums.
+ *
+ * This function will try to avoid returning due to errors encountered when
+ * checksums mismatch or immutable flags are missing so as to print as many
+ * warnings as possible.
  */
 static int
 pefs_traverse_fs(struct cuckoo_hash_table *chtp, const EVP_MD *md,
@@ -2058,14 +2082,11 @@
 	while (dirp) {
 		sdp = readdir(dirp);
 		if (sdp != NULL) {
-			/* XXXgpf: [TODO] Need to pay special attention to these files */
 			if (strcmp(sdp->d_name, "..") == 0 ||
 				strcmp(sdp->d_name, ".") == 0 ||
 				strcmp(sdp->d_name, ".pefs.db") == 0 ||
 				strcmp(sdp->d_name, ".pefs.conf") == 0 ||
-				strcmp(sdp->d_name, ".pefs.checksum") == 0 ||
-				strcmp(sdp->d_name, ".pefs.signature") == 0 ||
-				strcmp(sdp->d_name, ".pefs.pkey") == 0)
+				strcmp(sdp->d_name, ".pefs.checksum") == 0)
 				continue;
 
 			dprintf(("dirent: %s\n", sdp->d_name));
@@ -2089,8 +2110,8 @@
 			 * Look up the file and verify its checksums.
 			 * Total number of checksums should be the same and checksums
 			 * should match.
-			 * Also, take notice of hardlinks & symlink warnings.
-			 * After the traversal is done, we must have found all of our
+			 * Also, take care of hardlinks & symlink warnings.
+			 * After the traversal is done, we should have found all of the
 			 * entries in the checksum file.
 			 */
 			/* FALLTHROUGH */
@@ -2152,7 +2173,7 @@
 				}
 
 				/*
-				 * if error encountered during pefs_compare_cehcksums,
+				 * if error encountered during pefs_compare_checksums,
 				 * keep on traversing the fs to find other errors as well.
 				 */
 				error = pefs_compare_checksums(fhp, indexfhp, hash_len);
@@ -2160,7 +2181,7 @@
 					*checksum_error = error;
 
 				TAILQ_INSERT_TAIL(fh_headp, fhp, file_header_entries);
-				pefs_close_file(fhp);
+				pefs_close_files(fhp);
 				break;
 			default:
 				break;
@@ -2280,7 +2301,6 @@
 		pefs_symlink_warn(&cht, &fh_head);
 
 	error = pefs_found_all_entries(&cht);
-
 	if (error == 0 && checksum_error != 0)
 		error = checksum_error;
 
@@ -2324,4 +2344,5 @@
 	return (error);
 }
 
-RB_GENERATE(hardlink_head, hardlink_counter, hardlink_entries, pefs_hardlink_cmp);
+RB_GENERATE(hardlink_head, hardlink_counter, hardlink_entries,
+	pefs_hardlink_cmp);

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c	Tue Aug 14 14:07:34 2012	(r240350)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.c	Tue Aug 14 17:40:56 2012	(r240351)
@@ -1019,6 +1019,8 @@
  *
  * inputfile contains list of files that need integrity checking. If
  * the argument is not supplied, input is read from stdin by default.
+ * These files should be either regular files or symbolic links.
+ * Symlinks are not traversed.
  *
  * path defines where .pefs.checksum should be created. By default,
  * .pefs.checksum is created under $PWD. path should be a directory,
@@ -1038,7 +1040,7 @@
 pefs_addchecksum(int argc, char *argv[])
 {
 	char fsroot[MAXPATHLEN + 1];
-	char csm_path[MAXPATHLEN + 1], pk_path[MAXPATHLEN + 1];
+	char csm_path[MAXPATHLEN + 1];
 	struct stat sb;
 	FILE *fpin, *pk_fp;
 	int error, flags, i, j;
@@ -1049,9 +1051,8 @@
 	pk_fp = NULL;
 	/* by default use sha256 */
 	algo = supported_digests[0];
-	/* by default create checksum files under $PWD */
+	/* by default create checksum file under $PWD */
 	snprintf(csm_path, sizeof(csm_path), "./%s", PEFS_FILE_CHECKSUM);
-	snprintf(pk_path, sizeof(pk_path), "./%s", PEFS_FILE_PKEY);
 
 	while ((i = getopt(argc, argv, "fa:i:k:p:")) != -1)
 		switch(i) {
@@ -1102,8 +1103,6 @@
 
 			snprintf(csm_path, sizeof(csm_path), "%s/%s", optarg,
 					PEFS_FILE_CHECKSUM);
-			snprintf(pk_path, sizeof(pk_path), "%s/%s", optarg,
-					PEFS_FILE_PKEY);
 			break;
 		default:
 			if (fpin != NULL)
@@ -1286,6 +1285,8 @@
  *
  * filepath may refer to any kind of file that is encrypted by pefs filesystem,
  * such as directories, regular files, symlinks, etc.
+ *
+ * Symlinks are not traversed.
  */
 static int
 pefs_nameid(int argc, char *argv[])

Modified: soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h
==============================================================================
--- soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h	Tue Aug 14 14:07:34 2012	(r240350)
+++ soc2012/gpf/pefs_kmod/sbin/pefs/pefs_ctl.h	Tue Aug 14 17:40:56 2012	(r240351)
@@ -43,8 +43,6 @@
 #define	PEFS_FILE_KEYCHAIN		".pefs.db"
 #define	PEFS_FILE_KEYCONF		".pefs.conf"
 #define PEFS_FILE_CHECKSUM		".pefs.checksum"
-#define PEFS_FILE_SIGNATURE		".pefs.signature"
-#define PEFS_FILE_PKEY			".pefs.pkey"
 
 #define PEFS_NOKEY				0x0001
 #define PEFS_UNMOUNTED			0x0002

Modified: soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c
==============================================================================
--- soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c	Tue Aug 14 14:07:34 2012	(r240350)
+++ soc2012/gpf/pefs_kmod/sys/fs/pefs/pefs_checksum.c	Tue Aug 14 17:40:56 2012	(r240351)
@@ -48,65 +48,6 @@
 const char *pefs_checksum_supported_digests[] = {"sha256","sha512"};
 uint8_t pefs_checksum_supported_hash_lengths[] = {32, 64};
 
-/* XXXgpf: tmp 4 dbg purposes */
-//static void
-//pefs_dbg_checksum_file(struct pefs_checksum *pcs)
-//{
-	//char *p;
-	//int i;
-	//uint32_t nhashes;
-	//uint32_t offset;
-	//uint64_t file_id;
-
-	///* print .pefs.checksum file header info */
-	//printf("\n+++CHECKSUM FILE HEADER INFO+++\n");
-	//printf("version = %x\nreserved = %d\nhash len = %d\noffset = %d\nsize = %d\nalgo = %s\n\n",
-		//pcs->pcs_version, pcs->pcs_reserved, pcs->pcs_hash_len, pcs->pcs_offset_to_hash_table,
-		//pcs->pcs_hash_table_size, pcs->pcs_hash_algo_name);
-
-	///* print table1 */
-	//printf("+++HASH TABLE 1+++\n\n");
-	//for (i = 0; i < pcs->pcs_hash_table_size; i++) {
-		//p = &(pcs->pcs_table1[i * PEFS_HT_CELL_SIZE]);
-
-		//memcpy(&nhashes, p, sizeof(nhashes));
-		//nhashes = le32toh(nhashes);
-		//if (nhashes != 0) {
-			//p+=sizeof(nhashes);
-			//memcpy(&offset, p, sizeof(offset));
-			//offset = le32toh(offset);
-			//p+=sizeof(offset);
-			//memcpy(&file_id, p, sizeof(file_id));
-			//printf("cell %d:\n", i);
-			//printf("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
-				//nhashes, offset, file_id);
-		//}
-		//else
-			//printf("cell %d: empty\n", i);
-	//}
-
-	///* print table2 */
-	//printf("\n+++HASH TABLE 2+++\n\n");
-	//for (i = 0; i < pcs->pcs_hash_table_size; i++) {
-		//p = &(pcs->pcs_table2[i * PEFS_HT_CELL_SIZE]);
-
-		//memcpy(&nhashes, p, sizeof(nhashes));
-		//nhashes = le32toh(nhashes);
-		//if (nhashes != 0) {
-			//p+=sizeof(nhashes);
-			//memcpy(&offset, p, sizeof(offset));
-			//offset = le32toh(offset);
-			//p+=sizeof(offset);
-			//memcpy(&file_id, p, sizeof(file_id));
-			//printf("cell %d:\n", i);
-			//printf("\thashes = %d\n\toffset = %d\n\tfile id = %llu\n",
-				//nhashes, offset, file_id);
-		//}
-		//else
-			//printf("cell %d: empty\n", i);
-	//}
-//}
-
 /* sanitize .pefs.checkum's global file header that's read during VFS_MOUNT() */
 int
 pefs_sanitize_checksum_header(struct pefs_checksum *pcs)
@@ -164,7 +105,7 @@
 	return (nbucket);
 }
 
-/* fill out pcie for from data pointed to by p */
+/* fill out pcie from data pointed to by p */
 static void
 pefs_get_index_entry(char *p, struct pefs_checksum_index_entry *pcie)
 {
@@ -251,12 +192,9 @@
 		|| ((pn->pn_flags & PN_NO_CHECKSUM) != 0))
 		goto not_found;
 
-	/* XXXgpf: What if user wants integrity checking for .pefs.db or .conf? */
 	if (strncmp(enc_name, ".pefs.db", enc_name_len) == 0 ||
 		strncmp(enc_name, ".pefs.conf", enc_name_len) == 0 ||
-		strncmp(enc_name, ".pefs.checksum", enc_name_len) == 0 ||
-		strncmp(enc_name, ".pefs.signature", enc_name_len) == 0 ||
-		strncmp(enc_name, ".pefs.pkey", enc_name_len) == 0)
+		strncmp(enc_name, ".pefs.checksum", enc_name_len) == 0)
 		goto not_found;
 
 	enc_name++;
@@ -282,6 +220,10 @@
 	/*
 	 * Check to see if schg flag is set, if not mark the vnode so that all
 	 * read access is denied.
+	 *
+	 * XXXgpf: [TODO] I should make sure that the PN_WRONG_CHECKSUM flag is
+	 * used in other parts of the pefs codebase to deny access to the file
+	 * data.
 	 */
 	error = VOP_GETATTR(vp, &va, cred);
 	if (error != 0) {
@@ -406,10 +348,6 @@
 
 	dprintf(("integrity checking!\noffset %llu\n", offset));
 
-	/*
-	 * XXXgpf: For the moment, this flag's only purpose is to deny read access
-	 * to the file. Should it do more?
-	 */
 	if ((pn->pn_flags & PN_WRONG_CHECKSUM) != 0)
 		return (EAUTH);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120814174056.A5A21106564A>