hammer2 - Cleanup and bugfix pass to hammer2 setcomp paths
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 23 Sep 2013 22:18:35 +0000 (15:18 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 23 Sep 2013 23:56:34 +0000 (16:56 -0700)
* HAMMER2 will attempt to use LZ4 compression by default now.

* hammer2 setcomp now accepts text labels for the compression type and
  accepts a compression level of 0 to set the default, which is simply
  passed through to the VFS.

* Specify force and recursion flags in hammer2's main options rather than
  after the setcomp command.

* Allow a list of paths to be specified rather than just one.

* The HAMMER2IOC_INODE_SET ioctl now only sets field elements which can
  actually be modified by userland i.e. only the comp_algo field.  It
  used to set everything, including the indirect block table.

* Do not modify the hammer2_inode if the compression method is not being
  changed.

* Change the zero-block testing logic a little, with related code cleanups.

sbin/hammer2/cmd_setcomp.c
sbin/hammer2/cmd_stat.c
sbin/hammer2/hammer2.h
sbin/hammer2/main.c
sbin/newfs_hammer2/newfs_hammer2.c
sys/vfs/hammer2/hammer2_disk.h
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_vfsops.c

index 1c0b140..3d34aad 100644 (file)
  */
 
 #include "hammer2.h"
+
+static int cmd_setcomp_core(uint8_t comp_algo, const char *path_str,
+                           struct stat *st);
  
 int
-cmd_setcomp(char* comp_string, char* file_string)
+cmd_setcomp(const char *comp_str, char **paths)
 {
-       int comp_method;
-       if (strcmp(comp_string, "0") == 0) {
-               printf("Will turn off compression on directory/file %s\n", file_string);
-               comp_method = HAMMER2_COMP_NONE;
-       } else if (strcmp(comp_string, "1") == 0) {
-               printf("Will set zero-checking compression on directory/file %s.\n",
-                       file_string);
-               comp_method = HAMMER2_COMP_AUTOZERO;
-       } else if (strcmp(comp_string, "2") == 0) {
-               printf("Will set LZ4 compression on directory/file %s.\n", file_string);
-               comp_method = HAMMER2_COMP_LZ4;
-       } else if (strcmp(comp_string, "3:6") == 0) {
-               printf("Will set ZLIB level 6 compression on directory/file %s.\n", file_string);
-               comp_method = 6 << 4;
-               comp_method += HAMMER2_COMP_ZLIB;
-       } else if (strcmp(comp_string, "3") == 0 || strcmp(comp_string, "3:7") == 0) {
-               printf("Will set ZLIB level 7 (default) compression on directory/file %s.\n", file_string);
-               comp_method = 7 << 4;
-               comp_method += HAMMER2_COMP_ZLIB;
-       } else if (strcmp(comp_string, "3:8") == 0) {
-               printf("Will set ZLIB level 8 compression on directory/file %s.\n", file_string);
-               comp_method = 8 << 4;
-               comp_method += HAMMER2_COMP_ZLIB;
-       } else if (strcmp(comp_string, "3:9") == 0) {
-               printf("Will set ZLIB level 9 compression on directory/file %s.\n", file_string);
-               printf("CAUTION: May be extremely slow on big amount of data.\n");
-               comp_method = 9 << 4;
-               comp_method += HAMMER2_COMP_ZLIB;
-       } else if (strcmp(comp_string, "3:5") == 0 || strcmp(comp_string, "3:4") == 0 ||
-                               strcmp(comp_string, "3:3") == 0 || strcmp(comp_string, "3:2") == 0 ||
-                               strcmp(comp_string, "3:1") == 0) {
-               printf("ZLIB compression levels below 6 are not supported,\n");
-               printf("please use LZ4 (setcomp 2) for fast compression instead.\n");
-               return 1;
+       static const char *comps[] = HAMMER2_COMP_STRINGS;
+       struct stat st;
+       int comp_algo;
+       int comp_level;
+       int ecode;
+       int res;
+       char *str;
+       const char *s1;
+       const char *s2;
+
+       str = strdup(comp_str);
+       s1 = strtok(str, ":");
+       s2 = s1 ? strtok(NULL, ":") : NULL;
+       ecode = 0;
+
+       if (isdigit(s1[0])) {
+               comp_algo = strtol(s1, NULL, 0);
+       } else {
+               comp_algo = HAMMER2_COMP_STRINGS_COUNT;
+               while (--comp_algo >= 0) {
+                       if (strcasecmp(s1, comps[comp_algo]) == 0)
+                               break;
+               }
+               if (comp_algo < 0 && strcasecmp(s1, "default") == 0) {
+                       comp_algo = HAMMER2_COMP_LZ4;
+                       s1 = "lz4";
+               }
+               if (comp_algo < 0 && strcasecmp(s1, "disabled") == 0) {
+                       comp_algo = HAMMER2_COMP_AUTOZERO;
+                       s1 = "autozero";
+               }
+               if (comp_algo < 0) {
+                       fprintf(stderr, "Unknown compression type: %s\n", s1);
+                       ecode = 3;
+               }
        }
-       else {
-               printf("ERROR: Unknown compression method.\n");
-               return 1;
+       if (s2 == NULL) {
+               comp_level = 0;
+       } else if (isdigit(s2[0])) {
+               comp_level = strtol(s2, NULL, 0);
+       } else if (strcasecmp(s2, "default") == 0) {
+               comp_level = 0;
+       } else {
+               comp_level = 0;
+               fprintf(stderr, "Unknown compression level: %s\n", s2);
+               ecode = 3;
        }
-       int fd = hammer2_ioctl_handle(file_string);
+
+       if (comp_level) {
+               switch(comp_algo) {
+               case HAMMER2_COMP_ZLIB:
+                       if (comp_level < 6 || comp_level > 9) {
+                               fprintf(stderr,
+                                       "Unsupported comp_level %d for %s\n",
+                                       comp_level, s1);
+                               ecode = 3;
+                       }
+                       break;
+               default:
+                       fprintf(stderr,
+                               "Unsupported comp_level %d for %s\n",
+                               comp_level, s1);
+                       ecode = 3;
+               }
+       }
+
+       if (ecode == 0) {
+               while (*paths) {
+                       if (lstat(*paths, &st) == 0) {
+                               res = cmd_setcomp_core(
+                                       HAMMER2_ENC_COMP(comp_algo) |
+                                        HAMMER2_ENC_LEVEL(comp_level),
+                                       *paths,
+                                       &st);
+                               if (res)
+                                       ecode = res;
+                       } else {
+                               printf("%s: %s\n", *paths, strerror(errno));
+                               ecode = 3;
+                       }
+                       ++paths;
+               }
+       }
+       free (str);
+
+       return ecode;
+}
+
+static int
+cmd_setcomp_core(uint8_t comp_algo, const char *path_str, struct stat *st)
+{
        hammer2_ioc_inode_t inode;
-       int res = ioctl(fd, HAMMER2IOC_INODE_GET, &inode);
+       int fd;
+       int res;
+
+       fd = hammer2_ioctl_handle(path_str);
+       if (fd < 0) {
+               res = 3;
+               goto failed;
+       }
+       res = ioctl(fd, HAMMER2IOC_INODE_GET, &inode);
        if (res < 0) {
-               fprintf(stderr, "ERROR before setting the mode: %s\n",
-                       strerror(errno));
-               return 3;
+               fprintf(stderr,
+                       "%s: HAMMER2IOC_INODE_GET: error %s\n",
+                       path_str, strerror(errno));
+               res = 3;
+               goto failed;
        }
-       inode.ip_data.comp_algo = comp_method & 0x0FF;
+       printf("%s\tcomp_algo=0x%02x\n", path_str, comp_algo);
+       inode.ip_data.comp_algo = comp_algo;
        res = ioctl(fd, HAMMER2IOC_INODE_SET, &inode);
        if (res < 0) {
-               if (errno != EINVAL) {
-                       fprintf(stderr, "ERROR after trying to set the mode: %s\n",
-                               strerror(errno));
-                       return 3;
+               fprintf(stderr,
+                       "%s: HAMMER2IOC_INODE_SET: error %s\n",
+                       path_str, strerror(errno));
+               res = 3;
+               goto failed;
+       }
+       res = 0;
+
+       if (RecurseOpt && S_ISDIR(st->st_mode)) {
+               DIR *dir;
+               char *path;
+               struct dirent *den;
+
+               if ((dir = fdopendir(fd)) != NULL) {
+                       while ((den = readdir(dir)) != NULL) {
+                               if (strcmp(den->d_name, ".") == 0 ||
+                                   strcmp(den->d_name, "..") == 0) {
+                                       continue;
+                               }
+                               asprintf(&path, "%s/%s", path_str, den->d_name);
+                               if (lstat(path, st) == 0)
+                                       cmd_setcomp_core(comp_algo, path, st);
+                               free(path);
+                       }
+                       closedir(dir);
                }
        }
+failed:
        close(fd);
-       return 0;
+       return res;
 }
 
+#if 0
+
 int
 cmd_setcomp_recursive(char* option_string, char* comp_string, char* file_string)
 {
        int ecode = 0;
        int set_files;
+       int comp_method;
+
        if (strcmp(option_string, "-r") == 0) {
                set_files = 0;
-       }
-       else if (strcmp(option_string, "-rf") == 0) {
+       } else if (strcmp(option_string, "-rf") == 0) {
                set_files = 1;
-       }
-       else {
+       } else {
                printf("setcomp: Unrecognized option.\n");
                exit(1);
        }
-       int comp_method;
        if (strcmp(comp_string, "0") == 0) {
                printf("Will turn off compression on directory/file %s\n", file_string);
                comp_method = HAMMER2_COMP_NONE;
@@ -217,3 +305,5 @@ setcomp_recursive_call(char *directory, int comp_method, int set_files)
     }
     return ecode;
 }
+
+#endif
index faa3363..455e379 100644 (file)
@@ -34,6 +34,8 @@
 
 #include "hammer2.h"
 
+static const char *compmodestr(uint8_t comp_algo);
+
 /*
  * Should be run as root.  Creates /etc/hammer2/rsa.{pub,prv} using
  * an openssl command.
@@ -75,7 +77,7 @@ cmd_stat(int ac, const char **av)
                printf("%9s ", sizetostr(ino.ip_data.data_count));
                printf("%9s ", sizetostr(ino.ip_data.inode_count));
                printf("%p ", ino.kdata);
-               printf("%02x ", ino.ip_data.comp_algo);
+               printf("comp=%s ", compmodestr(ino.ip_data.comp_algo));
                if (ino.ip_data.data_quota || ino.ip_data.inode_quota) {
                        printf(" quota ");
                        printf("%12s", sizetostr(ino.ip_data.data_quota));
@@ -85,3 +87,30 @@ cmd_stat(int ac, const char **av)
        }
        return ec;
 }
+
+static
+const char *
+compmodestr(uint8_t comp_algo)
+{
+       static char buf[64];
+       static const char *comps[] = HAMMER2_COMP_STRINGS;
+       int comp = HAMMER2_DEC_COMP(comp_algo);
+       int level = HAMMER2_DEC_LEVEL(comp_algo);
+
+       if (level) {
+               if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT)
+                       snprintf(buf, sizeof(buf), "%s:%d",
+                                comps[comp], level);
+               else
+                       snprintf(buf, sizeof(buf), "unknown(%d):%d",
+                                comp, level);
+       } else {
+               if (comp >= 0 && comp < HAMMER2_COMP_STRINGS_COUNT)
+                       snprintf(buf, sizeof(buf), "%s:default",
+                                comps[comp]);
+               else
+                       snprintf(buf, sizeof(buf), "unknown(%d):default",
+                                comp);
+       }
+       return (buf);
+}
index f4ddf00..9d81c0d 100644 (file)
@@ -105,6 +105,8 @@ struct hammer2_udppkt {
 typedef struct hammer2_udppkt hammer2_udppkt_t;
 
 extern int DebugOpt;
+extern int ForceOpt;
+extern int RecurseOpt;
 extern int VerboseOpt;
 extern int QuietOpt;
 extern int NormalExit;
@@ -136,9 +138,7 @@ int cmd_show(const char *devpath, int dofreemap);
 int cmd_rsainit(const char *dir_path);
 int cmd_rsaenc(const char **keys, int nkeys);
 int cmd_rsadec(const char **keys, int nkeys);
-int cmd_setcomp(char* comp_string, char* file_string);
-int cmd_setcomp_recursive(char* option_string, char* comp_string,
-       char* file_string);
+int cmd_setcomp(const char *comp_str, char **paths);
 
 /*
  * Misc functions
@@ -154,6 +154,4 @@ uint32_t hammer2_icrc32(const void *buf, size_t size);
 uint32_t hammer2_icrc32c(const void *buf, size_t size, uint32_t crc);
 
 void hammer2_shell_parse(dmsg_msg_t *msg);
-int setcomp_recursive_call(char *directory, int comp_method,
-       int set_files);
 void print_inode(char* inode_string);
index 19a66cb..77a5e99 100644 (file)
 
 #include "hammer2.h"
 
-static void usage(int code);
-
 int DebugOpt;
 int VerboseOpt;
 int QuietOpt;
 int NormalExit = 1;    /* if set to 0 main() has to pthread_exit() */
+int RecurseOpt;
+int ForceOpt;
+
+static void usage(int code);
 
 int
 main(int ac, char **av)
@@ -60,7 +62,7 @@ main(int ac, char **av)
        /*
         * Core options
         */
-       while ((ch = getopt(ac, av, "adqs:t:u:v")) != -1) {
+       while ((ch = getopt(ac, av, "adfrqs:t:u:v")) != -1) {
                switch(ch) {
                case 'a':
                        all_opt = 1;
@@ -68,6 +70,12 @@ main(int ac, char **av)
                case 'd':
                        DebugOpt = 1;
                        break;
+               case 'f':
+                       ForceOpt = 1;
+                       break;
+               case 'r':
+                       RecurseOpt = 1;
+                       break;
                case 's':
                        sel_path = optarg;
                        break;
@@ -331,20 +339,25 @@ main(int ac, char **av)
                        cmd_show(av[1], 1);
                }
        } else if (strcmp(av[0], "setcomp") == 0) {
-               if (ac < 3 || ac > 4) {
-                       fprintf(stderr, "setcomp: requires compression method and"
+               if (ac < 3) {
+                       /*
+                        * Missing compression method and at least one
+                        * path.
+                        */
+                       fprintf(stderr,
+                               "setcomp: requires compression method and"
                                "directory/file path\n");
                        usage(1);
                } else {
-                       if (ac == 3) //no option specified, no recursion by default
-                               ecode = cmd_setcomp(av[1], av[2]);
-                       else
-                               ecode = cmd_setcomp_recursive(av[1], av[2], av[3]);
-                       if (ecode == 0) printf("Compression mode set.\n");                      
+                       /*
+                        * Multiple paths may be specified
+                        */
+                       ecode = cmd_setcomp(av[1], &av[2]);
                }
        } else if (strcmp(av[0], "printinode") == 0) {
                if (ac != 2) {
-                       fprintf(stderr, "printinode: requires directory/file path\n");
+                       fprintf(stderr,
+                               "printinode: requires directory/file path\n");
                        usage(1);
                }
                else
@@ -377,25 +390,44 @@ usage(int code)
                "    -t type            PFS type for pfs-create\n"
                "    -u uuid            uuid for pfs-create\n"
                "\n"
-               "    connect <target>   Add cluster link\n"
-               "    disconnect <target> Del cluster link\n"
-               "    hash filename*     Print directory hash\n"
-               "    status             Report cluster status\n"
-               "    pfs-list           List PFSs\n"
-               "    pfs-clid <label>   Print cluster id for specific PFS\n"
-               "    pfs-fsid <label>   Print private id for specific PFS\n"
-               "    pfs-create <label> Create a PFS\n"
-               "    pfs-delete <label> Destroy a PFS\n"
-               "    snapshot           Snapshot a PFS\n"
-               "    service            Start service daemon\n"
-               "    stat [<path>]      Return inode quota & config\n"
-               "    leaf               Start pfs leaf daemon\n"
-               "    shell [<host>]     Connect to debug shell\n"
-               "    debugspan <target> Connect to target, run CONN/SPAN\n"
-               "    rsainit            Initialize rsa fields\n"
-               "    show devpath       Raw hammer2 media dump\n"
-               "    freemap devpath    Raw hammer2 media dump\n"
-               "    setcomp comp_algo directory   Sets compression with comp_algo (0-2) on a directory\n"
+               "    connect <target>             "
+                       "Add cluster link\n"
+               "    disconnect <target>          "
+                       "Del cluster link\n"
+               "    hash filename*               "
+                       "Print directory hash\n"
+               "    status                       "
+                       "Report cluster status\n"
+               "    pfs-list                     "
+                       "List PFSs\n"
+               "    pfs-clid <label>             "
+                       "Print cluster id for specific PFS\n"
+               "    pfs-fsid <label>             "
+                       "Print private id for specific PFS\n"
+               "    pfs-create <label>           "
+                       "Create a PFS\n"
+               "    pfs-delete <label>           "
+                       "Destroy a PFS\n"
+               "    snapshot                     "
+                       "Snapshot a PFS\n"
+               "    service                      "
+                       "Start service daemon\n"
+               "    stat [<path>]                "
+                       "Return inode quota & config\n"
+               "    leaf                         "
+                       "Start pfs leaf daemon\n"
+               "    shell [<host>]               "
+                       "Connect to debug shell\n"
+               "    debugspan <target>           "
+                       "Connect to target, run CONN/SPAN\n"
+               "    rsainit                      "
+                       "Initialize rsa fields\n"
+               "    show devpath                 "
+                       "Raw hammer2 media dump\n"
+               "    freemap devpath              "
+                       "Raw hammer2 media dump\n"
+               "    setcomp comp[:level] path    "
+                       "Set compression {none, autozero, lz4, zlib} & level\n"
        );
        exit(code);
 }
index 6e30fc2..9eeae7d 100644 (file)
@@ -523,7 +523,7 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
        /*
         * Compression mode and supported copyids.
         */
-       rawip->comp_algo = HAMMER2_COMP_AUTOZERO;
+       rawip->comp_algo = HAMMER2_COMP_NEWFS_DEFAULT;
 
        rawip->pfs_clid = Hammer2_RPFSId;
        rawip->pfs_type = HAMMER2_PFSTYPE_MASTER;
@@ -546,7 +546,7 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space)
                        hammer2_icrc32(rawip, sizeof(*rawip));
        root_blockref.type = HAMMER2_BREF_TYPE_INODE;
        root_blockref.methods = HAMMER2_ENC_CHECK(HAMMER2_CHECK_ISCSI32) |
-                               HAMMER2_ENC_COMP(HAMMER2_COMP_AUTOZERO);
+                               HAMMER2_ENC_COMP(HAMMER2_COMP_NONE);
 
        /*
         * Format the super-root directory inode, giving it one directory
index 49b3118..eb16e62 100644 (file)
@@ -464,13 +464,19 @@ typedef struct hammer2_blockref hammer2_blockref_t;
 #define HAMMER2_CHECK_FREEMAP          4
 
 #define HAMMER2_ENC_COMP(n)            (n)
+#define HAMMER2_ENC_LEVEL(n)           ((n) << 4)
 #define HAMMER2_DEC_COMP(n)            ((n) & 15)
+#define HAMMER2_DEC_LEVEL(n)           (((n) >> 4) & 15)
 
 #define HAMMER2_COMP_NONE              0
-#define HAMMER2_COMP_AUTOZERO  1
+#define HAMMER2_COMP_AUTOZERO          1
 #define HAMMER2_COMP_LZ4               2
 #define HAMMER2_COMP_ZLIB              3
 
+#define HAMMER2_COMP_NEWFS_DEFAULT     HAMMER2_COMP_LZ4
+#define HAMMER2_COMP_STRINGS           { "none", "autozero", "lz4", "zlib" }
+#define HAMMER2_COMP_STRINGS_COUNT     4
+
 
 /*
  * HAMMER2 block references are collected into sets of 8 blockrefs.  These
index 1de50f7..dbfe896 100644 (file)
@@ -574,41 +574,29 @@ hammer2_ioctl_inode_get(hammer2_inode_t *ip, void *data)
        return (0);
 }
 
-//original
-/*static int
-hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
-{
-       hammer2_ioc_inode_t *ino = data;
-       int error = EINVAL;
-
-       if (ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) {
-       }
-       if (ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) {
-       }
-       if (ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) {
-       }
-
-       return (error);
-}*/
-
-//use this set function instead of dedicated ioctl for some time
+/*
+ * Set various parameters in an inode which cannot be set through
+ * normal filesystem VNOPS.
+ */
 static int
 hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
 {
        hammer2_inode_data_t *ipdata;
        hammer2_ioc_inode_t *ino = data;
-       hammer2_chain_t *parent;
+       hammer2_chain_t *chain;
        hammer2_trans_t trans;
-       int error = EINVAL;
+       int error = 0;
 
        hammer2_trans_init(&trans, ip->pmp, 0);
-       parent = hammer2_inode_lock_ex(ip);
-       ipdata = hammer2_chain_modify_ip(&trans, ip, &parent,
-                                                 HAMMER2_MODIFY_ASSERTNOCOPY);
-       ip->chain->data->ipdata = ino->ip_data;
+       chain = hammer2_inode_lock_ex(ip);
+
+       if (ino->ip_data.comp_algo != chain->data->ipdata.comp_algo) {
+               ipdata = hammer2_chain_modify_ip(&trans, ip, &chain, 0);
+               ipdata->comp_algo = ino->ip_data.comp_algo;
+       }
        ino->kdata = ip;
        
-       /*Ignore those flags for now...*/
+       /* Ignore these flags for now...*/
        if (ino->flags & HAMMER2IOC_INODE_FLAG_IQUOTA) {
        }
        if (ino->flags & HAMMER2IOC_INODE_FLAG_DQUOTA) {
@@ -616,7 +604,7 @@ hammer2_ioctl_inode_set(hammer2_inode_t *ip, void *data)
        if (ino->flags & HAMMER2IOC_INODE_FLAG_COPIES) {
        }
        hammer2_trans_done(&trans);
-       hammer2_inode_unlock_ex(ip, parent);
+       hammer2_inode_unlock_ex(ip, chain);
 
        return (error);
 }
index 391eb6c..55f7db6 100644 (file)
@@ -209,14 +209,14 @@ static void hammer2_compress_and_write(struct buf *bp, hammer2_trans_t *trans,
                                hammer2_inode_data_t *ipdata,
                                hammer2_chain_t **parentp,
                                hammer2_key_t lbase, int ioflag,
-                               int pblksize, int *errorp, int comp_method);
+                               int pblksize, int *errorp, int comp_algo);
 static void hammer2_zero_check_and_write(struct buf *bp,
                                hammer2_trans_t *trans, hammer2_inode_t *ip,
                                hammer2_inode_data_t *ipdata,
                                hammer2_chain_t **parentp,
                                hammer2_key_t lbase,
                                int ioflag, int pblksize, int *errorp);
-static int test_block_not_zeros(char *buf, size_t bytes);
+static int test_block_zeros(const char *buf, size_t bytes);
 static void zero_write(struct buf *bp, hammer2_trans_t *trans,
                                hammer2_inode_t *ip,
                                hammer2_inode_data_t *ipdata,
@@ -887,17 +887,9 @@ hammer2_write_file_core(struct buf *bp, hammer2_trans_t *trans,
                        int *errorp)
 {
        hammer2_chain_t *chain;
-       if (ipdata->comp_algo > HAMMER2_COMP_AUTOZERO) {
-               hammer2_compress_and_write(bp, trans, ip,
-                                          ipdata, parentp,
-                                          lbase, ioflag,
-                                          pblksize, errorp,
-                                          ipdata->comp_algo);
-       } else if (ipdata->comp_algo == HAMMER2_COMP_AUTOZERO) {
-               hammer2_zero_check_and_write(bp, trans, ip,
-                                   ipdata, parentp, lbase,
-                                   ioflag, pblksize, errorp);
-       } else {
+
+       switch(HAMMER2_DEC_COMP(ipdata->comp_algo)) {
+       case HAMMER2_COMP_NONE:
                /*
                 * We have to assign physical storage to the buffer
                 * we intend to dirty or write now to avoid deadlocks
@@ -912,6 +904,27 @@ hammer2_write_file_core(struct buf *bp, hammer2_trans_t *trans,
                hammer2_write_bp(chain, bp, ioflag, pblksize, errorp);
                if (chain)
                        hammer2_chain_unlock(chain);
+               break;
+       case HAMMER2_COMP_AUTOZERO:
+               /*
+                * Check for zero-fill only
+                */
+               hammer2_zero_check_and_write(bp, trans, ip,
+                                   ipdata, parentp, lbase,
+                                   ioflag, pblksize, errorp);
+               break;
+       case HAMMER2_COMP_LZ4:
+       case HAMMER2_COMP_ZLIB:
+       default:
+               /*
+                * Check for zero-fill and attempt compression.
+                */
+               hammer2_compress_and_write(bp, trans, ip,
+                                          ipdata, parentp,
+                                          lbase, ioflag,
+                                          pblksize, errorp,
+                                          ipdata->comp_algo);
+               break;
        }
        ipdata = &ip->chain->data->ipdata;      /* reload */
 }
@@ -928,222 +941,234 @@ hammer2_compress_and_write(struct buf *bp, hammer2_trans_t *trans,
        hammer2_inode_t *ip, hammer2_inode_data_t *ipdata,
        hammer2_chain_t **parentp,
        hammer2_key_t lbase, int ioflag, int pblksize,
-       int *errorp, int comp_method)
+       int *errorp, int comp_algo)
 {
        hammer2_chain_t *chain;
+       int comp_size;
+       int comp_block_size;
+       char *comp_buffer;
 
-       if (test_block_not_zeros(bp->b_data, pblksize)) {
-               int comp_size = 0;
-               int comp_block_size;
-               char *comp_buffer;
+       if (test_block_zeros(bp->b_data, pblksize)) {
+               zero_write(bp, trans, ip, ipdata, parentp, lbase, errorp);
+               return;
+       }
 
-               comp_buffer = NULL;
+       comp_size = 0;
+       comp_buffer = NULL;
 
-               KKASSERT(pblksize / 2 <= 32768);
+       KKASSERT(pblksize / 2 <= 32768);
                
-               if (ip->comp_heuristic < 8 || (ip->comp_heuristic & 7) == 0) {
-                       if ((comp_method & 0x0F) == HAMMER2_COMP_LZ4) {
-                               comp_buffer = objcache_get(cache_buffer_write,
-                                                          M_INTWAIT);
-                               comp_size = LZ4_compress_limitedOutput(
-                                               bp->b_data,
-                                               &comp_buffer[sizeof(int)],
-                                               pblksize,
-                                               pblksize / 2 - sizeof(int));
-                               /*
-                                * We need to prefix with the size, LZ4
-                                * doesn't do it for us.  Add the related
-                                * overhead.
-                                */
-                               *(int *)comp_buffer = comp_size;
-                               if (comp_size)
-                                       comp_size += sizeof(int);
-                       } else if ((comp_method & 0x0F) == HAMMER2_COMP_ZLIB) {
-                               int comp_level = (comp_method >> 4) & 0x0F;
-                               z_stream strm_compress;
-                               int ret;
-
-                               ret = deflateInit(&strm_compress, comp_level);
-                               if (ret != Z_OK)
-                                       kprintf("HAMMER2 ZLIB: fatal error "
-                                               "on deflateInit.\n");
-                               
-                               comp_buffer = objcache_get(cache_buffer_write,
-                                                          M_INTWAIT);
-                               strm_compress.next_in = bp->b_data;
-                               strm_compress.avail_in = pblksize;
-                               strm_compress.next_out = comp_buffer;
-                               strm_compress.avail_out = pblksize / 2;
-                               ret = deflate(&strm_compress, Z_FINISH);
-                               if (ret == Z_STREAM_END) {
-                                       comp_size = pblksize / 2 -
-                                                   strm_compress.avail_out;
-                               } else {
-                                       comp_size = 0;
-                               }
-                               ret = deflateEnd(&strm_compress);
+       if (ip->comp_heuristic < 8 || (ip->comp_heuristic & 7) == 0) {
+               z_stream strm_compress;
+               int comp_level;
+               int ret;
+
+               switch(HAMMER2_DEC_COMP(comp_algo)) {
+               case HAMMER2_COMP_LZ4:
+                       comp_buffer = objcache_get(cache_buffer_write,
+                                                  M_INTWAIT);
+                       comp_size = LZ4_compress_limitedOutput(
+                                       bp->b_data,
+                                       &comp_buffer[sizeof(int)],
+                                       pblksize,
+                                       pblksize / 2 - sizeof(int));
+                       /*
+                        * We need to prefix with the size, LZ4
+                        * doesn't do it for us.  Add the related
+                        * overhead.
+                        */
+                       *(int *)comp_buffer = comp_size;
+                       if (comp_size)
+                               comp_size += sizeof(int);
+                       break;
+               case HAMMER2_COMP_ZLIB:
+                       comp_level = HAMMER2_DEC_LEVEL(comp_algo);
+                       if (comp_level == 0)
+                               comp_level = 6; /* default zlib compression */
+                       else if (comp_level < 6)
+                               comp_level = 6;
+                       else if (comp_level > 9)
+                               comp_level = 9;
+                       ret = deflateInit(&strm_compress, comp_level);
+                       if (ret != Z_OK) {
+                               kprintf("HAMMER2 ZLIB: fatal error "
+                                       "on deflateInit.\n");
+                       }
+
+                       comp_buffer = objcache_get(cache_buffer_write,
+                                                  M_INTWAIT);
+                       strm_compress.next_in = bp->b_data;
+                       strm_compress.avail_in = pblksize;
+                       strm_compress.next_out = comp_buffer;
+                       strm_compress.avail_out = pblksize / 2;
+                       ret = deflate(&strm_compress, Z_FINISH);
+                       if (ret == Z_STREAM_END) {
+                               comp_size = pblksize / 2 -
+                                           strm_compress.avail_out;
                        } else {
-                               kprintf("Error: Unknown compression method.\n");
-                               kprintf("Comp_method = %d.\n", comp_method);
+                               comp_size = 0;
                        }
+                       ret = deflateEnd(&strm_compress);
+                       break;
+               default:
+                       kprintf("Error: Unknown compression method.\n");
+                       kprintf("Comp_method = %d.\n", comp_algo);
+                       break;
                }
+       }
 
-               if (comp_size == 0) {
-                       /*
-                        * compression failed or turned off
-                        */
-                       comp_block_size = pblksize;     /* safety */
-                       if (++ip->comp_heuristic > 128)
-                               ip->comp_heuristic = 8;
+       if (comp_size == 0) {
+               /*
+                * compression failed or turned off
+                */
+               comp_block_size = pblksize;     /* safety */
+               if (++ip->comp_heuristic > 128)
+                       ip->comp_heuristic = 8;
+       } else {
+               /*
+                * compression succeeded
+                */
+               ip->comp_heuristic = 0;
+               if (comp_size <= 1024) {
+                       comp_block_size = 1024;
+               } else if (comp_size <= 2048) {
+                       comp_block_size = 2048;
+               } else if (comp_size <= 4096) {
+                       comp_block_size = 4096;
+               } else if (comp_size <= 8192) {
+                       comp_block_size = 8192;
+               } else if (comp_size <= 16384) {
+                       comp_block_size = 16384;
+               } else if (comp_size <= 32768) {
+                       comp_block_size = 32768;
                } else {
+                       panic("hammer2: WRITE PATH: "
+                             "Weird comp_size value.");
+                       /* NOT REACHED */
+                       comp_block_size = pblksize;
+               }
+       }
+
+       chain = hammer2_assign_physical(trans, ip, parentp,
+                                       lbase, comp_block_size,
+                                       errorp);
+       ipdata = &ip->chain->data->ipdata;      /* RELOAD */
+
+       if (*errorp) {
+               kprintf("WRITE PATH: An error occurred while "
+                       "assigning physical space.\n");
+               KKASSERT(chain == NULL);
+       } else {
+               /* Get device offset */
+               hammer2_off_t pbase;
+               hammer2_off_t pmask;
+               hammer2_off_t peof;
+               size_t boff;
+               size_t psize;
+               struct buf *dbp;
+               int temp_check;
+
+               KKASSERT(chain->flags & HAMMER2_CHAIN_MODIFIED);
+
+               switch(chain->bref.type) {
+               case HAMMER2_BREF_TYPE_INODE:
+                       KKASSERT(chain->data->ipdata.op_flags &
+                                HAMMER2_OPFLAG_DIRECTDATA);
+                       KKASSERT(bp->b_loffset == 0);
+                       bcopy(bp->b_data, chain->data->ipdata.u.data,
+                             HAMMER2_EMBEDDED_BYTES);
+                       break;
+               case HAMMER2_BREF_TYPE_DATA:
+                       psize = hammer2_devblksize(chain->bytes);
+                       pmask = (hammer2_off_t)psize - 1;
+                       pbase = chain->bref.data_off & ~pmask;
+                       boff = chain->bref.data_off &
+                              (HAMMER2_OFF_MASK & pmask);
+                       peof = (pbase + HAMMER2_SEGMASK64) &
+                              ~HAMMER2_SEGMASK64;
+                       temp_check = HAMMER2_DEC_CHECK(chain->bref.methods);
+
                        /*
-                        * compression succeeded
+                        * Optimize out the read-before-write
+                        * if possible.
                         */
-                       ip->comp_heuristic = 0;
-                       if (comp_size <= 1024) {
-                               comp_block_size = 1024;
-                       } else if (comp_size <= 2048) {
-                               comp_block_size = 2048;
-                       } else if (comp_size <= 4096) {
-                               comp_block_size = 4096;
-                       } else if (comp_size <= 8192) {
-                               comp_block_size = 8192;
-                       } else if (comp_size <= 16384) {
-                               comp_block_size = 16384;
-                       } else if (comp_size <= 32768) {
-                               comp_block_size = 32768;
+                       if (comp_block_size == psize) {
+                               dbp = getblk(chain->hmp->devvp, pbase,
+                                            psize, 0, 0);
                        } else {
-                               panic("hammer2: WRITE PATH: "
-                                     "Weird comp_size value.");
-                               /* NOT REACHED */
-                               comp_block_size = pblksize;
+                               *errorp = bread(chain->hmp->devvp,
+                                               pbase, psize, &dbp);
+                               if (*errorp) {
+                                       kprintf("hammer2: WRITE PATH: "
+                                               "dbp bread error\n");
+                                       break;
+                               }
                        }
-               }
 
-               chain = hammer2_assign_physical(trans, ip, parentp,
-                                               lbase, comp_block_size,
-                                               errorp);
-               ipdata = &ip->chain->data->ipdata;      /* RELOAD */
-                       
-               if (*errorp) {
-                       kprintf("WRITE PATH: An error occurred while "
-                               "assigning physical space.\n");
-                       KKASSERT(chain == NULL);
-               } else {
-                       /* Get device offset */
-                       hammer2_off_t pbase;
-                       hammer2_off_t pmask;
-                       hammer2_off_t peof;
-                       size_t boff;
-                       size_t psize;
-                       struct buf *dbp;
-                       int temp_check;
-                       
-                       KKASSERT(chain->flags & HAMMER2_CHAIN_MODIFIED);
-                       
-                       switch(chain->bref.type) {
-                       case HAMMER2_BREF_TYPE_INODE:
-                               KKASSERT(chain->data->ipdata.op_flags &
-                                       HAMMER2_OPFLAG_DIRECTDATA);
-                               KKASSERT(bp->b_loffset == 0);
-                               bcopy(bp->b_data, chain->data->ipdata.u.data,
-                                       HAMMER2_EMBEDDED_BYTES);
-                               break;
-                       case HAMMER2_BREF_TYPE_DATA:                            
-                               psize = hammer2_devblksize(chain->bytes);
-                               pmask = (hammer2_off_t)psize - 1;
-                               pbase = chain->bref.data_off & ~pmask;
-                               boff = chain->bref.data_off &
-                                      (HAMMER2_OFF_MASK & pmask);
-                               peof = (pbase + HAMMER2_SEGMASK64) &
-                                      ~HAMMER2_SEGMASK64;
-                               temp_check = HAMMER2_DEC_CHECK(
-                                                       chain->bref.methods);
-
-                               /*
-                                * Optimize out the read-before-write
-                                * if possible.
-                                */
-                               if (comp_block_size == psize) {
-                                       dbp = getblk(chain->hmp->devvp, pbase,
-                                                    psize, 0, 0);
-                               } else {
-                                       *errorp = bread(chain->hmp->devvp,
-                                                       pbase, psize, &dbp);
-                                       if (*errorp) {
-                                               kprintf("hammer2: WRITE PATH: "
-                                                       "dbp bread error\n");
-                                               break;
-                                       }
+                       /*
+                        * When loading the block make sure we don't
+                        * leave garbage after the compressed data.
+                        */
+                       if (comp_size) {
+                               chain->bref.methods =
+                                       HAMMER2_ENC_COMP(comp_algo) +
+                                       HAMMER2_ENC_CHECK(temp_check);
+                               bcopy(comp_buffer, dbp->b_data + boff,
+                                     comp_size);
+                               if (comp_size != comp_block_size) {
+                                       bzero(dbp->b_data + boff +
+                                               comp_size,
+                                             comp_block_size -
+                                               comp_size);
                                }
+                       } else {
+                               chain->bref.methods =
+                                       HAMMER2_ENC_COMP(
+                                               HAMMER2_COMP_NONE) +
+                                       HAMMER2_ENC_CHECK(temp_check);
+                               bcopy(bp->b_data, dbp->b_data + boff,
+                                     pblksize);
+                       }
 
-                               /*
-                                * When loading the block make sure we don't
-                                * leave garbage after the compressed data.
-                                */
-                               if (comp_size) {
-                                       chain->bref.methods =
-                                               HAMMER2_ENC_COMP(comp_method) +
-                                               HAMMER2_ENC_CHECK(temp_check);
-                                       bcopy(comp_buffer, dbp->b_data + boff,
-                                             comp_size);
-                                       if (comp_size != comp_block_size) {
-                                               bzero(dbp->b_data + boff +
-                                                       comp_size,
-                                                     comp_block_size -
-                                                       comp_size);
-                                       }
-                               } else {
-                                       chain->bref.methods =
-                                               HAMMER2_ENC_COMP(
-                                                       HAMMER2_COMP_NONE) +
-                                               HAMMER2_ENC_CHECK(temp_check);
-                                       bcopy(bp->b_data, dbp->b_data + boff,
-                                             pblksize);
-                               }
+                       /*
+                        * Device buffer is now valid, chain is no
+                        * longer in the initial state.
+                        */
+                       atomic_clear_int(&chain->flags,
+                                        HAMMER2_CHAIN_INITIAL);
 
+                       /* Now write the related bdp. */
+                       if (ioflag & IO_SYNC) {
                                /*
-                                * Device buffer is now valid, chain is no
-                                * longer in the initial state.
+                                * Synchronous I/O requested.
                                 */
-                               atomic_clear_int(&chain->flags,
-                                                HAMMER2_CHAIN_INITIAL);
-
-                               /* Now write the related bdp. */
-                               if (ioflag & IO_SYNC) {
-                                       /*
-                                        * Synchronous I/O requested.
-                                        */
-                                       bwrite(dbp);
-                               /*
-                               } else if ((ioflag & IO_DIRECT) &&
-                                          loff + n == pblksize) {
-                                       bdwrite(dbp);
-                               */
-                               } else if (ioflag & IO_ASYNC) {
-                                       bawrite(dbp);
-                               } else if (hammer2_cluster_enable) {
-                                       cluster_write(dbp, peof,
-                                                     HAMMER2_PBUFSIZE,
-                                                     4/*XXX*/);
-                               } else {
-                                       bdwrite(dbp);
-                               }
-                               break;
-                       default:
-                               panic("hammer2_write_bp: bad chain type %d\n",
-                                       chain->bref.type);
-                               /* NOT REACHED */
-                               break;
+                               bwrite(dbp);
+                       /*
+                       } else if ((ioflag & IO_DIRECT) &&
+                                  loff + n == pblksize) {
+                               bdwrite(dbp);
+                       */
+                       } else if (ioflag & IO_ASYNC) {
+                               bawrite(dbp);
+                       } else if (hammer2_cluster_enable) {
+                               cluster_write(dbp, peof,
+                                             HAMMER2_PBUFSIZE,
+                                             4/*XXX*/);
+                       } else {
+                               bdwrite(dbp);
                        }
-                       
-                       hammer2_chain_unlock(chain);
+                       break;
+               default:
+                       panic("hammer2_write_bp: bad chain type %d\n",
+                               chain->bref.type);
+                       /* NOT REACHED */
+                       break;
                }
-               if (comp_buffer)
-                       objcache_put(cache_buffer_write, comp_buffer);
-       } else {
-               zero_write(bp, trans, ip, ipdata, parentp, lbase, errorp);
+
+               hammer2_chain_unlock(chain);
        }
+       if (comp_buffer)
+               objcache_put(cache_buffer_write, comp_buffer);
 }
 
 /*
@@ -1159,32 +1184,32 @@ hammer2_zero_check_and_write(struct buf *bp, hammer2_trans_t *trans,
 {
        hammer2_chain_t *chain;
 
-       if (test_block_not_zeros(bp->b_data, pblksize)) {
+       if (test_block_zeros(bp->b_data, pblksize)) {
+               zero_write(bp, trans, ip, ipdata, parentp, lbase, errorp);
+       } else {
                chain = hammer2_assign_physical(trans, ip, parentp,
                                                lbase, pblksize, errorp);
                hammer2_write_bp(chain, bp, ioflag, pblksize, errorp);
                if (chain)
                        hammer2_chain_unlock(chain);
-       } else {
-               zero_write(bp, trans, ip, ipdata, parentp, lbase, errorp);
        }
 }
 
 /*
  * A function to test whether a block of data contains only zeros,
- * returns 0 in that case or returns 1 otherwise.
+ * returns TRUE (non-zero) if the block is all zeros.
  */
 static
 int
-test_block_not_zeros(char *buf, size_t bytes)
+test_block_zeros(const char *buf, size_t bytes)
 {
        size_t i;
 
        for (i = 0; i < bytes; i += sizeof(long)) {
-               if (*(long *)(buf + i) != 0)
-                       return (1);
+               if (*(const long *)(buf + i) != 0)
+                       return (0);
        }
-       return (0);
+       return (1);
 }
 
 /*