Subject: kern/34267: kevent(2): EVFILT_VNODE filter returns events upon denied link(2) of a directory
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <rudolf@eq.cz>
List: netbsd-bugs
Date: 08/23/2006 13:10:00
>Number: 34267
>Category: kern
>Synopsis: kevent(2): EVFILT_VNODE filter returns events upon denied link(2) of a directory
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Aug 23 13:10:00 +0000 2006
>Originator: rudolf
>Release: 4.0_BETA
>Organization:
>Environment:
NetBSD 4.0_BETA (GENERIC) Sat Aug 19 02:01:55 CEST 2006
>Description:
EVFILT_VNODE kqueue filter returns events upon denied link(2) of a directory, but according to manual (kevent(2)), the events should be returned only when the requested action succeeds:
``NOTE_WRITE A write occurred on the file referenced by the descriptor.''
``NOTE_LINK The link count on the file changed.''
Tested on ffs with NetBSD 3.99.21 (i386) and 4.0_BETA (amd64).
(the 'ln' of a directory is denied because ffs doesn't permit to use of link() on a directory ('man 2 ln':
``EPERM The file named by name1 is a directory and the effective user ID is not super-user, or the file system containing the file does not permit the use of link() on a directory.''))
I also tested that the NOTE_LINK event is not returned when the watched vnode belongs to a regular file and the link is denied (EEXIST - 'ln /tmp/watchfile /tmp/watchfile').
>How-To-Repeat:
Here is my naive test code:
-- 8< --
#include <sys/event.h>
#include <fcntl.h>
#include <stdio.h>
#define WATCHVNODE "/tmp/watchdir"
int
main()
{
int kq, ke, wvnfd;
struct kevent eventlist[1], changelist[1];
char *wvn = WATCHVNODE;
wvnfd = open(wvn, O_RDONLY, 0);
if (wvnfd < 0) {
printf("error: open\n");
return 111;
}
kq = kqueue();
if (kq < 0) {
printf("error: kqueue\n");
return 111;
}
EV_SET(&eventlist[0], wvnfd, EVFILT_VNODE,
EV_ADD | EV_ONESHOT, NOTE_DELETE |
NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB |
NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, 0, 0);
ke = kevent(kq, eventlist, 1, NULL, 0, NULL);
if (ke < 0) {
printf("error: kevent\n");
return 111;
}
printf("NOTE_DELETE\t%d\nNOTE_WRITE\t%d\n"
"NOTE_EXTEND\t%d\nNOTE_ATTRIB\t%d\n"
"NOTE_LINK\t%d\nNOTE_RENAME\t%d\nNOTE_REVOKE\t%d\n",
NOTE_DELETE, NOTE_WRITE, NOTE_EXTEND,
NOTE_ATTRIB, NOTE_LINK, NOTE_RENAME, NOTE_REVOKE);
printf("waiting for kevent() ...\n");
ke = kevent(kq, NULL, 0, changelist, 1, NULL);
if (ke < 0) {
printf("error: kevent\n");
return 111;
} else {
printf("%d\n", (int)changelist[0].fflags);
}
return 0;
}
-- >8 --
make the directory:
$ mkdir /tmp/watchdir
run the binary on one terminal and try to make a hardlink on another one (directories "/tmp/w", "/tmp/watchdir/w" don't exist):
_#1_ - kevent returns NOTE_LINK:
(1)$ ./watchvnode
NOTE_DELETE 1
NOTE_WRITE 2
NOTE_EXTEND 4
NOTE_ATTRIB 8
NOTE_LINK 16
NOTE_RENAME 32
NOTE_REVOKE 64
waiting for kevent() ...
16
(2)$ ln /tmp/watchdir /tmp/w
ln: /tmp/w: Operation not permitted
_#2_ - kevent returns NOTE_WRITE and NOTE_LINK:
(1)$ ./watchvnode
NOTE_DELETE 1
NOTE_WRITE 2
NOTE_EXTEND 4
NOTE_ATTRIB 8
NOTE_LINK 16
NOTE_RENAME 32
NOTE_REVOKE 64
waiting for kevent() ...
18
(2)$ ln /tmp/watchdir /tmp/watchdir/w
ln: /tmp/watchdir/w: Operation not permitted
>Fix: