Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fsmonitor: completing a stale patch that Implements fsmonitor for Linux #1667

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,10 @@ ifdef HAVE_CLOCK_GETTIME
BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Jean-Noël Avila wrote (reply to this):

Hello,

Le 15/02/2024 à 11:29, Eric DeCosta via GitGitGadget a écrit :
> From: Eric DeCosta <edecosta@mathworks.com>
> > Compare the given path to the mounted filesystems. Find the mount that is
> the longest prefix of the path (if any) and determine if that mount is on a
> local or remote filesystem.
> > Signed-off-by: Eric DeCosta <edecosta@mathworks.com>
> ---
>   Makefile                                |   4 +
>   compat/fsmonitor/fsm-path-utils-linux.c | 195 ++++++++++++++++++++++++
>   compat/fsmonitor/fsm-path-utils-linux.h |  91 +++++++++++
>   config.mak.uname                        |  11 ++
>   4 files changed, 301 insertions(+)
>   create mode 100644 compat/fsmonitor/fsm-path-utils-linux.c
>   create mode 100644 compat/fsmonitor/fsm-path-utils-linux.h
> > diff --git a/Makefile b/Makefile
> index 78e874099d9..0f36a0fd83a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -2088,6 +2088,10 @@ ifdef HAVE_CLOCK_GETTIME
>   	BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
>   endif
>   > +ifdef HAVE_LINUX_MAGIC_H
> +	BASIC_CFLAGS += -DHAVE_LINUX_MAGIC_H
> +endif
> +
>   ifdef HAVE_CLOCK_MONOTONIC
>   	BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
>   endif
> diff --git a/compat/fsmonitor/fsm-path-utils-linux.c b/compat/fsmonitor/fsm-path-utils-linux.c
> new file mode 100644
> index 00000000000..c21d1349532
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-path-utils-linux.c
> @@ -0,0 +1,195 @@
> +#include "git-compat-util.h"
> +#include "abspath.h"
> +#include "fsmonitor.h"
> +#include "fsmonitor-path-utils.h"
> +#include "fsm-path-utils-linux.h"
> +#include <errno.h>
> +#include <mntent.h>
> +#include <sys/mount.h>
> +#include <sys/vfs.h>
> +#include <sys/statvfs.h>
> +
> +static int is_remote_fs(const char *path)
> +{
> +	struct statfs fs;
> +
> +	if (statfs(path, &fs))
> +		return error_errno(_("statfs('%s') failed"), path);

For the sake of simplifying of the work of translators, would it be wise to change this to

+	if (statfs(path, &fs))
+		/* TRANSLATORS: %s('%s') is a libc function call */
+		return error_errno(_("%s('%s') failed"), "statfs", +				path);

and generalize this to all other messages?

Thanks,

JN

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Thu, Feb 15, 2024 at 10:29:33AM +0000, Eric DeCosta via GitGitGadget wrote:
> From: Eric DeCosta <edecosta@mathworks.com>
> 
> Compare the given path to the mounted filesystems. Find the mount that is
> the longest prefix of the path (if any) and determine if that mount is on a
> local or remote filesystem.

It would be nice to motivate this change in the commit message. Right
now it only explains what the commit does, but it does not mention at
all why that would be a good idea in the first place. Explaining that
this is part of the interface that the existing fsmonitor infrastructure
expects to exist would help.

> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>
> ---
>  Makefile                                |   4 +
>  compat/fsmonitor/fsm-path-utils-linux.c | 195 ++++++++++++++++++++++++
>  compat/fsmonitor/fsm-path-utils-linux.h |  91 +++++++++++
>  config.mak.uname                        |  11 ++
>  4 files changed, 301 insertions(+)
>  create mode 100644 compat/fsmonitor/fsm-path-utils-linux.c
>  create mode 100644 compat/fsmonitor/fsm-path-utils-linux.h
> 
> diff --git a/Makefile b/Makefile
> index 78e874099d9..0f36a0fd83a 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -2088,6 +2088,10 @@ ifdef HAVE_CLOCK_GETTIME
>  	BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
>  endif
>  
> +ifdef HAVE_LINUX_MAGIC_H
> +	BASIC_CFLAGS += -DHAVE_LINUX_MAGIC_H
> +endif
> +
>  ifdef HAVE_CLOCK_MONOTONIC
>  	BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
>  endif
> diff --git a/compat/fsmonitor/fsm-path-utils-linux.c b/compat/fsmonitor/fsm-path-utils-linux.c
> new file mode 100644
> index 00000000000..c21d1349532
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-path-utils-linux.c
> @@ -0,0 +1,195 @@
> +#include "git-compat-util.h"
> +#include "abspath.h"
> +#include "fsmonitor.h"
> +#include "fsmonitor-path-utils.h"
> +#include "fsm-path-utils-linux.h"
> +#include <errno.h>
> +#include <mntent.h>
> +#include <sys/mount.h>
> +#include <sys/vfs.h>
> +#include <sys/statvfs.h>
> +
> +static int is_remote_fs(const char *path)
> +{
> +	struct statfs fs;
> +
> +	if (statfs(path, &fs))
> +		return error_errno(_("statfs('%s') failed"), path);
> +
> +	switch (fs.f_type) {
> +	case ACFS_SUPER_MAGIC:
> +	case AFS_SUPER_MAGIC:
> +	case CEPH_SUPER_MAGIC:
> +	case CIFS_SUPER_MAGIC:
> +	case CODA_SUPER_MAGIC:
> +	case FHGFS_SUPER_MAGIC:
> +	case GFS_SUPER_MAGIC:
> +	case GPFS_SUPER_MAGIC:
> +	case IBRIX_SUPER_MAGIC:
> +	case KAFS_SUPER_MAGIC:
> +	case LUSTRE_SUPER_MAGIC:
> +	case NCP_SUPER_MAGIC:
> +	case NFS_SUPER_MAGIC:
> +	case NFSD_SUPER_MAGIC:
> +	case OCFS2_SUPER_MAGIC:
> +	case PANFS_SUPER_MAGIC:
> +	case SMB_SUPER_MAGIC:
> +	case SMB2_SUPER_MAGIC:
> +	case SNFS_SUPER_MAGIC:
> +	case VMHGFS_SUPER_MAGIC:
> +	case VXFS_SUPER_MAGIC:
> +		return 1;
> +	default:
> +		return 0;
> +	}
> +}

This list doesn't feel all that maintainable to me, but so be it if
there is no better interface available.

> +static int find_mount(const char *path, const struct statvfs *fs,
> +			struct mntent *entry)

I don't quite understand why `find_mount()` is required in the first
place. Why can't we statfs(2) the path directly? This syscall provides
the fsid and should be sufficient for us to fill in `struct fs_info`.

Explaining details like this in the commit message would help guide the
reader's expectations.

Patrick

> +{
> +	const char *const mounts = "/proc/mounts";
> +	char *rp = real_pathdup(path, 1);
> +	struct mntent *ment = NULL;
> +	struct statvfs mntfs;
> +	FILE *fp;
> +	int found = 0;
> +	int ret = 0;
> +	size_t dlen, plen, flen = 0;
> +
> +	entry->mnt_fsname = NULL;
> +	entry->mnt_dir = NULL;
> +	entry->mnt_type = NULL;
> +
> +	fp = setmntent(mounts, "r");
> 
> +	if (!fp) {
> +		free(rp);
> +		return error_errno(_("setmntent('%s') failed"), mounts);
> +	}
> +
> +	plen = strlen(rp);
> +
> +	/* read all the mount information and compare to path */
> +	while ((ment = getmntent(fp))) {
> +		if (statvfs(ment->mnt_dir, &mntfs)) {
> +			switch (errno) {
> +			case EPERM:
> +			case ESRCH:
> +			case EACCES:
> +				continue;
> +			default:
> +				error_errno(_("statvfs('%s') failed"), ment->mnt_dir);
> +				ret = -1;
> +				goto done;
> +			}
> +		}
> +
> +		/* is mount on the same filesystem and is a prefix of the path */
> +		if ((fs->f_fsid == mntfs.f_fsid) &&
> +			!strncmp(ment->mnt_dir, rp, strlen(ment->mnt_dir))) {
> +			dlen = strlen(ment->mnt_dir);
> +			if (dlen > plen)
> +				continue;
> +			/*
> +			 * look for the longest prefix (including root)
> +			 */
> +			if (dlen > flen &&
> +				((dlen == 1 && ment->mnt_dir[0] == '/') ||
> +				 (!rp[dlen] || rp[dlen] == '/'))) {
> +				flen = dlen;
> +				found = 1;
> +
> +				/*
> +				 * https://man7.org/linux/man-pages/man3/getmntent.3.html
> +				 *
> +				 * The pointer points to a static area of memory which is
> +				 * overwritten by subsequent calls to getmntent().
> +				 */
> +				free(entry->mnt_fsname);
> +				free(entry->mnt_dir);
> +				free(entry->mnt_type);
> +				entry->mnt_fsname = xstrdup(ment->mnt_fsname);
> +				entry->mnt_dir = xstrdup(ment->mnt_dir);
> +				entry->mnt_type = xstrdup(ment->mnt_type);
> +			}
> +		}
> +	}
> +
> +done:
> +	free(rp);
> +	endmntent(fp);
> +
> +	if (!found)
> +		return -1;
> +
> +	return ret;
> +}
> +
> +int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
> +{
> +	int ret = 0;
> +	struct mntent entry;
> +	struct statvfs fs;
> +
> +	fs_info->is_remote = -1;
> +	fs_info->typename = NULL;
> +
> +	if (statvfs(path, &fs))
> +		return error_errno(_("statvfs('%s') failed"), path);
> +
> +	if (find_mount(path, &fs, &entry) < 0) {
> +		ret = -1;
> +		goto done;
> +	}
> +
> +	trace_printf_key(&trace_fsmonitor,
> +			 "statvfs('%s') [flags 0x%08lx] '%s' '%s'",
> +			 path, fs.f_flag, entry.mnt_type, entry.mnt_fsname);
> +
> +	fs_info->is_remote = is_remote_fs(entry.mnt_dir);
> +	fs_info->typename = xstrdup(entry.mnt_fsname);
> +
> +	if (fs_info->is_remote < 0)
> +		ret = -1;
> +
> +	trace_printf_key(&trace_fsmonitor,
> +				"'%s' is_remote: %d",
> +				path, fs_info->is_remote);
> +
> +done:
> +	free(entry.mnt_fsname);
> +	free(entry.mnt_dir);
> +	free(entry.mnt_type);
> +	return ret;
> +}
> +
> +int fsmonitor__is_fs_remote(const char *path)
> +{
> +	int ret = 0;
> +	struct fs_info fs;
> +
> +	if (fsmonitor__get_fs_info(path, &fs))
> +		ret = -1;
> +	else
> +		ret = fs.is_remote;
> +
> +	free(fs.typename);
> +
> +	return ret;
> +}
> +
> +/*
> + * No-op for now.
> + */
> +int fsmonitor__get_alias(const char *path, struct alias_info *info)
> +{
> +	return 0;
> +}
> +
> +/*
> + * No-op for now.
> + */
> +char *fsmonitor__resolve_alias(const char *path,
> +		const struct alias_info *info)
> +{
> +	return NULL;
> +}
> diff --git a/compat/fsmonitor/fsm-path-utils-linux.h b/compat/fsmonitor/fsm-path-utils-linux.h
> new file mode 100644
> index 00000000000..49bdb3c4728
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-path-utils-linux.h
> @@ -0,0 +1,91 @@
> +#ifndef FSM_PATH_UTILS_LINUX_H
> +#define FSM_PATH_UTILS_LINUX_H
> +#endif
> +
> +#ifdef HAVE_LINUX_MAGIC_H
> +#include <linux/magic.h>
> +#endif
> +
> +#ifndef ACFS_SUPER_MAGIC
> +#define ACFS_SUPER_MAGIC 0x61636673
> +#endif
> +
> +#ifndef AFS_SUPER_MAGIC
> +#define AFS_SUPER_MAGIC 0x5346414f
> +#endif
> +
> +#ifndef CEPH_SUPER_MAGIC
> +#define CEPH_SUPER_MAGIC 0x00c36400
> +#endif
> +
> +#ifndef CIFS_SUPER_MAGIC
> +#define CIFS_SUPER_MAGIC 0xff534d42
> +#endif
> +
> +#ifndef CODA_SUPER_MAGIC
> +#define CODA_SUPER_MAGIC 0x73757245
> +#endif
> +
> +#ifndef FHGFS_SUPER_MAGIC
> +#define FHGFS_SUPER_MAGIC 0x19830326
> +#endif
> +
> +#ifndef GFS_SUPER_MAGIC
> +#define GFS_SUPER_MAGIC 0x1161970
> +#endif
> +
> +#ifndef GPFS_SUPER_MAGIC
> +#define GPFS_SUPER_MAGIC 0x47504653
> +#endif
> +
> +#ifndef IBRIX_SUPER_MAGIC
> +#define IBRIX_SUPER_MAGIC 0x013111a8
> +#endif
> +
> +#ifndef KAFS_SUPER_MAGIC
> +#define KAFS_SUPER_MAGIC 0x6b414653
> +#endif
> +
> +#ifndef LUSTRE_SUPER_MAGIC
> +#define LUSTRE_SUPER_MAGIC 0x0bd00bd0
> +#endif
> +
> +#ifndef NCP_SUPER_MAGIC
> +#define NCP_SUPER_MAGIC 0x564c
> +#endif
> +
> +#ifndef NFS_SUPER_MAGIC
> +#define NFS_SUPER_MAGIC 0x6969
> +#endif
> +
> +#ifndef NFSD_SUPER_MAGIC
> +#define NFSD_SUPER_MAGIC 0x6e667364
> +#endif
> +
> +#ifndef OCFS2_SUPER_MAGIC
> +#define OCFS2_SUPER_MAGIC 0x7461636f
> +#endif
> +
> +#ifndef PANFS_SUPER_MAGIC
> +#define PANFS_SUPER_MAGIC 0xaad7aaea
> +#endif
> +
> +#ifndef SMB_SUPER_MAGIC
> +#define SMB_SUPER_MAGIC 0x517b
> +#endif
> +
> +#ifndef SMB2_SUPER_MAGIC
> +#define SMB2_SUPER_MAGIC 0xfe534d42
> +#endif
> +
> +#ifndef SNFS_SUPER_MAGIC
> +#define SNFS_SUPER_MAGIC 0xbeefdead
> +#endif
> +
> +#ifndef VMHGFS_SUPER_MAGIC
> +#define VMHGFS_SUPER_MAGIC 0xbacbacbc
> +#endif
> +
> +#ifndef VXFS_SUPER_MAGIC
> +#define VXFS_SUPER_MAGIC 0xa501fcf5
> +#endif
> diff --git a/config.mak.uname b/config.mak.uname
> index dacc95172dc..80d7e2a2e68 100644
> --- a/config.mak.uname
> +++ b/config.mak.uname
> @@ -68,6 +68,17 @@ ifeq ($(uname_S),Linux)
>  	ifneq ($(findstring .el7.,$(uname_R)),)
>  		BASIC_CFLAGS += -std=c99
>  	endif
> +	ifeq ($(shell test -f /usr/include/linux/magic.h && echo y),y)
> +		HAVE_LINUX_MAGIC_H = YesPlease
> +	endif
> +	# The builtin FSMonitor on Linux builds upon Simple-IPC.  Both require
> +	# Unix domain sockets and PThreads.
> +	ifndef NO_PTHREADS
> +	ifndef NO_UNIX_SOCKETS
> +	FSMONITOR_DAEMON_BACKEND = linux
> +	FSMONITOR_OS_SETTINGS = linux
> +	endif
> +	endif
>  endif
>  ifeq ($(uname_S),GNU/kFreeBSD)
>  	HAVE_ALLOCA_H = YesPlease
> -- 
> gitgitgadget
> 
> 

endif

ifdef HAVE_LINUX_MAGIC_H
BASIC_CFLAGS += -DHAVE_LINUX_MAGIC_H
endif

ifdef HAVE_CLOCK_MONOTONIC
BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
endif
Expand Down
24 changes: 24 additions & 0 deletions compat/fsmonitor/fsm-health-linux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "cache.h"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the Git mailing list, Patrick Steinhardt wrote (reply to this):

On Thu, Feb 15, 2024 at 10:29:32AM +0000, Eric DeCosta via GitGitGadget wrote:
> From: Eric DeCosta <edecosta@mathworks.com>
> 
> rebased with master, and resolved conflicts

It would be a lot more useful if you adopted the original phrasing of
the commit:

```
fsmonitor: prepare to share code between Mac OS and Linux 

Linux and Mac OS can share some of the code originally developed for Mac OS.

Mac OS and Linux can share fsm-ipc-unix.c and fsm-settings-unix.c

Signed-off-by: Eric DeCosta <edecosta@mathworks.com>
```

Depending on whether or not you have made significant changes during the
rebase I'd also convert the trailers to:

Patch-originally-by: Eric DeCoste <edecosta@mathworks.com>
Signed-off-by: Marzieh Esipreh <m.ispare63@gmail.com>

Furthermore, I think it would be useful if this commit was split up even
further than it already is. Having a preparatory patch that moves around
shareable code is a different topic than introducing the code skeleton
for Linux support, and as far as I can see the shared code does not end
up requiring anything from the new "*-linux.c" files.

Patrick

> Signed-off-by: Eric DeCosta <edecosta@mathworks.com>
> ---
>  compat/fsmonitor/fsm-health-linux.c    | 24 ++++++++++
>  compat/fsmonitor/fsm-ipc-darwin.c      | 57 +----------------------
>  compat/fsmonitor/fsm-ipc-linux.c       |  1 +
>  compat/fsmonitor/fsm-ipc-unix.c        | 53 +++++++++++++++++++++
>  compat/fsmonitor/fsm-settings-darwin.c | 64 +-------------------------
>  compat/fsmonitor/fsm-settings-linux.c  |  1 +
>  compat/fsmonitor/fsm-settings-unix.c   | 61 ++++++++++++++++++++++++
>  7 files changed, 142 insertions(+), 119 deletions(-)
>  create mode 100644 compat/fsmonitor/fsm-health-linux.c
>  create mode 100644 compat/fsmonitor/fsm-ipc-linux.c
>  create mode 100644 compat/fsmonitor/fsm-ipc-unix.c
>  create mode 100644 compat/fsmonitor/fsm-settings-linux.c
>  create mode 100644 compat/fsmonitor/fsm-settings-unix.c
> 
> diff --git a/compat/fsmonitor/fsm-health-linux.c b/compat/fsmonitor/fsm-health-linux.c
> new file mode 100644
> index 00000000000..b9f709e8548
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-health-linux.c
> @@ -0,0 +1,24 @@
> +#include "cache.h"
> +#include "config.h"
> +#include "fsmonitor.h"
> +#include "fsm-health.h"
> +#include "fsmonitor--daemon.h"
> +
> +int fsm_health__ctor(struct fsmonitor_daemon_state *state)
> +{
> +	return 0;
> +}
> +
> +void fsm_health__dtor(struct fsmonitor_daemon_state *state)
> +{
> +	return;
> +}
> +
> +void fsm_health__loop(struct fsmonitor_daemon_state *state)
> +{
> +	return;
> +}
> +
> +void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
> +{
> +}
> diff --git a/compat/fsmonitor/fsm-ipc-darwin.c b/compat/fsmonitor/fsm-ipc-darwin.c
> index 6f3a95410cc..4c3c92081ee 100644
> --- a/compat/fsmonitor/fsm-ipc-darwin.c
> +++ b/compat/fsmonitor/fsm-ipc-darwin.c
> @@ -1,56 +1 @@
> -#include "git-compat-util.h"
> -#include "config.h"
> -#include "gettext.h"
> -#include "hex.h"
> -#include "path.h"
> -#include "repository.h"
> -#include "strbuf.h"
> -#include "fsmonitor-ll.h"
> -#include "fsmonitor-ipc.h"
> -#include "fsmonitor-path-utils.h"
> -
> -static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")
> -
> -const char *fsmonitor_ipc__get_path(struct repository *r)
> -{
> -	static const char *ipc_path = NULL;
> -	git_SHA_CTX sha1ctx;
> -	char *sock_dir = NULL;
> -	struct strbuf ipc_file = STRBUF_INIT;
> -	unsigned char hash[GIT_MAX_RAWSZ];
> -
> -	if (!r)
> -		BUG("No repository passed into fsmonitor_ipc__get_path");
> -
> -	if (ipc_path)
> -		return ipc_path;
> -
> -
> -	/* By default the socket file is created in the .git directory */
> -	if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
> -		ipc_path = fsmonitor_ipc__get_default_path();
> -		return ipc_path;
> -	}
> -
> -	git_SHA1_Init(&sha1ctx);
> -	git_SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
> -	git_SHA1_Final(hash, &sha1ctx);
> -
> -	repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);
> -
> -	/* Create the socket file in either socketDir or $HOME */
> -	if (sock_dir && *sock_dir) {
> -		strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
> -					sock_dir, hash_to_hex(hash));
> -	} else {
> -		strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
> -	}
> -	free(sock_dir);
> -
> -	ipc_path = interpolate_path(ipc_file.buf, 1);
> -	if (!ipc_path)
> -		die(_("Invalid path: %s"), ipc_file.buf);
> -
> -	strbuf_release(&ipc_file);
> -	return ipc_path;
> -}
> +#include "fsm-ipc-unix.c"
> diff --git a/compat/fsmonitor/fsm-ipc-linux.c b/compat/fsmonitor/fsm-ipc-linux.c
> new file mode 100644
> index 00000000000..4c3c92081ee
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-ipc-linux.c
> @@ -0,0 +1 @@
> +#include "fsm-ipc-unix.c"
> diff --git a/compat/fsmonitor/fsm-ipc-unix.c b/compat/fsmonitor/fsm-ipc-unix.c
> new file mode 100644
> index 00000000000..eb25123fa12
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-ipc-unix.c
> @@ -0,0 +1,53 @@
> +#include "cache.h"
> +#include "config.h"
> +#include "hex.h"
> +#include "strbuf.h"
> +#include "fsmonitor.h"
> +#include "fsmonitor-ipc.h"
> +#include "fsmonitor-path-utils.h"
> +
> +static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")
> +
> +const char *fsmonitor_ipc__get_path(struct repository *r)
> +{
> +	static const char *ipc_path = NULL;
> +	git_SHA_CTX sha1ctx;
> +	char *sock_dir = NULL;
> +	struct strbuf ipc_file = STRBUF_INIT;
> +	unsigned char hash[GIT_MAX_RAWSZ];
> +
> +	if (!r)
> +		BUG("No repository passed into fsmonitor_ipc__get_path");
> +
> +	if (ipc_path)
> +		return ipc_path;
> +
> +
> +	/* By default the socket file is created in the .git directory */
> +	if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
> +		ipc_path = fsmonitor_ipc__get_default_path();
> +		return ipc_path;
> +	}
> +
> +	git_SHA1_Init(&sha1ctx);
> +	git_SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
> +	git_SHA1_Final(hash, &sha1ctx);
> +
> +	repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);
> +
> +	/* Create the socket file in either socketDir or $HOME */
> +	if (sock_dir && *sock_dir) {
> +		strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
> +					sock_dir, hash_to_hex(hash));
> +	} else {
> +		strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
> +	}
> +	free(sock_dir);
> +
> +	ipc_path = interpolate_path(ipc_file.buf, 1);
> +	if (!ipc_path)
> +		die(_("Invalid path: %s"), ipc_file.buf);
> +
> +	strbuf_release(&ipc_file);
> +	return ipc_path;
> +}
> diff --git a/compat/fsmonitor/fsm-settings-darwin.c b/compat/fsmonitor/fsm-settings-darwin.c
> index a3825906351..14baf9f0603 100644
> --- a/compat/fsmonitor/fsm-settings-darwin.c
> +++ b/compat/fsmonitor/fsm-settings-darwin.c
> @@ -1,63 +1 @@
> -#include "git-compat-util.h"
> -#include "config.h"
> -#include "fsmonitor-ll.h"
> -#include "fsmonitor-ipc.h"
> -#include "fsmonitor-settings.h"
> -#include "fsmonitor-path-utils.h"
> -
> - /*
> - * For the builtin FSMonitor, we create the Unix domain socket for the
> - * IPC in the .git directory.  If the working directory is remote,
> - * then the socket will be created on the remote file system.  This
> - * can fail if the remote file system does not support UDS file types
> - * (e.g. smbfs to a Windows server) or if the remote kernel does not
> - * allow a non-local process to bind() the socket.  (These problems
> - * could be fixed by moving the UDS out of the .git directory and to a
> - * well-known local directory on the client machine, but care should
> - * be taken to ensure that $HOME is actually local and not a managed
> - * file share.)
> - *
> - * FAT32 and NTFS working directories are problematic too.
> - *
> - * The builtin FSMonitor uses a Unix domain socket in the .git
> - * directory for IPC.  These Windows drive formats do not support
> - * Unix domain sockets, so mark them as incompatible for the daemon.
> - *
> - */
> -static enum fsmonitor_reason check_uds_volume(struct repository *r)
> -{
> -	struct fs_info fs;
> -	const char *ipc_path = fsmonitor_ipc__get_path(r);
> -	struct strbuf path = STRBUF_INIT;
> -	strbuf_add(&path, ipc_path, strlen(ipc_path));
> -
> -	if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
> -		strbuf_release(&path);
> -		return FSMONITOR_REASON_ERROR;
> -	}
> -
> -	strbuf_release(&path);
> -
> -	if (fs.is_remote ||
> -		!strcmp(fs.typename, "msdos") ||
> -		!strcmp(fs.typename, "ntfs")) {
> -		free(fs.typename);
> -		return FSMONITOR_REASON_NOSOCKETS;
> -	}
> -
> -	free(fs.typename);
> -	return FSMONITOR_REASON_OK;
> -}
> -
> -enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
> -{
> -	enum fsmonitor_reason reason;
> -
> -	if (ipc) {
> -		reason = check_uds_volume(r);
> -		if (reason != FSMONITOR_REASON_OK)
> -			return reason;
> -	}
> -
> -	return FSMONITOR_REASON_OK;
> -}
> +#include "fsm-settings-unix.c"
> diff --git a/compat/fsmonitor/fsm-settings-linux.c b/compat/fsmonitor/fsm-settings-linux.c
> new file mode 100644
> index 00000000000..14baf9f0603
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-settings-linux.c
> @@ -0,0 +1 @@
> +#include "fsm-settings-unix.c"
> diff --git a/compat/fsmonitor/fsm-settings-unix.c b/compat/fsmonitor/fsm-settings-unix.c
> new file mode 100644
> index 00000000000..d16dca89416
> --- /dev/null
> +++ b/compat/fsmonitor/fsm-settings-unix.c
> @@ -0,0 +1,61 @@
> +#include "fsmonitor.h"
> +#include "fsmonitor-ipc.h"
> +#include "fsmonitor-path-utils.h"
> +
> + /*
> + * For the builtin FSMonitor, we create the Unix domain socket for the
> + * IPC in the .git directory.  If the working directory is remote,
> + * then the socket will be created on the remote file system.  This
> + * can fail if the remote file system does not support UDS file types
> + * (e.g. smbfs to a Windows server) or if the remote kernel does not
> + * allow a non-local process to bind() the socket.  (These problems
> + * could be fixed by moving the UDS out of the .git directory and to a
> + * well-known local directory on the client machine, but care should
> + * be taken to ensure that $HOME is actually local and not a managed
> + * file share.)
> + *
> + * FAT32 and NTFS working directories are problematic too.
> + *
> + * The builtin FSMonitor uses a Unix domain socket in the .git
> + * directory for IPC.  These Windows drive formats do not support
> + * Unix domain sockets, so mark them as incompatible for the daemon.
> + *
> + */
> +static enum fsmonitor_reason check_uds_volume(struct repository *r)
> +{
> +	struct fs_info fs;
> +	const char *ipc_path = fsmonitor_ipc__get_path(r);
> +	struct strbuf path = STRBUF_INIT;
> +	strbuf_addstr(&path, ipc_path);
> +
> +	if (fsmonitor__get_fs_info(dirname(path.buf), &fs) == -1) {
> +		free(fs.typename);
> +		strbuf_release(&path);
> +		return FSMONITOR_REASON_ERROR;
> +	}
> +
> +	strbuf_release(&path);
> +
> +	if (fs.is_remote ||
> +		!strcmp(fs.typename, "msdos") ||
> +		!strcmp(fs.typename, "ntfs")) {
> +		free(fs.typename);
> +		return FSMONITOR_REASON_NOSOCKETS;
> +	}
> +
> +	free(fs.typename);
> +	return FSMONITOR_REASON_OK;
> +}
> +
> +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc)
> +{
> +	enum fsmonitor_reason reason;
> +
> +	if (ipc) {
> +		reason = check_uds_volume(r);
> +		if (reason != FSMONITOR_REASON_OK)
> +			return reason;
> +	}
> +
> +	return FSMONITOR_REASON_OK;
> +}
> -- 
> gitgitgadget
> 
> 

#include "config.h"
#include "fsmonitor.h"
#include "fsm-health.h"
#include "fsmonitor--daemon.h"

int fsm_health__ctor(struct fsmonitor_daemon_state *state)
{
return 0;
}

void fsm_health__dtor(struct fsmonitor_daemon_state *state)
{
return;
}

void fsm_health__loop(struct fsmonitor_daemon_state *state)
{
return;
}

void fsm_health__stop_async(struct fsmonitor_daemon_state *state)
{
}
57 changes: 1 addition & 56 deletions compat/fsmonitor/fsm-ipc-darwin.c
Original file line number Diff line number Diff line change
@@ -1,56 +1 @@
#include "git-compat-util.h"
#include "config.h"
#include "gettext.h"
#include "hex.h"
#include "path.h"
#include "repository.h"
#include "strbuf.h"
#include "fsmonitor-ll.h"
#include "fsmonitor-ipc.h"
#include "fsmonitor-path-utils.h"

static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")

const char *fsmonitor_ipc__get_path(struct repository *r)
{
static const char *ipc_path = NULL;
git_SHA_CTX sha1ctx;
char *sock_dir = NULL;
struct strbuf ipc_file = STRBUF_INIT;
unsigned char hash[GIT_MAX_RAWSZ];

if (!r)
BUG("No repository passed into fsmonitor_ipc__get_path");

if (ipc_path)
return ipc_path;


/* By default the socket file is created in the .git directory */
if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
ipc_path = fsmonitor_ipc__get_default_path();
return ipc_path;
}

git_SHA1_Init(&sha1ctx);
git_SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
git_SHA1_Final(hash, &sha1ctx);

repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);

/* Create the socket file in either socketDir or $HOME */
if (sock_dir && *sock_dir) {
strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
sock_dir, hash_to_hex(hash));
} else {
strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
}
free(sock_dir);

ipc_path = interpolate_path(ipc_file.buf, 1);
if (!ipc_path)
die(_("Invalid path: %s"), ipc_file.buf);

strbuf_release(&ipc_file);
return ipc_path;
}
#include "fsm-ipc-unix.c"
1 change: 1 addition & 0 deletions compat/fsmonitor/fsm-ipc-linux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "fsm-ipc-unix.c"
53 changes: 53 additions & 0 deletions compat/fsmonitor/fsm-ipc-unix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "cache.h"
#include "config.h"
#include "hex.h"
#include "strbuf.h"
#include "fsmonitor.h"
#include "fsmonitor-ipc.h"
#include "fsmonitor-path-utils.h"

static GIT_PATH_FUNC(fsmonitor_ipc__get_default_path, "fsmonitor--daemon.ipc")

const char *fsmonitor_ipc__get_path(struct repository *r)
{
static const char *ipc_path = NULL;
git_SHA_CTX sha1ctx;
char *sock_dir = NULL;
struct strbuf ipc_file = STRBUF_INIT;
unsigned char hash[GIT_MAX_RAWSZ];

if (!r)
BUG("No repository passed into fsmonitor_ipc__get_path");

if (ipc_path)
return ipc_path;


/* By default the socket file is created in the .git directory */
if (fsmonitor__is_fs_remote(r->gitdir) < 1) {
ipc_path = fsmonitor_ipc__get_default_path();
return ipc_path;
}

git_SHA1_Init(&sha1ctx);
git_SHA1_Update(&sha1ctx, r->worktree, strlen(r->worktree));
git_SHA1_Final(hash, &sha1ctx);

repo_config_get_string(r, "fsmonitor.socketdir", &sock_dir);

/* Create the socket file in either socketDir or $HOME */
if (sock_dir && *sock_dir) {
strbuf_addf(&ipc_file, "%s/.git-fsmonitor-%s",
sock_dir, hash_to_hex(hash));
} else {
strbuf_addf(&ipc_file, "~/.git-fsmonitor-%s", hash_to_hex(hash));
}
free(sock_dir);

ipc_path = interpolate_path(ipc_file.buf, 1);
if (!ipc_path)
die(_("Invalid path: %s"), ipc_file.buf);

strbuf_release(&ipc_file);
return ipc_path;
}
195 changes: 195 additions & 0 deletions compat/fsmonitor/fsm-path-utils-linux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include "git-compat-util.h"
#include "abspath.h"
#include "fsmonitor.h"
#include "fsmonitor-path-utils.h"
#include "fsm-path-utils-linux.h"
#include <errno.h>
#include <mntent.h>
#include <sys/mount.h>
#include <sys/vfs.h>
#include <sys/statvfs.h>

static int is_remote_fs(const char *path)
{
struct statfs fs;

if (statfs(path, &fs))
return error_errno(_("statfs('%s') failed"), path);

switch (fs.f_type) {
case ACFS_SUPER_MAGIC:
case AFS_SUPER_MAGIC:
case CEPH_SUPER_MAGIC:
case CIFS_SUPER_MAGIC:
case CODA_SUPER_MAGIC:
case FHGFS_SUPER_MAGIC:
case GFS_SUPER_MAGIC:
case GPFS_SUPER_MAGIC:
case IBRIX_SUPER_MAGIC:
case KAFS_SUPER_MAGIC:
case LUSTRE_SUPER_MAGIC:
case NCP_SUPER_MAGIC:
case NFS_SUPER_MAGIC:
case NFSD_SUPER_MAGIC:
case OCFS2_SUPER_MAGIC:
case PANFS_SUPER_MAGIC:
case SMB_SUPER_MAGIC:
case SMB2_SUPER_MAGIC:
case SNFS_SUPER_MAGIC:
case VMHGFS_SUPER_MAGIC:
case VXFS_SUPER_MAGIC:
return 1;
default:
return 0;
}
}

static int find_mount(const char *path, const struct statvfs *fs,
struct mntent *entry)
{
const char *const mounts = "/proc/mounts";
char *rp = real_pathdup(path, 1);
struct mntent *ment = NULL;
struct statvfs mntfs;
FILE *fp;
int found = 0;
int ret = 0;
size_t dlen, plen, flen = 0;

entry->mnt_fsname = NULL;
entry->mnt_dir = NULL;
entry->mnt_type = NULL;

fp = setmntent(mounts, "r");
if (!fp) {
free(rp);
return error_errno(_("setmntent('%s') failed"), mounts);
}

plen = strlen(rp);

/* read all the mount information and compare to path */
while ((ment = getmntent(fp))) {
if (statvfs(ment->mnt_dir, &mntfs)) {
switch (errno) {
case EPERM:
case ESRCH:
case EACCES:
continue;
default:
error_errno(_("statvfs('%s') failed"), ment->mnt_dir);
ret = -1;
goto done;
}
}

/* is mount on the same filesystem and is a prefix of the path */
if ((fs->f_fsid == mntfs.f_fsid) &&
!strncmp(ment->mnt_dir, rp, strlen(ment->mnt_dir))) {
dlen = strlen(ment->mnt_dir);
if (dlen > plen)
continue;
/*
* look for the longest prefix (including root)
*/
if (dlen > flen &&
((dlen == 1 && ment->mnt_dir[0] == '/') ||
(!rp[dlen] || rp[dlen] == '/'))) {
flen = dlen;
found = 1;

/*
* https://man7.org/linux/man-pages/man3/getmntent.3.html
*
* The pointer points to a static area of memory which is
* overwritten by subsequent calls to getmntent().
*/
free(entry->mnt_fsname);
free(entry->mnt_dir);
free(entry->mnt_type);
entry->mnt_fsname = xstrdup(ment->mnt_fsname);
entry->mnt_dir = xstrdup(ment->mnt_dir);
entry->mnt_type = xstrdup(ment->mnt_type);
}
}
}

done:
free(rp);
endmntent(fp);

if (!found)
return -1;

return ret;
}

int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
{
int ret = 0;
struct mntent entry;
struct statvfs fs;

fs_info->is_remote = -1;
fs_info->typename = NULL;

if (statvfs(path, &fs))
return error_errno(_("statvfs('%s') failed"), path);

if (find_mount(path, &fs, &entry) < 0) {
ret = -1;
goto done;
}

trace_printf_key(&trace_fsmonitor,
"statvfs('%s') [flags 0x%08lx] '%s' '%s'",
path, fs.f_flag, entry.mnt_type, entry.mnt_fsname);

fs_info->is_remote = is_remote_fs(entry.mnt_dir);
fs_info->typename = xstrdup(entry.mnt_fsname);

if (fs_info->is_remote < 0)
ret = -1;

trace_printf_key(&trace_fsmonitor,
"'%s' is_remote: %d",
path, fs_info->is_remote);

done:
free(entry.mnt_fsname);
free(entry.mnt_dir);
free(entry.mnt_type);
return ret;
}

int fsmonitor__is_fs_remote(const char *path)
{
int ret = 0;
struct fs_info fs;

if (fsmonitor__get_fs_info(path, &fs))
ret = -1;
else
ret = fs.is_remote;

free(fs.typename);

return ret;
}

/*
* No-op for now.
*/
int fsmonitor__get_alias(const char *path, struct alias_info *info)
{
return 0;
}

/*
* No-op for now.
*/
char *fsmonitor__resolve_alias(const char *path,
const struct alias_info *info)
{
return NULL;
}
Loading