M usr/src/pkg/manifests/system-test-zfstest.mf => usr/src/pkg/manifests/system-test-zfstest.mf +14 -0
@@ 53,6 53,7 @@ dir path=opt/zfs-tests/tests/functional/cli_root/zfs_clone
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_copies
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_create
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_destroy
+dir path=opt/zfs-tests/tests/functional/cli_root/zfs_diff
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_get
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_inherit
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_load-key
@@ 891,6 892,19 @@ file \
file \
path=opt/zfs-tests/tests/functional/cli_root/zfs_destroy/zfs_destroy_common.kshlib \
mode=0444
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/setup mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/socket mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_changes \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_cliargs \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_encrypted \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_timestamp \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_types \
+ mode=0555
file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/setup mode=0555
file path=opt/zfs-tests/tests/functional/cli_root/zfs_get/zfs_get_001_pos \
M usr/src/test/zfs-tests/runfiles/delphix.run => usr/src/test/zfs-tests/runfiles/delphix.run +5 -0
@@ 144,6 144,11 @@ tests = ['zfs_destroy_001_pos', 'zfs_destroy_002_pos', 'zfs_destroy_003_pos',
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
'zfs_destroy_016_pos']
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+ 'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
[/opt/zfs-tests/tests/functional/cli_root/zfs_get]
tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
M usr/src/test/zfs-tests/runfiles/omnios.run => usr/src/test/zfs-tests/runfiles/omnios.run +5 -0
@@ 144,6 144,11 @@ tests = ['zfs_destroy_001_pos', 'zfs_destroy_002_pos', 'zfs_destroy_003_pos',
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
'zfs_destroy_016_pos']
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+ 'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
[/opt/zfs-tests/tests/functional/cli_root/zfs_get]
tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
M usr/src/test/zfs-tests/runfiles/openindiana.run => usr/src/test/zfs-tests/runfiles/openindiana.run +5 -0
@@ 144,6 144,11 @@ tests = ['zfs_destroy_001_pos', 'zfs_destroy_002_pos', 'zfs_destroy_003_pos',
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
'zfs_destroy_016_pos']
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+ 'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
[/opt/zfs-tests/tests/functional/cli_root/zfs_get]
tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
M usr/src/test/zfs-tests/runfiles/smartos.run => usr/src/test/zfs-tests/runfiles/smartos.run +5 -0
@@ 106,6 106,11 @@ tests = ['zfs_destroy_001_pos', 'zfs_destroy_002_pos', 'zfs_destroy_003_pos',
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
'zfs_destroy_016_pos']
+[/opt/zfs-tests/tests/functional/cli_root/zfs_diff]
+tests = ['zfs_diff_changes', 'zfs_diff_cliargs', 'zfs_diff_timestamp',
+ 'zfs_diff_types', 'zfs_diff_encrypted']
+tags = ['functional', 'cli_root', 'zfs_diff']
+
[/opt/zfs-tests/tests/functional/cli_root/zfs_get]
tests = ['zfs_get_001_pos', 'zfs_get_002_pos', 'zfs_get_003_pos',
'zfs_get_004_pos', 'zfs_get_005_neg', 'zfs_get_006_neg', 'zfs_get_007_neg',
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/Makefile => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/Makefile +44 -0
@@ 0,0 1,44 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/zfs-tests
+TARGETDIR = $(ROOTOPTPKG)/tests/functional/cli_root/zfs_diff
+
+PROG = socket
+OBJS = $(PROG:%=%.o)
+SRCS = $(OBJS:%.o=%.c)
+
+include $(SRC)/cmd/Makefile.cmd
+
+$(TARGETDIR)/$(PROG) := FILEMODE = 0555
+
+CPPFLAGS = -D__EXTENSIONS__
+LDLIBS = -lsocket
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: $(TARGETDIR)/$(PROG)
+
+clobber: clean
+ -$(RM) $(PROG)
+
+clean:
+ -$(RM) $(OBJS)
+
+include $(SRC)/test/zfs-tests/Makefile.com
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/cleanup.ksh +19 -0
@@ 0,0 1,19 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/setup.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/setup.ksh +21 -0
@@ 0,0 1,21 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+DISK=${DISKS%% *}
+
+default_volume_setup $DISK
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/socket.c => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/socket.c +59 -0
@@ 0,0 1,59 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* ARGSUSED */
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_un sock;
+ int fd;
+ char *path;
+ size_t size;
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s /path/to/socket\n", argv[0]);
+ exit(1);
+ }
+ path = argv[1];
+ size = sizeof (sock.sun_path);
+ (void) strncpy(sock.sun_path, (char *)path, size - 1);
+ sock.sun_path[size - 1] = '\0';
+
+ sock.sun_family = AF_UNIX;
+ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
+ perror("socket");
+ return (1);
+ }
+ if (bind(fd, (struct sockaddr *)&sock, sizeof (struct sockaddr_un))) {
+ perror("bind");
+ return (1);
+ }
+ if (close(fd)) {
+ perror("close");
+ return (1);
+ }
+ return (0);
+}
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_changes.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_changes.ksh +96 -0
@@ 0,0 1,96 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should display changes correctly.
+#
+# STRATEGY:
+# 1. Create a filesystem with both files and directories, then snapshot it
+# 2. Generate different types of changes and verify 'zfs diff' displays them
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must zfs destroy -r "$DATASET"
+ rm -f "$FILEDIFF"
+}
+
+#
+# Verify object $path has $change type
+# Valid types are:
+# * - (The path has been removed)
+# * + (The path has been created)
+# * M (The path has been modified)
+# * R (The path has been renamed)
+#
+function verify_object_change # <path> <change>
+{
+ path="$1"
+ change="$2"
+
+ log_must eval "zfs diff -F $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+ diffchg="$(nawk -v path="$path" '$NF == path { print $1 }' < $FILEDIFF)"
+ if [[ "$diffchg" != "$change" ]]; then
+ log_fail "Unexpected change for $path ('$diffchg' != '$change')"
+ else
+ log_note "Object $path change is displayed correctly: '$change'"
+ fi
+}
+
+log_assert "'zfs diff' should display changes correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS/fs"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+
+# 1. Create a filesystem with both files and directories, then snapshot it
+log_must zfs create $DATASET
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+log_must touch "$MNTPOINT/fremoved"
+log_must touch "$MNTPOINT/frenamed"
+log_must touch "$MNTPOINT/fmodified"
+log_must mkdir "$MNTPOINT/dremoved"
+log_must mkdir "$MNTPOINT/drenamed"
+log_must mkdir "$MNTPOINT/dmodified"
+log_must zfs snapshot "$TESTSNAP1"
+
+# 2. Generate different types of changes and verify 'zfs diff' displays them
+log_must rm -f "$MNTPOINT/fremoved"
+log_must mv "$MNTPOINT/frenamed" "$MNTPOINT/frenamed.new"
+log_must touch "$MNTPOINT/fmodified"
+log_must rmdir "$MNTPOINT/dremoved"
+log_must mv "$MNTPOINT/drenamed" "$MNTPOINT/drenamed.new"
+log_must touch "$MNTPOINT/dmodified/file"
+log_must touch "$MNTPOINT/fcreated"
+log_must mkdir "$MNTPOINT/dcreated"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_change "$MNTPOINT/fremoved" "-"
+verify_object_change "$MNTPOINT/frenamed.new" "R"
+verify_object_change "$MNTPOINT/fmodified" "M"
+verify_object_change "$MNTPOINT/fcreated" "+"
+verify_object_change "$MNTPOINT/dremoved" "-"
+verify_object_change "$MNTPOINT/drenamed.new" "R"
+verify_object_change "$MNTPOINT/dmodified" "M"
+verify_object_change "$MNTPOINT/dcreated" "+"
+
+log_pass "'zfs diff' displays changes correctly."
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_cliargs.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_cliargs.ksh +80 -0
@@ 0,0 1,80 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should only work with supported options.
+#
+# STRATEGY:
+# 1. Create two snapshots
+# 2. Verify every supported option is accepted
+# 3. Verify supported options raise an error with unsupported arguments
+# 4. Verify other unsupported options raise an error
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $TESTSNAP1 $TESTSNAP2; do
+ if snapexists "$snap"; then
+ log_must zfs destroy "$snap"
+ fi
+ done
+}
+
+log_assert "'zfs diff' should only work with supported options."
+log_onexit cleanup
+
+typeset goodopts=("" "-F" "-H" "-t" "-FH" "-Ft" "-Ht" "-FHt")
+typeset badopts=("-f" "-h" "-h" "-T" "-Fx" "-Ho" "-tT" "-")
+
+DATASET="$TESTPOOL/$TESTFS"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+
+# 1. Create two snapshots
+log_must zfs snapshot "$TESTSNAP1"
+log_must zfs snapshot "$TESTSNAP2"
+
+# 2. Verify every supported option is accepted
+for opt in ${goodopts[@]}
+do
+ log_must zfs diff $opt "$TESTSNAP1"
+ log_must zfs diff $opt "$TESTSNAP1" "$DATASET"
+ log_must zfs diff $opt "$TESTSNAP1" "$TESTSNAP2"
+done
+
+# 3. Verify supported options raise an error with unsupported arguments
+for opt in ${goodopts[@]}
+do
+ log_mustnot zfs diff $opt
+ log_mustnot zfs diff $opt "$DATASET"
+ log_mustnot zfs diff $opt "$DATASET@noexists"
+ log_mustnot zfs diff $opt "$DATASET" "$TESTSNAP1"
+ log_mustnot zfs diff $opt "$TESTSNAP2" "$TESTSNAP1"
+done
+
+# 4. Verify other unsupported options raise an error
+for opt in ${badopts[@]}
+do
+ log_mustnot zfs diff $opt "$TESTSNAP1" "$DATASET"
+ log_mustnot zfs diff $opt "$TESTSNAP1" "$TESTSNAP2"
+done
+
+log_pass "'zfs diff' only works with supported options."
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_encrypted.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_encrypted.ksh +54 -0
@@ 0,0 1,54 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018, Datto Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff' should work with encrypted datasets
+#
+# STRATEGY:
+# 1. Create an encrypted dataset
+# 2. Create two snapshots of the dataset
+# 3. Perform 'zfs diff -Ft' and verify no errors occur
+# 4. Perform the same test on a dataset with large dnodes
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must zfs destroy -r $TESTPOOL/$TESTFS1
+}
+
+log_assert "'zfs diff' should work with encrypted datasets"
+log_onexit cleanup
+
+# 1. Create an encrypted dataset
+log_must eval "echo 'password' | zfs create -o encryption=on \
+ -o keyformat=passphrase $TESTPOOL/$TESTFS1"
+MNTPOINT="$(get_prop mountpoint $TESTPOOL/$TESTFS1)"
+
+# 2. Create two snapshots of the dataset
+log_must zfs snapshot $TESTPOOL/$TESTFS1@snap1
+log_must touch "$MNTPOINT/file"
+log_must zfs snapshot $TESTPOOL/$TESTFS1@snap2
+
+# 3. Perform 'zfs diff' and verify no errors occur
+log_must zfs diff -Ft $TESTPOOL/$TESTFS1@snap1 $TESTPOOL/$TESTFS1@snap2
+
+log_pass "'zfs diff' works with encrypted datasets"
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_timestamp.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_timestamp.ksh +100 -0
@@ 0,0 1,100 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff -t' should display inode change time correctly.
+#
+# STRATEGY:
+# 1. Create a snapshot
+# 2. Create some files with a random delay and snapshot the filesystem again
+# 3. Verify 'zfs diff -t' correctly display timestamps
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ for snap in $TESTSNAP1 $TESTSNAP2; do
+ if snapexists "$snap"; then
+ log_must zfs destroy "$snap"
+ fi
+ done
+ find "$MNTPOINT" -type f -delete
+ rm -f "$FILEDIFF"
+}
+
+#
+# Creates $count files in $fspath. Waits a random delay between each file.
+#
+function create_random # <fspath> <count>
+{
+ fspath="$1"
+ typeset -i count="$2"
+ typeset -i i=0
+
+ while (( i < count )); do
+ log_must touch "$fspath/file$i"
+ sleep $(random 3)
+ (( i = i + 1 ))
+ done
+}
+
+log_assert "'zfs diff -t' should display inode change time correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+FILENUM=5
+
+# 1. Create a snapshot
+log_must zfs snapshot "$TESTSNAP1"
+
+# 2. Create some files with a random delay and snapshot the filesystem again
+create_random "$MNTPOINT" $FILENUM
+log_must zfs snapshot "$TESTSNAP2"
+
+# 3. Verify 'zfs diff -t' correctly display timestamps
+typeset -i count=0
+log_must eval "zfs diff -t $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+nawk '{print substr($1,1,index($1,".")-1)" "$NF}' < "$FILEDIFF" | while read line
+do
+ read ctime file <<< "$line"
+
+ # If path from 'zfs diff' is not a file (could be xattr object) skip it
+ if [[ ! -f "$file" ]]; then
+ continue;
+ fi
+
+ filetime="$(stat -c '%Z' $file)"
+ if [[ "$filetime" != "$ctime" ]]; then
+ log_fail "Unexpected ctime for file $file ($filetime != $ctime)"
+ else
+ log_note "Correct ctime read on $file: $ctime"
+ fi
+
+ (( i = i + 1 ))
+done
+if [[ $i != $FILENUM ]]; then
+ log_fail "Wrong number of files verified ($i != $FILENUM)"
+fi
+
+log_pass "'zfs diff -t' displays inode change time correctly."
A usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_types.ksh => usr/src/test/zfs-tests/tests/functional/cli_root/zfs_diff/zfs_diff_types.ksh +126 -0
@@ 0,0 1,126 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs diff -F' shows different object types correctly.
+#
+# STRATEGY:
+# 1. Prepare a dataset
+# 2. Create different objects and verify 'zfs diff -F' shows the correct type
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ log_must zfs destroy -r "$DATASET"
+ rm -f "$FILEDIFF"
+}
+
+#
+# Verify object at $path is of type $symbol using 'zfs diff -F'
+# Valid types are:
+# * B (Block device)
+# * C (Character device)
+# * / (Directory)
+# * > (Door)
+# * | (Named pipe)
+# * @ (Symbolic link)
+# * P (Event port)
+# * = (Socket)
+# * F (Regular file)
+#
+function verify_object_class # <path> <symbol>
+{
+ path="$1"
+ symbol="$2"
+
+ log_must eval "zfs diff -F $TESTSNAP1 $TESTSNAP2 > $FILEDIFF"
+ diffsym="$(nawk -v path="$path" '$NF == path { print $2 }' < $FILEDIFF)"
+ if [[ "$diffsym" != "$symbol" ]]; then
+ log_fail "Unexpected type for $path ('$diffsym' != '$symbol')"
+ else
+ log_note "Object $path type is correctly displayed as '$symbol'"
+ fi
+
+ log_must zfs destroy "$TESTSNAP1"
+ log_must zfs destroy "$TESTSNAP2"
+}
+
+log_assert "'zfs diff -F' should show different object types correctly."
+log_onexit cleanup
+
+DATASET="$TESTPOOL/$TESTFS/fs"
+TESTSNAP1="$DATASET@snap1"
+TESTSNAP2="$DATASET@snap2"
+FILEDIFF="$TESTDIR/zfs-diff.txt"
+MAJOR=$(stat -c %t /dev/null)
+MINOR=$(stat -c %T /dev/null)
+
+# 1. Prepare a dataset
+log_must zfs create $DATASET
+MNTPOINT="$(get_prop mountpoint $DATASET)"
+log_must zfs set devices=on $DATASET
+log_must zfs set xattr=on $DATASET
+
+# 2. Create different objects and verify 'zfs diff -F' shows the correct type
+# 2. F (Regular file)
+log_must zfs snapshot "$TESTSNAP1"
+log_must touch "$MNTPOINT/file"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/file" "F"
+
+# 2. @ (Symbolic link)
+log_must zfs snapshot "$TESTSNAP1"
+log_must ln -s "$MNTPOINT/file" "$MNTPOINT/link"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/link" "@"
+
+# 2. B (Block device)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/bdev" b $MAJOR $MINOR
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/bdev" "B"
+
+# 2. C (Character device)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/cdev" c $MAJOR $MINOR
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/cdev" "C"
+
+# 2. | (Named pipe)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mknod "$MNTPOINT/fifo" p
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/fifo" "|"
+
+# 2. / (Directory)
+log_must zfs snapshot "$TESTSNAP1"
+log_must mkdir "$MNTPOINT/dir"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/dir" "/"
+
+# 2. = (Socket)
+log_must zfs snapshot "$TESTSNAP1"
+log_must $STF_SUITE/tests/functional/cli_root/zfs_diff/socket "$MNTPOINT/sock"
+log_must zfs snapshot "$TESTSNAP2"
+verify_object_class "$MNTPOINT/sock" "="
+
+log_pass "'zfs diff -F' shows different object types correctly."