--- old/usr/src/cmd/tail/extern.h	Fri Jan 18 13:59:17 2013
+++ new/usr/src/cmd/tail/extern.h	Fri Jan 18 13:59:17 2013
@@ -27,8 +27,19 @@
  * SUCH DAMAGE.
  *
  */
+/*
+ * Copyright (c) 2013, Joyent, Inc. All Rights Reserved.
+ */
 
 
+#ifndef	EXTERN_H
+#define	EXTERN_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <port.h>
+
+
 #define	WR(p, size) do { \
 	if (write(STDOUT_FILENO, p, size) != (ssize_t)size) \
 		oerr(); \
@@ -48,6 +59,7 @@
 	FILE *fp;
 	char *file_name;
 	struct stat st;
+	file_obj_t fobj;
 };
 
 typedef struct file_info file_info_t;
@@ -67,3 +79,5 @@
 int maparound(struct mapinfo *, off_t);
 
 extern int Fflag, fflag, qflag, rflag, rval, no_files;
+
+#endif	/* EXTERN_H */
--- old/usr/src/cmd/tail/forward.c	Fri Jan 18 13:59:17 2013
+++ new/usr/src/cmd/tail/forward.c	Fri Jan 18 13:59:17 2013
@@ -31,9 +31,7 @@
  */
 
 /*
- * Solaris porting notes:  the original FreeBSD version made use of the
- *                         BSD kqueue event notification framework; this
- *                         was changed to use usleep()
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -53,6 +51,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <unistd.h>
 
 #include "extern.h"
@@ -63,9 +62,11 @@
 
 /* defines for inner loop actions */
 #define	USE_SLEEP	0
+#define	USE_PORT	1
 #define	ADD_EVENTS	2
 
-int action = USE_SLEEP;
+int port;
+int action = USE_PORT;
 
 static const file_info_t *last;
 
@@ -261,6 +262,65 @@
 }
 
 static void
+associate(file_info_t *file, boolean_t assoc)
+{
+	char buf[64];
+
+	if (action != USE_PORT || file->fp == NULL)
+		return;
+
+	if (!S_ISREG(file->st.st_mode)) {
+		/*
+		 * For FIFOs, we use PORT_SOURCE_FD as our port event source.
+		 */
+		if (assoc) {
+			(void) port_associate(port, PORT_SOURCE_FD,
+			    fileno(file->fp), POLLIN, file);
+		} else {
+			(void) port_dissociate(port, PORT_SOURCE_FD,
+			    fileno(file->fp));
+		}
+
+		return;
+	}
+
+	bzero(&file->fobj, sizeof (file->fobj));
+
+	/*
+	 * We pull a bit of a stunt here.  PORT_SOURCE_FILE only allows us to
+	 * specify a file name -- not a file descriptor.  If we were to specify
+	 * the name of the file to port_associate() and that file were moved
+	 * aside, we would not be able to reassociate an event because we would
+	 * not know a name that would resolve to the new file (indeed, there
+	 * might not be such a name -- the file may have been unlinked).  But
+	 * there _is_ a name that we know maps to the file and doesn't change:
+	 * the name of the representation of the open file descriptor in /proc.
+	 * We therefore associate with this name (and the underlying file),
+	 * not the name of the file as specified at the command line.
+	 */
+	(void) snprintf(buf,
+	    sizeof (buf), "/proc/self/fd/%d", fileno(file->fp));
+
+	/*
+	 * Note that portfs uses the address of the specified file_obj_t to
+	 * tag an association; if one creates a different association with a
+	 * (different) file_ob_t that happens to be at the same address,
+	 * the first association will be implicitly removed.  To assure that
+	 * each file has a disjoint file_obj_t, we allocate the memory for it
+	 * in the file_info, not on the stack.
+	 */
+	file->fobj.fo_name = buf;
+
+	if (assoc) {
+		(void) port_associate(port, PORT_SOURCE_FILE,
+		    (uintptr_t)&file->fobj, FILE_MODIFIED | FILE_TRUNC, file);
+	} else {
+		(void) port_dissociate(port, PORT_SOURCE_FILE,
+		    (uintptr_t)&file->fobj);
+	}
+}
+
+static void
 set_events(file_info_t *files)
 {
 	int i;
@@ -271,6 +331,8 @@
 			continue;
 
 		(void) fstat(fileno(file->fp), &file->st);
+
+		associate(file, B_TRUE);
 	}
 }
 
@@ -284,6 +346,8 @@
 	int active, ev_change, i, n = -1;
 	struct stat sb2;
 	file_info_t *file;
+	struct timespec ts;
+	port_event_t ev;
 
 	/* Position each of the files */
 
@@ -307,6 +371,12 @@
 		return;
 
 	last = --file;
+
+	if (action == USE_PORT &&
+	    (stat("/proc/self/fd", &sb2) == -1 || !S_ISDIR(sb2.st_mode) ||
+	    (port = port_create()) == -1))
+		action = USE_SLEEP;
+
 	set_events(files);
 
 	for (;;) {
@@ -341,12 +411,13 @@
 				    sb2.st_dev != file->st.st_dev ||
 				    sb2.st_nlink == 0) {
 					(void) show(file);
+					associate(file, B_FALSE);
 					file->fp = freopen(file->file_name, "r",
 					    file->fp);
-					if (file->fp != NULL)
+					if (file->fp != NULL) {
 						(void) memcpy(&file->st, &sb2,
 						    sizeof (struct stat));
-					else if (errno != ENOENT)
+					} else if (errno != ENOENT)
 						ierr(file->file_name);
 					ev_change++;
 				}
@@ -361,6 +432,26 @@
 			set_events(files);
 
 		switch (action) {
+		case USE_PORT:
+			ts.tv_sec = 1;
+			ts.tv_nsec = 0;
+
+			/*
+			 * In the -F case we set a timeout to ensure that
+			 * we re-stat the file at least once every second.
+			 */
+			n = port_get(port, &ev, Fflag ? &ts : NULL);
+
+			if (n == 0) {
+				file = (file_info_t *)ev.portev_user;
+				associate(file, B_TRUE);
+
+				if (ev.portev_events & FILE_TRUNC)
+					(void) fseek(file->fp, 0, SEEK_SET);
+			}
+
+			break;
+
 		case USE_SLEEP:
 			(void) usleep(250000);
 			break;
--- old/usr/src/cmd/tail/tests/tailtests.sh	Fri Jan 18 13:59:17 2013
+++ new/usr/src/cmd/tail/tests/tailtests.sh	Fri Jan 18 13:59:17 2013
@@ -13,129 +13,355 @@
 
 #
 # Copyright 2010 Chris Love.  All rights reserved.
+# Copyright (c) 2013, Joyent, Inc. All rights reserved.
 #
 
+checktest()
+{
+	local actual=$1
+	local output=$2
+	local test=$3
 
+	if [[ "$actual" != "$output" ]]; then
+		echo "$CMD: test $test: FAIL"
+		echo -e "$CMD: test $test: expected output:\n$o"
+		echo -e "$CMD: test $test: actual output:\n$a"
+	else
+		echo "$CMD: test $test: pass"
+	fi
+}
+
 # 
 # Test cases for 'tail', some based on CoreUtils test cases (validated
-# with legacy Solaris 'tail' and/or xpg4 'tail')
+# with legacy Solaris 'tail' and/or xpg4 'tail').  Note that this is designed
+# to be able to run on BSD systems as well to check our behavior against
+# theirs (some behavior that is known to be idiosyncratic to illumos is
+# skipped on non-illumos systems).
 #
 PROG=/usr/bin/tail
+CMD=`basename $0`
+DIR=""
 
-case $1 in 
-    -x)
-    	PROG=/usr/xpg4/bin/tail
-	;;
-    -o)
-    	PROG=$2
-	;;
-    -?)
-    	echo "Usage: tailtests.sh [-x][-o <override tail executable>]"
-	exit 1
-	;;
-esac
+while [[ $# -gt 0 ]]; do
+	case $1 in
+	    -x)
+		PROG=/usr/xpg4/bin/tail
+		shift
+		;;
+	    -o)
+		PROG=$2
+		shift 2
+		;;
+	    -d)
+		DIR=$2
+		shift 2
+		;;
+	    *)
+		echo "Usage: tailtests.sh" \
+		    "[-x][-o <override tail executable>]" \
+		    "[-d <override output directory>]"
+		exit 1
+		;;
+	esac
+done
 
-echo "Using $PROG"
+#
+# Shut bash up upon receiving a term so we can drop it on our children
+# without disrupting the output.
+#
+trap "exit 0" TERM
 
+echo "$CMD: program is $PROG"
+
+if [[ $DIR != "" ]]; then
+	echo "$CMD: directory is $DIR"
+fi
+
 o=`echo -e "bcd"`
 a=`echo -e "abcd" | $PROG +2c`
-[[ "$a" != "$o" ]] && echo "Fail test 1 - $a"
+checktest "$a" "$o" 1
 
 o=`echo -e ""`
 a=`echo "abcd" | $PROG +8c`
-[[ "$a" != "$o" ]] && echo "Fail test 2 - $a"
+checktest "$a" "$o" 2
 
 o=`echo -e "abcd"`
 a=`echo "abcd" | $PROG -9c`
-[[ "$a" != "$o" ]] && echo "Fail test 3 - $a"
+checktest "$a" "$o" 3
 
 o=`echo -e "x"`
 a=`echo -e "x" | $PROG -1l`
-[[ "$a" != "x" ]] && echo "Fail test 4 - $a"
+checktest "$a" "$o" 4
 
 o=`echo -e "\n"`
 a=`echo -e "x\ny\n" | $PROG -1l`
-[[ "$a" != "$o" ]] && echo "Fail test 5 - $a"
+checktest "$a" "$o" 5
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG -2l`
-[[ "$a" != "$o" ]] && echo "Fail test 6 - $a"
+checktest "$a" "$o" 6
 
 o=`echo -e "y"`
 a=`echo -e "x\ny" | $PROG -1l`
-[[ "$a" != "$o" ]] && echo "Fail test 7 - $a"
+checktest "$a" "$o" 7
 
 o=`echo -e "x\ny\n"`
 a=`echo -e "x\ny\n" | $PROG +1l`
-[[ "$a" != "$o" ]] && echo "Fail test 8 - $a"
+checktest "$a" "$o" 8
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG +2l`
-[[ "$a" != "$o" ]] && echo "Fail test 9 - $a"
+checktest "$a" "$o" 9
 
 o=`echo -e "x"`
 a=`echo -e "x" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 10 - $a"
+checktest "$a" "$o" 10
 
 o=`echo -e "\n"`
 a=`echo -e "x\ny\n" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 11 - $a"
+checktest "$a" "$o" 11
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG -2`
-[[ "$a" != "$o" ]] && echo "Fail test 12 - $a"
+checktest "$a" "$o" 12
 
 o=`echo -e "y"`
 a=`echo -e "x\ny" | $PROG -1`
-[[ "$a" != "$o" ]] && echo "Fail test 13 - $a"
+checktest "$a" "$o" 13
 
 o=`echo -e "x\ny\n"`
 a=`echo -e "x\ny\n" | $PROG +1`
-[[ "$a" != "$o" ]] && echo "Fail test 14 - $a"
+checktest "$a" "$o" 14
 
 o=`echo -e "y\n"`
 a=`echo -e "x\ny\n" | $PROG +2`
-[[ "$a" != "$o" ]] && echo "Fail test 15 - $a"
+checktest "$a" "$o" 15
 
-# For compatibility with Legacy Solaris tail this should also work as '+c'
 o=`echo -e "yyz"`
 a=`echo -e "xyyyyyyyyyyz" | $PROG +10c`
-[[ "$a" != "$o" ]] && echo "Fail test 16 - $a"
+checktest "$a" "$o" 16
 
-o=`echo -e "yyz"`
-a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
-[[ "$a" != "$o" ]] && echo "Fail test 16a - $a"
-
-
-# For compatibility with Legacy Solaris tail this should also work as '+l'
 o=`echo -e "y\ny\nz"`
 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l`
-[[ "$a" != "$o" ]] && echo "Fail test 17 - $a"
+checktest "$a" "$o" 17
 
-o=`echo -e "y\ny\nz"`
-a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
-[[ "$a" != "$o" ]] && echo "Fail test 17a - $a"
-
-
-# For compatibility with Legacy Solaris tail this should also work as '-l'
 o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l`
-[[ "$a" != "$o" ]] && echo "Fail test 18 - $a"
+checktest "$a" "$o" 18
 
-o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
-a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
-[[ "$a" != "$o" ]] && echo "Fail test 18a - $a"
+#
+# For reasons that are presumably as accidental as they are ancient, legacy
+# (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c,
+# +10l and -10l, respectively.  If we are on SunOS, verify that this silly
+# behavior is functional.
+#
+if [[ `uname -s` == "SunOS" ]]; then
+	o=`echo -e "yyz"`
+	a=`echo -e "xyyyyyyyyyyz" | $PROG +c`
+	checktest "$a" "$o" 16a
 
+	o=`echo -e "y\ny\nz"`
+	a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l`
+	checktest "$a" "$o" 17a
+
+	o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"`
+	a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l`
+	checktest "$a" "$o" 18a
+fi
+
 o=`echo -e "c\nb\na"`
 a=`echo -e "a\nb\nc" | $PROG -r`
-[[ "$a" != "$o" ]] && echo "Fail test 19 - $a"
+checktest "$a" "$o" 19
 
+#
+# Now we want to do a series of follow tests.
+#
+if [[ $DIR == "" ]]; then
+	tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1)
+else
+	tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1)
+fi
 
-echo "Completed"
+follow=$tdir/follow
+moved=$tdir/follow.moved
+out=$tdir/out
 
-exit 0
+#
+# First, verify that following works in its most basic sense.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+kill $child
+sleep 1
 
-# Template for additional test cases
-#o=`echo -e ""`
-#a=`echo -e "" | $PROG `
-#[[ "$a" != "$o" ]] && echo "Fail test  - $a"
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 20
+rm $follow
+
+#
+# Now verify that following correctly follows the file being moved.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "d\ne\nf" >> $moved
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 21
+rm $moved
+
+#
+# And now truncation with the new offset being less than the old offset.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
+a=`cat $out`
+checktest "$a" "$o" 22
+rm $follow
+
+#
+# And truncation with the new offset being greater than the old offset.
+#
+echo -e "a\nb\nc" > $follow
+sleep 1
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
+a=`cat $out`
+checktest "$a" "$o" 23
+rm $follow
+
+#
+# Verify that we can follow the moved file and correctly see a truncation.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -f $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "d\ne\nf" >> $moved
+sleep 1
+echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"`
+a=`cat $out`
+checktest "$a" "$o" 24
+rm $moved
+
+#
+# Verify that capital-F follow properly deals with truncation
+#
+echo -e "a\nb\nc" > $follow
+$PROG -F $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "d\ne\nf" >> $follow
+sleep 1
+echo -e "g\nh\ni" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"`
+a=`cat $out`
+checktest "$a" "$o" 25
+rm $follow
+
+#
+# Verify that capital-F follow _won't_ follow the moved file and will
+# correctly deal with recreation of the original file.
+#
+echo -e "a\nb\nc" > $follow
+$PROG -F $follow > $out 2> /dev/null &
+child=$!
+sleep 2
+mv $follow $moved
+
+echo -e "x\ny\nz" >> $moved
+echo -e "d\ne\nf" > $follow
+sleep 1
+kill $child
+sleep 1
+
+o=`echo -e "a\nb\nc\nd\ne\nf\n"`
+a=`cat $out`
+checktest "$a" "$o" 26
+rm $moved
+
+#
+# Verify that following two files works.
+#
+echo -e "one" > $follow
+echo -e "two" > $moved
+$PROG -f $follow $moved > $out 2> /dev/null &
+child=$!
+sleep 2
+echo -e "three" >> $follow
+sleep 1
+echo -e "four" >> $moved
+sleep 1
+echo -e "five" >> $follow
+sleep 1
+kill $child
+sleep 1
+
+# There is a bug where the content comes before the header lines,
+# where rlines/mapprint happens before the header.  A pain to fix.
+# In this test, just make sure we see both files change.
+o="one
+
+==> $follow <==
+two
+
+==> $moved <==
+
+==> $follow <==
+three
+
+==> $moved <==
+four
+
+==> $follow <==
+five"
+a=`cat $out`
+checktest "$a" "$o" 27
+rm $follow $moved
+
+echo "$CMD: completed"
+
+exit $errs
+
--- old/usr/src/man/man1/tail.1	Fri Jan 18 13:59:17 2013
+++ new/usr/src/man/man1/tail.1	Fri Jan 18 13:59:17 2013
@@ -7,7 +7,8 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH TAIL 1 "Jul 13, 2005"
+.\" Copyright (c) 2013, Joyent, Inc. All Rights Reserved.
+.TH TAIL 1 "Jan 18, 2013"
 .SH NAME
 tail \- deliver the last part of a file
 .SH SYNOPSIS
@@ -23,12 +24,12 @@
 
 .LP
 .nf
-\fB/usr/bin/tail\fR [\(+- \fInumber\fR [lbcf]] [\fIfile\fR]
+\fB/usr/bin/tail\fR [\(+- \fInumber\fR [lbcfF]] [\fIfile\fR]
 .fi
 
 .LP
 .nf
-\fB/usr/bin/tail\fR [\fB-lbcf\fR] [\fIfile\fR]
+\fB/usr/bin/tail\fR [\fB-lbcfF\fR] [\fIfile\fR]
 .fi
 
 .LP
@@ -92,13 +93,26 @@
 \fB\fB-f\fR \fR
 .ad
 .RS 7n
-Follow. If the input-file is not a pipe, the program does not terminate after
-the line of the input-file has been copied, but enters an endless loop, wherein
-it sleeps for a second and then attempts to read and copy further records from
-the input-file. Thus it can be used to monitor the growth of a file that is
-being written by some other process.
+Follow. If the input-file is not a pipe, \fBtail\fR does not terminate after
+the last line of the input-file has been copied, but enters an endless loop,
+wherein it watches the file for modifications and attempts to read and copy
+further records from the input-file. Thus it can be used to monitor the growth
+of a file that is being written by some other process. If the watched file is
+truncated \fBtail\fR will begin reading records from the start of the file.
 .RE
 
+.sp
+.ne 2
+.na
+\fB\fB-F\fR \fR
+.ad
+.RS 7n
+Follow named file. Operates as with \fB-f\fR, except that if the file is moved
+(e.g. if a watched log file is rotated) \fBtail\fR will close the original file
+and begin reading records from the start of the file with the specified name
+if and when that file is recreated.
+.RE
+
 .sp
 .ne 2
 .na
--- old/usr/src/man/man3c/port_associate.3c	Fri Jan 18 13:59:18 2013
+++ new/usr/src/man/man3c/port_associate.3c	Fri Jan 18 13:59:18 2013
@@ -1,9 +1,10 @@
 '\" te
 .\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (c) 2013, Joyent, Inc. All Rights Reserved.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH PORT_ASSOCIATE 3C "Nov 9, 2007"
+.TH PORT_ASSOCIATE 3C "Jan 18, 2013"
 .SH NAME
 port_associate, port_dissociate \- associate or dissociate the object with the
 port
@@ -73,14 +74,14 @@
 .LP
 Objects of type \fBPORT_SOURCE_FILE\fR are pointer to the structure
 \fBfile_obj\fR defined in \fB<sys/port.h>\fR. This event source provides event
-notification when the specified file/directory is accessed or modified or when
-its status changes. The path name of the file/directory to be watched is passed
-in the \fBstruct file_obj\fR along with the \fBaccess\fR, \fBmodification\fR,
-and \fBchange\fR time stamps acquired from a \fBstat\fR(2) call. If the file
-name is a symbolic links, it is followed by default. The \fBFILE_NOFOLLOW\fR
-needs to be passed in along with the specified events if the symbolic link
-itself needs to be watched and \fBlstat()\fR needs to be used to get the file
-status of the symbolic link file.
+notification when the specified file/directory is accessed, modified,
+truncated or when its status changes. The path name of the file/directory to
+be watched is passed in the \fBstruct file_obj\fR along with the \fBaccess\fR,
+\fBmodification\fR, and \fBchange\fR time stamps acquired from a \fBstat\fR(2)
+call. If the file name is a symbolic link, it is followed by default. The
+\fBFILE_NOFOLLOW\fR needs to be passed in along with the specified events if
+the symbolic link itself needs to be watched and \fBlstat()\fR needs to be
+used to get the file status of the symbolic link file.
 .sp
 .LP
 The \fBstruct file_obj\fR contains the following elements:
@@ -87,7 +88,7 @@
 .sp
 .in +2
 .nf
-timestruc_t     fo_atime;  /* Access time got from stat() */
+timestruc_t     fo_atime;  /* Access time from stat() */
 timestruc_t     fo_mtime;  /* Modification time from stat() */
 timestruc_t     fo_ctime;  /* Change time from stat() */
 char            *fo_name;  /* Pointer to a null terminated path name */
@@ -104,11 +105,13 @@
 .sp
 .LP
 The event types that can be specified at \fBport_associate()\fR time for
-\fBPORT_SOURCE_FILE\fR are \fBFILE_ACCESS\fR, \fBFILE_MODIFIED\fR, and
-\fBFILE_ATTRIB\fR, corresponding to the three time stamps. An \fBfo_atime\fR
-change results in the \fBFILE_ACCESS\fR event, an \fBfo_mtime\fR change results
-in the \fBFILE_MODIFIED\fR event, and an \fBfo_time\fR change results in the
-\fBFILE_ATTRIB\fR event.
+\fBPORT_SOURCE_FILE\fR are \fBFILE_ACCESS\fR, \fBFILE_MODIFIED\fR,
+\fBFILE_ATTRIB\fR, and \fbFILE_TRUNC\fR. The first three of these correspond
+to the three time stamps: an \fBfo_atime\fR change results in the
+\fBFILE_ACCESS\fR event, an \fBfo_mtime\fR change results in the
+\fBFILE_MODIFIED\fR event, and an \fBfo_ctime\fR change results in the
+\fBFILE_ATTRIB\fR event. If the operation that induced the time stamp update
+also truncated the file, \fBFILE_TRUNC\fR will be set in the resulting event.
 .sp
 .LP
 The following exception events are delivered when they occur. These event types
--- old/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Fri Jan 18 13:59:18 2013
+++ new/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Fri Jan 18 13:59:18 2013
@@ -28,6 +28,10 @@
  *	All rights reserved.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -2298,6 +2302,12 @@
 						vattr.va_mask = AT_SIZE;
 						error = nfs3setattr(vp,
 						    &vattr, 0, cr);
+
+						/*
+						 * Existing file was truncated;
+						 * emit a create event.
+						 */
+						vnevent_create(vp, ct);
 					}
 				}
 			}
@@ -2306,12 +2316,9 @@
 		if (error) {
 			VN_RELE(vp);
 		} else {
-			/*
-			 * existing file got truncated, notify.
-			 */
-			vnevent_create(vp, ct);
 			*vpp = vp;
 		}
+
 		return (error);
 	}
 
--- old/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Fri Jan 18 13:59:18 2013
+++ new/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Fri Jan 18 13:59:18 2013
@@ -31,6 +31,10 @@
  *	All Rights Reserved
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -6653,9 +6657,6 @@
 	} else {
 		vnode_t *tvp;
 		rnode4_t *trp;
-		/*
-		 * existing file got truncated, notify.
-		 */
 		tvp = vp;
 		if (vp->v_type == VREG) {
 			trp = VTOR4(vp);
@@ -6662,7 +6663,14 @@
 			if (IS_SHADOW(vp, trp))
 				tvp = RTOV4(trp);
 		}
-		vnevent_create(tvp, ct);
+
+		if (must_trunc) {
+			/*
+			 * existing file got truncated, notify.
+			 */
+			vnevent_create(tvp, ct);
+		}
+
 		*vpp = vp;
 	}
 	return (error);
--- old/usr/src/uts/common/fs/nfs/nfs_vnops.c	Fri Jan 18 13:59:20 2013
+++ new/usr/src/uts/common/fs/nfs/nfs_vnops.c	Fri Jan 18 13:59:20 2013
@@ -25,6 +25,10 @@
  *	All rights reserved.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -2030,6 +2034,14 @@
 				    vp->v_type == VREG) {
 					vattr.va_mask = AT_SIZE;
 					error = nfssetattr(vp, &vattr, 0, cr);
+
+					if (!error) {
+						/*
+						 * Existing file was truncated;
+						 * emit a create event.
+						 */
+						vnevent_create(vp, ct);
+					}
 				}
 			}
 		}
@@ -2037,10 +2049,6 @@
 		if (error) {
 			VN_RELE(vp);
 		} else {
-			/*
-			 * existing file got truncated, notify.
-			 */
-			vnevent_create(vp, ct);
 			*vpp = vp;
 		}
 		return (error);
--- old/usr/src/uts/common/fs/portfs/port_fop.c	Fri Jan 18 13:59:20 2013
+++ new/usr/src/uts/common/fs/portfs/port_fop.c	Fri Jan 18 13:59:20 2013
@@ -23,6 +23,9 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
 
 /*
  * File Events Notification
@@ -1965,7 +1968,9 @@
 	if (op & FOP_ATTRIB_MASK) {
 		event  |= FILE_ATTRIB;
 	}
-
+	if (op & FOP_TRUNC_MASK) {
+		event  |= FILE_TRUNC;
+	}
 	if (event) {
 		port_fop_sendevent(vp, 	event, NULL, NULL);
 	}
@@ -2147,6 +2152,9 @@
 	int		events = 0;
 
 	retval = vnext_setattr(vf, vap, flags, cr, ct);
+	if (vap->va_mask & AT_SIZE) {
+		events |= FOP_FILE_TRUNC;
+	}
 	if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
 		events |= FOP_FILE_SETATTR_MTIME;
 	}
@@ -2322,8 +2330,8 @@
 			port_fop_sendevent(vp, FILE_DELETE, dvp, name);
 		break;
 	case	VE_CREATE:
-			port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
-			    NULL, NULL);
+			port_fop_sendevent(vp,
+			    FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
 		break;
 	case	VE_LINK:
 			port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
--- old/usr/src/uts/common/fs/tmpfs/tmp_vnops.c	Fri Jan 18 13:59:20 2013
+++ new/usr/src/uts/common/fs/tmpfs/tmp_vnops.c	Fri Jan 18 13:59:20 2013
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/t_lock.h>
@@ -978,6 +982,8 @@
 	}
 
 	if (error == 0) {	/* name found */
+		boolean_t trunc = B_FALSE;
+
 		ASSERT(oldtp);
 
 		rw_enter(&oldtp->tn_rwlock, RW_WRITER);
@@ -1005,6 +1011,7 @@
 			rw_enter(&oldtp->tn_contents, RW_WRITER);
 			(void) tmpnode_trunc(tm, oldtp, 0);
 			rw_exit(&oldtp->tn_contents);
+			trunc = B_TRUE;
 		}
 		rw_exit(&oldtp->tn_rwlock);
 		if (IS_DEVVP(*vpp)) {
@@ -1019,9 +1026,9 @@
 			*vpp = newvp;
 		}
 
-		if (error == 0) {
+		if (trunc)
 			vnevent_create(*vpp, ct);
-		}
+
 		return (0);
 	}
 
--- old/usr/src/uts/common/sys/port.h	Fri Jan 18 13:59:20 2013
+++ new/usr/src/uts/common/sys/port.h	Fri Jan 18 13:59:20 2013
@@ -24,11 +24,13 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #ifndef	_SYS_PORT_H
 #define	_SYS_PORT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -106,6 +108,7 @@
 #define	FILE_ACCESS		0x00000001
 #define	FILE_MODIFIED		0x00000002
 #define	FILE_ATTRIB		0x00000004
+#define	FILE_TRUNC		0x00100000
 #define	FILE_NOFOLLOW		0x10000000
 
 /*
--- old/usr/src/uts/common/sys/port_impl.h	Fri Jan 18 13:59:21 2013
+++ new/usr/src/uts/common/sys/port_impl.h	Fri Jan 18 13:59:21 2013
@@ -24,6 +24,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
 #ifndef	_SYS_PORT_IMPL_H
 #define	_SYS_PORT_IMPL_H
 
@@ -311,6 +315,7 @@
 #define	FOP_FILE_SETATTR_MTIME	0x00080000
 #define	FOP_FILE_SETATTR_CTIME	0x00100000
 #define	FOP_FILE_LINK_SRC	0x00200000
+#define	FOP_FILE_TRUNC		0x00400000
 
 /*
  * File modification event.
@@ -339,10 +344,15 @@
 
 
 /*
+ * File trunc event
+ */
+#define	FOP_TRUNC_MASK		(FOP_FILE_TRUNC|FOP_FILE_CREATE)
+
+/*
  * valid watchable events
  */
 #define	FILE_EVENTS_MASK	(FILE_ACCESS|FILE_MODIFIED|FILE_ATTRIB \
-				|FILE_NOFOLLOW)
+				|FILE_NOFOLLOW|FILE_TRUNC)
 /* --- End file events --- */
 
 /*
