Add remote drive lookup functionality to fuse

- Replace xfuse_cb_enum_dir() directory callback for adding files with
  more general xfuse_devredir_add_file_or_dir() to be called from a
  directory or a lookup operation.
- Moved XRDP_INODE out of public interface for chansrv_fuse, and replaced
  with simpler struct file_attr to pass to
  xfuse_devredir_add_file_or_dir()
- Allow a struct file_attr to be placed in an IRP for assembly of file
  attributes over multiple IRP_MJ_QUERY_INFORMATION requats.
- Add dev_redir_lookup_entry() to public interface for devredir.c
- Add xfuse_devredir_cb_lookup_entry() callback to public interface for
  chansrv-fuse.c
This commit is contained in:
matt335672 2019-03-21 16:48:01 +00:00
parent ef9013b4d9
commit d154df5059
5 changed files with 587 additions and 107 deletions

View File

@ -24,7 +24,6 @@
* this is in func xfuse_check_wait_objs() * this is in func xfuse_check_wait_objs()
* o if fuse mount point is already mounted, I get segfault * o if fuse mount point is already mounted, I get segfault
* o in open, check for modes such as O_TRUNC, O_APPEND * o in open, check for modes such as O_TRUNC, O_APPEND
* o copying over an existing file does not work
* o after a dir is created, the device cannot be unmounted on the client side * o after a dir is created, the device cannot be unmounted on the client side
* so something is holding it up * so something is holding it up
* o in thunar, when I move a file by dragging to another folder, the file * o in thunar, when I move a file by dragging to another folder, the file
@ -33,7 +32,6 @@
* o fuse ops to support * o fuse ops to support
* o touch does not work * o touch does not work
* o chmod must work * o chmod must work
* o cat >> file is not working
* *
*/ */
@ -74,7 +72,10 @@ int xfuse_create_share(tui32 device_id, const char *dirname)
void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, tui32 FileId) {} void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, tui32 FileId) {}
void xfuse_devredir_cb_write_file(void *vp, tui32 IoStatus, const char *buf, size_t length) {} void xfuse_devredir_cb_write_file(void *vp, tui32 IoStatus, const char *buf, size_t length) {}
void xfuse_devredir_cb_read_file(void *vp, const char *buf, size_t length) {} void xfuse_devredir_cb_read_file(void *vp, const char *buf, size_t length) {}
int xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) { return 0; } struct xrdp_inode *xfuse_devredir_add_file_or_dir(
void *vp,
const struct file_attr *fattr)
{ return NULL;}
void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus) {} void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus) {}
void xfuse_devredir_cb_rmdir_or_file(void *vp, tui32 IoStatus) {} void xfuse_devredir_cb_rmdir_or_file(void *vp, tui32 IoStatus) {}
void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) {} void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) {}
@ -129,7 +130,7 @@ void xfuse_devredir_cb_file_close(void *vp) {}
#define LOG_LEVEL LOG_ERROR #define LOG_LEVEL LOG_ERROR
/* Uncomment for detail of added entries */ /* Uncomment for detail of added entries */
#define XFUSE_DUMP_ADDED_ENTRIES /* #define XFUSE_DUMP_ADDED_ENTRIES */
#define log_error(_params...) \ #define log_error(_params...) \
{ \ { \
@ -167,6 +168,30 @@ void xfuse_devredir_cb_file_close(void *vp) {}
#define OP_RENAME_FILE 0x01 #define OP_RENAME_FILE 0x01
/* a file or dir entry in the xrdp file system */
struct xrdp_inode
{
tui32 parent_inode; /* Parent serial number. */
tui32 inode; /* File serial number. */
tui32 mode; /* File mode. */
tui32 nlink; /* symbolic link count. */
tui32 nentries; /* number of entries in a dir */
tui32 nopen; /* number of simultaneous opens */
tui32 uid; /* User ID of the file's owner. */
tui32 gid; /* Group ID of the file's group. */
size_t size; /* Size of file, in bytes. */
time_t atime; /* Time of last access. */
time_t mtime; /* Time of last modification. */
time_t ctime; /* Time of last status change. */
char name[1024]; /* Dir or filename */
tui32 device_id; /* for file system redirection */
int lindex; /* used in clipboard operations */
int is_loc_resource; /* this is not a redirected resource */
int close_in_progress; /* close cmd sent to client */
int stale; /* mark file as stale, ok to remove */
};
typedef struct xrdp_inode XRDP_INODE; // LK_TODO use this instead of using struct xrdp_inode
/* the xrdp file system in memory */ /* the xrdp file system in memory */
struct xrdp_fs struct xrdp_fs
{ {
@ -356,6 +381,7 @@ static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino,
/* misc calls */ /* misc calls */
static void xfuse_mark_as_stale(fuse_ino_t pinode); static void xfuse_mark_as_stale(fuse_ino_t pinode);
static void xfuse_delete_stale_entries(fuse_ino_t pinode); static void xfuse_delete_stale_entries(fuse_ino_t pinode);
static void make_fuse_entry_reply(fuse_req_t req, const XRDP_INODE *xinode);
/*****************************************************************************/ /*****************************************************************************/
int int
@ -1492,64 +1518,111 @@ static void xfuse_update_xrdpfs_size(void)
/** /**
* Add a file or directory to xrdp file system * Add a file or directory to xrdp file system
*
* If the file or directory already exists, its attributes are updated.
*
* Returns a pointer to the xinode if the object was added or updated
*****************************************************************************/ *****************************************************************************/
int xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) struct xrdp_inode *xfuse_devredir_add_file_or_dir(
void *vp,
const struct file_attr *fattr)
{ {
XFUSE_INFO *fip = (XFUSE_INFO *) vp; XFUSE_INFO *fip = (XFUSE_INFO *) vp;
XRDP_INODE *xip = NULL; XRDP_INODE *xinode = NULL;
if ((fip == NULL) || (xinode == NULL)) if (fip == NULL)
{ {
log_error("fip or xinode are NULL"); log_error("fip is NULL");
return -1;
} }
else if (!xfuse_is_inode_valid(fip->inode))
if (!xfuse_is_inode_valid(fip->inode))
{ {
log_error("inode %ld is not valid", fip->inode); log_error("inode %ld is not valid", fip->inode);
g_free(xinode);
return -1;
} }
else if ((strcmp(fattr->name, ".") == 0) ||
log_debug("parent_inode=%ld name=%s", fip->inode, xinode->name); (strcmp(fattr->name, "..") == 0))
/* if filename is . or .. don't add it */
if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0))
{ {
g_free(xinode); ; /* filename is . or .. - don't add it */
return -1;
} }
else
xfuse_dump_fs();
if ((xip = xfuse_get_inode_from_pinode_name(fip->inode, xinode->name)) != NULL)
{ {
log_debug("inode=%ld name=%s already exists in xrdp_fs; not adding it", log_debug("parent_inode=%ld name=%s", fip->inode, fattr->name);
fip->inode, xinode->name); xfuse_dump_fs();
g_free(xinode);
xip->stale = 0;
return -1;
}
xinode->parent_inode = fip->inode; /* Does the file already exist ? */
xinode->inode = g_xrdp_fs.next_node++; xinode = xfuse_get_inode_from_pinode_name(fip->inode, fattr->name);
xinode->uid = getuid(); if (xinode != NULL)
xinode->gid = getgid(); {
xinode->device_id = fip->device_id; /* Is the existing file the same type ? */
if ((xinode->mode & (S_IFREG | S_IFDIR)) ==
(fattr->mode & (S_IFREG | S_IFDIR)))
{
log_debug("inode=%ld name=%s already exists in xrdp_fs"
" - updating attributes",
fip->inode, xinode->name);
xinode->mode = fattr->mode;
xinode->size = fattr->size;
xinode->atime = fattr->atime;
xinode->mtime = fattr->mtime;
xinode->ctime = fattr->ctime;
xinode->stale = 0;
}
else
{
/* File type has changed */
log_debug("inode=%ld name=%s of different type in xrdp_fs"
" - removing",
fip->inode, xinode->name);
if (xinode->mode & S_IFREG)
{
xfuse_delete_file_with_xinode(xinode);
}
else
{
xfuse_delete_dir_with_xinode(xinode);
}
xinode = NULL;
}
}
g_xrdp_fs.num_entries++; if (xinode == NULL)
{
/* Add a new node to the file system */
xinode = g_new0(struct xrdp_inode, 1);
if (xinode == NULL)
{
log_debug("g_new0() failed");
}
else
{
strcpy(xinode->name, fattr->name);
xinode->mode = fattr->mode;
xinode->size = fattr->size;
xinode->atime = fattr->atime;
xinode->mtime = fattr->mtime;
xinode->ctime = fattr->ctime;
/* insert it in xrdp fs and update lookup count */ xinode->parent_inode = fip->inode;
g_xrdp_fs.inode_table[xinode->inode] = xinode; xinode->inode = g_xrdp_fs.next_node++;
g_xrdp_fs.inode_table[fip->inode]->nentries++; xinode->uid = getuid();
xfuse_update_xrdpfs_size(); xinode->gid = getgid();
xinode->device_id = fip->device_id;
xfuse_dump_fs(); g_xrdp_fs.num_entries++;
/* insert it in xrdp fs and update lookup count */
g_xrdp_fs.inode_table[xinode->inode] = xinode;
g_xrdp_fs.inode_table[fip->inode]->nentries++;
xfuse_update_xrdpfs_size();
xfuse_dump_fs();
#ifdef XFUSE_DUMP_ADDED_ENTRIES #ifdef XFUSE_DUMP_ADDED_ENTRIES
xfuse_dump_xrdp_inode(xinode); xfuse_dump_xrdp_inode(xinode);
#endif #endif
return 0; }
}
}
return xinode;
} }
/** /**
@ -1619,6 +1692,44 @@ done:
} }
} }
/**
* This routine is caused as a result of a devredir remote lookup
* instigated by xfuse_cb_lookup()
*
* If the IoStatus is good but there's no xinode, something has gone wrong
* in the devredir layer (no memory, unrecognised reply)
*****************************************************************************/
void xfuse_devredir_cb_lookup_entry(void *vp, tui32 IoStatus, struct xrdp_inode *xinode)
{
XFUSE_INFO *fip = (XFUSE_INFO *) vp;
if (IoStatus == NT_STATUS_SUCCESS)
{
if (xinode != NULL)
{
make_fuse_entry_reply(fip->req, xinode);
}
else
{
fuse_reply_err(fip->req, EIO);
}
}
else
{
switch (IoStatus)
{
case NT_STATUS_NO_SUCH_FILE:
fuse_reply_err(fip->req, ENOENT);
break;
default:
log_info("Error code %08x - fallthrough", (int) IoStatus);
fuse_reply_err(fip->req, EIO);
break;
}
}
}
void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId,
tui32 FileId) tui32 FileId)
{ {
@ -1791,6 +1902,8 @@ void xfuse_devredir_cb_write_file(void *vp,
fuse_reply_write(fip->req, length); fuse_reply_write(fip->req, length);
/* update file size */ /* update file size */
/* ??? This needs to take into account the offset sent with
* the original request */
if ((xinode = g_xrdp_fs.inode_table[fip->inode]) != NULL) if ((xinode = g_xrdp_fs.inode_table[fip->inode]) != NULL)
{ {
xinode->size += length; xinode->size += length;
@ -1959,12 +2072,20 @@ void xfuse_devredir_cb_file_close(void *vp)
/** /**
* Look up a directory entry by name and get its attributes * Look up a directory entry by name and get its attributes
* *
* If the file can't be found, and it resides on a remote share, devredir
* is asked to look it up. This will result in the following
* callbacks:-
* - xfuse_devredir_add_file_or_dir() (only if the file is found)
* - xfuse_devredir_cb_lookup_entry() to return status to the caller
*****************************************************************************/ *****************************************************************************/
static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{ {
XRDP_INODE *xinode; XFUSE_INFO *fip;
struct fuse_entry_param e; XRDP_INODE *xinode;
char full_path[4096];
tui32 device_id;
const char *cptr;
log_debug("looking for parent=%ld name=%s", parent, name); log_debug("looking for parent=%ld name=%s", parent, name);
xfuse_dump_fs(); xfuse_dump_fs();
@ -1976,32 +2097,60 @@ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
return; return;
} }
xinode = xfuse_get_inode_from_pinode_name(parent, name); if (parent == 1 ||
if (xinode == NULL) g_xrdp_fs.inode_table[parent]->is_loc_resource)
{ {
log_debug("did not find entry for parent=%ld name=%s", parent, name); /* File cannot be remote - we either know about it or we don't */
fuse_reply_err(req, ENOENT); xinode = xfuse_get_inode_from_pinode_name(parent, name);
return; if (xinode != NULL)
{
log_debug("found entry for parent=%ld name=%s uid=%d gid=%d",
parent, name, xinode->uid, xinode->gid);
make_fuse_entry_reply(req, xinode);
}
else
{
fuse_reply_err(req, ENOENT);
}
} }
else
{
/*
* specified file resides on redirected share.
*
* We always look these up, as fuse caches stuff for us anyway, and
* it's pointless caching it twice
*/
fip = g_new0(XFUSE_INFO, 1);
if (fip == NULL)
{
log_error("system out of memory");
fuse_reply_err(req, ENOMEM);
}
else
{
device_id = xfuse_get_device_id_for_inode(parent, full_path);
memset(&e, 0, sizeof(e)); /* we want path minus 'root node of the share' */
e.ino = xinode->inode; if ((cptr = strchr(full_path, '/')) == NULL)
e.attr_timeout = XFUSE_ATTR_TIMEOUT; {
e.entry_timeout = XFUSE_ENTRY_TIMEOUT; cptr = "\\";
e.attr.st_ino = xinode->inode; }
e.attr.st_mode = xinode->mode;
e.attr.st_nlink = xinode->nlink;
e.attr.st_uid = xinode->uid;
e.attr.st_gid = xinode->gid;
e.attr.st_size = xinode->size;
e.attr.st_atime = xinode->atime;
e.attr.st_mtime = xinode->mtime;
e.attr.st_ctime = xinode->ctime;
e.generation = 1;
fuse_reply_entry(req, &e); fip->req = req;
log_debug("found entry for parent=%ld name=%s uid=%d gid=%d", fip->inode = parent;
parent, name, xinode->uid, xinode->gid); fip->device_id = device_id;
log_debug("Looking up %s in %s on %d", name, cptr, device_id);
if (dev_redir_lookup_entry((void *) fip, device_id,
cptr, name))
{
log_error("failed to send dev_redir_lookup_entry() cmd");
fuse_reply_err(req, EREMOTEIO);
free(fip);
}
}
}
} }
/** /**
@ -3145,4 +3294,26 @@ xfuse_delete_stale_entries(fuse_ino_t pinode)
} }
} }
static void make_fuse_entry_reply(fuse_req_t req, const XRDP_INODE *xinode)
{
struct fuse_entry_param e;
memset(&e, 0, sizeof(e));
e.ino = xinode->inode;
e.attr_timeout = XFUSE_ATTR_TIMEOUT;
e.entry_timeout = XFUSE_ENTRY_TIMEOUT;
e.attr.st_ino = xinode->inode;
e.attr.st_mode = xinode->mode;
e.attr.st_nlink = xinode->nlink;
e.attr.st_uid = xinode->uid;
e.attr.st_gid = xinode->gid;
e.attr.st_size = xinode->size;
e.attr.st_atime = xinode->atime;
e.attr.st_mtime = xinode->mtime;
e.attr.st_ctime = xinode->ctime;
e.generation = 1;
fuse_reply_entry(req, &e);
}
#endif /* end else #ifndef XRDP_FUSE */ #endif /* end else #ifndef XRDP_FUSE */

View File

@ -19,29 +19,27 @@
#ifndef _CHANSRV_FUSE_H #ifndef _CHANSRV_FUSE_H
#define _CHANSRV_FUSE_H #define _CHANSRV_FUSE_H
/* a file or dir entry in the xrdp file system */ #include "arch.h"
struct xrdp_inode
/*
* a file or dir entry in the xrdp file system (opaque type externally) */
struct xrdp_inode;
/* Used to pass file info in to xfuse_devredir_add_file_or_dir()
*
* The string storage the name field points to is only valid
* for the duration of the xfuse_devredir_add_file_or_dir()
* call
*/
struct file_attr
{ {
tui32 parent_inode; /* Parent serial number. */ const char *name; /* Name of file or directory */
tui32 inode; /* File serial number. */
tui32 mode; /* File mode. */ tui32 mode; /* File mode. */
tui32 nlink; /* symbolic link count. */
tui32 nentries; /* number of entries in a dir */
tui32 nopen; /* number of simultaneous opens */
tui32 uid; /* User ID of the file's owner. */
tui32 gid; /* Group ID of the file's group. */
size_t size; /* Size of file, in bytes. */ size_t size; /* Size of file, in bytes. */
time_t atime; /* Time of last access. */ time_t atime; /* Time of last access. */
time_t mtime; /* Time of last modification. */ time_t mtime; /* Time of last modification. */
time_t ctime; /* Time of last status change. */ time_t ctime; /* Time of last status change. */
char name[1024]; /* Dir or filename */
tui32 device_id; /* for file system redirection */
int lindex; /* used in clipboard operations */
int is_loc_resource; /* this is not a redirected resource */
int close_in_progress; /* close cmd sent to client */
int stale; /* mark file as stale, ok to remove */
}; };
typedef struct xrdp_inode XRDP_INODE; // LK_TODO use this instead of using struct xrdp_inode
int xfuse_init(void); int xfuse_init(void);
int xfuse_deinit(void); int xfuse_deinit(void);
@ -55,8 +53,12 @@ int xfuse_file_contents_size(int stream_id, int file_size);
int xfuse_add_clip_dir_item(const char *filename, int flags, int size, int lindex); int xfuse_add_clip_dir_item(const char *filename, int flags, int size, int lindex);
/* functions that are invoked from devredir */ /* functions that are invoked from devredir */
int xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode); struct xrdp_inode *xfuse_devredir_add_file_or_dir(
void *vp,
const struct file_attr *file_info);
void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus); void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus);
void xfuse_devredir_cb_lookup_entry(void *vp, tui32 IoStatus,
struct xrdp_inode *xinode);
void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, tui32 FileId); void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, tui32 FileId);
void xfuse_devredir_cb_write_file( void xfuse_devredir_cb_write_file(
void *vp, void *vp,

View File

@ -91,6 +91,13 @@
} \ } \
} }
/*
* Size of structs supported by IRP_MJ_QUERY_INFORMATION without
* trailing RESERVED fields (MS-RDPEFS 2.2.3.3.8)
*/
#define FILE_BASIC_INFORMATION_SIZE 36
#define FILE_STD_INFORMATION_SIZE 22
/* globals */ /* globals */
extern int g_rdpdr_chan_id; /* in chansrv.c */ extern int g_rdpdr_chan_id; /* in chansrv.c */
int g_is_printer_redir_supported = 0; int g_is_printer_redir_supported = 0;
@ -113,6 +120,19 @@ static void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus);
static void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus); static void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus);
static void devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus); static void devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus);
static void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus); static void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus);
static void devredir_proc_cid_lookup_basic_entry(IRP *irp, tui32 IoStatus);
static void devredir_proc_cid_lookup_basic_entry_resp(
IRP *irp,
struct stream *s_in,
tui32 IoStatus);
static void devredir_proc_cid_lookup_std_entry_resp(
IRP *irp,
struct stream *s_in,
tui32 DeviceId,
tui32 CompletionId,
tui32 IoStatus);
/* Other local functions */
static void lookup_std_entry(IRP *irp);
/*****************************************************************************/ /*****************************************************************************/
int int
@ -873,6 +893,27 @@ dev_redir_proc_device_iocompletion(struct stream *s)
devredir_proc_cid_rename_file_resp(irp, IoStatus); devredir_proc_cid_rename_file_resp(irp, IoStatus);
break; break;
case CID_LOOKUP_BASIC_ENTRY:
log_debug("got CID_LOOKUP_BASIC_ENTRY");
xstream_rd_u32_le(s, irp->FileId);
/* Issue a call to get the FileBasicInformation */
devredir_proc_cid_lookup_basic_entry(irp, IoStatus);
break;
case CID_LOOKUP_STD_ENTRY:
log_debug("got CID_LOOKUP_STD_ENTRY");
/* Parse the FileBasicInformation and request the
* FileStandardInformation */
devredir_proc_cid_lookup_basic_entry_resp(irp, s, IoStatus);
break;
case CID_LOOKUP_ENTRY_RESP:
/* Parse the FileStandardInformation and respond to caller */
log_debug("got CID_LOOKUP_ENTRY_RESP");
devredir_proc_cid_lookup_std_entry_resp(irp, s, DeviceId,
CompletionId, IoStatus);
break;
default: default:
log_error("got unknown CompletionID: DeviceId=0x%x " log_error("got unknown CompletionID: DeviceId=0x%x "
"CompletionId=0x%x IoStatus=0x%x", "CompletionId=0x%x IoStatus=0x%x",
@ -899,7 +940,6 @@ dev_redir_proc_query_dir_response(IRP *irp,
tui32 IoStatus) tui32 IoStatus)
{ {
FUSE_DATA *fuse_data = NULL; FUSE_DATA *fuse_data = NULL;
XRDP_INODE *xinode;
tui32 Length; tui32 Length;
tui64 CreationTime; tui64 CreationTime;
@ -909,6 +949,7 @@ dev_redir_proc_query_dir_response(IRP *irp,
tui32 FileAttributes; tui32 FileAttributes;
tui32 FileNameLength; tui32 FileNameLength;
tui32 status; tui32 status;
struct file_attr fattr;
char filename[256]; char filename[256];
unsigned int i = 0; unsigned int i = 0;
@ -974,25 +1015,16 @@ dev_redir_proc_query_dir_response(IRP *irp,
//log_debug("FileNameLength: %d", FileNameLength); //log_debug("FileNameLength: %d", FileNameLength);
log_debug("FileName: %s", filename); log_debug("FileName: %s", filename);
xinode = g_new0(struct xrdp_inode, 1); fattr.name = filename;
if (xinode == NULL) fattr.mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes);
{ fattr.size = (size_t) EndOfFile;
log_error("system out of memory"); fattr.atime = WINDOWS_TO_LINUX_TIME(LastAccessTime);
fuse_data = devredir_fuse_data_peek(irp); fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime);
xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, NULL); fattr.ctime = WINDOWS_TO_LINUX_TIME(CreationTime);
return;
}
strcpy(xinode->name, filename);
xinode->size = (size_t) EndOfFile;
xinode->mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes);
xinode->atime = WINDOWS_TO_LINUX_TIME(LastAccessTime);
xinode->mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime);
xinode->ctime = WINDOWS_TO_LINUX_TIME(CreationTime);
/* add this entry to xrdp file system */ /* add this entry to xrdp file system */
fuse_data = devredir_fuse_data_peek(irp); fuse_data = devredir_fuse_data_peek(irp);
xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, xinode); (void)xfuse_devredir_add_file_or_dir(fuse_data->data_ptr, &fattr);
} }
dev_redir_send_drive_dir_request(irp, DeviceId, 0, NULL); dev_redir_send_drive_dir_request(irp, DeviceId, 0, NULL);
@ -1055,6 +1087,78 @@ dev_redir_get_dir_listing(void *fusep, tui32 device_id, const char *path)
return rval; return rval;
} }
/**
* FUSE calls this function whenever it wants us to lookup a file or directory
*
* @param fusep opaque data struct that we just pass back to FUSE when done
* @param device_id device_id of the redirected share
* @param path the name of the directory containing the file
* @param file the filename
*
* @return 0 on success, -1 on failure
*****************************************************************************/
int
dev_redir_lookup_entry(void *fusep, tui32 device_id, const char *dirpath,
const char *entry)
{
tui32 DesiredAccess;
tui32 CreateOptions;
tui32 CreateDisposition;
int rval = -1;
IRP *irp;
size_t pathlen;
log_debug("fusep=%p", fusep);
/* Check the qualified name of the file fits in the IRP */
pathlen = strlen(dirpath) + strlen(entry);
if (!dev_redir_string_ends_with(dirpath,'/'))
{
++pathlen;
}
if (pathlen < sizeof(irp->pathname))
{
if ((irp = devredir_irp_new()) != NULL)
{
strcpy(irp->pathname, dirpath);
if (!dev_redir_string_ends_with(dirpath, '/'))
{
strcat(irp->pathname, "/");
}
strcat(irp->pathname, entry);
/* convert / to windows compatible \ */
devredir_cvt_slash(irp->pathname);
/*
* Allocate an IRP to open the file, read the basic attributes,
* read the standard attributes, and then close the file
*/
irp->CompletionId = g_completion_id++;
irp->completion_type = CID_LOOKUP_BASIC_ENTRY;
irp->DeviceId = device_id;
devredir_fuse_data_enqueue(irp, fusep);
DesiredAccess = DA_FILE_READ_ATTRIBUTES | DA_SYNCHRONIZE;
CreateOptions = 0x020; /* Same as rmdir or file */
CreateDisposition = CD_FILE_OPEN;
log_debug("lookup for device_id=%d path=%s",
device_id, irp->pathname);
rval = dev_redir_send_drive_create_request(device_id,
irp->pathname,
DesiredAccess, CreateOptions,
CreateDisposition,
irp->CompletionId);
}
}
return rval;
}
int int
dev_redir_file_open(void *fusep, tui32 device_id, const char *path, dev_redir_file_open(void *fusep, tui32 device_id, const char *path,
int mode, int type, const char *gen_buf) int mode, int type, const char *gen_buf)
@ -1073,7 +1177,7 @@ dev_redir_file_open(void *fusep, tui32 device_id, const char *path,
if (type & OP_RENAME_FILE) if (type & OP_RENAME_FILE)
{ {
irp->completion_type = CID_RENAME_FILE; irp->completion_type = CID_RENAME_FILE;
strncpy(irp->gen_buf, gen_buf, 1023); strncpy(irp->gen.buf, gen_buf, 1023);
} }
else else
{ {
@ -1641,7 +1745,7 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus)
} }
/* Path in unicode needs this much space */ /* Path in unicode needs this much space */
flen = ((g_mbstowcs(NULL, irp->gen_buf, 0) * sizeof(twchar)) / 2) + 2; flen = ((g_mbstowcs(NULL, irp->gen.buf, 0) * sizeof(twchar)) / 2) + 2;
sblen = 6 + flen; sblen = 6 + flen;
xstream_new(s, 1024 + flen); xstream_new(s, 1024 + flen);
@ -1659,7 +1763,7 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus)
xstream_wr_u32_le(s, flen); /* FileNameLength */ xstream_wr_u32_le(s, flen); /* FileNameLength */
/* filename in unicode */ /* filename in unicode */
devredir_cvt_to_unicode(s->p, irp->gen_buf); /* UNICODE_TODO */ devredir_cvt_to_unicode(s->p, irp->gen.buf); /* UNICODE_TODO */
xstream_seek(s, flen); xstream_seek(s, flen);
/* send to client */ /* send to client */
@ -1698,3 +1802,192 @@ devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus)
irp->CompletionId, irp->CompletionId,
IRP_MJ_CLOSE, 0, 32); IRP_MJ_CLOSE, 0, 32);
} }
static void
devredir_proc_cid_lookup_basic_entry(IRP *irp, tui32 IoStatus)
{
struct stream *s;
int bytes;
if (IoStatus != NT_STATUS_SUCCESS)
{
log_debug("lookup returned with IoStatus=0x%08x", IoStatus);
FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp);
if (fuse_data)
{
xfuse_devredir_cb_lookup_entry(fuse_data->data_ptr, IoStatus, NULL);
free(fuse_data);
}
devredir_irp_delete(irp);
}
else
{
xstream_new(s, 1024);
irp->completion_type = CID_LOOKUP_STD_ENTRY;
devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId,
irp->CompletionId,
IRP_MJ_QUERY_INFORMATION, 0);
xstream_wr_u32_le(s, FileBasicInformation);
xstream_wr_u32_le(s, FILE_BASIC_INFORMATION_SIZE);
/* buffer length */
xstream_seek(s, 24); /* padding */
xstream_seek(s, FILE_BASIC_INFORMATION_SIZE);
/* buffer */
/* send to client */
bytes = xstream_len(s);
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
xstream_free(s);
}
}
static void
devredir_proc_cid_lookup_basic_entry_resp(IRP *irp,
struct stream *s_in,
tui32 IoStatus)
{
tui32 Length = 0;
tui64 CreationTime;
tui64 LastAccessTime;
tui64 LastWriteTime;
tui32 FileAttributes;
log_debug("basic_lookup returned with IoStatus=0x%08x", IoStatus);
/* Data as we expect? */
if (IoStatus == NT_STATUS_SUCCESS)
{
xstream_rd_u32_le(s_in, Length);
if (Length != FILE_BASIC_INFORMATION_SIZE)
{
log_error("Expected FILE_BASIC_OPEN_INFORMATION data but len=%d",
Length);
}
}
if (IoStatus != NT_STATUS_SUCCESS ||
Length != FILE_BASIC_INFORMATION_SIZE)
{
/* Return a lookup fail to the FUSE caller */
FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp);
if (fuse_data)
{
xfuse_devredir_cb_lookup_entry(fuse_data->data_ptr, IoStatus, NULL);
free(fuse_data);
}
devredir_irp_delete(irp);
}
else
{
log_debug("processing FILE_BASIC_INFORMATION");
xstream_rd_u64_le(s_in, CreationTime);
xstream_rd_u64_le(s_in, LastAccessTime);
xstream_rd_u64_le(s_in, LastWriteTime);
xstream_seek(s_in, 8); /* ChangeTime */
xstream_rd_u32_le(s_in, FileAttributes);
//log_debug("CreationTime: 0x%llx",
// (unsigned long long)CreationTime);
//log_debug("LastAccessTime: 0x%llx",
// (unsigned long long)LastAccessTime);
//log_debug("LastWriteTime: 0x%llx",
// (unsigned long long)LastWriteTime);
//log_debug("FileAttributes: 0x%x", (unsigned int)FileAttributes);
/* Save the basic attributes in the IRP */
irp->gen.fattr.mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes);
irp->gen.fattr.atime = WINDOWS_TO_LINUX_TIME(LastAccessTime);
irp->gen.fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime);
irp->gen.fattr.ctime = WINDOWS_TO_LINUX_TIME(CreationTime);
/* Re-use the IRP to lookup the FileStandardInformation */
lookup_std_entry(irp);
}
}
static void
lookup_std_entry(IRP *irp)
{
struct stream *s;
int bytes;
xstream_new(s, 1024);
irp->completion_type = CID_LOOKUP_ENTRY_RESP;
devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId,
irp->CompletionId,
IRP_MJ_QUERY_INFORMATION, 0);
xstream_wr_u32_le(s, FileStandardInformation);
xstream_wr_u32_le(s, FILE_STD_INFORMATION_SIZE);
/* buffer length */
xstream_seek(s, 24); /* padding */
xstream_seek(s, FILE_STD_INFORMATION_SIZE);
/* buffer */
/* send to client */
bytes = xstream_len(s);
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
xstream_free(s);
}
static void
devredir_proc_cid_lookup_std_entry_resp(IRP *irp,
struct stream *s_in,
tui32 DeviceId,
tui32 CompletionId,
tui32 IoStatus)
{
FUSE_DATA *fuse_data;
tui32 Length;
tui64 EndOfFile;
struct xrdp_inode *xinode = NULL;
fuse_data = devredir_fuse_data_dequeue(irp);
if (!fuse_data)
{
log_error("fuse_data unexpectedly NULL!");
}
else
{
if (IoStatus == NT_STATUS_SUCCESS)
{
/* Data as we expect? */
xstream_rd_u32_le(s_in, Length);
if (Length != FILE_STD_INFORMATION_SIZE)
{
log_error("Expected FILE_STD_OPEN_INFORMATION data but len=%d",
Length);
}
else
{
log_debug("processing FILE_STD_INFORMATION");
xstream_seek(s_in, 8); /* AllocationSize */
xstream_rd_u64_le(s_in, EndOfFile);
//log_debug("EndOfFile: %lld",
// (unsigned long long)EndOfFile);
/* Finish the attribute block off and add the file */
irp->gen.fattr.size = EndOfFile;
irp->gen.fattr.name = strrchr(irp->pathname,'\\') + 1;
xinode = xfuse_devredir_add_file_or_dir(fuse_data->data_ptr,
&irp->gen.fattr);
}
}
xfuse_devredir_cb_lookup_entry(fuse_data->data_ptr, IoStatus, xinode);
free(fuse_data);
}
/* Close the file handle */
irp->completion_type = CID_CLOSE;
dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
PAKID_CORE_DEVICE_IOREQUEST,
irp->DeviceId,
irp->FileId,
irp->CompletionId,
IRP_MJ_CLOSE, 0, 32);
}

View File

@ -92,6 +92,9 @@ void devredir_insert_RDPDR_header(struct stream *s, tui16 Component,
/* called from FUSE module */ /* called from FUSE module */
int dev_redir_get_dir_listing(void *fusep, tui32 device_id, const char *path); int dev_redir_get_dir_listing(void *fusep, tui32 device_id, const char *path);
int dev_redir_lookup_entry(void *fusep, tui32 device_id, const char *dirpath,
const char *entry);
int dev_redir_file_open(void *fusep, tui32 device_id, const char *path, int dev_redir_file_open(void *fusep, tui32 device_id, const char *path,
int mode, int type, const char *gen_buf); int mode, int type, const char *gen_buf);
@ -255,11 +258,12 @@ enum CREATE_OPTIONS
#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002 #define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002
/* /*
* NTSTATUS codes (used by IoStatus) * NTSTATUS codes (used by IoStatus) - see section 2.3 of MS-ERREF
*/ */
#define NT_STATUS_SUCCESS 0x00000000 #define NT_STATUS_SUCCESS 0x00000000
#define NT_STATUS_UNSUCCESSFUL 0xC0000001 #define NT_STATUS_UNSUCCESSFUL 0xC0000001
#define NT_STATUS_NO_SUCH_FILE 0xC000000F
#define NT_STATUS_ACCESS_DENIED 0xC0000022 #define NT_STATUS_ACCESS_DENIED 0xC0000022
#define NT_STATUS_OBJECT_NAME_INVALID 0xC0000033 #define NT_STATUS_OBJECT_NAME_INVALID 0xC0000033
#define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 #define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034
@ -287,12 +291,16 @@ enum COMPLETION_ID
CID_RMDIR_OR_FILE, CID_RMDIR_OR_FILE,
CID_RMDIR_OR_FILE_RESP, CID_RMDIR_OR_FILE_RESP,
CID_RENAME_FILE, CID_RENAME_FILE,
CID_RENAME_FILE_RESP CID_RENAME_FILE_RESP,
CID_LOOKUP_BASIC_ENTRY,
CID_LOOKUP_STD_ENTRY,
CID_LOOKUP_ENTRY_RESP
}; };
enum FS_INFORMATION_CLASS enum FS_INFORMATION_CLASS
{ {
FileBasicInformation = 0x00000004, /* set atime, mtime, ctime etc */ FileBasicInformation = 0x00000004, /* set atime, mtime, ctime etc */
FileStandardInformation = 0x00000005, /* Alloc size, EOF #links, etc */
FileEndOfFileInformation = 0x00000014, /* set EOF info */ FileEndOfFileInformation = 0x00000014, /* set EOF info */
FileDispositionInformation = 0x0000000D, /* mark a file for deletion */ FileDispositionInformation = 0x0000000D, /* mark a file for deletion */
FileRenameInformation = 0x0000000A, /* rename a file */ FileRenameInformation = 0x0000000A, /* rename a file */

View File

@ -24,6 +24,8 @@
#ifndef __IRP_H #ifndef __IRP_H
#define __IRP_H #define __IRP_H
#include "chansrv_fuse.h"
typedef struct fuse_data FUSE_DATA; typedef struct fuse_data FUSE_DATA;
struct fuse_data struct fuse_data
{ {
@ -42,7 +44,11 @@ struct irp
tui32 FileId; /* RDP client provided unique number */ tui32 FileId; /* RDP client provided unique number */
char completion_type; /* describes I/O type */ char completion_type; /* describes I/O type */
char pathname[256]; /* absolute pathname */ char pathname[256]; /* absolute pathname */
char gen_buf[1024]; /* for general use */ union
{
char buf[1024]; /* General character data */
struct file_attr fattr; /* Used to assemble file attributes */
} gen; /* for general use */
int type; int type;
FUSE_DATA *fd_head; /* point to first FUSE opaque object */ FUSE_DATA *fd_head; /* point to first FUSE opaque object */
FUSE_DATA *fd_tail; /* point to last FUSE opaque object */ FUSE_DATA *fd_tail; /* point to last FUSE opaque object */