From d154df5059cdfc2ac1eb34025e5bf8c07b962f1a Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 21 Mar 2019 16:48:01 +0000 Subject: [PATCH] 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 --- sesman/chansrv/chansrv_fuse.c | 305 ++++++++++++++++++++++++------- sesman/chansrv/chansrv_fuse.h | 36 ++-- sesman/chansrv/devredir.c | 333 ++++++++++++++++++++++++++++++++-- sesman/chansrv/devredir.h | 12 +- sesman/chansrv/irp.h | 8 +- 5 files changed, 587 insertions(+), 107 deletions(-) diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index 6256ccf9..3e8bcab5 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -24,7 +24,6 @@ * this is in func xfuse_check_wait_objs() * o if fuse mount point is already mounted, I get segfault * 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 * so something is holding it up * 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 touch does not 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_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) {} -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_rmdir_or_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 /* Uncomment for detail of added entries */ -#define XFUSE_DUMP_ADDED_ENTRIES +/* #define XFUSE_DUMP_ADDED_ENTRIES */ #define log_error(_params...) \ { \ @@ -167,6 +168,30 @@ void xfuse_devredir_cb_file_close(void *vp) {} #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 */ struct xrdp_fs { @@ -356,6 +381,7 @@ static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, /* misc calls */ static void xfuse_mark_as_stale(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 @@ -1492,64 +1518,111 @@ static void xfuse_update_xrdpfs_size(void) /** * 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; - XRDP_INODE *xip = NULL; + XRDP_INODE *xinode = NULL; - if ((fip == NULL) || (xinode == NULL)) + if (fip == NULL) { - log_error("fip or xinode are NULL"); - return -1; + log_error("fip is NULL"); } - - if (!xfuse_is_inode_valid(fip->inode)) + else if (!xfuse_is_inode_valid(fip->inode)) { log_error("inode %ld is not valid", fip->inode); - g_free(xinode); - return -1; } - - log_debug("parent_inode=%ld name=%s", fip->inode, xinode->name); - - /* if filename is . or .. don't add it */ - if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0)) + else if ((strcmp(fattr->name, ".") == 0) || + (strcmp(fattr->name, "..") == 0)) { - g_free(xinode); - return -1; + ; /* filename is . or .. - don't add it */ } - - xfuse_dump_fs(); - - if ((xip = xfuse_get_inode_from_pinode_name(fip->inode, xinode->name)) != NULL) + else { - log_debug("inode=%ld name=%s already exists in xrdp_fs; not adding it", - fip->inode, xinode->name); - g_free(xinode); - xip->stale = 0; - return -1; - } + log_debug("parent_inode=%ld name=%s", fip->inode, fattr->name); + xfuse_dump_fs(); - xinode->parent_inode = fip->inode; - xinode->inode = g_xrdp_fs.next_node++; - xinode->uid = getuid(); - xinode->gid = getgid(); - xinode->device_id = fip->device_id; + /* Does the file already exist ? */ + xinode = xfuse_get_inode_from_pinode_name(fip->inode, fattr->name); + if (xinode != NULL) + { + /* 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 */ - g_xrdp_fs.inode_table[xinode->inode] = xinode; - g_xrdp_fs.inode_table[fip->inode]->nentries++; - xfuse_update_xrdpfs_size(); + xinode->parent_inode = fip->inode; + xinode->inode = g_xrdp_fs.next_node++; + xinode->uid = getuid(); + 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 - xfuse_dump_xrdp_inode(xinode); + xfuse_dump_xrdp_inode(xinode); #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, tui32 FileId) { @@ -1791,6 +1902,8 @@ void xfuse_devredir_cb_write_file(void *vp, fuse_reply_write(fip->req, length); /* 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) { 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 * + * 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) { - XRDP_INODE *xinode; - struct fuse_entry_param e; + XFUSE_INFO *fip; + XRDP_INODE *xinode; + char full_path[4096]; + tui32 device_id; + const char *cptr; log_debug("looking for parent=%ld name=%s", parent, name); xfuse_dump_fs(); @@ -1976,32 +2097,60 @@ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) return; } - xinode = xfuse_get_inode_from_pinode_name(parent, name); - if (xinode == NULL) + if (parent == 1 || + g_xrdp_fs.inode_table[parent]->is_loc_resource) { - log_debug("did not find entry for parent=%ld name=%s", parent, name); - fuse_reply_err(req, ENOENT); - return; + /* File cannot be remote - we either know about it or we don't */ + xinode = xfuse_get_inode_from_pinode_name(parent, name); + 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)); - 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; + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) + { + cptr = "\\"; + } - fuse_reply_entry(req, &e); - log_debug("found entry for parent=%ld name=%s uid=%d gid=%d", - parent, name, xinode->uid, xinode->gid); + fip->req = req; + fip->inode = parent; + 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 */ diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index 945a4bef..89fd7095 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -19,29 +19,27 @@ #ifndef _CHANSRV_FUSE_H #define _CHANSRV_FUSE_H -/* a file or dir entry in the xrdp file system */ -struct xrdp_inode +#include "arch.h" + +/* + * 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. */ - tui32 inode; /* File serial number. */ + const char *name; /* Name of file or directory */ 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 int xfuse_init(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); /* 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_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_write_file( void *vp, diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index e2a29394..6e1130b7 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -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 */ extern int g_rdpdr_chan_id; /* in chansrv.c */ 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_rename_file(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 @@ -873,6 +893,27 @@ dev_redir_proc_device_iocompletion(struct stream *s) devredir_proc_cid_rename_file_resp(irp, IoStatus); 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: log_error("got unknown CompletionID: DeviceId=0x%x " "CompletionId=0x%x IoStatus=0x%x", @@ -899,7 +940,6 @@ dev_redir_proc_query_dir_response(IRP *irp, tui32 IoStatus) { FUSE_DATA *fuse_data = NULL; - XRDP_INODE *xinode; tui32 Length; tui64 CreationTime; @@ -909,6 +949,7 @@ dev_redir_proc_query_dir_response(IRP *irp, tui32 FileAttributes; tui32 FileNameLength; tui32 status; + struct file_attr fattr; char filename[256]; unsigned int i = 0; @@ -974,25 +1015,16 @@ dev_redir_proc_query_dir_response(IRP *irp, //log_debug("FileNameLength: %d", FileNameLength); log_debug("FileName: %s", filename); - xinode = g_new0(struct xrdp_inode, 1); - if (xinode == NULL) - { - log_error("system out of memory"); - fuse_data = devredir_fuse_data_peek(irp); - xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, NULL); - 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); + fattr.name = filename; + fattr.mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes); + fattr.size = (size_t) EndOfFile; + fattr.atime = WINDOWS_TO_LINUX_TIME(LastAccessTime); + fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); + fattr.ctime = WINDOWS_TO_LINUX_TIME(CreationTime); /* add this entry to xrdp file system */ 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); @@ -1055,6 +1087,78 @@ dev_redir_get_dir_listing(void *fusep, tui32 device_id, const char *path) 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 dev_redir_file_open(void *fusep, tui32 device_id, const char *path, 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) { irp->completion_type = CID_RENAME_FILE; - strncpy(irp->gen_buf, gen_buf, 1023); + strncpy(irp->gen.buf, gen_buf, 1023); } else { @@ -1641,7 +1745,7 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) } /* 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; 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 */ /* 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); /* send to client */ @@ -1698,3 +1802,192 @@ devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) irp->CompletionId, 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); +} diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 00ed5dda..8042347c 100644 --- a/sesman/chansrv/devredir.h +++ b/sesman/chansrv/devredir.h @@ -92,6 +92,9 @@ void devredir_insert_RDPDR_header(struct stream *s, tui16 Component, /* called from FUSE module */ 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 mode, int type, const char *gen_buf); @@ -255,11 +258,12 @@ enum CREATE_OPTIONS #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_UNSUCCESSFUL 0xC0000001 +#define NT_STATUS_NO_SUCH_FILE 0xC000000F #define NT_STATUS_ACCESS_DENIED 0xC0000022 #define NT_STATUS_OBJECT_NAME_INVALID 0xC0000033 #define NT_STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 @@ -287,12 +291,16 @@ enum COMPLETION_ID CID_RMDIR_OR_FILE, CID_RMDIR_OR_FILE_RESP, 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 { FileBasicInformation = 0x00000004, /* set atime, mtime, ctime etc */ + FileStandardInformation = 0x00000005, /* Alloc size, EOF #links, etc */ FileEndOfFileInformation = 0x00000014, /* set EOF info */ FileDispositionInformation = 0x0000000D, /* mark a file for deletion */ FileRenameInformation = 0x0000000A, /* rename a file */ diff --git a/sesman/chansrv/irp.h b/sesman/chansrv/irp.h index 39840a16..8fd3b91e 100644 --- a/sesman/chansrv/irp.h +++ b/sesman/chansrv/irp.h @@ -24,6 +24,8 @@ #ifndef __IRP_H #define __IRP_H +#include "chansrv_fuse.h" + typedef struct fuse_data FUSE_DATA; struct fuse_data { @@ -42,7 +44,11 @@ struct irp tui32 FileId; /* RDP client provided unique number */ char completion_type; /* describes I/O type */ 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; FUSE_DATA *fd_head; /* point to first FUSE opaque object */ FUSE_DATA *fd_tail; /* point to last FUSE opaque object */