Add xfuse_path_in_xfuse_fs()

This commit is contained in:
matt335672 2021-10-25 16:16:54 +01:00
parent a7b48cd1cf
commit b87b7f9ee8
2 changed files with 137 additions and 2 deletions

View File

@ -18,6 +18,11 @@
/* FUSE mount point */
char g_fuse_root_path[256] = "";
#ifdef XRDP_FUSE
static const char *g_fuse_root_path_basename; /* See xfuse_path_in_xfuse_fs() */
static int g_fuse_root_parent_dev; /* Ditto */
static int g_fuse_root_parent_ino; /* Ditto */
#endif
char g_fuse_clipboard_path[256] = ""; /* for clipboard use */
#if defined(HAVE_CONFIG_H)
@ -122,6 +127,11 @@ void xfuse_devredir_cb_rename_file(struct state_rename *fip,
void xfuse_devredir_cb_file_close(struct state_close *fip)
{}
int xfuse_path_in_xfuse_fs(const char *path)
{
return 0;
}
#else
/******************************************************************************
@ -324,6 +334,7 @@ static char *g_buffer = 0;
static int g_fd = 0;
static tintptr g_bufsize = 0;
/* forward declarations for internal access */
static int xfuse_init_xrdp_fs(void);
static int xfuse_deinit_xrdp_fs(void);
@ -461,6 +472,7 @@ int
xfuse_init(void)
{
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
char *p;
/* if already inited, just return */
if (g_xfuse_inited)
@ -507,16 +519,59 @@ xfuse_init(void)
g_snprintf(g_fuse_root_path, sizeof(g_fuse_root_path), "%s/%s",
g_getenv("HOME"), g_cfg->fuse_mount_name);
}
/* Remove all trailing '/' from the root path */
p = g_fuse_root_path + g_strlen(g_fuse_root_path);
while ( p > g_fuse_root_path && *(p - 1) == '/')
{
--p;
*p = '\0';
}
/* This shouldn't happen */
if (g_strlen(g_fuse_root_path) == 0)
{
LOG(LOG_LEVEL_ERROR,
"Fuse root path is empty after removing trailing '/'");
return -1;
}
g_snprintf(g_fuse_clipboard_path, 255, "%s/.clipboard", g_fuse_root_path);
/* Get the characteristics of the parent directory of the FUSE mount
* point. Used by xfuse_path_in_xfuse_fs() */
p = (char *)g_strrchr(g_fuse_root_path, '/');
if (p != NULL)
{
/* Temporarily finish the root path at this point */
*p = '\0';
g_fuse_root_path_basename = p + 1;
g_fuse_root_parent_dev = g_file_get_device_number(g_fuse_root_path);
g_fuse_root_parent_ino = g_file_get_inode_num(g_fuse_root_path);
*p = '/';
}
else
{
g_fuse_root_parent_dev = -1;
g_fuse_root_parent_ino = -1;
}
if (g_fuse_root_parent_dev == -1 || g_fuse_root_parent_ino == -1)
{
LOG(LOG_LEVEL_ERROR,
"Unable to obtain characteristics of directory containing %s",
g_fuse_root_path);
return -1;
}
/* if FUSE mount point does not exist, create it */
if (!g_directory_exist(g_fuse_root_path))
{
(void)g_create_path(g_fuse_root_path);
if (!g_create_dir(g_fuse_root_path))
{
LOG_DEVEL(LOG_LEVEL_ERROR, "mkdir %s failed. If %s is already mounted, you must "
"first unmount it", g_fuse_root_path, g_fuse_root_path);
LOG(LOG_LEVEL_ERROR, "mkdir %s failed. If %s is already mounted, you must "
"first unmount it", g_fuse_root_path, g_fuse_root_path);
return -1;
}
}
@ -1491,6 +1546,77 @@ void xfuse_devredir_cb_file_close(struct state_close *fip)
free(fip);
}
/*
* Determine is a file is in the FUSE filesystem
*
* The whole purpose of this function is to avoid deadlocks. If we try
* to determine the size (or any other attribute) of a file in the FUSE
* filesystem, the kernel will block the calling thread and send a message
* to libfuse. Since we're single-threaded, this will result in deadlock.
*
* Ideally we'd use a stat() of the file to compare with a stat() of
* the mountpoint. This won't work however, for the reasons outline above.
*
* A simple implementation might be to compare the start of the filename
* with the FUSE mount point. This will work, but can easily be defeated,
* (perhaps unintentionally) by symlinks in the path.
*
* The approach taken here is to record the basename of the FUSE mount
* point directory (e.g. 'thinclient_drives') and look for that in the
* passed-in filename as a directory element. If we find it, we look
* at the device name and inode number of the parent directory of the
* FUSE mount point. If these match we consider the filename to be in
* the FUSE filesystem.
*/
int xfuse_path_in_xfuse_fs(const char *path)
{
char *wpath = NULL; /* Writeable copy of path */
char *p;
int blen = g_strlen(g_fuse_root_path_basename);
int rv = 0;
if ((wpath = g_strdup(path)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "system out of memory");
}
else
{
/* Look ahead in the string for the basename of the FUSE mount point */
for (p = wpath;
(p = g_strstr(p, g_fuse_root_path_basename)) != NULL ;
p = p + 1)
{
/* Is this string preceded by a '/' ? */
if (p == wpath || *(p - 1) != '/')
{
continue;
}
/* Is the string followed by a '/' or '\0' ? We know it's
* valid to look ahead this far in the string, as g_strstr()
* tells us it is */
if (*(p + blen) != '/' && *(p + blen) != '\0')
{
continue;
}
/* Temporarily terminate the string early, and check the
* file system characteristics of the preceding directory */
*(p - 1) = '\0';
if (g_file_get_device_number(wpath) == g_fuse_root_parent_dev &&
g_file_get_inode_num(wpath) == g_fuse_root_parent_ino)
{
rv = 1;
break;
}
*(p - 1) = '/';
}
g_free(wpath);
}
return rv;
}
/******************************************************************************
** **
** callbacks for fuse **

View File

@ -114,4 +114,13 @@ void xfuse_devredir_cb_rename_file(struct state_rename *fip,
void xfuse_devredir_cb_file_close(struct state_close *fip);
/*
* Returns true if a filesystem path lies in the FUSE filesystem
*
* Use to prevent deadlocks. For example, if chansrv tries to open
* a file in the FUSE filesystem it will fail, as it will call back
* into itself to handle the I/O
*/
int xfuse_path_in_xfuse_fs(const char *path);
#endif