[PATCH v3] server: support inotify file change notifications on FreeBSD
Damjan Jovanovic
damjan.jov at gmail.com
Fri Dec 3 10:37:43 CST 2021
The inotify code uses the Linux-specific /proc/self/fd/<FD> symlinks
to translate
file descriptors to filesystem paths. On FreeBSD, do this translation using
its own sysctl instead.
Try 3 makes the mib variable in fd_path() non-static, and moves the
fd_path() functions into the HAVE_SYS_INOTIFY_H block where they should be
invisible on MacOS.
Signed-off-by: Damjan Jovanovic <damjan.jov at gmail.com>
---
server/change.c | 132 ++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 111 insertions(+), 21 deletions(-)
-------------- next part --------------
diff --git a/server/change.c b/server/change.c
index a01d6894151..20124a42abf 100644
--- a/server/change.c
+++ b/server/change.c
@@ -33,6 +33,15 @@
#include <errno.h>
#include <unistd.h>
#include <poll.h>
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+#endif
+#ifdef HAVE_SYS_USER_H
+# define thread __unix_thread
+# include <sys/user.h>
+# undef thread
+#endif
+
#ifdef HAVE_SYS_INOTIFY_H
#include <sys/inotify.h>
#endif
@@ -488,6 +497,69 @@ static struct list wd_hash[ HASH_SIZE ];
static int inotify_add_dir( char *path, unsigned int filter );
+#ifdef __linux__
+
+static char *fd_path( int unix_fd, const char *suffix )
+{
+ char *buffer;
+ static const char *proc_path = "/proc/self/fd/%u%s";
+
+ buffer = malloc( sizeof(proc_path) + 6 + strlen( suffix ) + 1);
+ if (buffer) sprintf( buffer, proc_path, unix_fd, suffix );
+ return buffer;
+}
+
+#elif defined(__FreeBSD__)
+
+static char *fd_path( int unix_fd, const char *suffix )
+{
+ int mib[4];
+ size_t i, len;
+ char *buffer = NULL;
+ struct kinfo_file *kf;
+ char *path = NULL;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_FILEDESC;
+ mib[3] = getpid();
+ len = 0;
+ if (sysctl( mib, sizeof(mib)/sizeof(mib[0]), NULL, &len, NULL, 0 ))
+ goto end;
+ len *= 2;
+ buffer = malloc( len );
+ if (!buffer)
+ goto end;
+ if (sysctl( mib, sizeof(mib)/sizeof(mib[0]), buffer, &len, NULL, 0 ))
+ goto end;
+
+ for (i = 0; i < len; )
+ {
+ kf = (struct kinfo_file*) &buffer[i];
+ if (kf->kf_structsize == 0)
+ break;
+ i += kf->kf_structsize;
+
+ if (kf->kf_fd == unix_fd)
+ {
+ if (kf->kf_path[0])
+ {
+ path = malloc( strlen( kf->kf_path ) + strlen( suffix ) + 1 );
+ if (path) sprintf( path, "%s%s", kf->kf_path, suffix );
+ }
+ break;
+ }
+ }
+
+end:
+ free( buffer );
+ return path;
+}
+
+#else
+#error No fd_path() function for this platform
+#endif
+
static struct inode *inode_from_wd( int wd )
{
struct list *bucket = &wd_hash[ wd % HASH_SIZE ];
@@ -734,9 +806,7 @@ static char *inode_get_path( struct inode *inode, int sz )
if (head)
{
int unix_fd = get_unix_fd( LIST_ENTRY( head, struct dir, in_entry )->fd );
- path = malloc ( 32 + sz );
- if (path)
- sprintf( path, "/proc/self/fd/%u/", unix_fd );
+ path = fd_path( unix_fd, "/" );
return path;
}
@@ -964,7 +1034,7 @@ static int inotify_adjust_changes( struct dir *dir )
unsigned int filter;
struct inode *inode;
struct stat st;
- char path[32];
+ char *path;
int wd, unix_fd;
if (!inotify_fd)
@@ -990,8 +1060,10 @@ static int inotify_adjust_changes( struct dir *dir )
filter = filter_from_inode( inode, 0 );
- sprintf( path, "/proc/self/fd/%u", unix_fd );
- wd = inotify_add_dir( path, filter );
+ path = fd_path( unix_fd, "" );
+ wd = -1;
+ if (path) wd = inotify_add_dir( path, filter );
+ free( path );
if (wd == -1) return 0;
inode_set_wd( inode, wd );
@@ -1011,7 +1083,16 @@ static char *get_basename( const char *link )
r = readlink( link, buffer, n );
if (r < 0)
+ {
+ if (errno == EINVAL) /* not a symlink, normal for non-/proc paths */
+ {
+ free( buffer );
+ buffer = name = strdup( link );
+ if (buffer)
+ r = strlen( buffer );
+ }
break;
+ }
if (r < n)
{
@@ -1042,25 +1123,29 @@ static int dir_add_to_existing_notify( struct dir *dir )
struct inode *inode, *parent;
unsigned int filter = 0;
struct stat st, st_new;
- char link[35], *name;
- int wd, unix_fd;
+ char *link = NULL, *name = NULL;
+ int wd, unix_fd, ret = 0;
if (!inotify_fd)
- return 0;
+ goto end;
unix_fd = get_unix_fd( dir->fd );
/* check if it's in the list of inodes we want to watch */
if (-1 == fstat( unix_fd, &st_new ))
- return 0;
+ goto end;
inode = find_inode( st_new.st_dev, st_new.st_ino );
if (inode)
- return 0;
+ goto end;
/* lookup the parent */
- sprintf( link, "/proc/self/fd/%u/..", unix_fd );
+ link = fd_path( unix_fd, "/.." );
+ if (!link)
+ goto end;
if (-1 == stat( link, &st ))
- return 0;
+ goto end;
+ free( link );
+ link = NULL;
/*
* If there's no parent, stop. We could keep going adding
@@ -1070,23 +1155,24 @@ static int dir_add_to_existing_notify( struct dir *dir )
*/
parent = find_inode( st.st_dev, st.st_ino );
if (!parent)
- return 0;
+ goto end;
if (parent->wd == -1)
- return 0;
+ goto end;
filter = filter_from_inode( parent, 1 );
if (!filter)
- return 0;
+ goto end;
- sprintf( link, "/proc/self/fd/%u", unix_fd );
+ link = fd_path( unix_fd, "" );
+ if (!link)
+ goto end;
name = get_basename( link );
if (!name)
- return 0;
+ goto end;
inode = inode_add( parent, st_new.st_dev, st_new.st_ino, name );
- free( name );
if (!inode)
- return 0;
+ goto end;
/* Couldn't find this inode at the start of the function, must be new */
assert( inode->wd == -1 );
@@ -1094,8 +1180,12 @@ static int dir_add_to_existing_notify( struct dir *dir )
wd = inotify_add_dir( link, filter );
if (wd != -1)
inode_set_wd( inode, wd );
+ ret = 1;
- return 1;
+end:
+ free( name );
+ free( link );
+ return ret;
}
#else
More information about the wine-devel
mailing list