diff options
author | Bryan Cantrill <bryan@joyent.com> | 2012-08-03 19:23:29 +0000 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2013-11-06 17:20:21 -0800 |
commit | 72102e7461c97dc268d21d9dd8f02da45f174acd (patch) | |
tree | bb9ad3093bfda709f66f4c8758f02edeec24e8c1 | |
parent | b3d32f0ceb59362ba287dcfd6de471e98bfc7fa9 (diff) | |
download | illumos-joyent-72102e7461c97dc268d21d9dd8f02da45f174acd.tar.gz |
3928 `tail -f ...` doesn't notice file truncation
3929 `man tail` doesn't mentioned "-F" option
3930 'tail -F ...' not resetting the offset of file rotation properly
3968 want FILE_TRUNC event for PORT_SOURCE_FILE
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Approved by: Dan McDonald <danmcd@nexenta.com>
-rw-r--r-- | usr/src/cmd/tail/extern.h | 9 | ||||
-rw-r--r-- | usr/src/cmd/tail/forward.c | 145 | ||||
-rw-r--r-- | usr/src/cmd/tail/tests/tailtests.sh | 451 | ||||
-rw-r--r-- | usr/src/man/man1/tail.1 | 27 | ||||
-rw-r--r-- | usr/src/man/man3c/port_associate.3c | 30 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs3_vnops.c | 25 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_vnops.c | 27 | ||||
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_vnops.c | 26 | ||||
-rw-r--r-- | usr/src/uts/common/fs/pcfs/pc_vnops.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/fs/portfs/port_fop.c | 45 | ||||
-rw-r--r-- | usr/src/uts/common/fs/tmpfs/tmp_vnops.c | 18 | ||||
-rw-r--r-- | usr/src/uts/common/fs/udfs/udf_vnops.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/fs/ufs/ufs_vnops.c | 7 | ||||
-rw-r--r-- | usr/src/uts/common/fs/vnode.c | 10 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_vnops.c | 6 | ||||
-rw-r--r-- | usr/src/uts/common/sys/port.h | 7 | ||||
-rw-r--r-- | usr/src/uts/common/sys/port_impl.h | 12 | ||||
-rw-r--r-- | usr/src/uts/common/sys/vnode.h | 5 |
18 files changed, 752 insertions, 116 deletions
diff --git a/usr/src/cmd/tail/extern.h b/usr/src/cmd/tail/extern.h index 147a887272..7f121b2d9d 100644 --- a/usr/src/cmd/tail/extern.h +++ b/usr/src/cmd/tail/extern.h @@ -28,6 +28,12 @@ * */ +#ifndef _TAIL_EXTERN_H +#define _TAIL_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) \ @@ -48,6 +54,7 @@ struct file_info { FILE *fp; char *file_name; struct stat st; + file_obj_t fobj[2]; }; typedef struct file_info file_info_t; @@ -67,3 +74,5 @@ int mapprint(struct mapinfo *, off_t, off_t); int maparound(struct mapinfo *, off_t); extern int Fflag, fflag, qflag, rflag, rval, no_files; + +#endif /* _TAIL_EXTERN_H */ diff --git a/usr/src/cmd/tail/forward.c b/usr/src/cmd/tail/forward.c index e4f22582f3..8b87dfff06 100644 --- a/usr/src/cmd/tail/forward.c +++ b/usr/src/cmd/tail/forward.c @@ -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 @@ static void set_events(file_info_t *files); /* 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,107 @@ show(file_info_t *file) } static void +associate(file_info_t *file, boolean_t assoc, port_event_t *ev) +{ + char buf[64], *name; + int i; + + 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)); + + if (!Fflag) { + /* + * PORT_SOURCE_FILE only allows us to specify a file name, not + * a file descriptor. If we are following a specific file (as + * opposed to a file name) and 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. This also has the (desirable) side-effect of + * insulating against FILE_RENAME_FROM and FILE_RENAME_TO + * events that we need to ignore to assure that we don't lose + * FILE_TRUNC events. + */ + (void) snprintf(buf, + sizeof (buf), "/proc/self/fd/%d", fileno(file->fp)); + name = buf; + } else { + name = file->file_name; + } + + /* + * 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_obj_t that happens to be at the same address, + * the first association will be implicitly removed. To assure that + * each association has a disjoint file_obj_t, we allocate the memory + * for each in the file_info, not on the stack. + */ + file->fobj[0].fo_name = name; + file->fobj[1].fo_name = name; + + if (assoc) { + /* + * To assure that we cannot possibly drop a FILE_TRUNC event, + * we have two different PORT_SOURCE_FILE associations with the + * port: one to get only FILE_MODIFIED events and another to + * get only FILE_TRUNC events. This assures that we always + * have an active association for FILE_TRUNC events when the + * seek offset is non-zero. Note that the association order + * _must_ be FILE_TRUNC followed by FILE_MODIFIED: if a single + * event induces both a FILE_TRUNC and a FILE_MODIFIED (as + * a VE_CREATE vnode event does), we must process the + * FILE_TRUNC before FILE_MODIFIED -- and the order in which + * these are processed will be the association order. So + * if we see a FILE_TRUNC, we dissociate/reassociate the + * FILE_MODIFIED association. + */ + if (ev == NULL || (ev->portev_events & FILE_TRUNC) || + !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) { + (void) port_associate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj[0], FILE_TRUNC, file); + (void) port_dissociate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj[1]); + ev = NULL; + } + + if (ev == NULL || (ev->portev_events & FILE_MODIFIED) || + !(ev->portev_events & (FILE_MODIFIED | FILE_TRUNC))) { + (void) port_associate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj[1], FILE_MODIFIED, file); + } + } else { + for (i = 0; i <= 1; i++) { + (void) port_dissociate(port, PORT_SOURCE_FILE, + (uintptr_t)&file->fobj[i]); + } + } +} + +static void set_events(file_info_t *files) { int i; @@ -271,6 +373,8 @@ set_events(file_info_t *files) continue; (void) fstat(fileno(file->fp), &file->st); + + associate(file, B_TRUE, NULL); } } @@ -284,6 +388,8 @@ follow(file_info_t *files, enum STYLE style, off_t off) 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 +413,12 @@ follow(file_info_t *files, enum STYLE style, off_t off) 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 +453,13 @@ follow(file_info_t *files, enum STYLE style, off_t off) sb2.st_dev != file->st.st_dev || sb2.st_nlink == 0) { (void) show(file); + associate(file, B_FALSE, NULL); 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 +474,26 @@ follow(file_info_t *files, enum STYLE style, off_t off) 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, &ev); + + if (ev.portev_events & FILE_TRUNC) + (void) fseek(file->fp, 0, SEEK_SET); + } + + break; + case USE_SLEEP: (void) usleep(250000); break; diff --git a/usr/src/cmd/tail/tests/tailtests.sh b/usr/src/cmd/tail/tests/tailtests.sh index 57ec1c46cc..d3a8bb1cf2 100644 --- a/usr/src/cmd/tail/tests/tailtests.sh +++ b/usr/src/cmd/tail/tests/tailtests.sh @@ -13,129 +13,468 @@ # # 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$output" + echo -e "$CMD: test $test: actual output:\n$actual" + 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="" + +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 -case $1 in - -x) - PROG=/usr/xpg4/bin/tail - ;; - -o) - PROG=$2 - ;; - -?) - echo "Usage: tailtests.sh [-x][-o <override tail executable>]" - exit 1 - ;; -esac - -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" - -o=`echo -e "yyz"` -a=`echo -e "xyyyyyyyyyyz" | $PROG +c` -[[ "$a" != "$o" ]] && echo "Fail test 16a - $a" - +checktest "$a" "$o" 16 -# 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 + export TMPDIR=/var/tmp + tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1) +else + tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1) +fi + +follow=$tdir/follow +moved=$tdir/follow.moved +out=$tdir/out + +# +# 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 + +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 + +# +# At this point, tail is polling on stat'ing the missing file; we need to +# be sure to sleep long enough after recreating it to know that it will pick +# it up. +# +echo -e "d\ne\nf" > $follow +sleep 5 +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 + +if [[ `uname -s` == "SunOS" ]]; then + # + # Use DTrace to truncate the file between the return from port_get() + # and the reassociation of the file object with the port, exposing + # any race conditions whereby FILE_TRUNC events are lost. + # + cat /dev/null > $follow + dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF + #pragma D option destructive + #pragma D option quiet + + pid\$target::port_get:return + /++i == 5/ + { + stop(); + system("cat /dev/null > $follow"); + system("prun %d", pid); + } + + tick-1sec + { + system("echo %d >> $follow", j++); + } + + tick-1sec + /j == 10/ + { + exit(0); + } +EOF + + o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` + a=`cat $out` + checktest "$a" "$o" 27a + rm $follow + + cat /dev/null > $follow + dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF + #pragma D option destructive + #pragma D option quiet + + pid\$target::port_get:return + /++i == 5/ + { + stop(); + system("mv $follow $moved"); + system("cat /dev/null > $moved"); + system("prun %d", pid); + } + + tick-1sec + { + system("echo %d >> %s", j++, + i < 5 ? "$follow" : "$moved"); + } + + tick-1sec + /j == 10/ + { + exit(0); + } +EOF + + o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` + a=`cat $out` + checktest "$a" "$o" 27b + rm $moved + + # + # Verify that -F will deal properly with the file being truncated + # not by truncation, but rather via an ftruncate() from logadm. + # + cat /dev/null > $follow + ( $PROG -F $follow > $out ) & + child=$! + echo -e "a\nb\nc\nd\ne\nf" >> $follow + logadm -c $follow + sleep 2 + echo -e "g\nh\ni" >> $follow + sleep 2 + kill $child + sleep 1 + + o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` + a=`cat $out` + checktest "$a" "$o" 27c +fi + +# +# We're now going to test that while we may miss output due to truncations +# occurring faster than tail can read, we don't ever repeat output. +# +cat /dev/null > $follow +( $PROG -f $follow > $out ) & +tchild=$! +( let i=0 ; while true; do echo $i > $follow ; sleep 0.1; let i=i+1 ; done ) & +child=$! +sleep 10 +kill $tchild +kill $child + +a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'` +o=1 +checktest "$a" "$o" 28 -echo "Completed" +echo "$CMD: completed" -exit 0 +exit $errs -# Template for additional test cases -#o=`echo -e ""` -#a=`echo -e "" | $PROG ` -#[[ "$a" != "$o" ]] && echo "Fail test - $a" diff --git a/usr/src/man/man1/tail.1 b/usr/src/man/man1/tail.1 index aca819269e..b96e6400dd 100644 --- a/usr/src/man/man1/tail.1 +++ b/usr/src/man/man1/tail.1 @@ -23,12 +23,12 @@ tail \- deliver the last part of a file .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,11 +92,24 @@ Units of bytes. \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 diff --git a/usr/src/man/man3c/port_associate.3c b/usr/src/man/man3c/port_associate.3c index d876b5c9d7..f6e66b0d65 100644 --- a/usr/src/man/man3c/port_associate.3c +++ b/usr/src/man/man3c/port_associate.3c @@ -73,21 +73,21 @@ the port. .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: .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 +104,13 @@ occurs. .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 diff --git a/usr/src/uts/common/fs/nfs/nfs3_vnops.c b/usr/src/uts/common/fs/nfs/nfs3_vnops.c index d31b53d2e9..fd1595b9f5 100644 --- a/usr/src/uts/common/fs/nfs/nfs3_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs3_vnops.c @@ -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> @@ -1327,7 +1331,12 @@ nfs3_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, if (error) return (error); - return (nfs3setattr(vp, vap, flags, cr)); + error = nfs3setattr(vp, vap, flags, cr); + + if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0) + vnevent_truncate(vp, ct); + + return (error); } static int @@ -2298,6 +2307,12 @@ top: 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 +2321,9 @@ top: if (error) { VN_RELE(vp); } else { - /* - * existing file got truncated, notify. - */ - vnevent_create(vp, ct); *vpp = vp; } + return (error); } @@ -5514,6 +5526,9 @@ nfs3_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, va.va_mask = AT_SIZE; va.va_size = bfp->l_start; error = nfs3setattr(vp, &va, 0, cr); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } else error = EINVAL; } diff --git a/usr/src/uts/common/fs/nfs/nfs4_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_vnops.c index 5ae2c28d53..0c97fcc176 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c @@ -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> @@ -3712,6 +3716,8 @@ static int nfs4_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, caller_context_t *ct) { + int error; + if (vap->va_mask & AT_NOSET) return (EINVAL); @@ -3727,8 +3733,12 @@ nfs4_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, * to setattr (e.g. basic without chmod) then we will * need to add a check here before calling the server. */ + error = nfs4setattr(vp, vap, flags, cr, NULL); - return (nfs4setattr(vp, vap, flags, cr, NULL)); + if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0) + vnevent_truncate(vp, ct); + + return (error); } /* @@ -6653,16 +6663,20 @@ top: } else { vnode_t *tvp; rnode4_t *trp; - /* - * existing file got truncated, notify. - */ tvp = vp; if (vp->v_type == VREG) { trp = VTOR4(vp); 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); @@ -10991,6 +11005,9 @@ nfs4_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, va.va_mask = AT_SIZE; va.va_size = bfp->l_start; error = nfs4setattr(vp, &va, 0, cr, NULL); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } else error = EINVAL; } diff --git a/usr/src/uts/common/fs/nfs/nfs_vnops.c b/usr/src/uts/common/fs/nfs/nfs_vnops.c index a3f43a4e95..df128f4bc6 100644 --- a/usr/src/uts/common/fs/nfs/nfs_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs_vnops.c @@ -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> @@ -1167,7 +1171,12 @@ nfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, if (error) return (error); - return (nfssetattr(vp, vap, flags, cr)); + error = nfssetattr(vp, vap, flags, cr); + + if (error == 0 && (mask & AT_SIZE) && vap->va_size == 0) + vnevent_truncate(vp, ct); + + return (error); } static int @@ -2030,6 +2039,14 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, 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 +2054,6 @@ nfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, if (error) { VN_RELE(vp); } else { - /* - * existing file got truncated, notify. - */ - vnevent_create(vp, ct); *vpp = vp; } return (error); @@ -4610,6 +4623,9 @@ nfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, va.va_mask = AT_SIZE; va.va_size = bfp->l_start; error = nfssetattr(vp, &va, 0, cr); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } else error = EINVAL; } diff --git a/usr/src/uts/common/fs/pcfs/pc_vnops.c b/usr/src/uts/common/fs/pcfs/pc_vnops.c index ce9711140b..e2c6f3a371 100644 --- a/usr/src/uts/common/fs/pcfs/pc_vnops.c +++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + #include <sys/param.h> #include <sys/t_lock.h> #include <sys/systm.h> @@ -772,8 +776,12 @@ pcfs_setattr( goto out; } error = pc_truncate(pcp, (uint_t)vap->va_size); + if (error) goto out; + + if (vap->va_size == 0) + vnevent_truncate(vp, ct); } /* * Change file modified times. diff --git a/usr/src/uts/common/fs/portfs/port_fop.c b/usr/src/uts/common/fs/portfs/port_fop.c index 2852a98f52..03a03dd2a6 100644 --- a/usr/src/uts/common/fs/portfs/port_fop.c +++ b/usr/src/uts/common/fs/portfs/port_fop.c @@ -23,6 +23,9 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * File Events Notification @@ -1256,7 +1259,7 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events, void *user) { portfop_cache_t *pfcp; - vnode_t *vp, *dvp, *oldvp = NULL, *olddvp = NULL; + vnode_t *vp, *dvp, *oldvp = NULL, *olddvp = NULL, *orig; portfop_t *pfp; int error = 0; file_obj_t fobj; @@ -1313,8 +1316,7 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events, goto errout; } - vp = port_resolve_vp(vp); - + vp = port_resolve_vp(orig = vp); if (vp != NULL && vnevent_support(vp, NULL)) { error = ENOTSUP; @@ -1322,10 +1324,14 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events, } /* - * If dvp belongs to a different filesystem just ignore it. - * Hardlinks cannot exist across filesystems. + * If dvp belongs to a different filesystem just ignore it, as hard + * links cannot exist across filesystems. We make an exception for + * procfs, however, the magic of which we treat semantically as a hard + * link, allowing one to use /proc/[pid]/fd/[fd] for PORT_SOURCE_FILE + * and avoid spurious FILE_RENAME_FROM/FILE_RENAME_TO events. */ - if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp) { + if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp && + !(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) { VN_RELE(dvp); dvp = NULL; } @@ -1822,12 +1828,19 @@ port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname) if (!removeall) { /* * All the active ones are in the beginning of the list. + * Note that we process this list in reverse order to assure + * that events are delivered in the order that they were + * associated. */ - for (pfp = (portfop_t *)list_head(&pvp->pvp_pfoplist); - pfp && pfp->pfop_flags & PORT_FOP_ACTIVE; pfp = npfp) { + for (pfp = (portfop_t *)list_tail(&pvp->pvp_pfoplist); + pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE); pfp = npfp) { + npfp = list_prev(&pvp->pvp_pfoplist, pfp); + } + + for (; pfp != NULL; pfp = npfp) { int levents = events; - npfp = list_next(&pvp->pvp_pfoplist, pfp); + npfp = list_prev(&pvp->pvp_pfoplist, pfp); /* * Hard links case - If the file is being * removed/renamed, and the name matches @@ -1965,7 +1978,9 @@ port_fop(vnode_t *vp, int op, int retval) 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 +2162,9 @@ port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr, 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 +2340,8 @@ port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name, 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); @@ -2337,6 +2355,9 @@ port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name, case VE_MOUNTEDOVER: port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL); break; + case VE_TRUNCATE: + port_fop_sendevent(vp, FILE_TRUNC, NULL, NULL); + break; default: break; } diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c index 61d72a4015..e44bba1252 100644 --- a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c +++ b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + #include <sys/types.h> #include <sys/param.h> #include <sys/t_lock.h> @@ -780,6 +784,10 @@ tmp_setattr( error = tmpnode_trunc(tm, tp, (ulong_t)vap->va_size); rw_exit(&tp->tn_contents); rw_exit(&tp->tn_rwlock); + + if (error == 0 && vap->va_size == 0) + vnevent_truncate(vp, ct); + goto out1; } out: @@ -978,6 +986,8 @@ again: } if (error == 0) { /* name found */ + boolean_t trunc = B_FALSE; + ASSERT(oldtp); rw_enter(&oldtp->tn_rwlock, RW_WRITER); @@ -1005,6 +1015,7 @@ again: 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 +1030,9 @@ again: *vpp = newvp; } - if (error == 0) { + if (trunc) vnevent_create(*vpp, ct); - } + return (0); } @@ -2318,6 +2329,9 @@ tmp_space( if ((bfp->l_start > MAXOFF_T) || (bfp->l_len > MAXOFF_T)) return (EFBIG); error = tmp_freesp(vp, bfp, flag); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } return (error); } diff --git a/usr/src/uts/common/fs/udfs/udf_vnops.c b/usr/src/uts/common/fs/udfs/udf_vnops.c index 90ad1b8d48..307d3987ed 100644 --- a/usr/src/uts/common/fs/udfs/udf_vnops.c +++ b/usr/src/uts/common/fs/udfs/udf_vnops.c @@ -23,6 +23,10 @@ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + #include <sys/types.h> #include <sys/t_lock.h> #include <sys/param.h> @@ -564,6 +568,9 @@ udf_setattr( if (error = ud_itrunc(ip, vap->va_size, 0, cr)) { goto update_inode; } + + if (vap->va_size == 0) + vnevent_truncate(vp, ct); } /* * Change file access or modified times. @@ -1621,6 +1628,9 @@ udf_space( error = EINVAL; } else if ((error = convoff(vp, bfp, 0, offset)) == 0) { error = ud_freesp(vp, bfp, flag, cr); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } return (error); diff --git a/usr/src/uts/common/fs/ufs/ufs_vnops.c b/usr/src/uts/common/fs/ufs/ufs_vnops.c index e210339dfb..92bdb0d0a7 100644 --- a/usr/src/uts/common/fs/ufs/ufs_vnops.c +++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -2191,6 +2192,9 @@ again: rw_enter(&ip->i_contents, RW_WRITER); goto update_inode; } + + if (error == 0 && vap->va_size) + vnevent_truncate(vp, ct); } if (ulp) { @@ -4460,6 +4464,9 @@ ufs_space(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, if (error) return (error); error = ufs_freesp(vp, bfp, flag, cr); + + if (error == 0 && bfp->l_start == 0) + vnevent_truncate(vp, ct); } else if (cmd == F_ALLOCSP) { error = ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_FALLOCATE_MASK); diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c index 382369c7fc..d67d164286 100644 --- a/usr/src/uts/common/fs/vnode.c +++ b/usr/src/uts/common/fs/vnode.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -2582,6 +2583,15 @@ vnevent_mountedover(vnode_t *vp, caller_context_t *ct) (void) VOP_VNEVENT(vp, VE_MOUNTEDOVER, NULL, NULL, ct); } +void +vnevent_truncate(vnode_t *vp, caller_context_t *ct) +{ + if (vp == NULL || vp->v_femhead == NULL) { + return; + } + (void) VOP_VNEVENT(vp, VE_TRUNCATE, NULL, NULL, ct); +} + /* * Vnode accessors. */ diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c index 6fa1d83700..78e2a8cce5 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vnops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c @@ -2756,6 +2756,9 @@ top: ZFS_EXIT(zfsvfs); return (err); } + + if (vap->va_size == 0) + vnevent_truncate(ZTOV(zp), ct); } if (mask & (AT_ATIME|AT_MTIME) || @@ -4770,6 +4773,9 @@ zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, error = zfs_freesp(zp, off, len, flag, TRUE); + if (error == 0 && off == 0 && len == 0) + vnevent_truncate(ZTOV(zp), ct); + ZFS_EXIT(zfsvfs); return (error); } diff --git a/usr/src/uts/common/sys/port.h b/usr/src/uts/common/sys/port.h index ccb0308255..d4d74d55ea 100644 --- a/usr/src/uts/common/sys/port.h +++ b/usr/src/uts/common/sys/port.h @@ -24,11 +24,13 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2012, 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 @@ typedef struct port_notify32 { #define FILE_ACCESS 0x00000001 #define FILE_MODIFIED 0x00000002 #define FILE_ATTRIB 0x00000004 +#define FILE_TRUNC 0x00100000 #define FILE_NOFOLLOW 0x10000000 /* diff --git a/usr/src/uts/common/sys/port_impl.h b/usr/src/uts/common/sys/port_impl.h index 9f3f291874..504fb9ece1 100644 --- a/usr/src/uts/common/sys/port_impl.h +++ b/usr/src/uts/common/sys/port_impl.h @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + #ifndef _SYS_PORT_IMPL_H #define _SYS_PORT_IMPL_H @@ -311,6 +315,7 @@ typedef struct portfop_vp { #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 @@ typedef struct portfop_vp { /* + * 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 --- */ /* diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h index d29152346e..af9516fe52 100644 --- a/usr/src/uts/common/sys/vnode.h +++ b/usr/src/uts/common/sys/vnode.h @@ -21,6 +21,7 @@ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -733,7 +734,8 @@ typedef enum vnevent { VE_CREATE = 5, /* Create with vnode's name which exists */ VE_LINK = 6, /* Link with vnode's name as source */ VE_RENAME_DEST_DIR = 7, /* Rename with vnode as target dir */ - VE_MOUNTEDOVER = 8 /* File or Filesystem got mounted over vnode */ + VE_MOUNTEDOVER = 8, /* File or Filesystem got mounted over vnode */ + VE_TRUNCATE = 9 /* Truncate */ } vnevent_t; /* @@ -1290,6 +1292,7 @@ void vnevent_create(vnode_t *, caller_context_t *); void vnevent_link(vnode_t *, caller_context_t *); void vnevent_rename_dest_dir(vnode_t *, caller_context_t *ct); void vnevent_mountedover(vnode_t *, caller_context_t *); +void vnevent_truncate(vnode_t *, caller_context_t *); int vnevent_support(vnode_t *, caller_context_t *); /* Vnode specific data */ |