From 4d8f2b5a318f495fd3f1eaaddc529e13bc0626f5 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 14 Nov 2019 14:43:57 +0000 Subject: [PATCH 1/2] Significant remote file system improvements - Reimplemented inode store in separate module chansrv_xfs.[hc] - Allowed atimes and mtimes to be written to Windows side - Mapped file user write bit to (inverted) Windows FILE_ATTRIBUTE_READONLY bit - Mapped file user execute bit to Windows FILE_ATTRIBUTE_SYSTEM bit - Implemented improved security for remotely mounted drives - Implemented USB device removal, allowing hot-plug/remove of memory sticks - Fixed pagefile.sys breaking Ubuntu file browser - Fixed write offset bug - Allowed renaming of open files - Improved reported error codes - Fixed various memory leaks - Addressed valgrind errors related to struct fuse_file_info pointers. --- docs/man/sesman.ini.5.in | 8 + sesman/chansrv/Makefile.am | 2 + sesman/chansrv/chansrv.c | 12 +- sesman/chansrv/chansrv_fuse.c | 3522 +++++++++++++-------------------- sesman/chansrv/chansrv_fuse.h | 93 +- sesman/chansrv/chansrv_xfs.c | 909 +++++++++ sesman/chansrv/chansrv_xfs.h | 321 +++ sesman/chansrv/devredir.c | 1892 ++++++++++-------- sesman/chansrv/devredir.h | 352 +--- sesman/chansrv/irp.c | 67 +- sesman/chansrv/irp.h | 71 +- sesman/chansrv/ms-erref.h | 44 + sesman/chansrv/ms-fscc.h | 64 + sesman/chansrv/ms-rdpefs.h | 125 ++ sesman/chansrv/ms-smb2.h | 79 + sesman/sesman.ini.in | 3 + 16 files changed, 4261 insertions(+), 3303 deletions(-) create mode 100644 sesman/chansrv/chansrv_xfs.c create mode 100644 sesman/chansrv/chansrv_xfs.h create mode 100644 sesman/chansrv/ms-erref.h create mode 100644 sesman/chansrv/ms-fscc.h create mode 100644 sesman/chansrv/ms-rdpefs.h create mode 100644 sesman/chansrv/ms-smb2.h diff --git a/docs/man/sesman.ini.5.in b/docs/man/sesman.ini.5.in index 29bed60c..8fca3a9e 100644 --- a/docs/man/sesman.ini.5.in +++ b/docs/man/sesman.ini.5.in @@ -225,6 +225,14 @@ Following parameters can be used in the \fB[Chansrv]\fR section. Directory for drive redirection, relative to the user home directory. Created if it doesn't exist. If not specified, defaults to \fIxrdp_client\fR. +.TP +\fBFileUmask\fR=\fImode\fR +Additional umask to apply to files in the \fBFuseMountName\fR directory. +The default value of 077 prevents other users on the system from reading +files on your redirected drives. This may not be approprate for all +environents, and so you can change this value to allow other users to +access your remote files if required. + .SH "SESSIONS VARIABLES" All entries in the \fB[SessionVariables]\fR section are set as environment variables in the user's session. diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index 1b7df07e..cf1d1f96 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -53,6 +53,8 @@ xrdp_chansrv_SOURCES = \ chansrv_common.h \ chansrv_fuse.c \ chansrv_fuse.h \ + chansrv_xfs.c \ + chansrv_xfs.h \ clipboard.c \ clipboard.h \ clipboard_common.h \ diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 4dde8bef..6b1b9326 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -407,7 +407,7 @@ process_message_channel_setup(struct stream *s) if (g_rdpdr_index >= 0) { - dev_redir_init(); + devredir_init(); xfuse_init(); } @@ -458,7 +458,7 @@ process_message_channel_data(struct stream *s) } else if (chan_id == g_rdpdr_chan_id) { - rv = dev_redir_data_in(s, chan_id, chan_flags, length, total_length); + rv = devredir_data_in(s, chan_id, chan_flags, length, total_length); } else if (chan_id == g_rail_chan_id) { @@ -1412,7 +1412,7 @@ channel_thread_loop(void *in_val) LOGM((LOG_LEVEL_INFO, "channel_thread_loop: g_term_event set")); clipboard_deinit(); sound_deinit(); - dev_redir_deinit(); + devredir_deinit(); rail_deinit(); break; } @@ -1434,7 +1434,7 @@ channel_thread_loop(void *in_val) "trans_check_wait_objs error resetting")); clipboard_deinit(); sound_deinit(); - dev_redir_deinit(); + devredir_deinit(); rail_deinit(); /* delete g_con_trans */ trans_delete(g_con_trans); @@ -1460,7 +1460,7 @@ channel_thread_loop(void *in_val) api_con_trans_list_check_wait_objs(); xcommon_check_wait_objs(); sound_check_wait_objs(); - dev_redir_check_wait_objs(); + devredir_check_wait_objs(); xfuse_check_wait_objs(); timeout = -1; num_objs = 0; @@ -1479,7 +1479,7 @@ channel_thread_loop(void *in_val) &timeout); xcommon_get_wait_objs(objs, &num_objs, &timeout); sound_get_wait_objs(objs, &num_objs, &timeout); - dev_redir_get_wait_objs(objs, &num_objs, &timeout); + devredir_get_wait_objs(objs, &num_objs, &timeout); xfuse_get_wait_objs(objs, &num_objs, &timeout); get_timeout(&timeout); } /* end while (g_obj_wait(objs, num_objs, 0, 0, timeout) == 0) */ diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index dca1e9f2..c439a7ac 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -16,25 +16,6 @@ * limitations under the License. */ -/* - * TODO - * o when creating dir/file, ensure it does not already exist - * o do not allow dirs to be created in ino==1 except for .clipboard and share mounts - * o fix the HACK where I have to use my own buf instead of g_buffer - * 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 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 - * is getting copied instead of being moved - * o unable to edit files in vi - * o fuse ops to support - * o touch does not work - * o chmod must work - * - */ - /* FUSE mount point */ char g_fuse_root_path[256] = ""; char g_fuse_clipboard_path[256] = ""; /* for clipboard use */ @@ -58,32 +39,62 @@ char g_fuse_clipboard_path[256] = ""; /* for clipboard use */ #include "arch.h" #include "chansrv_fuse.h" +#include "chansrv_xfs.h" /* dummy calls when XRDP_FUSE is not defined */ int xfuse_init(void) { return 0; } int xfuse_deinit(void) { return 0; } int xfuse_check_wait_objs(void) { return 0; } int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) { return 0; } +int xfuse_create_share(tui32 device_id, const char *dirname) { return 0; } +void xfuse_delete_share(tui32 share_id) {} int xfuse_clear_clip_dir(void) { return 0; } int xfuse_file_contents_range(int stream_id, const char *data, int data_bytes) { return 0; } -int xfuse_file_contents_size(int stream_id, int file_size) { return 0; } -int xfuse_add_clip_dir_item(const char *filename, int flags, int size, int lindex) { return 0; } -int xfuse_create_share(tui32 device_id, const char *dirname) { return 0; } -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) {} -struct xrdp_inode *xfuse_devredir_add_file_or_dir( - void *vp, +int xfuse_file_contents_size(int stream_id, int file_size) + { return 0; } +int xfuse_add_clip_dir_item(const char *filename, + int flags, int size, int lindex) { return 0; } + +void xfuse_devredir_cb_enum_dir_add_entry( + struct state_dirscan *fip, + const char *name, const struct file_attr *fattr) - { return NULL;} -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_rmdir_or_file(void *vp, tui32 IoStatus) {} -void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) {} -void xfuse_devredir_cb_file_close(void *vp) {} + {} +void xfuse_devredir_cb_enum_dir_done(struct state_dirscan *fip, + enum NTSTATUS IoStatus) + {} +void xfuse_devredir_cb_lookup_entry(struct state_lookup *fip, + enum NTSTATUS IoStatus, + const struct file_attr *file_info) + {} +void xfuse_devredir_cb_setattr(struct state_setattr *fip, + enum NTSTATUS IoStatus) + {} +void xfuse_devredir_cb_create_file(struct state_create *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId) + {} +void xfuse_devredir_cb_open_file(struct state_open *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId) + {} +void xfuse_devredir_cb_read_file(struct state_read *fip, + const char *buf, size_t length) + {} +void xfuse_devredir_cb_write_file( + struct state_write *fip, + enum NTSTATUS IoStatus, + off_t offset, + size_t length) + {} +void xfuse_devredir_cb_rmdir_or_file(struct state_remove *fip, + enum NTSTATUS IoStatus) + {} +void xfuse_devredir_cb_rename_file(struct state_rename *fip, + enum NTSTATUS IoStatus) + {} +void xfuse_devredir_cb_file_close(struct state_close *fip) + {} #else @@ -109,32 +120,25 @@ void xfuse_devredir_cb_file_close(void *vp) {} #include "os_calls.h" #include "clipboard_file.h" #include "chansrv_fuse.h" +#include "chansrv_xfs.h" #include "devredir.h" #include "list.h" -#include "fifo.h" #include "file.h" #ifndef EREMOTEIO #define EREMOTEIO EIO #endif -#define min(x, y) ((x) < (y) ? (x) : (y)) - -#define XFUSE_ATTR_TIMEOUT 1.0 -#define XFUSE_ENTRY_TIMEOUT 1.0 - -#define DOTDOT_INODE 0 -#define DOT_INODE 0 -#define FIRST_INODE 1 +#define XFUSE_ATTR_TIMEOUT 5.0 +#define XFUSE_ENTRY_TIMEOUT 5.0 /* module based logging */ #define LOG_ERROR 0 #define LOG_INFO 1 #define LOG_DEBUG 2 +#ifndef LOG_LEVEL #define LOG_LEVEL LOG_ERROR - -/* Uncomment for detail of added entries */ -/* #define XFUSE_DUMP_ADDED_ENTRIES */ +#endif #define log_error(_params...) \ { \ @@ -170,73 +174,123 @@ 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 -{ - struct xrdp_inode **inode_table; /* a table of entries; can grow */ - unsigned int max_entries; /* size of inode_table[] */ - unsigned int num_entries; /* num entries available in inode_table */ - unsigned int next_node; /* next free node number */ -}; - -struct dirbuf -{ - char *p; - size_t size; -}; +/* Type of buffer used for fuse_add_direntry() calls */ struct dirbuf1 { char buf[4096]; - int bytes_in_buf; - int first_time; + size_t len; }; -/* FUSE reply types */ -#define RT_FUSE_REPLY_OPEN 1 -#define RT_FUSE_REPLY_CREATE 2 - -struct xfuse_info +/* + * Record type used to maintain state when running a directory scan + */ +struct state_dirscan { - struct fuse_file_info *fi; - fuse_req_t req; - fuse_ino_t inode; - fuse_ino_t new_inode; - char name[1024]; - char new_name[1024]; - tui32 device_id; - int reply_type; /* RT_FUSE_REPLY_xxx */ - int mode; - int type; - size_t size; + fuse_req_t req; /* Original FUSE request from opendir */ + struct fuse_file_info fi; /* File info struct passed to opendir */ + fuse_ino_t pinum; /* inum of parent directory */ +}; + + +/* + * Record type used to maintain state when running an entry lookup + */ +struct state_lookup +{ + fuse_req_t req; /* Original FUSE request from lookup */ + fuse_ino_t pinum; /* inum of parent directory */ + char name[XFS_MAXFILENAMELEN]; + /* Name to look up */ + fuse_ino_t existing_inum; + /* inum of an existing entry */ + tui32 existing_generation; + /* generation of the above */ +}; + +/* + * Record type used to maintain state when running an entry setattr + */ +struct state_setattr +{ + fuse_req_t req; /* Original FUSE request from lookup */ + fuse_ino_t inum; /* inum of entry */ + struct file_attr fattr; /* File attributes to set */ + tui32 change_mask; /* Attributes to set in fattr */ +}; + + +/* + * Record type used to maintain state when running an open + */ +struct state_open +{ + fuse_req_t req; /* Original FUSE request from lookup */ + struct fuse_file_info fi; /* File info struct passed to open */ + fuse_ino_t inum; /* inum of file to open */ +}; + + +/* + * Record type used to maintain state when running a create + */ +struct state_create +{ + fuse_req_t req; /* Original FUSE request from lookup */ + struct fuse_file_info fi; /* File info struct passed to open */ + fuse_ino_t pinum; /* inum of parent directory */ + char name[XFS_MAXFILENAMELEN]; + /* Name of file in parent directory */ + mode_t mode; /* Mode of file to create */ +}; + +/* + * Record type used to maintain state when running a read + */ +struct state_read +{ + fuse_req_t req; /* Original FUSE request from lookup */ +}; + +/* + * Record type used to maintain state when running a write + */ +struct state_write +{ + fuse_req_t req; /* Original FUSE request from lookup */ + fuse_ino_t inum; /* inum of file we're writing */ +}; + +/* + * Record type used to maintain state when running a remove + */ +struct state_remove +{ + fuse_req_t req; /* Original FUSE request from lookup */ + fuse_ino_t inum; /* inum of file we're removing */ +}; + +/* + * Record type used to maintain state when running a rename + */ +struct state_rename +{ + fuse_req_t req; /* Original FUSE request from lookup */ + fuse_ino_t pinum; /* inum of parent of file */ + fuse_ino_t new_pinum; /* inum of new parent of file */ + char name[XFS_MAXFILENAMELEN]; + /* New name of file in new parent dir */ +}; + +/* + * Record type used to maintain state when running a close + */ +struct state_close +{ + fuse_req_t req; /* Original FUSE request from lookup */ + struct fuse_file_info fi; /* File info struct passed to open */ + fuse_ino_t inum; /* inum of file to open */ }; -typedef struct xfuse_info XFUSE_INFO; struct xfuse_handle { @@ -256,26 +310,12 @@ struct req_list_item int size; }; -struct dir_info -{ - /* last index accessed in g_xrdp_fs.inode_table[] */ - int index; -}; - -/* queue FUSE opendir commands so we run only one at a time */ -struct opendir_req -{ - fuse_req_t req; - fuse_ino_t ino; - struct fuse_file_info *fi; -}; - static char g_fuse_mount_name[256] = "xrdp_client"; - -FIFO g_fifo_opendir; +static mode_t g_umask = 077; /* Umask for files in fs */ static struct list *g_req_list = 0; -static struct xrdp_fs g_xrdp_fs; /* an inst of xrdp file system */ +static struct xfs_fs *g_xfs; /* an inst of xrdp file system */ +static ino_t g_clipboard_inum; /* inode of clipboard dir */ static char *g_mount_point = 0; /* our FUSE mount point */ static struct fuse_lowlevel_ops g_xfuse_ops; /* setup FUSE callbacks */ static int g_xfuse_inited = 0; /* true when FUSE is inited */ @@ -289,30 +329,6 @@ static tintptr g_bufsize = 0; static int xfuse_init_xrdp_fs(void); static int xfuse_deinit_xrdp_fs(void); static int xfuse_init_lib(struct fuse_args *args); -static int xfuse_is_inode_valid(fuse_ino_t ino); - -// LK_TODO -#if 0 -static void xfuse_create_file(fuse_req_t req, fuse_ino_t parent, - const char *name, mode_t mode, int type); -#endif - -static void xfuse_dump_fs(void); -static tui32 xfuse_get_device_id_for_inode(fuse_ino_t ino, char *full_path); -static void fuse_reverse_pathname(char *full_path, const char *reverse_path); - -static struct xrdp_inode * xfuse_get_inode_from_pinode_name(fuse_ino_t pinode, - const char *name); - -static struct xrdp_inode * -xfuse_create_file_in_xrdp_fs(tui32 device_id, int pinode, const char *name, - int type); - -static int xfuse_does_file_exist(fuse_ino_t parent, char *name); -static int xfuse_delete_file_with_xinode(XRDP_INODE *xinode); -static int xfuse_delete_dir_with_xinode(XRDP_INODE *xinode); -static int xfuse_recursive_delete_dir_with_xinode(XRDP_INODE *xinode); -static void xfuse_update_xrdpfs_size(void); /* forward declarations for FUSE callbacks */ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, @@ -323,7 +339,7 @@ static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino, /* this is not a callback, but it's used by xfuse_cb_readdir() */ static int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b, - const char *name, fuse_ino_t ino); + XFS_INODE *xinode, off_t offset); static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); @@ -331,9 +347,6 @@ static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode); -static void xfuse_cb_rmdir(fuse_req_t req, fuse_ino_t parent, - const char *name); - static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, const char *name); @@ -341,13 +354,11 @@ static void xfuse_cb_rename(fuse_req_t req, fuse_ino_t old_parent, const char *old_name, fuse_ino_t new_parent, const char *new_name); -/* this is not a callback, but it is used by the above two functions */ -static void xfuse_remove_dir_or_file(fuse_req_t req, fuse_ino_t parent, - const char *name, int type); - +/* Whether to create a dir of file depends on whether S_IFDIR is set in the + mode field */ static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, - struct fuse_file_info *fi, int type); + struct fuse_file_info *fi); static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); @@ -376,16 +387,18 @@ static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -static int xfuse_proc_opendir_req(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi); - static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -/* 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); +/* miscellaneous functions */ +static void xfs_inode_to_fuse_entry_param(const XFS_INODE *xinode, + struct fuse_entry_param *e); +static void make_fuse_entry_reply(fuse_req_t req, const XFS_INODE *xinode); +static void make_fuse_attr_reply(fuse_req_t req, const XFS_INODE *xinode); +static const char *filename_on_device(const char *full_path); +static void update_inode_file_attributes(const struct file_attr *fattr, + tui32 change_mask, XFS_INODE *xinode); +static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name); /*****************************************************************************/ int @@ -412,6 +425,11 @@ load_fuse_config(void) { g_strncpy(g_fuse_mount_name, value, 255); } + else if (g_strcasecmp(item, "FileUmask") == 0) + { + g_umask = strtol(value, NULL, 0); + log_info("g_umask set to 0%o", g_umask); + } } list_delete(items); list_delete(values); @@ -469,15 +487,12 @@ xfuse_init(void) if (xfuse_init_xrdp_fs()) return -1; - /* setup FIFOs */ - fifo_init(&g_fifo_opendir, 30); - /* setup FUSE callbacks */ g_memset(&g_xfuse_ops, 0, sizeof(g_xfuse_ops)); g_xfuse_ops.lookup = xfuse_cb_lookup; g_xfuse_ops.readdir = xfuse_cb_readdir; g_xfuse_ops.mkdir = xfuse_cb_mkdir; - g_xfuse_ops.rmdir = xfuse_cb_rmdir; + g_xfuse_ops.rmdir = xfuse_cb_unlink; g_xfuse_ops.unlink = xfuse_cb_unlink; g_xfuse_ops.rename = xfuse_cb_rename; g_xfuse_ops.open = xfuse_cb_open; @@ -515,16 +530,6 @@ xfuse_init(void) int xfuse_deinit(void) { - struct opendir_req *odreq; - /* Fail any waiting FUSE requests in the FIFO */ - while (!fifo_is_empty(&g_fifo_opendir)) - { - odreq = (struct opendir_req *)fifo_remove(&g_fifo_opendir); - fuse_reply_err(odreq->req, EBADF); - g_free(odreq); - } - fifo_deinit(&g_fifo_opendir); - if (g_ch != 0) { fuse_session_remove_chan(g_ch); @@ -566,12 +571,6 @@ int xfuse_check_wait_objs(void) struct fuse_chan *tmpch; int rval; -#define HACK - -#ifdef HACK -char buf[135168]; -#endif - if (g_ch == 0) return 0; @@ -579,11 +578,7 @@ char buf[135168]; { tmpch = g_ch; -#ifdef HACK - rval = fuse_chan_recv(&tmpch, buf, g_bufsize); -#else rval = fuse_chan_recv(&tmpch, g_buffer, g_bufsize); -#endif if (rval == -EINTR) return -1; @@ -593,11 +588,7 @@ char buf[135168]; if (rval <= 0) return -1; -#ifdef HACK - fuse_session_process(g_se, buf, rval, tmpch); -#else fuse_session_process(g_se, g_buffer, rval, tmpch); -#endif } return 0; @@ -634,75 +625,36 @@ int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) int xfuse_create_share(tui32 device_id, const char *dirname) { -#if 0 - XFUSE_INFO *fip; -#endif - XRDP_INODE *xinode; - /* tui32 saved_inode; */ + int result = -1; + XFS_INODE *xinode; - if (dirname == NULL || strlen(dirname) == 0) - return -1; - - /* Do we have an inode table yet? */ - if (xfuse_init_xrdp_fs()) + if (dirname != NULL && strlen(dirname) > 0 && + xfuse_init_xrdp_fs() == 0) { - return -1; + xinode = xfs_add_entry(g_xfs, FUSE_ROOT_ID, dirname, (0777 | S_IFDIR)); + if (xinode == NULL) + { + log_debug("xfs_add_entry() failed"); + } + else + { + xinode->device_id = device_id; + result = 0; + } } - xinode = g_new0(struct xrdp_inode, 1); - if (xinode == NULL) - { - log_debug("g_new0() failed"); - return -1; - } + return result; +} - /* create directory entry */ - xinode->parent_inode = 1; - xinode->inode = g_xrdp_fs.next_node++; - xinode->mode = 0755 | S_IFDIR; - xinode->nlink = 1; - xinode->uid = getuid(); - xinode->gid = getgid(); - xinode->size = 0; - xinode->atime = time(0); - xinode->mtime = time(0); - xinode->ctime = time(0); - strcpy(xinode->name, dirname); - xinode->device_id = device_id; +/** + * @brief Remove specified share directory. + * + * This code gets called from devredir + *****************************************************************************/ - g_xrdp_fs.num_entries++; - /* saved_inode = xinode->inode; */ - - /* insert it in xrdp fs */ - g_xrdp_fs.inode_table[xinode->inode] = xinode; - log_debug("created new share named %s at inode_table[%d]", - dirname, xinode->inode); - - /* update nentries in parent inode */ - xinode = g_xrdp_fs.inode_table[1]; - if (xinode == NULL) - return -1; - xinode->nentries++; - -#if 0 - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) - { - log_error("system out of memory"); - return -1; - } - - /* enumerate root dir, do not call FUSE when done */ - fip->req = NULL; - fip->inode = 1; /* TODO saved_inode; */ - strncpy(fip->name, dirname, 1024); - fip->name[1023] = 0; - fip->device_id = device_id; - - dev_redir_get_dir_listing((void *) fip, device_id, "\\"); -#endif - - return 0; +void xfuse_delete_share(tui32 device_id) +{ + xfs_delete_entries_with_device_id(g_xfs, device_id); } /** @@ -715,34 +667,14 @@ int xfuse_create_share(tui32 device_id, const char *dirname) int xfuse_clear_clip_dir(void) { - fuse_ino_t i; - XRDP_INODE *xinode; - XRDP_INODE *xip; + int result = 0; - log_debug("entered"); - - if (g_xrdp_fs.inode_table == NULL) + if (g_xfs != NULL && g_clipboard_inum > 0) { - return 0; + xfs_remove_directory_contents(g_xfs, g_clipboard_inum); } - /* xinode for .clipboard */ - xip = g_xrdp_fs.inode_table[2]; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - if (xinode->parent_inode == 2) - { - g_xrdp_fs.inode_table[i] = NULL; - free(xinode); - xip->nentries--; - } - } - - return 0; + return result; } /** @@ -806,10 +738,10 @@ xfuse_add_clip_dir_item(const char *filename, int flags, int size, int lindex) filename, flags, size, lindex); /* add entry to xrdp_fs */ - XRDP_INODE *xinode = xfuse_create_file_in_xrdp_fs(0, /* device id */ - 2, /* parent inode */ - filename, - S_IFREG); + XFS_INODE *xinode = xfs_add_entry( g_xfs, + g_clipboard_inum, /* parent inode */ + filename, + (0666 | S_IFREG)); if (xinode == NULL) { log_debug("failed to create file in xrdp filesystem"); @@ -817,7 +749,6 @@ xfuse_add_clip_dir_item(const char *filename, int flags, int size, int lindex) } xinode->size = size; xinode->lindex = lindex; - xinode->is_loc_resource = 1; return 0; } @@ -894,103 +825,36 @@ static int xfuse_init_lib(struct fuse_args *args) static int xfuse_init_xrdp_fs(void) { - struct xrdp_inode *xino; + XFS_INODE *xino; + int result = -1; /* Already called? */ - if (g_xrdp_fs.inode_table != NULL) + if (g_xfs != NULL) { - return 0; + result = 0; } - - g_xrdp_fs.inode_table = g_new0(struct xrdp_inode *, 4096); - if (g_xrdp_fs.inode_table == NULL) + else if ((g_xfs = xfs_create_xfs_fs(0, g_getuid(), g_getgid())) == NULL) { log_error("system out of memory"); - return -1; } - - /* - * index 0 is our .. dir - */ - - xino = g_new0(struct xrdp_inode, 1); - if (xino == NULL) + else { - log_error("system out of memory"); - free(g_xrdp_fs.inode_table); - return -1; + /* Need a top-level .clipboard directory */ + xino = xfs_add_entry(g_xfs, FUSE_ROOT_ID, ".clipboard", + (0777 | S_IFDIR)); + if (xino == NULL) + { + log_error("system out of memory"); + xfs_delete_xfs_fs(g_xfs); + g_xfs = NULL; + } + else + { + g_clipboard_inum = xino->inum; + result = 0; + } } - g_xrdp_fs.inode_table[0] = xino; - xino->parent_inode = 0; - xino->inode = 0; - xino->mode = S_IFDIR | 0755; - xino->nentries = 1; - xino->uid = getuid(); - xino->gid = getgid(); - xino->size = 0; - xino->atime = time(0); - xino->mtime = time(0); - xino->ctime = time(0); - strcpy(xino->name, ".."); - - /* - * index 1 is our . dir - */ - - xino = g_new0(struct xrdp_inode, 1); - if (xino == NULL) - { - log_error("system out of memory"); - free(g_xrdp_fs.inode_table[0]); - free(g_xrdp_fs.inode_table); - return -1; - } - g_xrdp_fs.inode_table[1] = xino; - xino->parent_inode = 0; - xino->inode = 1; - xino->mode = S_IFDIR | 0755; - xino->nentries = 1; - xino->uid = getuid(); - xino->gid = getgid(); - xino->size = 0; - xino->atime = time(0); - xino->mtime = time(0); - xino->ctime = time(0); - strcpy(xino->name, "."); - - /* - * index 2 is for clipboard use - */ - - xino = g_new0(struct xrdp_inode, 1); - if (xino == NULL) - { - log_error("system out of memory"); - free(g_xrdp_fs.inode_table[0]); - free(g_xrdp_fs.inode_table[1]); - free(g_xrdp_fs.inode_table); - return -1; - } - - g_xrdp_fs.inode_table[2] = xino; - xino->parent_inode = 1; - xino->inode = 2; - xino->nentries = 0; - xino->mode = S_IFDIR | 0755; - xino->uid = getuid(); - xino->gid = getgid(); - xino->size = 0; - xino->atime = time(0); - xino->mtime = time(0); - xino->ctime = time(0); - xino->is_loc_resource = 1; - strcpy(xino->name, ".clipboard"); - - g_xrdp_fs.max_entries = 4096; - g_xrdp_fs.num_entries = 3; - g_xrdp_fs.next_node = 3; - - return 0; + return result; } /** @@ -1001,590 +865,213 @@ static int xfuse_init_xrdp_fs(void) static int xfuse_deinit_xrdp_fs(void) { - fuse_ino_t i; - struct xrdp_inode *xinode; - - if (g_xrdp_fs.inode_table != NULL) - { - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; ++i) - { - if ((xinode = g_xrdp_fs.inode_table[i]) != NULL) - { - g_xrdp_fs.inode_table[i] = NULL; - log_debug("Freeing %s",xinode->name); - free(xinode); - } - } - free(g_xrdp_fs.inode_table); - g_xrdp_fs.inode_table = NULL; - } - - g_xrdp_fs.max_entries = 0; - g_xrdp_fs.num_entries = 0; - g_xrdp_fs.next_node = 1; + xfs_delete_xfs_fs(g_xfs); + g_xfs = NULL; + g_clipboard_inum = 0; return 0; } -/** - * determine if specified ino exists in xrdp file system - * - * @return 1 if it does, 0 otherwise - *****************************************************************************/ - -static int xfuse_is_inode_valid(fuse_ino_t ino) -{ - /* is ino present in our table? */ - if ((ino < FIRST_INODE) || (ino >= g_xrdp_fs.next_node)) - return 0; - - if (g_xrdp_fs.inode_table[ino] == NULL) - return 0; - - return 1; -} - -/** - * @brief Create a directory or regular file. - *****************************************************************************/ - -// LK_TODO -#if 0 -static void xfuse_create_file(fuse_req_t req, fuse_ino_t parent, - const char *name, mode_t mode, int type) -{ - struct xrdp_inode *xinode; - struct fuse_entry_param e; - - log_debug("parent=%ld name=%s", parent, name); - - /* do we have a valid parent inode? */ - if (!xfuse_is_inode_valid(parent)) - { - log_error("inode %ld is not valid", parent); - fuse_reply_err(req, EBADF); - } - - xinode = g_new0(struct xrdp_inode, 1); - if (xinode == NULL) - { - log_error("g_new0() failed"); - fuse_reply_err(req, ENOMEM); - } - - /* create directory entry */ - xinode->parent_inode = parent; - xinode->inode = g_xrdp_fs.next_node++; /* TODO should be thread safe */ - xinode->mode = mode | type; - xinode->uid = getuid(); - xinode->gid = getgid(); - xinode->size = 0; - xinode->atime = time(0); - xinode->mtime = time(0); - xinode->ctime = time(0); - strcpy(xinode->name, name); - - g_xrdp_fs.num_entries++; - - /* insert it in xrdp fs */ - g_xrdp_fs.inode_table[xinode->inode] = xinode; - xfuse_update_xrdpfs_size(); - log_debug("inserted new dir at inode_table[%d]", xinode->inode); - - xfuse_dump_fs(); - - log_debug("new inode=%d", xinode->inode); - - /* setup return value */ - 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 = 0; - 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 - -static void xfuse_dump_fs(void) -{ - fuse_ino_t i; - struct xrdp_inode *xinode; - - log_debug("found %d entries", g_xrdp_fs.num_entries - FIRST_INODE); - -#if 0 - log_debug("not dumping xrdp fs"); - return; -#endif - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - log_debug("pinode=%d inode=%d nentries=%d nopen=%d name=%s", - xinode->parent_inode, xinode->inode, - xinode->nentries, xinode->nopen, - xinode->name); - } - log_debug("%s", ""); -} - -/** - * Dump contents of xinode structure - * - * @param xino xinode structure to dump - *****************************************************************************/ - -#ifdef XFUSE_DUMP_ADDED_ENTRIES -static void xfuse_dump_xrdp_inode(struct xrdp_inode *xino) -{ - log_debug("--- dumping struct xinode %p ---",xino); - log_debug("name: %s", xino->name); - log_debug("parent_inode: %d", xino->parent_inode); - log_debug("inode: %d", xino->inode); - log_debug("mode: %o", xino->mode); - log_debug("nlink: %d", xino->nlink); - log_debug("uid: %d", xino->uid); - log_debug("gid: %d", xino->gid); - log_debug("size: %zd", xino->size); - log_debug("device_id: %d", xino->device_id); - log_debug("%s", ""); -} -#endif - -/** - * Return the device_id associated with specified inode and copy the - * full path to the specified inode into full_path - * - * @param ino the inode - * @param full_path full path to the inode - * - * @return the device_id of specified inode - *****************************************************************************/ - -static tui32 xfuse_get_device_id_for_inode(fuse_ino_t ino, char *full_path) -{ - fuse_ino_t parent_inode = 0; - fuse_ino_t child_inode = ino; - char reverse_path[4096]; - - /* ino == 1 is a special case; we already know that it is not */ - /* associated with any device redirection */ - if (ino == 1) - { - /* just return the device_id for the file in full_path */ - log_debug("looking for file with pinode=%ld name=%s", ino, full_path); - xfuse_dump_fs(); - - XRDP_INODE *xinode = xfuse_get_inode_from_pinode_name(ino, full_path); - full_path[0] = 0; - if (xinode) - return xinode->device_id; - else - return 0; - } - - reverse_path[0] = 0; - full_path[0] = 0; - - while (1) - { - strcat(reverse_path, g_xrdp_fs.inode_table[child_inode]->name); - - parent_inode = g_xrdp_fs.inode_table[child_inode]->parent_inode; - if (parent_inode == 1) - break; - - strcat(reverse_path, "/"); - child_inode = parent_inode; - } - - fuse_reverse_pathname(full_path, reverse_path); - - return g_xrdp_fs.inode_table[child_inode]->device_id; -} - -/** - * Reverse the pathname in 'reverse_path' and insert it into 'full_path' - * - * Example: abba/music/share1 becomes share1/music/abba - * - * @param full_path path name in the correct order - * @param reverse_path path name in the reverse order - *****************************************************************************/ - -static void fuse_reverse_pathname(char *full_path, const char *reverse_path) -{ - char *optr; /* Output pointer */ - const char *start;/* Pointer to start of current element in input */ - const char *end; /* Ditto for the end */ - - /* Start writing the result from the end */ - optr = full_path + strlen(reverse_path); - *optr = '\0'; - - start = reverse_path; - while ((end = strchr(start, '/')) != NULL) - { - /* Copy current element to the end of the result... */ - optr -= (end - start); - memcpy(optr, start, (end - start)); - /* And precede with a '/' */ - *--optr = '/'; - - /* Next element starts after the '/' */ - start = end + 1; - } - - /* Last bit (if reverse_path doesn't end with a '/') */ - if (optr != full_path) - { - memcpy(full_path, start, optr - full_path); - } - -} - -/** - * Return the inode that matches the name and parent inode - *****************************************************************************/ - -static struct xrdp_inode * xfuse_get_inode_from_pinode_name(fuse_ino_t pinode, - const char *name) -{ - fuse_ino_t i; - struct xrdp_inode * xinode; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - /* match parent inode */ - if (xinode->parent_inode != pinode) - continue; - - /* match name */ - if (strcmp(xinode->name, name) != 0) - continue; - - return xinode; - } - return NULL; -} - -/** - * Create file in xrdp file system - * - * @param pinode the parent inode - * @param name filename - * - * @return XRDP_INODE on success, NULL on failure - *****************************************************************************/ - -static struct xrdp_inode * -xfuse_create_file_in_xrdp_fs(tui32 device_id, int pinode, const char *name, - int type) -{ - XRDP_INODE *xinode; - XRDP_INODE *xinodep; - time_t cur_time; - - if ((name == NULL) || (strlen(name) == 0)) - return NULL; - - /* Do we have an inode table yet? */ - if (xfuse_init_xrdp_fs()) - { - return NULL; - } - - xinode = g_new0(XRDP_INODE, 1); - if (xinode == NULL) - { - log_error("system out of memory"); - return NULL; - } - - cur_time = time(0); - - xinode->parent_inode = pinode; - xinode->inode = g_xrdp_fs.next_node++; - xinode->nlink = 1; - xinode->uid = getuid(); - xinode->gid = getgid(); - xinode->atime = cur_time; - xinode->mtime = cur_time; - xinode->ctime = cur_time; - xinode->device_id = device_id; - strcpy(xinode->name, name); - - if (type == S_IFDIR) - { - xinode->mode = 0755 | type; - xinode->size = 4096; - } - else - { - xinode->mode = 0644 | type; - xinode->size = 0; - } - - g_xrdp_fs.inode_table[xinode->inode] = xinode; - g_xrdp_fs.num_entries++; -#ifdef XFUSE_DUMP_ADDED_ENTRIES - xfuse_dump_xrdp_inode(xinode); -#endif - - /* bump up lookup count in parent dir */ - xinodep = g_xrdp_fs.inode_table[pinode]; - xinodep->nentries++; - xfuse_update_xrdpfs_size(); - - log_debug("incremented nentries; parent=%d nentries=%d", - pinode, xinodep->nentries); - - return xinode; -} - -/** - * Check if specified file exists - * - * @param parent parent inode of file - * @param name filename or dirname - * - * @return 1 if specified file exists, 0 otherwise - *****************************************************************************/ - -static int xfuse_does_file_exist(fuse_ino_t parent, char *name) -{ - fuse_ino_t i; - XRDP_INODE *xinode; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - if ((xinode->parent_inode == parent) && - (strcmp(xinode->name, name) == 0)) - { - return 1; - } - } - - return 0; -} - -static int xfuse_delete_file_with_xinode(XRDP_INODE *xinode) -{ - /* make sure it is not a dir */ - if ((xinode == NULL) || (xinode->mode & S_IFDIR)) - return -1; - - log_always("deleting: inode=%d name=%s", xinode->inode, xinode->name); - log_debug("deleting: inode=%d name=%s", xinode->inode, xinode->name); - - g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--; - g_xrdp_fs.inode_table[xinode->inode] = NULL; - free(xinode); - - return 0; -} - -static int xfuse_delete_dir_with_xinode(XRDP_INODE *xinode) -{ - XRDP_INODE *xip; - fuse_ino_t i; - - /* make sure it is not a file */ - if ((xinode == NULL) || (xinode->mode & S_IFREG)) - return -1; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xip = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - /* look for child inodes */ - if (xip->parent_inode == xinode->inode) - { - /* got one, delete it */ - g_xrdp_fs.inode_table[xip->inode] = NULL; - free(xip); - } - } - - /* our parent will have one less dir */ - g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--; - - g_xrdp_fs.inode_table[xinode->inode] = NULL; - free(xinode); - - return 0; -} - -/** - * Recursively delete dir with specified inode - *****************************************************************************/ - -static int xfuse_recursive_delete_dir_with_xinode(XRDP_INODE *xinode) -{ - XRDP_INODE *xip; - fuse_ino_t i; - - /* make sure it is not a file */ - if ((xinode == NULL) || (xinode->mode & S_IFREG)) - return -1; - - log_always("recursively deleting dir with inode=%d name=%s", - xinode->inode, xinode->name); - - log_debug("recursively deleting dir with inode=%d name=%s", - xinode->inode, xinode->name); - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xip = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - /* look for child inodes */ - if (xip->parent_inode == xinode->inode) - { - /* got one */ - if (xip->mode & S_IFREG) - { - /* regular file */ - g_xrdp_fs.inode_table[xip->parent_inode]->nentries--; - g_xrdp_fs.inode_table[xip->inode] = NULL; - free(xip); - } - else - { - /* got another dir */ - xfuse_recursive_delete_dir_with_xinode(xip); - } - } - } - - /* our parent will have one less dir */ - g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--; - - g_xrdp_fs.inode_table[xinode->inode] = NULL; - free(xinode); - - return 0; -} - -static void xfuse_update_xrdpfs_size(void) -{ - void *vp; - int diff; - - diff = g_xrdp_fs.max_entries - g_xrdp_fs.num_entries; - if (diff > 100) - return; - - /* extend memory */ - vp = realloc(g_xrdp_fs.inode_table, - (g_xrdp_fs.max_entries + 100) * sizeof(struct xrdp_inode *)); - - if (vp == NULL) - { - log_error("system out of memory"); - return; - } - - /* zero newly added memory */ - memset((char *) vp + g_xrdp_fs.max_entries * sizeof(struct xrdp_inode *), - 0, - 100 * sizeof(struct xrdp_inode *)); - - g_xrdp_fs.max_entries += 100; - g_xrdp_fs.inode_table = (struct xrdp_inode **) vp; -} - /****************************************************************************** ** ** ** callbacks for devredir ** ** ** ******************************************************************************/ + /** - * Add a file or directory to xrdp file system + * Add a file or directory to xrdp file system as part of a + * directory request * - * If the file or directory already exists, its attributes are updated. - * - * Returns a pointer to the xinode if the object was added or updated + * If the file or directory already exists, no changes are made to it. *****************************************************************************/ -struct xrdp_inode *xfuse_devredir_add_file_or_dir( - void *vp, +void xfuse_devredir_cb_enum_dir_add_entry( + struct state_dirscan *fip, + const char *name, const struct file_attr *fattr) { - XFUSE_INFO *fip = (XFUSE_INFO *) vp; - XRDP_INODE *xinode = NULL; + XFS_INODE *xinode = NULL; - if (fip == NULL) + if (!xfs_get(g_xfs, fip->pinum)) { - log_error("fip is NULL"); + log_error("inode %ld is not valid", fip->pinum); } - else if (!xfuse_is_inode_valid(fip->inode)) - { - log_error("inode %ld is not valid", fip->inode); - } - else if ((strcmp(fattr->name, ".") == 0) || - (strcmp(fattr->name, "..") == 0)) + else if ((strcmp(name, ".") == 0) || + (strcmp(name, "..") == 0)) { ; /* filename is . or .. - don't add it */ } else { - log_debug("parent_inode=%ld name=%s", fip->inode, fattr->name); - xfuse_dump_fs(); + log_debug("parent_inode=%ld name=%s", fip->pinum, name); + + /* Does the file already exist ? If it does it's important we + * don't mess with it, as we're only enumerating the directory, and + * we don't want to disrupt any existing operations on the file + */ + xinode = xfs_lookup_in_dir(g_xfs, fip->pinum, name); + if (xinode == NULL) + { + /* Add a new node to the file system */ + log_debug("Creating name=%s in parent=%ld in xrdp_fs", + name, fip->pinum); + xinode = xfs_add_entry(g_xfs, fip->pinum, name, fattr->mode); + if (xinode == NULL) + { + log_error("xfs_add_entry() failed"); + } + else + { + xinode->size = fattr->size; + xinode->atime = fattr->atime; + xinode->mtime = fattr->mtime; + /* Initially, set the attribute change time to the file data + change time */ + xinode->ctime = fattr->mtime; + + /* device_id is inherited from parent */ + } + } + } +} + +/** + * This routine is called by devredir when the opendir request has + * completed + *****************************************************************************/ + +void xfuse_devredir_cb_enum_dir_done(struct state_dirscan *fip, + enum NTSTATUS IoStatus) +{ + log_debug("fip=%p IoStatus=0x%x", fip, IoStatus); + + /* + * NT_STATUS_NO_SUCH_FILE is returned for empty directories + */ + if (IoStatus != NT_STATUS_SUCCESS && IoStatus != NT_STATUS_NO_SUCH_FILE) + { + int status; + switch (IoStatus) + { + case NT_STATUS_ACCESS_DENIED: + status = EACCES; + break; + default: + status = ENOENT; + } + fuse_reply_err(fip->req, status); + } + else if (!xfs_get(g_xfs, fip->pinum)) + { + log_error("inode %ld is not valid", fip->pinum); + fuse_reply_err(fip->req, ENOENT); + } + else + { + struct fuse_file_info *fi = &fip->fi; + + if ((fi->fh = (tintptr) xfs_opendir(g_xfs, fip->pinum)) == 0) + { + fuse_reply_err(fip->req, ENOMEM); + } + else + { + fuse_reply_open(fip->req, &fip->fi); + } + } + + free(fip); +} + +/** + * This routine is caused as a result of a devredir remote lookup + * instigated by xfuse_cb_lookup() + *****************************************************************************/ + +void xfuse_devredir_cb_lookup_entry(struct state_lookup *fip, + enum NTSTATUS IoStatus, + const struct file_attr *file_info) +{ + XFS_INODE *xinode = NULL; + + if (IoStatus != NT_STATUS_SUCCESS) + { + switch (IoStatus) + { + case NT_STATUS_SHARING_VIOLATION: + /* This can happen when trying to read the attributes of + * some system files (e.g. pagefile.sys) */ + case NT_STATUS_ACCESS_DENIED: + fuse_reply_err(fip->req, EACCES); + break; + + case NT_STATUS_UNSUCCESSFUL: + /* Happens if we try to lookup an illegal filename (e.g. + * one with a '*' in it) */ + fuse_reply_err(fip->req, ENOENT); + break; + + case NT_STATUS_NO_SUCH_FILE: + /* Remove our copy, if any */ + if (fip->existing_inum && + (xinode = xfs_get(g_xfs, fip->existing_inum)) != NULL && + xinode->generation == fip->existing_generation) + { + xfs_remove_entry(g_xfs, fip->existing_inum); + } + fuse_reply_err(fip->req, ENOENT); + break; + + default: + log_info("Error code %08x - fallthrough", (int) IoStatus); + fuse_reply_err(fip->req, EIO); + break; + } + } + else if (!xfs_get(g_xfs, fip->pinum)) + { + log_error("parent inode %ld is not valid", fip->pinum); + fuse_reply_err(fip->req, ENOENT); + } + else + { + log_debug("parent_inode=%ld name=%s", fip->pinum, fip->name); /* Does the file already exist ? */ - xinode = xfuse_get_inode_from_pinode_name(fip->inode, fattr->name); + xinode = xfs_lookup_in_dir(g_xfs, fip->pinum, fip->name); if (xinode != NULL) { /* Is the existing file the same type ? */ if ((xinode->mode & (S_IFREG | S_IFDIR)) == - (fattr->mode & (S_IFREG | S_IFDIR))) + (file_info->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) + log_debug("inode=%ld name=%s already exists in xrdp_fs as %ld", + fip->pinum, fip->name, xinode->inum); + if (xfs_get_file_open_count(g_xfs, xinode->inum) > 0) { - xfuse_delete_file_with_xinode(xinode); + /* + * Don't mess with open files. The local attributes are + * almost certainly more up-to-date. A worst case scenario + * would be truncating a file we're currently writing + * to, as the lookup value for the size is stale. + */ + log_debug("inode=%ld is open - " + "preferring local attributes", xinode->inum); } else { - xfuse_delete_dir_with_xinode(xinode); + log_debug("Updating attributes of inode=%ld", xinode->inum); + update_inode_file_attributes(file_info, TO_SET_ALL, xinode); } + } + else + { + /* Type has changed from file to directory, or vice-versa */ + log_debug("inode=%ld name=%s of different type in xrdp_fs" + " - removing", + fip->pinum, xinode->name); + xfs_remove_entry(g_xfs, xinode->inum); xinode = NULL; } } @@ -1592,123 +1079,25 @@ struct xrdp_inode *xfuse_devredir_add_file_or_dir( if (xinode == NULL) { /* Add a new node to the file system */ - xinode = g_new0(struct xrdp_inode, 1); + log_debug("Creating name=%s in parent=%ld in xrdp_fs", + fip->name, fip->pinum); + xinode = xfs_add_entry(g_xfs, fip->pinum, fip->name, + file_info->mode); if (xinode == NULL) { - log_debug("g_new0() failed"); + log_debug("xfs_add_entry() 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; - - xinode->parent_inode = fip->inode; - xinode->inode = g_xrdp_fs.next_node++; - xinode->uid = getuid(); - xinode->gid = getgid(); - xinode->device_id = fip->device_id; - - 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); -#endif + xinode->size = file_info->size; + xinode->atime = file_info->atime; + xinode->mtime = file_info->mtime; + /* Initially, set the attribute change time to the file data + change time */ + xinode->ctime = file_info->mtime; + /* device_id is inherited from parent */ } } - } - return xinode; -} - -/** - * This routine is called by devredir when the opendir_req entry at the - * front of the opendir FIFO has completed processing. - *****************************************************************************/ - -void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus) -{ - XFUSE_INFO *fip; - struct dir_info *di; - struct opendir_req *odreq; - - log_debug("vp=%p IoStatus=0x%x", vp, IoStatus); - - fip = (XFUSE_INFO *) vp; - if (fip == NULL) - { - log_debug("fip is NULL"); - goto done; - } - - if (IoStatus != 0) - { - /* command failed */ - fuse_reply_err(fip->req, ENOENT); - goto done; - } - - /* do we have a valid inode? */ - if (!xfuse_is_inode_valid(fip->inode)) - { - log_error("inode %ld is not valid", fip->inode); - fuse_reply_err(fip->req, EBADF); - goto done; - } - - xfuse_delete_stale_entries(fip->inode); - - /* Generate a FUSE reply. - * The dir_info struct will be used by xfuse_cb_readdir() - */ - di = g_new0(struct dir_info, 1); - di->index = FIRST_INODE; - fip->fi->fh = (tintptr) di; - - fuse_reply_open(fip->req, fip->fi); - -done: - - free(fip); - - /* remove completed request */ - g_free(fifo_remove(&g_fifo_opendir)); - - while (1) - { - /* process next request */ - odreq = (struct opendir_req *) fifo_peek(&g_fifo_opendir); - if (!odreq) - return; - - if (xfuse_proc_opendir_req(odreq->req, odreq->ino, odreq->fi)) - g_free(fifo_remove(&g_fifo_opendir)); /* req failed */ - else - break; /* req has been queued */ - } -} - -/** - * 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); @@ -1718,10 +1107,32 @@ void xfuse_devredir_cb_lookup_entry(void *vp, tui32 IoStatus, struct xrdp_inode fuse_reply_err(fip->req, EIO); } } - else + + free(fip); +} + +/** + * This routine is caused as a result of a devredir remote setattr + * instigated by xfuse_cb_setattr() + *****************************************************************************/ +void xfuse_devredir_cb_setattr(struct state_setattr *fip, + enum NTSTATUS IoStatus) +{ + XFS_INODE *xinode; + + if (IoStatus != NT_STATUS_SUCCESS) { switch (IoStatus) { + case NT_STATUS_SHARING_VIOLATION: + /* This can happen when trying to read the attributes of + * some system files (e.g. pagefile.sys) */ + case NT_STATUS_ACCESS_DENIED: + fuse_reply_err(fip->req, EACCES); + break; + + case NT_STATUS_UNSUCCESSFUL: + /* Happens if we try to lookup an illegal filename */ case NT_STATUS_NO_SUCH_FILE: fuse_reply_err(fip->req, ENOENT); break; @@ -1732,23 +1143,26 @@ void xfuse_devredir_cb_lookup_entry(void *vp, tui32 IoStatus, struct xrdp_inode break; } } + else if ((xinode = xfs_get(g_xfs, fip->inum)) == NULL) + { + fuse_reply_err(fip->req, ENOENT); + } + else + { + update_inode_file_attributes(&fip->fattr, fip->change_mask, xinode); + make_fuse_attr_reply(fip->req, xinode); + } + free(fip); } -void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, - tui32 FileId) +/** + * This routine is caused as a result of a file or directory + * create request */ +void xfuse_devredir_cb_create_file(struct state_create *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId) { - XFUSE_HANDLE *fh; - XRDP_INODE *xinode; - - XFUSE_INFO *fip = (XFUSE_INFO *) vp; - if (fip == NULL) - { - log_debug("fip is NULL"); - goto done; - } - - log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p DeviceId=%d FileId=%d", - fip, fip->fi, DeviceId, FileId); + XFUSE_HANDLE *fh = NULL; if (IoStatus != 0) { @@ -1767,304 +1181,234 @@ void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId, fuse_reply_err(fip->req, EIO); break; } - - goto done; - } - - if (fip->fi != NULL) - { - fh = g_new0(XFUSE_HANDLE, 1); - if (fh == NULL) - { - log_error("system out of memory"); - fuse_reply_err(fip->req, ENOMEM); - - free(fip); - return; - } - - /* save file handle for later use */ - fh->DeviceId = DeviceId; - fh->FileId = FileId; - - fip->fi->fh = (tintptr) fh; - log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=0x%llx", - fip, fip->fi, (long long) fip->fi->fh); - } - - if (fip->reply_type == RT_FUSE_REPLY_OPEN) - { - log_debug("sending fuse_reply_open(); " - "DeviceId=%d FileId=%d req=%p fi=%p", - fh->DeviceId, fh->FileId, fip->req, fip->fi); - - /* update open count */ - if ((xinode = g_xrdp_fs.inode_table[fip->inode]) != NULL) - xinode->nopen++; - - fuse_reply_open(fip->req, fip->fi); - } - else if (fip->reply_type == RT_FUSE_REPLY_CREATE) - { - struct fuse_entry_param e; - -// LK_TODO -#if 0 - if ((xinode = g_xrdp_fs.inode_table[fip->inode]) == NULL) - { - log_error("inode at inode_table[%ld] is NULL", fip->inode); - fuse_reply_err(fip->req, EBADF); - goto done; - } -#else - /* create entry in xrdp file system */ - xinode = xfuse_create_file_in_xrdp_fs(fip->device_id, fip->inode, - fip->name, fip->mode); - if (xinode == NULL) - { - fuse_reply_err(fip->req, ENOMEM); - return; - } -#endif - memset(&e, 0, sizeof(struct fuse_entry_param)); - - 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; - - if (fip->mode == S_IFDIR) - { - fuse_reply_entry(fip->req, &e); - } - else - { - xinode->nopen++; - fuse_reply_create(fip->req, &e, fip->fi); - } } else { - log_error("invalid reply type: %d", fip->reply_type); - } + if ((fip->mode & S_IFREG) != 0) + { + /* We've created a regular file */ + /* Allocate an XFUSE_HANDLE for future file operations */ + if ((fh = g_new0(XFUSE_HANDLE, 1)) != NULL) + { + /* save file handle for later use */ + fh->DeviceId = DeviceId; + fh->FileId = FileId; -done: + fip->fi.fh = (tintptr) fh; + } + } + + if ((fip->mode & S_IFREG) != 0 && fh == NULL) + { + /* We failed to allocate a file handle */ + log_error("system out of memory"); + fuse_reply_err(fip->req, ENOMEM); + } + else + { + XFS_INODE *xinode; + /* create entry in xrdp file system */ + xinode = xfs_add_entry(g_xfs, fip->pinum, fip->name, fip->mode); + if (xinode == NULL) + { + /* It's possible xfs_add_entry() has failed, as the + * file has already been added (by a lookup request) + * in the time between our request and the response + * This can happen if an 'ls' is happening in the same + * directory as this file is being created. + * + * We'll check for this before we fail the create */ + if ((xinode = xfs_lookup_in_dir(g_xfs, + fip->pinum, + fip->name)) != NULL) + { + /* + * The mode should be correct anyway, but we'll + * set it to the mode requested at creation + */ + xinode->mode = fip->mode; + } + } + + if (xinode == NULL) + { + log_error("Out of memory!"); + fuse_reply_err(fip->req, ENOMEM); + } + else + { + + if ((fip->mode & S_IFDIR) != 0) + { + make_fuse_entry_reply(fip->req, xinode); + } + else + { + struct fuse_entry_param e; + xfs_inode_to_fuse_entry_param(xinode, &e); + fuse_reply_create(fip->req, &e, &fip->fi); + xfs_increment_file_open_count(g_xfs, xinode->inum); + } + } + } + } free(fip); } -void xfuse_devredir_cb_read_file(void *vp, const char *buf, size_t length) -{ - XFUSE_INFO *fip; - fip = (XFUSE_INFO *) vp; - if ((fip == NULL) || (fip->req == NULL)) +/** + * This routine is caused as a result of a file open request */ +void xfuse_devredir_cb_open_file(struct state_open *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId) +{ + XFUSE_HANDLE *fh; + + if (IoStatus != 0) { - log_error("fip for fip->req is NULL"); - return; + switch (IoStatus) + { + case NT_STATUS_ACCESS_DENIED: + fuse_reply_err(fip->req, EACCES); + break; + + case NT_STATUS_OBJECT_NAME_INVALID: + case NT_STATUS_OBJECT_NAME_NOT_FOUND: + fuse_reply_err(fip->req, ENOENT); + break; + + default: + fuse_reply_err(fip->req, EIO); + break; + } + } + else + { + /* Allocate an XFUSE_HANDLE for future file operations */ + if ((fh = g_new0(XFUSE_HANDLE, 1)) == NULL) + { + log_error("system out of memory"); + fuse_reply_err(fip->req, ENOMEM); + } + else + { + /* save file handle for later use */ + fh->DeviceId = DeviceId; + fh->FileId = FileId; + + fip->fi.fh = (tintptr) fh; + + log_debug("sending fuse_reply_open(); " + "DeviceId=%d FileId=%d req=%p", + fh->DeviceId, fh->FileId, fip->req); + + /* update open count */ + xfs_increment_file_open_count(g_xfs, fip->inum); + + fuse_reply_open(fip->req, &fip->fi); + } } + free(fip); +} + +void xfuse_devredir_cb_read_file(struct state_read *fip, + const char *buf, size_t length) +{ fuse_reply_buf(fip->req, buf, length); free(fip); } -void xfuse_devredir_cb_write_file(void *vp, - tui32 IoStatus, - const char *buf, - size_t length) +void xfuse_devredir_cb_write_file( + struct state_write *fip, + enum NTSTATUS IoStatus, + off_t offset, + size_t length) { - XRDP_INODE *xinode; - XFUSE_INFO *fip; + XFS_INODE *xinode; - fip = (XFUSE_INFO *) vp; - if ((fip == NULL) || (fip->req == NULL) || (fip->fi == NULL)) + if (IoStatus != NT_STATUS_SUCCESS) { - log_error("fip, fip->req or fip->fi is NULL"); + log_error("Write NTSTATUS is %d", (int) IoStatus); + fuse_reply_err(fip->req, EIO); } else { - log_debug( - "+++ XFUSE_INFO=%p, XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=0x%llx", - fip, fip->fi, (long long) fip->fi->fh); + size_t new_size = offset + length; + fuse_reply_write(fip->req, length); - if (IoStatus != NT_STATUS_SUCCESS) + /* update file size */ + if ((xinode = xfs_get(g_xfs, fip->inum)) != NULL) { - log_error("Write NTSTATUS is %d", (int) IoStatus); - fuse_reply_err(fip->req, EIO); + if (new_size > xinode->size) + { + xinode->size = new_size; + } } else { - 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; - } - else - { - log_error("inode at inode_table[%ld] is NULL", fip->inode); - } - - } - } - free(fip); -} - -void xfuse_devredir_cb_rmdir_or_file(void *vp, tui32 IoStatus) -{ - XFUSE_INFO *fip; - XRDP_INODE *xinode; - - fip = (XFUSE_INFO *) vp; - if (fip == NULL) - return; - - if (IoStatus != 0) - { - fuse_reply_err(fip->req, EBADF); - free(fip); - return; - } - - /* now delete the item in xrdp fs */ - xinode = xfuse_get_inode_from_pinode_name(fip->inode, fip->name); - if (xinode == NULL) - { - fuse_reply_err(fip->req, EBADF); - free(fip); - return; - } - - g_xrdp_fs.inode_table[xinode->inode] = NULL; - free(xinode); - - /* update parent */ - xinode = g_xrdp_fs.inode_table[fip->inode]; - xinode->nentries--; - - fuse_reply_err(fip->req, 0); - free(fip); -} - -void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) -{ - XFUSE_INFO *fip; - XRDP_INODE *old_xinode; - XRDP_INODE *new_xinode; - - fip = (XFUSE_INFO *) vp; - if (fip == NULL) - return; - - if (IoStatus != 0) - { - fuse_reply_err(fip->req, EEXIST); - free(fip); - return; - } - - /* - * update xrdp file system - */ - - /* if destination dir/file exists, delete it */ - if (xfuse_does_file_exist(fip->new_inode, fip->new_name)) - { - new_xinode = xfuse_get_inode_from_pinode_name(fip->new_inode, - fip->new_name); - - if (new_xinode) - { - if (new_xinode->mode & S_IFREG) - xfuse_delete_file_with_xinode(new_xinode); - else - xfuse_delete_dir_with_xinode(new_xinode); - - new_xinode = NULL; + log_error("inode %ld is invalid", fip->inum); } } - old_xinode = xfuse_get_inode_from_pinode_name(fip->inode, fip->name); - if (old_xinode == NULL) - { - fuse_reply_err(fip->req, EBADF); - free(fip); - return; - } - - old_xinode->parent_inode = fip->new_inode; - strncpy(old_xinode->name, fip->new_name, 1023); - old_xinode->name[1023] = 0; - - if (fip->inode != fip->new_inode) - { - /* file has been moved to a different dir */ - g_xrdp_fs.inode_table[fip->inode]->nentries--; - g_xrdp_fs.inode_table[fip->new_inode]->nentries++; - } - - fuse_reply_err(fip->req, 0); free(fip); } -void xfuse_devredir_cb_file_close(void *vp) +void xfuse_devredir_cb_rmdir_or_file(struct state_remove *fip, + enum NTSTATUS IoStatus) { - XFUSE_INFO *fip; - XRDP_INODE *xinode; + XFS_INODE *xinode = xfs_get(g_xfs, fip->inum); - fip = (XFUSE_INFO *) vp; - if (fip == NULL) + switch (IoStatus) { - log_error("fip is NULL"); - return; + case NT_STATUS_SUCCESS: + case NT_STATUS_NO_SUCH_FILE: + xfs_remove_entry(g_xfs, xinode->inum); /* Remove local copy */ + fuse_reply_err(fip->req, 0); + break; + + case NT_STATUS_SHARING_VIOLATION: + case NT_STATUS_ACCESS_DENIED: + fuse_reply_err(fip->req, EACCES); + break; + + default: + log_info("Error code %08x - fallthrough", (int) IoStatus); + fuse_reply_err(fip->req, EBADF); + break; + } + free(fip); +} + +void xfuse_devredir_cb_rename_file(struct state_rename *fip, + enum NTSTATUS IoStatus) +{ + int status; + + if (IoStatus != NT_STATUS_SUCCESS) + { + status = + (IoStatus == NT_STATUS_SHARING_VIOLATION) ? EBUSY : + (IoStatus == NT_STATUS_ACCESS_DENIED) ? EACCES : + /* default */ EEXIST ; + } + else + { + status = xfs_move_entry(g_xfs, fip->pinum, + fip->new_pinum, fip->name); } - if (fip->fi == NULL) - { - log_error("fip->fi is NULL"); - return; - } - - log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=0x%llx", - fip, fip->fi, (long long) fip->fi->fh); - - if ((xinode = g_xrdp_fs.inode_table[fip->inode]) == NULL) - { - log_debug("inode_table[%ld] is NULL", fip->inode); - fuse_reply_err(fip->req, EBADF); - return; - } - - log_debug("before: inode=%d nopen=%d", xinode->inode, xinode->nopen); - - if (xinode->nopen > 0) - xinode->nopen--; - - /* LK_TODO */ -#if 0 - if ((xinode->nopen == 0) && fip->fi && fip->fi->fh) - { - printf("LK_TODO: ################################ fi=%p fi->fh=0x%llx\n", - fip->fi, (long long) fip->fi->fh); - - free((char *) (tintptr) (fip->fi->fh)); - fip->fi->fh = 0; - } -#endif + fuse_reply_err(fip->req, status); + free(fip); +} +void xfuse_devredir_cb_file_close(struct state_close *fip) +{ fuse_reply_err(fip->req, 0); + xfs_decrement_file_open_count(g_xfs, fip->inum); + + free(fip); } /****************************************************************************** @@ -2075,83 +1419,89 @@ 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) { - XFUSE_INFO *fip; - XRDP_INODE *xinode; - char full_path[4096]; - tui32 device_id; - const char *cptr; + XFS_INODE *parent_xinode; + XFS_INODE *xinode = NULL; log_debug("looking for parent=%ld name=%s", parent, name); - xfuse_dump_fs(); - if (!xfuse_is_inode_valid(parent)) + if (strlen(name) > XFS_MAXFILENAMELEN) + { + fuse_reply_err(req, ENAMETOOLONG); + } + else if ((parent_xinode = xfs_get(g_xfs, parent)) == NULL) { log_error("inode %ld is not valid", parent); - fuse_reply_err(req, EBADF); - return; - } - - if (parent == 1 || - g_xrdp_fs.inode_table[parent]->is_loc_resource) - { - /* 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); - } + 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) + if (parent_xinode->device_id == 0) { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); + /* File cannot be remote - we either know about it or we don't */ + if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL) + { + log_debug("found entry for parent=%ld name=%s", + parent, name); + make_fuse_entry_reply(req, xinode); + } + else + { + fuse_reply_err(req, ENOENT); + } } else { - device_id = xfuse_get_device_id_for_inode(parent, full_path); + /* specified file resides on redirected share + * + * We always look these up, and relying on libfuse to do sane + * caching */ + struct state_lookup *fip = g_new0(struct state_lookup, 1); + char *full_path = get_name_for_entry_in_parent(parent, name); - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(full_path, '/')) == NULL) - { - cptr = "\\"; - } - - 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)) + if (fip == NULL || full_path == NULL) { - log_error("failed to send dev_redir_lookup_entry() cmd"); - fuse_reply_err(req, EREMOTEIO); + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); free(fip); + free(full_path); + } + else + { + const char *cptr; + + fip->req = req; + fip->pinum = parent; + strcpy(fip->name, name); + + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); + + /* If the file already exists on our side, save the inum + * and generation. If it's not remote any more this means we + * can remove it when we get the response + */ + if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL) + { + fip->existing_inum = xinode->inum; + fip->existing_generation = xinode->generation; + } + log_debug("Looking up %s in %s on %d", name, cptr, + parent_xinode->device_id); + /* + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_lookup_entry() + */ + if (devredir_lookup_entry(fip, parent_xinode->device_id, cptr)) + { + log_error("failed to send devredir_lookup_entry() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + free(full_path); } } } @@ -2159,161 +1509,119 @@ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) /** * Get file attributes + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct xrdp_inode *xino; - struct stat stbuf; - - (void) fi; + XFS_INODE *xino; log_debug("req=%p ino=%ld", req, ino); /* if ino is not valid, just return */ - if (!xfuse_is_inode_valid(ino)) + if ((xino = xfs_get(g_xfs, ino)) == NULL) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - - xino = g_xrdp_fs.inode_table[ino]; - if (!xino) + else { - log_debug("****** invalid ino=%ld", ino); - fuse_reply_err(req, EBADF); - return; + make_fuse_attr_reply(req, xino); } - - memset(&stbuf, 0, sizeof(stbuf)); - stbuf.st_ino = ino; - stbuf.st_mode = xino->mode; - stbuf.st_nlink = xino->nlink; - stbuf.st_size = xino->size; - fuse_reply_attr(req, &stbuf, 1.0); } /** * *****************************************************************************/ -#if 0 -static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b, - const char *name, fuse_ino_t ino) -{ - struct stat stbuf; - size_t oldsize = b->size; - - log_debug("adding ino=%ld name=%s", ino, name); - - b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0); - b->p = (char *) realloc(b->p, b->size); - - memset(&stbuf, 0, sizeof(stbuf)); - stbuf.st_ino = ino; - fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, - b->size); -} -#endif - +/* + * Adds an entry to the buffer, using fuse_add_direnty() + * + * Returns 1 for success, or zero if the entry couldn't be added + */ static int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b, - const char *name, fuse_ino_t ino) + XFS_INODE *xinode, off_t offset) { struct stat stbuf; - int len; - - len = fuse_add_direntry(req, NULL, 0, name, NULL, 0); - if (b->bytes_in_buf + len > 4096) - { - log_debug("not adding entry because dirbuf overflow would occur"); - return -1; - } + size_t len; + int result = 0; memset(&stbuf, 0, sizeof(stbuf)); - stbuf.st_ino = ino; + stbuf.st_ino = xinode->inum; + stbuf.st_mode = xinode->mode & ~g_umask; - fuse_add_direntry(req, - &b->buf[b->bytes_in_buf], /* index where new entry will be added to buf */ - 4096 - len, /* remaining size of buf */ - name, /* name of entry */ + /* + * Try to add the entry. From the docs for fuse_add_direntry():- + * "Buffer needs to be large enough to hold the entry. If it's not, + * then the entry is not filled in but the size of the entry is + * still returned." + */ + len = fuse_add_direntry(req, + &b->buf[b->len], /* index where new entry will be added to buf */ + sizeof(b->buf) - b->len, /* Space left */ + xinode->name, /* name of entry */ &stbuf, /* file attributes */ - b->bytes_in_buf + len /* offset of next entry */ + offset /* offset of next entry */ ); + if (len + b->len <= sizeof(b->buf)) + { + /* Entry fitted in OK */ + b->len += len; + result = 1; + } - b->bytes_in_buf += len; - return 0; + return result; } /** - * + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { - XRDP_INODE *xinode; - XRDP_INODE *ti; - struct dir_info *di; + XFS_INODE *xinode; + struct xfs_dir_handle *dh; struct dirbuf1 b; - fuse_ino_t i; - int first_time; log_debug("req=%p inode=%ld size=%zd offset=%lld", req, ino, size, (long long) off); - /* do we have a valid inode? */ - if (!xfuse_is_inode_valid(ino)) + /* On the first call, check the inode is valid */ + if (off == 0 && !xfs_get(g_xfs, ino)) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - - di = (struct dir_info *) (tintptr) (fi->fh); - if (di == NULL) + else if ((dh = (struct xfs_dir_handle *) fi->fh) == NULL) { /* something seriously wrong somewhere! */ fuse_reply_buf(req, 0, 0); - return; } - - b.bytes_in_buf = 0; - first_time = (di->index == FIRST_INODE) ? 1 : 0; - - for (i = di->index; i < g_xrdp_fs.num_entries; i++, di->index++) + else { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; + b.len = 0; - /* match parent inode */ - if (xinode->parent_inode != ino) - continue; - - if (first_time) + off_t new_off = off; + while ((xinode = xfs_readdir(g_xfs, dh, &new_off)) != NULL) { - first_time = 0; - ti = g_xrdp_fs.inode_table[ino]; - if (!ti) + if (xfuse_dirbuf_add1(req, &b, xinode, new_off) == 0) { - log_debug("****** g_xrdp_fs.inode_table[%ld] is NULL", ino); - fuse_reply_buf(req, NULL, 0); - return; + break; /* buffer is full */ } - xfuse_dirbuf_add1(req, &b, ".", ino); - xfuse_dirbuf_add1(req, &b, "..", ti->parent_inode); + /* Make sure we get the next entry next time */ + off = new_off; } - if (xfuse_dirbuf_add1(req, &b, xinode->name, xinode->inode)) - break; /* buffer is full */ + fuse_reply_buf(req, b.buf, b.len); } - - if (b.bytes_in_buf) - fuse_reply_buf(req, b.buf, b.bytes_in_buf); - else - fuse_reply_buf(req, NULL, 0); } + /** * Create a directory *****************************************************************************/ @@ -2321,149 +1629,95 @@ static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { - XRDP_INODE *xinode; - struct fuse_entry_param e; + XFS_INODE *xinode; log_debug("entered: parent_inode=%ld name=%s", parent, name); - if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) != NULL) + if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL) { /* dir already exists, just return it */ - memset(&e, 0, sizeof(struct fuse_entry_param)); - - 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); - return; + make_fuse_entry_reply(req, xinode); + } + else + { + /* dir does not exist, create it */ + xfuse_create_dir_or_file(req, parent, name, mode | S_IFDIR, NULL); } - - /* dir does not exist, create it */ - xfuse_create_dir_or_file(req, parent, name, mode, NULL, S_IFDIR); -} - -/** - * Remove specified dir - *****************************************************************************/ - -static void xfuse_cb_rmdir(fuse_req_t req, fuse_ino_t parent, - const char *name) -{ - xfuse_remove_dir_or_file(req, parent, name, 1); -} - -static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, - const char *name) -{ - xfuse_remove_dir_or_file(req, parent, name, 2); } /** * Remove a dir or file * - * @param type 1=dir, 2=file *****************************************************************************/ -static void xfuse_remove_dir_or_file(fuse_req_t req, fuse_ino_t parent, - const char *name, int type) +static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) { - XFUSE_INFO *fip; - XRDP_INODE *xinode; - const char *cptr; - char full_path[4096]; - tui32 device_id; + XFS_INODE *xinode; log_debug("entered: parent=%ld name=%s", parent, name); - /* is parent inode valid? */ - if (!xfuse_is_inode_valid(parent)) + if (strlen(name) > XFS_MAXFILENAMELEN) { - log_error("inode %ld is not valid", parent); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENAMETOOLONG); } - - if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) == NULL) + else if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) == NULL) { log_error("did not find file with pinode=%ld name=%s", parent, name); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - device_id = xfuse_get_device_id_for_inode(parent, full_path); - - log_debug("path=%s nentries=%d", full_path, xinode->nentries); - - if ((type == 1) && (xinode->nentries != 0)) + else if ((xinode->mode & S_IFDIR) != 0 && + !xfs_is_dir_empty(g_xfs, xinode->inum)) { - log_debug("cannot rmdir; lookup count is %d", xinode->nentries); + log_debug("cannot rmdir; directory is not empty"); fuse_reply_err(req, ENOTEMPTY); - return; - } - else if (type == 2) - { - if ((xinode->nopen > 1) || ((xinode->nopen == 1) && - (xinode->close_in_progress == 0))) - { - log_debug("cannot unlink; open count is %d", xinode->nopen); - fuse_reply_err(req, EBUSY); - return; - } } - strcat(full_path, "/"); - strncat(full_path, name, sizeof(full_path) - strlen(full_path) - 1); - - if (xinode->is_loc_resource) + else if (xinode->device_id == 0) { /* specified file is a local resource */ //XFUSE_HANDLE *fh; log_debug("LK_TODO: this is still a TODO"); fuse_reply_err(req, EINVAL); - return; } - - /* specified file resides on redirected share */ - - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } + /* specified file resides on redirected share */ + struct state_remove *fip = g_new0(struct state_remove, 1); + char *full_path = xfs_get_full_path(g_xfs, xinode->inum); + if (!full_path || !fip) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + free(fip); + free(full_path); + } + else + { + const char *cptr; - fip->req = req; - fip->inode = parent; - fip->device_id = device_id; - strncpy(fip->name, name, 1024); - fip->name[1023] = 0; - fip->type = type; + fip->req = req; + fip->inum = xinode->inum; - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(full_path, '/')) == NULL) - { - cptr = "\\"; - } + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); - /* get dev_redir to open the remote file */ - if (devredir_rmdir_or_file((void *) fip, device_id, cptr, O_RDWR)) - { - log_error("failed to send devredir_rmdir_or_file() cmd"); - fuse_reply_err(req, EREMOTEIO); - free(fip); + /* get devredir to open the remote file + * + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_rmdir_or_file() + */ + if (devredir_rmdir_or_file(fip, xinode->device_id, cptr)) + { + log_error("failed to send devredir_rmdir_or_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + + } + free(full_path); } } @@ -2471,127 +1725,92 @@ static void xfuse_cb_rename(fuse_req_t req, fuse_ino_t old_parent, const char *old_name, fuse_ino_t new_parent, const char *new_name) { - XRDP_INODE *old_xinode; - XFUSE_INFO *fip; - tui32 new_device_id; - const char *cptr; - char old_full_path[1024]; - char new_full_path[1024]; - const char *cp; - - tui32 device_id; + XFS_INODE *old_xinode; + XFS_INODE *new_parent_xinode; log_debug("entered: old_parent=%ld old_name=%s new_parent=%ld new_name=%s", old_parent, old_name, new_parent, new_name); - xfuse_dump_fs(); - /* is old_parent inode valid? */ - if (!xfuse_is_inode_valid(old_parent)) + if (strlen(old_name) > XFS_MAXFILENAMELEN || + strlen(new_name) > XFS_MAXFILENAMELEN) { - log_error("inode %ld is not valid", old_parent); - fuse_reply_err(req, EINVAL); - return; + fuse_reply_err(req, ENAMETOOLONG); } - - /* is new_parent inode valid? */ - if (!xfuse_is_inode_valid(new_parent)) - { - log_error("inode %ld is not valid", new_parent); - fuse_reply_err(req, EINVAL); - return; - } - - if ((old_name == NULL) || (strlen(old_name) == 0)) - { - fuse_reply_err(req, EINVAL); - return; - } - - if ((new_name == NULL) || (strlen(new_name) == 0)) - { - fuse_reply_err(req, EINVAL); - return; - } - - old_xinode = xfuse_get_inode_from_pinode_name(old_parent, old_name); - if (old_xinode == NULL) + else if (!(old_xinode = xfs_lookup_in_dir(g_xfs, old_parent, old_name))) { log_error("did not find file with pinode=%ld name=%s", old_parent, old_name); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - /* if file is open, cannot rename */ - if (old_xinode->nopen != 0) + else if (!(new_parent_xinode = xfs_get(g_xfs, new_parent))) { - fuse_reply_err(req, EBUSY); - return; + log_error("inode %ld is not valid", new_parent); + fuse_reply_err(req, EINVAL); } - /* rename across file systems not yet supported */ - new_device_id = xfuse_get_device_id_for_inode(new_parent, new_full_path); - strcat(new_full_path, "/"); - strcat(new_full_path, new_name); + else if (!xfs_check_move_entry(g_xfs, old_xinode->inum, + new_parent, new_name)) + { + fuse_reply_err(req, EINVAL); + } - if (new_device_id != old_xinode->device_id) + else if (new_parent_xinode->device_id != old_xinode->device_id) { log_error("rename across file systems not supported"); fuse_reply_err(req, EINVAL); - return; } - if (old_xinode->is_loc_resource) + else if (old_xinode->device_id == 0) { /* specified file is a local resource */ log_debug("LK_TODO: this is still a TODO"); fuse_reply_err(req, EINVAL); - return; } - /* resource is on a redirected share */ - - device_id = old_xinode->device_id; - - xfuse_get_device_id_for_inode(old_parent, old_full_path); - strcat(old_full_path, "/"); - strcat(old_full_path, old_name); - - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } + /* resource is on a redirected share */ + struct state_rename *fip = g_new0(struct state_rename, 1); + char *old_full_path = xfs_get_full_path(g_xfs, old_xinode->inum); + char *new_full_path = get_name_for_entry_in_parent(new_parent, + new_name); - fip->req = req; - fip->inode = old_parent; - fip->new_inode = new_parent; - strncpy(fip->name, old_name, 1024); - strncpy(fip->new_name, new_name, 1024); - fip->name[1023] = 0; - fip->new_name[1023] = 0; - fip->device_id = device_id; + if (!old_full_path || !new_full_path || !fip) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + free(fip); + free(old_full_path); + free(new_full_path); + } + else + { + const char *cptr; + const char *cp; - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(old_full_path, '/')) == NULL) - { - cptr = "\\"; - } + fip->req = req; + fip->pinum = old_xinode->inum; + fip->new_pinum = new_parent; + strcpy(fip->name, new_name); - if ((cp = strchr(new_full_path, '/')) == NULL) - { - cp = "\\"; - } + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(old_full_path); + cp = filename_on_device(new_full_path); - /* get dev_redir to open the remote file */ - if (dev_redir_file_open((void *) fip, device_id, cptr, - O_RDWR, S_IFREG | OP_RENAME_FILE, cp)) - { - log_error("failed to send dev_redir_file_open() cmd"); - fuse_reply_err(req, EREMOTEIO); - free(fip); + /* + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_rename_file() + */ + if (devredir_file_rename(fip, old_xinode->device_id, cptr, cp)) + { + log_error("failed to send devredir_file_rename() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + free(old_full_path); + free(new_full_path); + } } } @@ -2602,295 +1821,269 @@ static void xfuse_cb_rename(fuse_req_t req, * @param parent parent inode * @param name name of dir or file to create * @param mode creation mode - * @param fi for storing file handles - * @param type S_IFDIR for dir and S_IFREG for file + * @param fi File info from fuse_lowlevel_ops.create, or NULL. + * This may need to be copied if we're not replying immediately *****************************************************************************/ static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, - struct fuse_file_info *fi, int type) + struct fuse_file_info *fi) { - XFUSE_INFO *fip; - const char *cptr; - char full_path[1024]; - tui32 device_id; + XFS_INODE *xinode; - full_path[0] = 0; - - log_debug("entered: parent_ino=%ld name=%s type=%s", - parent, name, (type == S_IFDIR) ? "dir" : "file"); + log_debug("entered: parent_ino=%ld name=%s mode=%o type=%s", + parent, name, mode, (mode & S_IFDIR) ? "dir" : "file"); /* name must be valid */ - if ((name == NULL) || (strlen(name) == 0)) + if (strlen(name) > XFS_MAXFILENAMELEN) { - log_error("invalid name"); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENAMETOOLONG); } - - /* is parent inode valid? */ - if ((parent == 1) || (!xfuse_is_inode_valid(parent))) + else { - log_error("inode %ld is not valid", parent); - fuse_reply_err(req, EBADF); - return; - } + /* Sanitise the mode + * + * Currently group/world write is not allowed. + */ + if (mode & S_IFDIR) + { + mode = (mode & 0777) | S_IFDIR; + } + else + { + mode = (mode & 0777) | S_IFREG; + } - device_id = xfuse_get_device_id_for_inode(parent, full_path); - strcat(full_path, "/"); - strcat(full_path, name); + /* is parent inode valid? */ + if (parent == FUSE_ROOT_ID || + (xinode = xfs_get(g_xfs, parent)) == NULL || + (xinode->mode & S_IFDIR) == 0) + { + log_error("inode %ld is not valid", parent); + fuse_reply_err(req, ENOENT); + } + else if (xinode->device_id == 0) + { + /* specified file is a local resource */ + //XFUSE_HANDLE *fh; - XRDP_INODE *xinode = g_xrdp_fs.inode_table[parent]; - if (xinode->is_loc_resource) - { - /* specified file is a local resource */ - //XFUSE_HANDLE *fh; + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + } + else + { + struct state_create *fip = g_new0(struct state_create, 1); + char *full_path = get_name_for_entry_in_parent(parent, name); - log_debug("LK_TODO: this is still a TODO"); - fuse_reply_err(req, EINVAL); - return; - } + if (full_path == NULL || fip == NULL) + { + log_error("Out of memory"); + fuse_reply_err(req, ENOMEM); + free(fip); + free(full_path); + } + else + { + const char *cptr; - /* specified file resides on redirected share */ + fip->req = req; + if (fi != NULL) + { + fip->fi = *fi; + } + fip->pinum = parent; + strcpy(fip->name, name); + fip->mode = mode; - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) - { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); - fip->req = req; - fip->fi = fi; - fip->inode = parent; - fip->device_id = device_id; - fip->mode = type; - fip->reply_type = RT_FUSE_REPLY_CREATE; - strncpy(fip->name, name, 1024); - fip->name[1023] = 0; - - log_debug("+++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p", fip, fip->fi); - - /* LK_TODO need to handle open permissions */ - - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(full_path, '/')) == NULL) - { - cptr = "\\"; - } - - /* get dev_redir to open the remote file */ - if (dev_redir_file_open((void *) fip, device_id, cptr, - O_CREAT, type, NULL)) - { - log_error("failed to send dev_redir_file_open() cmd"); - fuse_reply_err(req, EREMOTEIO); + /* + * If this call succeeds, further request processing happens + * in xfuse_devredir_cb_create_file() + */ + if (devredir_file_create(fip, xinode->device_id, cptr, mode)) + { + log_error("failed to send devredir_file_create() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + free(full_path); + } + } } } /** * Open specified file +* + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - XRDP_INODE *xinode; - XFUSE_INFO *fip; - const char *cptr; - char full_path[4096]; - tui32 device_id; + XFS_INODE *xinode; log_debug("entered: ino=%ld", ino); - if (!xfuse_is_inode_valid(ino)) + if (!(xinode = xfs_get(g_xfs, ino))) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - - /* if ino points to a dir, fail the open request */ - xinode = g_xrdp_fs.inode_table[ino]; - if (!xinode) - { - log_debug("****** g_xrdp_fs.inode_table[%ld] is NULL", ino); - fuse_reply_err(req, EBADF); - return; - } - if (xinode->mode & S_IFDIR) + else if (xinode->mode & S_IFDIR) { + /* Can't open directories like this */ log_debug("reading/writing a dir not allowed!"); fuse_reply_err(req, EISDIR); - return; } - - switch (fi->flags & O_ACCMODE) + else if ((fi->flags & O_ACCMODE) != O_RDONLY && + (fi->flags & O_ACCMODE) != O_WRONLY && + (fi->flags & O_ACCMODE) != O_RDWR) { - case O_RDONLY: - case O_WRONLY: - case O_RDWR: - break; - - default: - log_debug("Invalid access mode specified"); - fuse_reply_err(req, EINVAL); - return; - } - - device_id = xfuse_get_device_id_for_inode(ino, full_path); - - if (xinode->is_loc_resource) + log_debug("Invalid access mode specified"); + fuse_reply_err(req, EINVAL); + } + else if (xinode->device_id == 0) { /* specified file is a local resource */ XFUSE_HANDLE *fh = g_new0(XFUSE_HANDLE, 1); fh->is_loc_resource = 1; fi->fh = (tintptr) fh; fuse_reply_open(req, fi); - return; } - - /* specified file resides on redirected share */ - - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } + /* specified file resides on redirected share */ + struct state_open *fip = g_new0(struct state_open, 1); + char *full_path = xfs_get_full_path(g_xfs, ino); - fip->req = req; - fip->inode = ino; - fip->device_id = device_id; - fip->fi = fi; + if (!full_path || !fip) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + free(fip); + free(full_path); + } + else + { + const char *cptr; - log_debug("LK_TODO: fip->fi = %p", fip->fi); + fip->req = req; + fip->fi = *fi; + fip->inum = ino; - strncpy(fip->name, full_path, 1024); - fip->name[1023] = 0; - fip->reply_type = RT_FUSE_REPLY_OPEN; + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(full_path, '/')) == NULL) - { - cptr = "\\"; - } - /* get dev_redir to open the remote file - * - * For a successful call, if the caller has set O_TRUNC when writing - * the file, fuse should call us back via fuse_cb_setattr() to set - * the size to zero - we don't need to do this ourselves. - */ - if (dev_redir_file_open((void *) fip, device_id, cptr, - fi->flags, S_IFREG, NULL)) - { - log_error("failed to send dev_redir_file_open() cmd"); - fuse_reply_err(req, EREMOTEIO); + /* get devredir to open the remote file + * + * If the caller has set O_TRUNC when writing the file, + * fuse should call us back via fuse_cb_setattr() to set + * the size to zero - we don't need to do this ourselves. + * + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_open_file() + */ + if (devredir_file_open(fip, xinode->device_id, cptr, fi->flags)) + { + log_error("failed to send devredir_file_open() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + free(full_path); + } } } +/* + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately + */ static void xfuse_cb_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - XFUSE_INFO *fip = NULL; + XFS_INODE *xinode; + XFUSE_HANDLE *handle = (XFUSE_HANDLE *) (tintptr) (fi->fh); log_debug("entered: ino=%ld fi=%p fi->fh=0x%llx", ino, fi, (long long) fi->fh); - if (!xfuse_is_inode_valid(ino)) + if ((xinode = xfs_get(g_xfs, ino)) == NULL) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - - XRDP_INODE *xinode = g_xrdp_fs.inode_table[ino]; - if (!xinode) - { - log_debug("****** g_xrdp_fs.inode_table[%ld] is NULL", ino); - fuse_reply_err(req, 0); - return; - } - if (xinode->is_loc_resource) + else if (xinode->device_id == 0) { /* specified file is a local resource */ fuse_reply_err(req, 0); - return; } - - /* specified file resides on redirected share */ - - log_debug("nopen=%d", xinode->nopen); - - /* if file is not opened, just return */ - if (xinode->nopen == 0) + else { - log_debug("cannot close because file not opened"); - fuse_reply_err(req, 0); - return; + /* specified file resides on redirected share */ + + struct state_close *fip = g_new0(struct state_close, 1); + if (fip == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; + } + + fip->req = req; + fip->inum = ino; + fip->fi = *fi; + + fi->fh = 0; + + /* + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_file_close() + */ + if (devredir_file_close(fip, xinode->device_id, handle->FileId)) + { + log_error("failed to send devredir_close_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + } + + free(handle); } - - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) - { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } - - fip->req = req; - fip->inode = ino; - fip->device_id = handle->DeviceId; - fip->fi = fi; - - log_debug(" +++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=0x%llx", - fip, fip->fi, (long long) fip->fi->fh); - - fip->fi->fh = 0; - xinode->close_in_progress = 1; - - if (devredir_file_close((void *) fip, fip->device_id, handle->FileId)) - { - log_error("failed to send devredir_close_file() cmd"); - fuse_reply_err(req, EREMOTEIO); - } - - free(handle); } /** + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { XFUSE_HANDLE *fh; - XFUSE_INFO *fusep; - XRDP_INODE *xinode; + struct state_read *fusep; + XFS_INODE *xinode; struct req_list_item *rli; - long handle; log_debug("want_bytes %zd bytes at off %lld", size, (long long) off); - if (fi->fh == 0) + if ((fh = (XFUSE_HANDLE *)fi->fh) == NULL) { fuse_reply_err(req, EINVAL); - return; } - - handle = fi->fh; - fh = (XFUSE_HANDLE *) handle; - - if (fh->is_loc_resource) + else if (fh->is_loc_resource) { /* target file is in .clipboard dir */ log_debug("target file is in .clipboard dir"); - if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL) + if ((xinode = xfs_get(g_xfs, ino)) == NULL) { log_error("ino does not exist in xrdp_fs"); fuse_reply_buf(req, 0, 0); @@ -2914,82 +2107,89 @@ static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, clipboard_request_file_data(rli->stream_id, rli->lindex, (int) off, (int) size); } - - return; } - - /* target file is on a remote device */ - - fusep = g_new0(XFUSE_INFO, 1); - if (fusep == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; - } - fusep->req = req; - fusep->inode = ino; - fusep->device_id = fh->DeviceId; - fusep->fi = fi; + /* target file is on a remote device */ - devredir_file_read(fusep, fh->DeviceId, fh->FileId, size, off); + fusep = g_new0(struct state_read, 1); + if (fusep == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + } + else + { + fusep->req = req; + + /* + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_read_file() + */ + devredir_file_read(fusep, fh->DeviceId, fh->FileId, size, off); + } + } } /** + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { XFUSE_HANDLE *fh; - XFUSE_INFO *fusep; - long handle; + struct state_write *fusep; log_debug("write %zd bytes at off %lld to inode=%ld", size, (long long) off, ino); - if (fi->fh == 0) + if ((fh = (XFUSE_HANDLE *)fi->fh) == NULL) { log_error("file handle fi->fh is NULL"); fuse_reply_err(req, EINVAL); - return; } - - handle = fi->fh; - fh = (XFUSE_HANDLE *) handle; - - if (fh->is_loc_resource) + else if (fh->is_loc_resource) { /* target file is in .clipboard dir */ log_debug("THIS IS STILL A TODO!"); - return; + fuse_reply_err(req, EINVAL); } - - /* target file is on a remote device */ - - fusep = g_new0(XFUSE_INFO, 1); - if (fusep == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - return; + /* target file is on a remote device */ + + fusep = g_new0(struct state_write, 1); + if (fusep == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + } + else + { + fusep->req = req; + fusep->inum = ino; + + /* + * If this call succeeds, further request processing happens in + * xfuse_devredir_cb_write_file() + */ + devredir_file_write(fusep, fh->DeviceId, fh->FileId, buf, + size, off); + } } - - fusep->req = req; - fusep->inode = ino; - fusep->device_id = fh->DeviceId; - fusep->fi = fi; - - log_debug("+++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=0x%llx", - fusep, fusep->fi, (long long) fusep->fi->fh); - - dev_redir_file_write(fusep, fh->DeviceId, fh->FileId, buf, size, off); - log_debug("exiting"); } /** *****************************************************************************/ +/* + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately + */ static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) @@ -2997,7 +2197,7 @@ static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, log_debug("entered: parent_inode=%ld, name=%s fi=%p", parent, name, fi); - xfuse_create_dir_or_file(req, parent, name, mode, fi, S_IFREG); + xfuse_create_dir_or_file(req, parent, name, mode & ~S_IFDIR , fi); } /** @@ -3014,218 +2214,208 @@ static void xfuse_cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, #endif /** + * Sets attributes for a directory entry. + * + * If the file resides on a remote share, devredir + * is asked to update it. This will result in the following + * callbacks:- + * - xfuse_devredir_cb_setattr() to update our copy and return status + * + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { - XRDP_INODE *xinode; - struct stat st; + XFS_INODE *xinode; log_debug("entered to_set=0x%x", to_set); - if (!xfuse_is_inode_valid(ino)) + if ((xinode = xfs_get(g_xfs, ino)) == NULL) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - return; + fuse_reply_err(req, ENOENT); } - - if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL) + else if (((to_set & FUSE_SET_ATTR_UID) && attr->st_uid != xinode->uid) || + ((to_set & FUSE_SET_ATTR_GID) && attr->st_gid != xinode->gid)) { - log_debug("g_xrdp_fs.inode_table[%ld] is NULL", ino); - fuse_reply_err(req, EBADF); - return; + /* We don't allow any of these */ + fuse_reply_err(req, EPERM); } - - if (to_set & FUSE_SET_ATTR_MODE) + else if ((to_set & FUSE_SET_ATTR_MODE) && + (attr->st_mode & ~(0777 | S_IFDIR | S_IFREG)) != 0) { - xinode->mode = attr->st_mode; - log_debug("FUSE_SET_ATTR_MODE"); - + /* We only support standard mode bits and S_IFDIR / S_IFREG */ + log_error("Asking for invalid mode bits 0%o to be set", attr->st_mode); + fuse_reply_err(req, EINVAL); } - - if (to_set & FUSE_SET_ATTR_UID) + else { - xinode->uid = attr->st_uid; - log_debug("FUSE_SET_ATTR_UID"); + struct file_attr attrs = {0}; + tui32 change_mask = 0; + + if ((to_set & FUSE_SET_ATTR_MODE) && xinode->mode != attr->st_mode) + { + attrs.mode = attr->st_mode & (0777 | S_IFDIR | S_IFREG); + change_mask |= TO_SET_MODE; + } + + if ((to_set & FUSE_SET_ATTR_SIZE) && xinode->size != attr->st_size) + { + attrs.size = attr->st_size; + change_mask |= TO_SET_SIZE; + } + + if ((to_set & FUSE_SET_ATTR_ATIME) && xinode->atime != attr->st_atime) + { + attrs.atime = attr->st_atime; + change_mask |= TO_SET_ATIME; + } + + if ((to_set & FUSE_SET_ATTR_MTIME) && xinode->mtime != attr->st_mtime) + { + attrs.mtime = attr->st_mtime; + change_mask |= TO_SET_MTIME; + } + + if (change_mask == 0) + { + /* No changes have been made */ + make_fuse_attr_reply(req, xinode); + } + else if (xinode->device_id == 0) + { + /* Update the local fs */ + update_inode_file_attributes(&attrs, change_mask, xinode); + make_fuse_attr_reply(req, xinode); + } + else + { + struct state_setattr *fip = g_new0(struct state_setattr, 1); + char *full_path = xfs_get_full_path(g_xfs, ino); + if (!full_path || !fip) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + free(fip); + free(full_path); + } + else + { + const char *cptr; + fip->req = req; + fip->inum = ino; + /* Save the important stuff so we can update our node if the + * remote update is successful */ + fip->fattr = attrs; + fip->change_mask = change_mask; + + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); + + /* + * If this call succeeds, further request processing happens + * in xfuse_devredir_cb_setattr() + */ + if (devredir_setattr_for_entry(fip, xinode->device_id, + cptr, &attrs, change_mask) < 0) + { + fuse_reply_err(req, EIO); + free(fip); + } + + free(full_path); + } + } } - - if (to_set & FUSE_SET_ATTR_GID) - { - xinode->gid = attr->st_gid; - log_debug("FUSE_SET_ATTR_GID"); - } - - if (to_set & FUSE_SET_ATTR_SIZE) - { - log_debug("previous file size: %lld", (long long) attr->st_size); - xinode->size = attr->st_size; - log_debug("returning file size: %zd", xinode->size); - } - - if (to_set & FUSE_SET_ATTR_ATIME) - { - xinode->atime = attr->st_atime; - log_debug("FUSE_SET_ATTR_ATIME"); - } - - if (to_set & FUSE_SET_ATTR_MTIME) - { - xinode->mtime = attr->st_mtime; - log_debug("FUSE_SET_ATTR_MTIME"); - } - - if (to_set & FUSE_SET_ATTR_ATIME_NOW) - { - xinode->atime = attr->st_atime; - log_debug("FUSE_SET_ATTR_ATIME_NOW"); - } - - if (to_set & FUSE_SET_ATTR_MTIME_NOW) - { - xinode->mtime = attr->st_mtime; - log_debug("FUSE_SET_ATTR_MTIME_NOW"); - } - - memset(&st, 0, sizeof(st)); - st.st_ino = xinode->inode; - st.st_mode = xinode->mode; - st.st_size = xinode->size; - st.st_uid = xinode->uid; - st.st_gid = xinode->gid; - st.st_atime = xinode->atime; - st.st_mtime = xinode->mtime; - st.st_ctime = xinode->ctime; - - fuse_reply_attr(req, &st, 1.0); /* LK_TODO just faking for now */ } /** * Get dir listing + * + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) -{ - struct opendir_req *odreq; - - /* save request */ - odreq = g_new(struct opendir_req, 1); - odreq->req = req; - odreq->ino = ino; - odreq->fi = fi; - - if (fifo_is_empty(&g_fifo_opendir)) - { - fifo_insert(&g_fifo_opendir, odreq); - xfuse_proc_opendir_req(req, ino, fi); - } - else - { - /* place req in FIFO; xfuse_devredir_cb_enum_dir_done() will handle it */ - fifo_insert(&g_fifo_opendir, odreq); - } -} - -/** - * Process the next opendir req - * - * @return 0 of the request was sent for remote lookup, -1 otherwise - *****************************************************************************/ -static int xfuse_proc_opendir_req(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct dir_info *di; - XRDP_INODE *xinode; - XFUSE_INFO *fip; - tui32 device_id; - char full_path[4096]; - const char *cptr; + XFS_INODE *xinode; log_debug("inode=%ld", ino); - if (!xfuse_is_inode_valid(ino)) + if ((xinode = xfs_get(g_xfs, ino)) == NULL) { log_error("inode %ld is not valid", ino); - fuse_reply_err(req, EBADF); - g_free(fifo_remove(&g_fifo_opendir)); - return -1; + fuse_reply_err(req, ENOENT); } - - if (ino == 1) - goto done; /* special case; enumerate top level dir */ - - if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL) + else if (xinode->device_id == 0) { - log_debug("g_xrdp_fs.inode_table[%ld] is NULL", ino); - fuse_reply_err(req, EBADF); - g_free(fifo_remove(&g_fifo_opendir)); - return -1; + if ((fi->fh = (tintptr) xfs_opendir(g_xfs, ino)) == 0) + { + fuse_reply_err(req, ENOMEM); + } + else + { + fuse_reply_open(req, fi); + } } - - if (xinode->is_loc_resource) - goto done; - - /* enumerate resources on a remote device */ - xfuse_mark_as_stale(ino); - - log_debug("did not find entry; redirecting call to dev_redir"); - device_id = xfuse_get_device_id_for_inode(ino, full_path); - - log_debug("dev_id=%d ino=%ld full_path=%s", device_id, ino, full_path); - - fip = g_new0(XFUSE_INFO, 1); - if (fip == NULL) + else { - log_error("system out of memory"); - fuse_reply_err(req, ENOMEM); - g_free(fifo_remove(&g_fifo_opendir)); - return -1; + log_debug("did not find entry; redirecting call to devredir"); + struct state_dirscan *fip = g_new0(struct state_dirscan, 1); + char *full_path = xfs_get_full_path(g_xfs, ino); + + if (full_path == NULL || fip == NULL) + { + fuse_reply_err(req, ENOMEM); + free(fip); + free(full_path); + } + else + { + const char *cptr; + log_debug("dev_id=%d ino=%ld full_path=%s", + xinode->device_id, ino, full_path); + + fip->req = req; + fip->pinum = ino; + fip->fi = *fi; + + /* we want path minus 'root node of the share' */ + cptr = filename_on_device(full_path); + + /* + * If this call succeeds, further request processing happens in:- + * - xfuse_devredir_cb_enum_dir_add_entry() + * Called for every file in the directory + * - xfuse_devredir_cb_enum_dir_done() + * Called at the end of the directory scan + */ + if (devredir_get_dir_listing(fip, xinode->device_id, cptr)) + { + log_error("failed to send devredir_get_dir_listing() cmd"); + fuse_reply_buf(req, NULL, 0); + free(fip); + } + free(full_path); + } } - - fip->req = req; - fip->inode = ino; - fip->size = 0; - fip->fi = fi; - - fip->device_id = device_id; - - /* we want path minus 'root node of the share' */ - if ((cptr = strchr(full_path, '/')) == NULL) - { - cptr = "\\"; - } - - if (dev_redir_get_dir_listing((void *) fip, device_id, cptr)) - { - log_error("failed to send dev_redir_get_dir_listing() cmd"); - fuse_reply_buf(req, NULL, 0); - } - - return 0; - -done: - - di = g_new0(struct dir_info, 1); - di->index = FIRST_INODE; - fi->fh = (tintptr) di; - fuse_reply_open(req, fi); - g_free(fifo_remove(&g_fifo_opendir)); - return -1; } /** + * GOTCHA : For FUSE 2.9 at least, the 'fi' parameter is allocated on the + * stack by the caller, so must be copied if we're not using it + * to reply to FUSE immediately *****************************************************************************/ static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct dir_info *di; - - di = (struct dir_info *) (tintptr) fi->fh; - if (di) - free(di); - + struct xfs_dir_handle *dh = (struct xfs_dir_handle *) fi->fh; + xfs_closedir(g_xfs, dh); fuse_reply_err(req, 0); } @@ -3233,91 +2423,133 @@ static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino, * miscellaneous functions *****************************************************************************/ -/** - * Mark all files with matching parent inode as stale - * - * @param pinode the parent inode - *****************************************************************************/ - -static void -xfuse_mark_as_stale(fuse_ino_t pinode) +static void xfs_inode_to_fuse_entry_param(const XFS_INODE *xinode, + struct fuse_entry_param *e) { - fuse_ino_t i; - XRDP_INODE *xinode; - - if ((pinode < FIRST_INODE) || (pinode >= g_xrdp_fs.num_entries)) - return; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - /* match parent inode */ - if (xinode->parent_inode != pinode) - continue; - - /* got a match */ - xinode->stale = 1; - } + memset(e, 0, sizeof(*e)); + e->ino = xinode->inum; + e->attr_timeout = XFUSE_ATTR_TIMEOUT; + e->entry_timeout = XFUSE_ENTRY_TIMEOUT; + e->attr.st_ino = xinode->inum; + e->attr.st_mode = xinode->mode & ~g_umask; + e->attr.st_nlink = 1; + 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 = xinode->generation; } -/** - * Delete all files with matching parent inode that are marked as stale - * - * @param pinode the parent inode - *****************************************************************************/ - -static void -xfuse_delete_stale_entries(fuse_ino_t pinode) -{ - fuse_ino_t i; - XRDP_INODE *xinode; - - if ((pinode < FIRST_INODE) || (pinode >= g_xrdp_fs.num_entries)) - return; - - for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) - { - if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) - continue; - - /* match parent inode */ - if (xinode->parent_inode != pinode) - continue; - - /* got a match, but is it stale? */ - if (!xinode->stale) - continue; - - /* ok to delete */ - if (xinode->mode & S_IFREG) - xfuse_delete_file_with_xinode(xinode); - else - xfuse_recursive_delete_dir_with_xinode(xinode); - } -} - -static void make_fuse_entry_reply(fuse_req_t req, const XRDP_INODE *xinode) +static void make_fuse_entry_reply(fuse_req_t req, const XFS_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; - + xfs_inode_to_fuse_entry_param(xinode, &e); fuse_reply_entry(req, &e); } +static void make_fuse_attr_reply(fuse_req_t req, const XFS_INODE *xinode) +{ + struct stat st; + + memset(&st, 0, sizeof(st)); + st.st_ino = xinode->inum; + st.st_mode = xinode->mode & ~g_umask; + st.st_nlink = 1; + st.st_uid = xinode->uid; + st.st_gid = xinode->gid; + st.st_size = xinode->size; + st.st_atime = xinode->atime; + st.st_mtime = xinode->mtime; + st.st_ctime = xinode->ctime; + + fuse_reply_attr(req, &st, XFUSE_ATTR_TIMEOUT); +} + +/* + * Get the name of a file on the device + * + * For redirected devices, the routine xfs_get_full_path() returns names + * like "/C:/Windows/System32". + * This routine simply returns a pointer to the part of the name following + * the device specification. + */ +static const char *filename_on_device(const char *full_path) +{ + const char *result = NULL; + if (full_path[0] != '\0') + { + result = strchr(full_path + 1, '/'); + } + return result ? result : "/"; +} + +/* + * Updates attributes on the filesystem, and bumps the inode ctime + * + * This call is used to set attributes, either locally, or following a + * setattr devredir call + */ +static void update_inode_file_attributes(const struct file_attr *fattr, + tui32 change_mask, XFS_INODE *xinode) +{ + int updated = 0; + + if ((change_mask & TO_SET_MODE) != 0 && xinode->mode != fattr->mode) + { + xinode->mode = fattr->mode; + updated = 1; + } + if ((change_mask & TO_SET_SIZE) != 0 && xinode->size != fattr->size) + { + xinode->size = fattr->size; + updated = 1; + } + if ((change_mask & TO_SET_ATIME) != 0 && xinode->atime != fattr->atime) + { + xinode->atime = fattr->atime; + updated = 1; + } + if ((change_mask & TO_SET_MTIME) != 0 && xinode->mtime != fattr->mtime) + { + xinode->mtime = fattr->mtime; + updated = 1; + } + + if (updated) + { + xinode->ctime = time(0); + } +} + +/* + * Gets the name for a file where we know the parent inode and the + * name for the file under that inode + * + * Result must be freed after use + */ +static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name) +{ + char *result; + + if ((result = xfs_get_full_path(g_xfs, parent)) != NULL) + { + char * p = realloc(result, strlen(result) + 1 + strlen(name) + 1); + if (p == NULL) + { + free(result); + result = NULL; + } + else + { + result = p; + strcat(result, "/"); + strcat(result, name); + } + } + + return result; +} + #endif /* end else #ifndef XRDP_FUSE */ diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index 89fd7095..dd1b9f76 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -21,53 +21,94 @@ #include "arch.h" -/* - * a file or dir entry in the xrdp file system (opaque type externally) */ -struct xrdp_inode; +#include "ms-erref.h" -/* 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 - */ +/* Used to pass file info back to chansrv_fuse from devredir */ struct file_attr { - const char *name; /* Name of file or directory */ tui32 mode; /* File mode. */ 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. */ }; +/* Bitmask values used to identify individual elements in + * struct file_attr + */ +#define TO_SET_MODE (1<<0) +#define TO_SET_SIZE (1<<1) +#define TO_SET_ATIME (1<<2) +#define TO_SET_MTIME (1<<3) +#define TO_SET_ALL (TO_SET_MODE | TO_SET_SIZE | TO_SET_ATIME | TO_SET_MTIME) + +/* Private type passed into and back-from devredir */ +typedef struct xfuse_info XFUSE_INFO; + int xfuse_init(void); int xfuse_deinit(void); int xfuse_check_wait_objs(void); int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout); int xfuse_create_share(tui32 share_id, const char *dirname); +void xfuse_delete_share(tui32 share_id); int xfuse_clear_clip_dir(void); int xfuse_file_contents_range(int stream_id, const char *data, int data_bytes); 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); + +/* State pointer types (opaque outside this module), used for + * callback data + */ +struct state_dirscan; +struct state_lookup; +struct state_setattr; +struct state_open; +struct state_create; +struct state_read; +struct state_write; +struct state_remove; +struct state_rename; +struct state_close; + /* functions that are invoked from devredir */ -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_enum_dir_add_entry( + struct state_dirscan *fip, + const char *name, + const struct file_attr *fattr); +void xfuse_devredir_cb_enum_dir_done(struct state_dirscan *fip, + enum NTSTATUS IoStatus); + +void xfuse_devredir_cb_lookup_entry(struct state_lookup *fip, + enum NTSTATUS IoStatus, + const struct file_attr *file_info); + +void xfuse_devredir_cb_setattr(struct state_setattr *fip, + enum NTSTATUS IoStatus); + +void xfuse_devredir_cb_create_file(struct state_create *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId); + +void xfuse_devredir_cb_open_file(struct state_open *fip, + enum NTSTATUS IoStatus, + tui32 DeviceId, tui32 FileId); + +void xfuse_devredir_cb_read_file(struct state_read *fip, + const char *buf, size_t length); void xfuse_devredir_cb_write_file( - void *vp, - tui32 IoStatus, - const char *buf, + struct state_write *fip, + enum NTSTATUS IoStatus, + off_t offset, size_t length); -void xfuse_devredir_cb_read_file(void *vp, const char *buf, size_t length); -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_file_close(void *vp); + +void xfuse_devredir_cb_rmdir_or_file(struct state_remove *fip, + enum NTSTATUS IoStatus); + +void xfuse_devredir_cb_rename_file(struct state_rename *fip, + enum NTSTATUS IoStatus); + +void xfuse_devredir_cb_file_close(struct state_close *fip); #endif diff --git a/sesman/chansrv/chansrv_xfs.c b/sesman/chansrv/chansrv_xfs.c new file mode 100644 index 00000000..d4985c2b --- /dev/null +++ b/sesman/chansrv/chansrv_xfs.c @@ -0,0 +1,909 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file implements the interface in chansrv_fuse_fs.h + */ +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include "os_calls.h" + +#include "chansrv_xfs.h" + +/* + * Skip this module if FUSE is not supported. A standards-compliant C + * translation unit must contain at least one declaration (C99:6.9), and we've + * fulfilled that requirement by this stage. + */ +#ifdef XRDP_FUSE + +#define INODE_TABLE_ALLOCATION_INITIAL 4096 +#define INODE_TABLE_ALLOCATION_GRANULARITY 100 + +/* inum of the delete pending directory */ +#define DELETE_PENDING_ID 2 + +/* + * A double-linked list of inodes, sorted by inum + * + * The elements in the list are sorted in increasing inum order, as this + * allows a directory enumeration to be easily resumed if elements + * are removed or added. See xfs_readdir() for details on this. + */ +typedef struct xfs_inode_all XFS_INODE_ALL; +typedef struct xfs_list +{ + XFS_INODE_ALL *begin; + XFS_INODE_ALL *end; +} XFS_LIST; + +/* + * A complete inode, including the private elements used by the + * implementation + */ +typedef struct xfs_inode_all +{ + XFS_INODE pub; /* Public elements */ + /* + * Directory linkage elements + * + * Because we don't support hard-linking, elements can be stored in + * one and only one directory:- + */ + struct xfs_inode_all *parent; /* Parent inode */ + struct xfs_inode_all *next; /* Next entry in parent */ + struct xfs_inode_all *previous; /* Previous entry in parent */ + XFS_LIST dir; /* Directory only - children */ + /* + * Other private elements + */ + unsigned int open_count; /* Regular files only */ +} XFS_INODE_ALL; + + +/* the xrdp file system in memory + * + * inode_table allows for O(1) access to any file based on the inum. + * Index 0 is unused, so we can use an inode of zero for + * an invalid inode, and avoid off-by-one errors index + * 1 is our '.' directory. + * 2 is the delete pending directory, where we can place + * inodes with a positive open count which are + * deleted. + * free_list List of free inode numbers. Allows for O(1) access to + * a free node, provided the free list is not empty. + */ +struct xfs_fs +{ + XFS_INODE_ALL **inode_table; /* a table of entries; can grow. */ + fuse_ino_t *free_list; /* Free inodes */ + unsigned int inode_count; /* Current number of inodes */ + unsigned int free_count; /* Size of free_list */ + unsigned int generation; /* Changes when an inode is deleted */ +}; + +/* A directory handle + * + * inum inum of the directory being scanned + * generation Generation of the inum we opened + */ +struct xfs_dir_handle +{ + fuse_ino_t inum; + tui32 generation; +}; + +/* module based logging */ +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_ERROR +#endif + +#define log_error(_params...) \ + { \ + g_write("[%10.10u]: XFS %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } + +#define log_always(_params...) \ + { \ + g_write("[%10.10u]: XFS %s: %d : ALWAYS: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } + +#define log_info(_params...) \ + { \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: XFS %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ + } + +#define log_debug(_params...) \ + { \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: XFS %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ + } + +/* ------------------------------------------------------------------------ */ +static int +grow_xfs(struct xfs_fs *xfs, unsigned int extra_inodes) +{ + int result = 0; + unsigned int new_count = xfs->inode_count + extra_inodes; + XFS_INODE_ALL **new_table; + fuse_ino_t *new_free_list; + + new_table = (XFS_INODE_ALL **) + realloc(xfs->inode_table, new_count * sizeof(new_table[0])); + if (new_table != NULL) + { + unsigned int i; + for (i = xfs->inode_count ; i < new_count ; ++i) + { + new_table[i] = NULL; + } + xfs->inode_table = new_table; + + new_free_list = (fuse_ino_t *) + realloc(xfs->free_list, + new_count * sizeof(new_free_list[0])); + if (new_free_list) + { + /* Add the new inodes in to the new_free_list, so the lowest + * number is allocated first + */ + i = new_count; + while (i > xfs->inode_count) + { + new_free_list[xfs->free_count++] = --i; + } + + xfs->free_list = new_free_list; + xfs->inode_count = new_count; + + result = 1; + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +static void +add_inode_to_list(XFS_LIST *list, XFS_INODE_ALL *xino) +{ + fuse_ino_t inum = xino->pub.inum; + + /* Find the element we need to insert after */ + XFS_INODE_ALL *predecessor = list->end; + while (predecessor != NULL && predecessor->pub.inum > inum) + { + predecessor = predecessor->previous; + } + + if (predecessor == NULL) + { + /* Inserting at the beginning */ + /* Set up links in node */ + xino->next = list->begin; + xino->previous = NULL; + + /* Set up back-link to node */ + if (list->begin == NULL) + { + /* We are the last node */ + list->end = xino; + } + else + { + list->begin->previous = xino; + } + /* Set up forward-link to node */ + list->begin = xino; + } + else + { + /* Set up links in node */ + xino->next = predecessor->next; + xino->previous = predecessor; + + /* Set up back-link to node */ + if (predecessor->next == NULL) + { + list->end = xino; + } + else + { + predecessor->next->previous = xino; + } + /* Set up forward-link to node */ + predecessor->next = xino; + } +} + +/* ------------------------------------------------------------------------ */ +static void +remove_inode_from_list(XFS_LIST *list, XFS_INODE_ALL *xino) +{ + if (xino->previous == NULL) + { + /* First element */ + list->begin = xino->next; + } + else + { + xino->previous->next = xino->next; + } + + if (xino->next == NULL) + { + /* Last element */ + list->end = xino->previous; + } + else + { + xino->next->previous = xino->previous; + } + +} + +/* ------------------------------------------------------------------------ */ +static void +link_inode_into_directory_node(XFS_INODE_ALL *dinode, XFS_INODE_ALL *xino) +{ + xino->parent = dinode; + add_inode_to_list(&dinode->dir, xino); +} + +/* ------------------------------------------------------------------------ */ +static void +unlink_inode_from_parent(XFS_INODE_ALL *xino) +{ + remove_inode_from_list(&xino->parent->dir, xino); + + xino->next = NULL; + xino->previous = NULL; + xino->parent = NULL; +} + +/* ------------------------------------------------------------------------ */ +struct xfs_fs * +xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid) +{ + struct xfs_fs *xfs = g_new0(struct xfs_fs, 1); + XFS_INODE_ALL *xino1 = NULL; + XFS_INODE_ALL *xino2 = NULL; + + if (xfs != NULL) + { + xfs->inode_count = 0; + xfs->free_count = 0; + xfs->inode_table = NULL; + xfs->free_list = NULL; + xfs->generation = 1; + + if (!grow_xfs(xfs, INODE_TABLE_ALLOCATION_INITIAL) || + (xino1 = g_new0(XFS_INODE_ALL, 1)) == NULL || + (xino2 = g_new0(XFS_INODE_ALL, 1)) == NULL) + { + free(xino1); + free(xino2); + xfs_delete_xfs_fs(xfs); + xfs = NULL; + } + else + { + /* + * The use of grow_xfs to allocate the inode table will make + * inodes 0, 1 (FUSE_ROOT_ID) and 2 (DELETE_PENDING_ID) the first + * available free inodes. We can ignore these */ + xfs->free_count -= 3; + + xfs->inode_table[0] = NULL; + xfs->inode_table[FUSE_ROOT_ID] = xino1; + xfs->inode_table[DELETE_PENDING_ID] = xino2; + + xino1->pub.inum = FUSE_ROOT_ID; + xino1->pub.mode = (S_IFDIR | 0777) & ~umask; + xino1->pub.uid = uid; + xino1->pub.gid = gid; + xino1->pub.size = 0; + xino1->pub.atime = time(0); + xino1->pub.mtime = xino1->pub.atime; + xino1->pub.ctime = xino1->pub.atime; + strcpy(xino1->pub.name, "."); + xino1->pub.generation = xfs->generation; + xino1->pub.device_id = 0; + + /* + * FUSE_ROOT_ID has no parent rather than being a parent + * of itself. This is intentional */ + xino1->parent = NULL; + xino1->next = NULL; + xino1->previous = NULL; + xino1->dir.begin = NULL; + xino1->dir.end = NULL; + + xino2->pub.inum = DELETE_PENDING_ID; + xino2->pub.mode = (S_IFDIR | 0777) & ~umask; + xino2->pub.uid = uid; + xino2->pub.gid = gid; + xino2->pub.size = 0; + xino2->pub.atime = time(0); + xino2->pub.mtime = xino2->pub.atime; + xino2->pub.ctime = xino2->pub.atime; + strcpy(xino2->pub.name, ".delete-pending"); + xino2->pub.generation = xfs->generation; + xino2->pub.device_id = 0; + + xino2->parent = NULL; + xino2->next = NULL; + xino2->previous = NULL; + xino2->dir.begin = NULL; + xino2->dir.end = NULL; + /* + * Uncomment this line to make the .delete-pending + * directory visible to the user in the root + */ + /* link_inode_into_directory_node(xino1, xino2); */ + } + } + + return xfs; +} + +/* ------------------------------------------------------------------------ */ +void +xfs_delete_xfs_fs(struct xfs_fs *xfs) +{ + if (xfs != NULL && xfs->inode_table != NULL) + { + size_t i; + for (i = 0 ; i < xfs->inode_count; ++i) + { + free(xfs->inode_table[i]); + } + } + free(xfs->inode_table); + free(xfs->free_list); + free(xfs); +} + +/* ------------------------------------------------------------------------ */ +XFS_INODE * +xfs_add_entry(struct xfs_fs *xfs, fuse_ino_t parent_inum, + const char *name, mode_t mode) +{ + XFS_INODE *result = NULL; + XFS_INODE_ALL *parent = NULL; + + /* Checks:- + * 1) the parent exists (and is a directory) + * 2) the caller is not inserting into the .delete-pending directory, + * 3) Name's not too long + * 4) Entry does not already exist + */ + if (parent_inum < xfs->inode_count && + ((parent = xfs->inode_table[parent_inum]) != NULL) && + (parent->pub.mode & S_IFDIR) != 0 && + parent_inum != DELETE_PENDING_ID && + strlen(name) <= XFS_MAXFILENAMELEN && + !xfs_lookup_in_dir(xfs, parent_inum, name)) + { + /* Sanitise the mode so one-and-only-one of S_IFDIR and + * S_IFREG is set */ + if ((mode & S_IFDIR) != 0) + { + mode = (mode & 0777) | S_IFDIR; + } + else + { + mode = (mode & 0777) | S_IFREG; + } + + /* Space for a new entry? */ + if (xfs->free_count > 0 || + grow_xfs(xfs, INODE_TABLE_ALLOCATION_GRANULARITY)) + { + XFS_INODE_ALL *xino = NULL; + + if ((xino = g_new0(XFS_INODE_ALL, 1)) != NULL) + { + fuse_ino_t inum = xfs->free_list[--xfs->free_count]; + if (xfs->inode_table[inum] != NULL) + { + log_error("Unexpected non-NULL value in inode table " + "entry %ld", inum); + } + xfs->inode_table[inum] = xino; + xino->pub.inum = inum; + xino->pub.mode = mode; + xino->pub.uid = parent->pub.uid; + xino->pub.gid = parent->pub.gid; + if (mode & S_IFDIR) + { + xino->pub.size = 4096; + } + else + { + xino->pub.size = 0; + } + xino->pub.atime = time(0); + xino->pub.mtime = xino->pub.atime; + xino->pub.ctime = xino->pub.atime; + strcpy(xino->pub.name, name); + xino->pub.generation = xfs->generation; + xino->pub.device_id = parent->pub.device_id; + xino->pub.lindex = 0; + + xino->parent = NULL; + xino->next = NULL; + xino->previous = NULL; + link_inode_into_directory_node(parent, xino); + result = &xino->pub; + } + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +void +xfs_remove_directory_contents(struct xfs_fs *xfs, fuse_ino_t inum) +{ + XFS_INODE_ALL *xino = NULL; + + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + ((xino->pub.mode & S_IFDIR) != 0)) + { + XFS_INODE_ALL *e; + while ((e = xino->dir.end) != NULL) + { + xfs_remove_entry(xfs, e->pub.inum); + } + } +} + +/* ------------------------------------------------------------------------ */ +void +xfs_remove_entry(struct xfs_fs *xfs, fuse_ino_t inum) +{ + XFS_INODE_ALL *xino = NULL; + + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL)) + { + if ((xino->pub.mode & S_IFDIR) != 0) + { + xfs_remove_directory_contents(xfs, inum); + } + + unlink_inode_from_parent(xino); + if ((xino->pub.mode & S_IFREG) != 0 && xino->open_count > 0) + { + link_inode_into_directory_node( + xfs->inode_table[DELETE_PENDING_ID], xino); + } + else + { + xfs->free_list[xfs->free_count++] = inum; + xfs->inode_table[inum] = NULL; + /* + * Bump the generation when we return an inum to the free list, + * so that the caller can distinguish re-uses of the same inum. + */ + ++xfs->generation; + free(xino); + } + } +} + +/* ------------------------------------------------------------------------ */ +XFS_INODE * +xfs_get(struct xfs_fs *xfs, fuse_ino_t inum) +{ + return (inum < xfs->inode_count) ? &xfs->inode_table[inum]->pub : NULL; +} + +/* ------------------------------------------------------------------------ */ +char * +xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum) +{ + char *result = NULL; + XFS_INODE_ALL *xino = NULL; + + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL)) + { + if (xino->pub.inum == FUSE_ROOT_ID) + { + return strdup("/"); + } + else + { + /* + * Add up the lengths of all the names up to the root, + * allowing one extra char for a '/' prefix for each element + */ + size_t len = 0; + XFS_INODE_ALL *p; + for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) + { + len += strlen(p->pub.name); + ++len; /* Allow for '/' prefix */ + } + + result = malloc(len + 1); + if (result != NULL) + { + /* Construct the path from the end */ + char *end = result + len; + *end = '\0'; + + for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent) + { + len = strlen(p->pub.name); + end -= (len + 1); + *end = '/'; + memcpy(end + 1, p->pub.name, len); + } + } + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +XFS_INODE * +xfs_lookup_in_dir(struct xfs_fs *xfs, fuse_ino_t inum, const char *name) +{ + XFS_INODE_ALL *xino; + XFS_INODE *result = NULL; + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + (xino->pub.mode & S_IFDIR) != 0) + { + XFS_INODE_ALL *p; + for (p = xino->dir.begin ; p != NULL; p = p->next) + { + if (strcmp(p->pub.name, name) == 0) + { + result = &p->pub; + break; + } + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +int +xfs_is_dir_empty(struct xfs_fs *xfs, fuse_ino_t inum) +{ + XFS_INODE_ALL *xino = NULL; + int result = 0; + + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + (xino->pub.mode & S_IFDIR) != 0) + { + result = (xino->dir.begin == NULL); + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +unsigned int +xfs_is_under(struct xfs_fs *xfs, fuse_ino_t dir, fuse_ino_t entry) +{ + unsigned int result = 0; + + XFS_INODE_ALL *dxino = NULL; + XFS_INODE_ALL *exino = NULL; + + if (dir < xfs->inode_count && + ((dxino = xfs->inode_table[dir]) != NULL) && + (dxino->pub.mode & S_IFDIR) != 0 && + entry < xfs->inode_count && + ((exino = xfs->inode_table[entry]) != NULL)) + { + unsigned int count = 0; + + while (exino != NULL && exino != dxino) + { + ++count; + exino = exino->parent; + } + + if (exino != NULL) + { + result = count; + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +struct xfs_dir_handle * +xfs_opendir(struct xfs_fs *xfs, fuse_ino_t dir) +{ + XFS_INODE_ALL *xino = NULL; + struct xfs_dir_handle *result = NULL; + + if (dir < xfs->inode_count && + ((xino = xfs->inode_table[dir]) != NULL) && + (xino->pub.mode & S_IFDIR) != 0) + { + result = g_new0(struct xfs_dir_handle, 1); + if (result) + { + result->inum = xino->pub.inum; + result->generation = xino->pub.generation; + } + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +XFS_INODE * +xfs_readdir(struct xfs_fs *xfs, struct xfs_dir_handle *handle, off_t *off) +{ + XFS_INODE_ALL *result = NULL; + XFS_INODE_ALL *dxino = NULL; + XFS_INODE_ALL *xino = NULL; + + /* Check the direcory is still valid */ + if (handle->inum < xfs->inode_count && + ((dxino = xfs->inode_table[handle->inum]) != NULL) && + (dxino->pub.mode & S_IFDIR) != 0 && + handle->generation == dxino->pub.generation) + { + fuse_ino_t inum; + + if (*off == (off_t) -1) + { + /* We're at the end already */ + } + else if ((inum = *off) == 0) + { + /* First call */ + result = dxino->dir.begin; + } + else if (inum < xfs->inode_count && + (xino = xfs->inode_table[inum]) != 0 && + xino->parent == dxino) + { + /* The node we're pointing to is still valid */ + result = xino; + } + else + { + /* + * The file we wanted has been pulled out from under us. + * We will look forward in the inode table to try to + * discover the next inode in the directory. Because + * files are stored in inode order, this guarantees + * we'll meet POSIX requirements. + */ + for (inum = inum + 1 ; inum < xfs->inode_count ; ++inum) + { + if ((xino = xfs->inode_table[inum]) != 0 && + xino->parent == dxino) + { + result = xino; + break; + } + } + } + } + + /* Update the offset */ + if (result == NULL || result->next == NULL) + { + /* We're done */ + *off = (off_t) -1; + } + else + { + *off = (off_t)result->next->pub.inum; + } + + /* Caller only sees public interface to the result */ + return (result) ? &result->pub : NULL; +} + +/* ------------------------------------------------------------------------ */ +void +xfs_closedir(struct xfs_fs *xfs, struct xfs_dir_handle *handle) +{ + free(handle); +} + +/* ------------------------------------------------------------------------ */ +void +xfs_increment_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) +{ + XFS_INODE_ALL *xino; + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + (xino->pub.mode & S_IFREG) != 0) + { + ++xino->open_count; + } +} + +/* ------------------------------------------------------------------------ */ +void +xfs_decrement_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) +{ + XFS_INODE_ALL *xino; + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + (xino->pub.mode & S_IFREG) != 0) + { + if (xino->open_count > 0) + { + --xino->open_count; + } + + if (xino->open_count == 0 && + xino->parent == xfs->inode_table[DELETE_PENDING_ID]) + { + /* We can get rid of this one now */ + xfs_remove_entry(xfs, inum); + } + } +} + +/* ------------------------------------------------------------------------ */ +unsigned int +xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum) +{ + unsigned int result = 0; + XFS_INODE_ALL *xino; + if (inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + (xino->pub.mode & S_IFREG) != 0) + { + result = xino->open_count; + } + + return result; +} + +/* ------------------------------------------------------------------------ */ +void +xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id) +{ + fuse_ino_t inum; + XFS_INODE_ALL *xino; + + if (device_id != 0) + { + /* Using xfs_remove_entry() is convenient, but it recurses + * in to directories. To make sure all entries are removed, set the + * open_count of all affected files to 0 first + */ + for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) + { + if ((xino = xfs->inode_table[inum]) != NULL && + xino->pub.device_id == device_id && + (xino->pub.mode & S_IFREG) != 0) + { + xino->open_count = 0; + } + } + + /* Now we can be sure everything will be deleted correctly */ + for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum) + { + if ((xino = xfs->inode_table[inum]) != NULL && + xino->pub.device_id == device_id) + { + xfs_remove_entry(xfs, xino->pub.inum); + } + } + } +} + +/* ------------------------------------------------------------------------ */ +int +xfs_check_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, + fuse_ino_t new_parent_inum, const char *name) +{ + XFS_INODE_ALL *xino; + XFS_INODE_ALL *parent; + + return + (strlen(name) <= XFS_MAXFILENAMELEN && + inum < xfs->inode_count && + ((xino = xfs->inode_table[inum]) != NULL) && + new_parent_inum != DELETE_PENDING_ID && + new_parent_inum < xfs->inode_count && + ((parent = xfs->inode_table[new_parent_inum]) != NULL) && + (parent->pub.mode & S_IFDIR) != 0 && + xfs_is_under(xfs, inum, new_parent_inum) == 0); +} + +/* ------------------------------------------------------------------------ */ +int +xfs_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, + fuse_ino_t new_parent_inum, const char *name) +{ + int result = EINVAL; + XFS_INODE_ALL *xino; + XFS_INODE_ALL *parent; + XFS_INODE *dest; + + if (xfs_check_move_entry(xfs, inum, new_parent_inum, name)) + { + xino = xfs->inode_table[inum]; + parent = xfs->inode_table[new_parent_inum]; + + if (xino->parent != parent) + { + /* We're moving between directories */ + + /* Does the target name already exist in the destination? */ + if ((dest = xfs_lookup_in_dir(xfs, new_parent_inum, name)) != NULL) + { + xfs_remove_entry(xfs, dest->inum); + } + + unlink_inode_from_parent(xino); + link_inode_into_directory_node(parent, xino); + strcpy(xino->pub.name, name); + } + else if (strcmp(xino->pub.name, name) != 0) + { + /* Same directory, but name has changed */ + if ((dest = xfs_lookup_in_dir(xfs, new_parent_inum, name)) != NULL) + { + xfs_remove_entry(xfs, dest->inum); + } + strcpy(xino->pub.name, name); + } + result = 0; + } + + return result; +} +#endif /* XRDP_FUSE */ diff --git a/sesman/chansrv/chansrv_xfs.h b/sesman/chansrv/chansrv_xfs.h new file mode 100644 index 00000000..17dc7b4f --- /dev/null +++ b/sesman/chansrv/chansrv_xfs.h @@ -0,0 +1,321 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is the interface to the FUSE file system used by + * chansrv + */ + +#ifndef _CHANSRV_XFS +#define _CHANSRV_XFS + +/* Skip this include if there's no FUSE */ +#ifdef XRDP_FUSE + +#include +#include +#include + +#include "arch.h" + +#define XFS_MAXFILENAMELEN 255 + +/* + * Incomplete types for the public interface + */ +struct xfs_fs; +struct xfs_dir_handle; + +typedef struct xfs_inode +{ + fuse_ino_t inum; /* File serial number. */ + mode_t mode; /* File mode. */ + uid_t uid; /* User ID of the file's owner. */ + gid_t gid; /* Group ID of the file's group. */ + off_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[XFS_MAXFILENAMELEN + 1]; /* Short name */ + tui32 generation; /* Changes if inode is reused */ + tui32 device_id; /* for file system redirection + * Non-redirected devices are guaranteed + * to have a device_id or zero */ + int lindex; /* used in clipboard operations */ +} XFS_INODE; + +/* + * Create a new filesystem instance + * + * @param umask Umask to apply to initial data structures + * @param uid Owner UID for initial root directory + * @param gid Owner GID for initial root directory + * @return Pointer to instance, or NULL if no memory + */ +struct xfs_fs * +xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid); + +/* + * Delete a filesystem instance + * + * @param xfs Filesystem instance + */ +void +xfs_delete_xfs_fs(struct xfs_fs *xfs); + +/* + * Add an entry to the filesystem + * + * The returned element has default values inherited from the parent + * + * The specified mode is sanitised in that:- + * - Bits other than the lowest nine permissions bits, the directory + * bit (S_IFDIR) and the regular bit (S_IFREG) are cleared. + * - S_IFREG is cleared if S_IFDIR is set + * - S_IFREG is set if neither S_IFDIR or S_IFREG is set + * + * NULL is returned for one of the following conditions:- + * - the parent does not exist + * - the parent is not a directory + * - the name length exceeds XFS_MAXFILENAMELEN + * - the entry already exists + * - memory is exhausted. + * + * @param xfs filesystem instance + * @param parent_inode parent inode + * @param name Name of entry + * @param mode Initial mode for file. + * @return inode, or NULL + */ +XFS_INODE * +xfs_add_entry(struct xfs_fs *xfs, fuse_ino_t parent_inum, + const char *name, mode_t mode); + +/* + * Delete the contents of a directory + * + * If normal files are opened when they are deleted, the inode is not + * released until the open count goes to zero. + * + * @param xfs filesystem instance + * @param inode Reference to entry to delete + * + */ +void +xfs_remove_directory_contents(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Delete an entry from the filesystem + * + * If normal files are opened when they are deleted, the inode is not + * released until the open count goes to zero. + * + * For directories, the contents are removed first. + * + * @param xfs filesystem instance + * @param inode Reference to entry to delete + * + */ +void +xfs_remove_entry(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Get an XFS_INODE for an inum + * + * @param xfs filesystem instance + * @param inum Inumber + * @return Pointer to XFS_INODE + */ +XFS_INODE * +xfs_get(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Get the full path for an inum + * + * The path is dynamically allocated, and must be freed after use + * + * @param xfs filesystem instance + * @param inum Inumber to get path for + * @return Full path (free after use) + */ +char * +xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Lookup a file in a directory + * + * @param xfs filesystem instance + * @param inum Inumber of the directory + * @param name Name of the file to lookup + * @return Pointer to XFS_INODE if found + */ +XFS_INODE * +xfs_lookup_in_dir(struct xfs_fs *xfs, fuse_ino_t inum, const char *name); + +/* + * Inquires as to whether a directory is empty. + * + * The caller must check that the inum is actually a directory, or + * the result is undefined. + * + * @param xfs filesystem instance + * @param inum Inumber of the directory + * @return True if the directory is empty + */ +int +xfs_is_dir_empty(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Inquires as to whether an entry is under a directory. + * + * This can be used to check for invalid renames, where we try to + * rename a directory into one of its sub-directories. + * + * Returned value means one of the following:- + * 0 Entry is not related to the directory + * 1 Entry is an immediate member of the directory + * 2.. Entry is a few levels below the directory + * + * @param xfs filesystem instance + * @param dir Inumber of the directory + * @param entry Inumber of the entry + * @return Nesting count of entry in the directory, or 0 for + * no nesting + */ +unsigned int +xfs_is_under(struct xfs_fs *xfs, fuse_ino_t dir, fuse_ino_t entry); + +/* + * Opens a directory for reading + * + * @param xfs filesystem instance + * @param dir Inumber of the directory + * @return handle to be passed to xfs_readdir() and xfs_closedir() + */ +struct xfs_dir_handle * +xfs_opendir(struct xfs_fs *xfs, fuse_ino_t dir); + +/* + * Gets the next entry from a directory + * + * This function is safe to call while the filesystem is being modified. + * Whether files added or removed from the directory in question are + * returned or not is unspecified by this interface. + * + * @param xfs filesystem instance + * @param handle Handle from xfs_opendir + * @param off Offset (by reference). Pass in zero to get the first + * entry. After the call, the offset is updated so that + * the next call gets the next entry. + * + * @return pointer to details of file entry, or NULL if no more + * entries are available. + */ +XFS_INODE * +xfs_readdir(struct xfs_fs *xfs, struct xfs_dir_handle *handle, off_t *off); + + +/* + * Closes a directory opened for reading + * + * @param xfs filesystem instance + * @param handle Earlier result of readdir() call + */ +void +xfs_closedir(struct xfs_fs *xfs, struct xfs_dir_handle *handle); + +/* + * Increment the file open count + * + * The file open count is used to be sure when an entry can be deleted from + * the data structures. It allows us to remove an entry while still retaining + * enough state to manage the file + * + * This call is only necessary for regular files - not directories + * + * @param xfs filesystem instance + * @param inum File to increment the count of + */ +void +xfs_increment_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Decrement the file open count + * + * This call will ensure that deleted inodes are cleared up at the appropriate + * time. + * + * This call is only necessary for regular files - not directories + * + * @param xfs filesystem instance + * @param inum File to decrement the count of + */ +void +xfs_decrement_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Return the file open count for a regular file + */ +unsigned int +xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum); + +/* + * Deletes all entries with the matching device id + * + * Files are deleted even if they are open + * + * The specified device_id must be non-zero so that the root + * filesystem is not deleted! + * + * @param device_id Device ID + */ +void +xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id); + +/* + * Check an entry move will be successful + * + * A move will fail if:- + * - Any of the parameters are invalid + * - if the entry is a directory, and the new parent is below the + * entry in the existing hierarchy. + * + * @param xfs filesystem instance + * @param inum Inumber of entry + * @param new_parent New parent + * @param name New name + * + * @result != 0 if all looks OK + */ +int +xfs_check_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, + fuse_ino_t new_parent_inum, const char *name); + + +/* + * Move (rename) an entry + * + * @param xfs filesystem instance + * @param inum Inumber of entry + * @param new_parent New parent + * @param name New name + * + * @result 0, or a suitable errno value. + */ +int +xfs_move_entry(struct xfs_fs *xfs, fuse_ino_t inum, + fuse_ino_t new_parent_inum, const char *name); + +#endif /* XRDP_FUSE */ +#endif /* _CHANSRV_XFS */ diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index 6e1130b7..ca16e2c1 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -54,15 +54,18 @@ #include "chansrv_fuse.h" #include "devredir.h" #include "smartcard.h" +#include "ms-rdpefs.h" +#include "ms-smb2.h" +#include "ms-fscc.h" +#include "ms-erref.h" /* module based logging */ #define LOG_ERROR 0 #define LOG_INFO 1 #define LOG_DEBUG 2 -#ifndef LOG_LEVEL +#undef LOG_LEVEL #define LOG_LEVEL LOG_ERROR -#endif #define log_error(_params...) \ { \ @@ -91,12 +94,40 @@ } \ } +/* client minor versions */ +#define RDP_CLIENT_50 0x0002 +#define RDP_CLIENT_51 0x0005 +#define RDP_CLIENT_52 0x000a +#define RDP_CLIENT_60_61 0x000c + +/* Windows time starts on Jan 1, 1601 */ +/* Linux time starts on Jan 1, 1970 */ +#define EPOCH_DIFF 11644473600LL +#define WINDOWS_TO_LINUX_TIME(_t) (((_t) / 10000000) - EPOCH_DIFF); +#define LINUX_TO_WINDOWS_TIME(_t) (((_t) + EPOCH_DIFF) * 10000000) + /* - * Size of structs supported by IRP_MJ_QUERY_INFORMATION without - * trailing RESERVED fields (MS-RDPEFS 2.2.3.3.8) + * CompletionID types, used in IRPs to indicate I/O operation */ -#define FILE_BASIC_INFORMATION_SIZE 36 -#define FILE_STD_INFORMATION_SIZE 22 + +enum COMPLETION_TYPE +{ + CID_CREATE_DIR_REQ = 1, + CID_DIRECTORY_CONTROL, + CID_CREATE_REQ, + CID_OPEN_REQ, + CID_READ, + CID_WRITE, + CID_CLOSE, + CID_FILE_CLOSE, + CID_RMDIR_OR_FILE, + CID_RMDIR_OR_FILE_RESP, + CID_RENAME_FILE, + CID_RENAME_FILE_RESP, + CID_LOOKUP, + CID_SETATTR +}; + /* globals */ extern int g_rdpdr_chan_id; /* in chansrv.c */ @@ -114,29 +145,42 @@ tui16 g_client_rdp_version; /* returned by client */ struct stream *g_input_stream = NULL; /* - * Local functions called from dev_redir_proc_device_iocompletion() + * Local functions called from devredir_proc_device_iocompletion() */ 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); +static void devredir_proc_cid_lookup( IRP *irp, + struct stream *s_in, + tui32 IoStatus); +static void devredir_proc_cid_setattr( IRP *irp, + struct stream *s_in, + tui32 IoStatus); /* Other local functions */ -static void lookup_std_entry(IRP *irp); +static void devredir_send_server_core_cap_req(void); +static void devredir_send_server_clientID_confirm(void); +static void devredir_send_server_user_logged_on(void); + +static void devredir_proc_client_core_cap_resp(struct stream *s); +static void devredir_proc_client_devlist_announce_req(struct stream *s); +static void devredir_proc_client_devlist_remove_req(struct stream *s); +static void devredir_proc_device_iocompletion(struct stream *s); +static void devredir_proc_query_dir_response( + IRP *irp, + struct stream *s_in, + tui32 DeviceId, + tui32 CompletionId, + tui32 IoStatus); + +static void devredir_cvt_slash(char *path); +static void devredir_cvt_to_unicode(char *unicode, const char *path); +static void devredir_cvt_from_unicode_len(char *path, char *unicode, int len); +static int devredir_string_ends_with(const char *string, char c); /*****************************************************************************/ int -dev_redir_init(void) +devredir_init(void) { struct stream *s; int bytes; @@ -183,12 +227,100 @@ dev_redir_init(void) /*****************************************************************************/ int -dev_redir_deinit(void) +devredir_deinit(void) { scard_deinit(); return 0; } +/*****************************************************************************/ + +/* + * Convert a COMPLETION_TYPE to a string + */ +const char *completion_type_to_str(enum COMPLETION_TYPE cid) +{ + return + (cid == CID_CREATE_DIR_REQ) ? "CID_CREATE_DIR_REQ" : + (cid == CID_DIRECTORY_CONTROL) ? "CID_DIRECTORY_CONTROL" : + (cid == CID_CREATE_REQ) ? "CID_CREATE_REQ" : + (cid == CID_OPEN_REQ) ? "CID_OPEN_REQ" : + (cid == CID_READ) ? "CID_READ" : + (cid == CID_WRITE) ? "CID_WRITE" : + (cid == CID_CLOSE) ? "CID_CLOSE" : + (cid == CID_FILE_CLOSE) ? "CID_FILE_CLOSE" : + (cid == CID_RMDIR_OR_FILE) ? "CID_RMDIR_OR_FILE" : + (cid == CID_RMDIR_OR_FILE_RESP) ? "CID_RMDIR_OR_FILE_RESP" : + (cid == CID_RENAME_FILE) ? "CID_RENAME_FILE" : + (cid == CID_RENAME_FILE_RESP) ? "CID_RENAME_FILE_RESP" : + (cid == CID_LOOKUP) ? "CID_LOOKUP" : + (cid == CID_SETATTR) ? "CID_SETATTR" : + /* default */ ""; +}; + +/*****************************************************************************/ + +/* + * Convert Windows permssions to Linux permissions. + * + * We can't curently support group or other permissions as separate from the + * owner (not that there's much point). We'll assume our caller will provide + * a umask if appropriate + */ +static tui32 +WindowsToLinuxFilePerm(tui32 wperm) +{ + tui32 result; + if (wperm & W_FILE_ATTRIBUTE_DIRECTORY) + { + result = S_IFDIR | 0555; /* dirs are always readable and executable */ + } + else + { + result = S_IFREG | 0444; /* files are always readable */ + if (wperm & W_FILE_ATTRIBUTE_SYSTEM) result |= 0111; /* Executable */ + } + + if ((wperm & W_FILE_ATTRIBUTE_READONLY) == 0) result |= 0222; + + return result; +} + +/*****************************************************************************/ +static tui32 +LinuxToWindowsFilePerm(tui32 lperm) +{ + tui32 result = 0; + + /* Writeable flag is common to files and directories */ + if ((lperm & S_IWUSR) == 0) + { + result |= W_FILE_ATTRIBUTE_READONLY; + } + + if (lperm & S_IFDIR) + { + result |= W_FILE_ATTRIBUTE_DIRECTORY; + } + else + { + /* For normal files the system attribute is used to store the owner + executable bit */ + if (lperm & S_IXUSR) + { + result |= W_FILE_ATTRIBUTE_SYSTEM; + } + if (result == 0) + { + /* See MS-FSCC section 2.6 */ + result = W_FILE_ATTRIBUTE_NORMAL; + } + } + + return result; +} + + /** * @brief process incoming data * @@ -196,8 +328,8 @@ dev_redir_deinit(void) *****************************************************************************/ int -dev_redir_data_in(struct stream *s, int chan_id, int chan_flags, int length, - int total_length) +devredir_data_in(struct stream *s, int chan_id, int chan_flags, int length, + int total_length) { struct stream *ls; tui16 comp_type; @@ -267,33 +399,37 @@ dev_redir_data_in(struct stream *s, int chan_id, int chan_flags, int length, case RDP_CLIENT_60_61: break; } - // LK_TODO dev_redir_send_server_clientID_confirm(); + // LK_TODO devredir_send_server_clientID_confirm(); break; case PAKID_CORE_CLIENT_NAME: /* client is telling us its computer name; do we even care? */ /* let client know login was successful */ - dev_redir_send_server_user_logged_on(); + devredir_send_server_user_logged_on(); usleep(1000 * 100); /* let client know our capabilities */ - dev_redir_send_server_core_cap_req(); + devredir_send_server_core_cap_req(); /* send confirm clientID */ - dev_redir_send_server_clientID_confirm(); + devredir_send_server_clientID_confirm(); break; case PAKID_CORE_CLIENT_CAPABILITY: - dev_redir_proc_client_core_cap_resp(ls); + devredir_proc_client_core_cap_resp(ls); break; case PAKID_CORE_DEVICELIST_ANNOUNCE: devredir_proc_client_devlist_announce_req(ls); break; + case PAKID_CORE_DEVICELIST_REMOVE: + devredir_proc_client_devlist_remove_req(ls); + break; + case PAKID_CORE_DEVICE_IOCOMPLETION: - dev_redir_proc_device_iocompletion(ls); + devredir_proc_device_iocompletion(ls); break; default: @@ -314,7 +450,7 @@ done: /*****************************************************************************/ int -dev_redir_get_wait_objs(tbus *objs, int *count, int *timeout) +devredir_get_wait_objs(tbus *objs, int *count, int *timeout) { if (g_is_smartcard_redir_supported) { @@ -325,7 +461,7 @@ dev_redir_get_wait_objs(tbus *objs, int *count, int *timeout) /*****************************************************************************/ int -dev_redir_check_wait_objs(void) +devredir_check_wait_objs(void) { if (g_is_smartcard_redir_supported) { @@ -338,7 +474,8 @@ dev_redir_check_wait_objs(void) * @brief let client know our capabilities *****************************************************************************/ -void dev_redir_send_server_core_cap_req(void) +static void +devredir_send_server_core_cap_req(void) { struct stream *s; int bytes; @@ -398,7 +535,8 @@ void dev_redir_send_server_core_cap_req(void) xstream_free(s); } -void dev_redir_send_server_clientID_confirm(void) +static void +devredir_send_server_clientID_confirm(void) { struct stream *s; int bytes; @@ -419,7 +557,8 @@ void dev_redir_send_server_clientID_confirm(void) xstream_free(s); } -void dev_redir_send_server_user_logged_on(void) +static void +devredir_send_server_user_logged_on(void) { struct stream *s; int bytes; @@ -437,7 +576,8 @@ void dev_redir_send_server_user_logged_on(void) xstream_free(s); } -void devredir_send_server_device_announce_resp(tui32 device_id) +static void +devredir_send_server_device_announce_resp(tui32 device_id) { struct stream *s; int bytes; @@ -461,25 +601,39 @@ void devredir_send_server_device_announce_resp(tui32 device_id) * @return 0 on success, -1 on failure *****************************************************************************/ -int dev_redir_send_drive_create_request(tui32 device_id, - const char *path, - tui32 DesiredAccess, - tui32 CreateOptions, - tui32 CreateDisposition, - tui32 completion_id) +static int +devredir_send_drive_create_request(tui32 device_id, + const char *path, + tui32 DesiredAccess, + tui32 CreateOptions, + tui32 FileAttributes, + tui32 CreateDisposition, + tui32 completion_id) { struct stream *s; int bytes; int len; + tui32 SharedAccess; - log_debug("DesiredAccess=0x%x CreateDisposition=0x%x CreateOptions=0x%x", - DesiredAccess, CreateDisposition, CreateOptions); + log_debug("device_id=%d path=\"%s\"" + " DesiredAccess=0x%x CreateDisposition=0x%x" + " FileAttributes=0x%x CreateOptions=0x%x" + " CompletionId=%d", + device_id, path, + DesiredAccess, CreateDisposition, + FileAttributes, CreateOptions, + completion_id); /* path in unicode needs this much space */ len = ((g_mbstowcs(NULL, path, 0) * sizeof(twchar)) / 2) + 2; xstream_new(s, 1024 + len); + /* FILE_SHARE_DELETE allows files to be renamed while in use + (in some circumstances) */ + SharedAccess = SA_FILE_SHARE_READ | SA_FILE_SHARE_WRITE | + SA_FILE_SHARE_DELETE; + devredir_insert_DeviceIoRequest(s, device_id, 0, @@ -490,8 +644,8 @@ int dev_redir_send_drive_create_request(tui32 device_id, xstream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */ xstream_wr_u32_le(s, 0); /* AllocationSize high unused */ xstream_wr_u32_le(s, 0); /* AllocationSize low unused */ - xstream_wr_u32_le(s, 0); /* FileAttributes */ - xstream_wr_u32_le(s, 3); /* SharedAccess LK_TODO */ + xstream_wr_u32_le(s, FileAttributes); /* FileAttributes */ + xstream_wr_u32_le(s, SharedAccess); /* SharedAccess */ xstream_wr_u32_le(s, CreateDisposition); /* CreateDisposition */ xstream_wr_u32_le(s, CreateOptions); /* CreateOptions */ xstream_wr_u32_le(s, len); /* PathLength */ @@ -507,16 +661,17 @@ int dev_redir_send_drive_create_request(tui32 device_id, } /** - * Close a request previously created by dev_redir_send_drive_create_request() + * Close a request previously created by devredir_send_drive_create_request() *****************************************************************************/ -int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId, - tui32 DeviceId, - tui32 FileId, - tui32 CompletionId, - tui32 MajorFunction, - tui32 MinorFunc, - int pad_len) +static int +devredir_send_drive_close_request(tui16 Component, tui16 PacketId, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + enum IRP_MJ MajorFunction, + enum IRP_MN MinorFunc, + int pad_len) { struct stream *s; int bytes; @@ -546,8 +701,9 @@ int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId, * *****************************************************************************/ // LK_TODO Path needs to be Unicode -void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, - tui32 InitialQuery, char *Path) +static void +devredir_send_drive_dir_request(IRP *irp, tui32 device_id, + tui32 InitialQuery, char *Path) { struct stream *s; int bytes; @@ -577,11 +733,7 @@ void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, IRP_MJ_DIRECTORY_CONTROL, IRP_MN_QUERY_DIRECTORY); -#ifdef USE_SHORT_NAMES_IN_DIR_LISTING - xstream_wr_u32_le(s, FileBothDirectoryInformation); /* FsInformationClass */ -#else xstream_wr_u32_le(s, FileDirectoryInformation); /* FsInformationClass */ -#endif xstream_wr_u8(s, InitialQuery); /* InitialQuery */ if (!InitialQuery) @@ -612,7 +764,8 @@ void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, * * @param s stream containing client's response *****************************************************************************/ -void dev_redir_proc_client_core_cap_resp(struct stream *s) +static void +devredir_proc_client_core_cap_resp(struct stream *s) { int i; tui16 num_caps; @@ -666,7 +819,8 @@ void dev_redir_proc_client_core_cap_resp(struct stream *s) } } -void devredir_proc_client_devlist_announce_req(struct stream *s) +static void +devredir_proc_client_devlist_announce_req(struct stream *s) { unsigned int i; int j; @@ -746,10 +900,29 @@ void devredir_proc_client_devlist_announce_req(struct stream *s) } } -void -dev_redir_proc_device_iocompletion(struct stream *s) +static void +devredir_proc_client_devlist_remove_req(struct stream *s) +{ + unsigned int i; + tui32 device_count; + tui32 device_id; + + /* get number of devices being announced */ + xstream_rd_u32_le(s, device_count); + + log_debug("num of devices removed: %d", device_count); + { + for (i = 0; i < device_count; i++) + { + xstream_rd_u32_le(s, device_id); + xfuse_delete_share(device_id); + } + } +} + +static void +devredir_proc_device_iocompletion(struct stream *s) { - FUSE_DATA *fuse_data = NULL; IRP *irp = NULL; tui32 DeviceId; @@ -761,273 +934,211 @@ dev_redir_proc_device_iocompletion(struct stream *s) xstream_rd_u32_le(s, CompletionId); xstream_rd_u32_le(s, IoStatus); - log_debug("entered: IoStatus=0x%x CompletionId=%d", IoStatus, CompletionId); - if ((irp = devredir_irp_find(CompletionId)) == NULL) { log_error("IRP with completion ID %d not found", CompletionId); - return; } - - /* if callback has been set, call it */ + else if (irp->callback) { + /* Callback has been set - call it */ (*irp->callback)(s, irp, DeviceId, CompletionId, IoStatus); - goto done; } - - switch (irp->completion_type) + else { - case CID_CREATE_DIR_REQ: - log_debug("got CID_CREATE_DIR_REQ"); - if (IoStatus != NT_STATUS_SUCCESS) + enum COMPLETION_TYPE comp_type = irp->completion_type; + /* Log something about the IRP */ + if (IoStatus == NT_STATUS_SUCCESS || + IoStatus == NT_STATUS_NO_MORE_FILES || + (IoStatus == NT_STATUS_NO_SUCH_FILE && comp_type == CID_LOOKUP)) { - /* we were trying to create a request to enumerate a dir */ - /* that does not exist; let FUSE know */ - fuse_data = devredir_fuse_data_dequeue(irp); - if (fuse_data) + /* Successes or common occurrences - debug logging only */ + log_debug("got %s", completion_type_to_str(comp_type)); + } + else + { + const char *pathname = (irp->pathname) ? irp->pathname : ""; + log_error("CompletionType = %s, IoStatus=%08x " + "Pathname = %s", + completion_type_to_str(comp_type), + IoStatus, + pathname); + } + + switch (comp_type) + { + case CID_CREATE_DIR_REQ: + if (IoStatus != NT_STATUS_SUCCESS) { - xfuse_devredir_cb_enum_dir_done(fuse_data->data_ptr, - IoStatus); - free(fuse_data); + xfuse_devredir_cb_enum_dir_done(irp->fuse_info, IoStatus); + devredir_irp_delete(irp); } + else + { + xstream_rd_u32_le(s, irp->FileId); + devredir_send_drive_dir_request(irp, DeviceId, + 1, irp->pathname); + } + break; + + case CID_CREATE_REQ: + xstream_rd_u32_le(s, irp->FileId); + + xfuse_devredir_cb_create_file(irp->fuse_info, IoStatus, + DeviceId, irp->FileId); + if (irp->gen.create.creating_dir || IoStatus != NT_STATUS_SUCCESS) + { + devredir_irp_delete(irp); + } + break; + + case CID_OPEN_REQ: + xstream_rd_u32_le(s, irp->FileId); + + xfuse_devredir_cb_open_file(irp->fuse_info, IoStatus, + DeviceId, irp->FileId); + if (IoStatus != NT_STATUS_SUCCESS) + { + devredir_irp_delete(irp); + } + break; + + case CID_READ: + xstream_rd_u32_le(s, Length); + xfuse_devredir_cb_read_file(irp->fuse_info, s->p, Length); devredir_irp_delete(irp); - return; - } + break; - xstream_rd_u32_le(s, irp->FileId); - log_debug("got CID_CREATE_DIR_REQ IoStatus=0x%x FileId=%d", - IoStatus, irp->FileId); - - dev_redir_send_drive_dir_request(irp, DeviceId, 1, irp->pathname); - break; - - case CID_CREATE_OPEN_REQ: - xstream_rd_u32_le(s, irp->FileId); - - log_debug("got CID_CREATE_OPEN_REQ IoStatus=0x%x FileId=%d", - IoStatus, irp->FileId); - - fuse_data = devredir_fuse_data_dequeue(irp); - xfuse_devredir_cb_open_file(fuse_data->data_ptr, IoStatus, - DeviceId, irp->FileId); - if ((irp->type == S_IFDIR) || (IoStatus != NT_STATUS_SUCCESS)) + case CID_WRITE: + xstream_rd_u32_le(s, Length); + xfuse_devredir_cb_write_file(irp->fuse_info, IoStatus, + irp->gen.write.offset, Length); devredir_irp_delete(irp); - break; + break; - case CID_READ: - log_debug("got CID_READ"); - xstream_rd_u32_le(s, Length); - fuse_data = devredir_fuse_data_dequeue(irp); - - if (fuse_data == NULL) - { - log_error("fuse_data is NULL"); - } - else - { - xfuse_devredir_cb_read_file(fuse_data->data_ptr, s->p, Length); + case CID_CLOSE: devredir_irp_delete(irp); - } - break; + break; - case CID_WRITE: - log_debug("got CID_WRITE"); - xstream_rd_u32_le(s, Length); - fuse_data = devredir_fuse_data_dequeue(irp); - - if (fuse_data == NULL) - { - log_error("fuse_data is NULL"); - } - else - { - xfuse_devredir_cb_write_file(fuse_data->data_ptr, IoStatus, - s->p, Length); + case CID_FILE_CLOSE: + xfuse_devredir_cb_file_close(irp->fuse_info); devredir_irp_delete(irp); + break; + + case CID_DIRECTORY_CONTROL: + devredir_proc_query_dir_response(irp, s, DeviceId, + CompletionId, IoStatus); + break; + + case CID_RMDIR_OR_FILE: + xstream_rd_u32_le(s, irp->FileId); + devredir_proc_cid_rmdir_or_file(irp, IoStatus); + break; + + case CID_RMDIR_OR_FILE_RESP: + devredir_proc_cid_rmdir_or_file_resp(irp, IoStatus); + break; + + case CID_RENAME_FILE: + xstream_rd_u32_le(s, irp->FileId); + devredir_proc_cid_rename_file(irp, IoStatus); + break; + + case CID_RENAME_FILE_RESP: + devredir_proc_cid_rename_file_resp(irp, IoStatus); + break; + + case CID_LOOKUP: + devredir_proc_cid_lookup(irp, s, IoStatus); + break; + + case CID_SETATTR: + devredir_proc_cid_setattr(irp, s, IoStatus); + break; + + default: + log_error("got unknown CompletionID: DeviceId=0x%x " + "CompletionId=0x%x IoStatus=0x%x", + DeviceId, CompletionId, IoStatus); + break; } - break; - - case CID_CLOSE: - log_debug("got CID_CLOSE"); - log_debug("deleting irp with completion_id=%d comp_type=%d", - irp->CompletionId, irp->completion_type); - devredir_irp_delete(irp); - break; - - case CID_FILE_CLOSE: - log_debug("got CID_FILE_CLOSE"); - fuse_data = devredir_fuse_data_dequeue(irp); - xfuse_devredir_cb_file_close(fuse_data->data_ptr); - devredir_irp_delete(irp); - break; - - case CID_DIRECTORY_CONTROL: - log_debug("got CID_DIRECTORY_CONTROL"); - - dev_redir_proc_query_dir_response(irp, s, DeviceId, - CompletionId, IoStatus); - break; - - case CID_RMDIR_OR_FILE: - log_debug("got CID_RMDIR_OR_FILE"); - xstream_rd_u32_le(s, irp->FileId); - devredir_proc_cid_rmdir_or_file(irp, IoStatus); - return; - break; - - case CID_RMDIR_OR_FILE_RESP: - log_debug("got CID_RMDIR_OR_FILE_RESP"); - devredir_proc_cid_rmdir_or_file_resp(irp, IoStatus); - break; - - case CID_RENAME_FILE: - log_debug("got CID_RENAME_FILE"); - xstream_rd_u32_le(s, irp->FileId); - devredir_proc_cid_rename_file(irp, IoStatus); - return; - break; - - case CID_RENAME_FILE_RESP: - log_debug("got CID_RENAME_FILE_RESP"); - 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", - DeviceId, CompletionId, IoStatus); - break; } - -done: - - if (fuse_data) - { - log_debug("free FUSE_DATA=%p", fuse_data); - free(fuse_data); - } - - log_debug("exiting"); } -void -dev_redir_proc_query_dir_response(IRP *irp, - struct stream *s_in, - tui32 DeviceId, - tui32 CompletionId, - tui32 IoStatus) +static void +devredir_proc_query_dir_response(IRP *irp, + struct stream *s_in, + tui32 DeviceId, + tui32 CompletionId, + tui32 IoStatus) { - FUSE_DATA *fuse_data = NULL; - tui32 Length; - tui64 CreationTime; - tui64 LastAccessTime; - tui64 LastWriteTime; - tui64 EndOfFile; - tui32 FileAttributes; - tui32 FileNameLength; - tui32 status; - struct file_attr fattr; - - char filename[256]; - unsigned int i = 0; - xstream_rd_u32_le(s_in, Length); - if ((IoStatus == NT_STATUS_UNSUCCESSFUL) || - (IoStatus == STATUS_NO_MORE_FILES)) + if (IoStatus == NT_STATUS_SUCCESS) { - status = (IoStatus == STATUS_NO_MORE_FILES) ? 0 : IoStatus; - fuse_data = devredir_fuse_data_dequeue(irp); - xfuse_devredir_cb_enum_dir_done(fuse_data->data_ptr, status); + unsigned int i; + /* process FILE_DIRECTORY_INFORMATION structures */ + for (i = 0 ; i < Length ; ++i) + { + char filename[256]; + tui64 LastAccessTime; + tui64 LastWriteTime; + tui64 EndOfFile; + tui32 FileAttributes; + tui32 FileNameLength; + struct file_attr fattr; + + xstream_seek(s_in, 4); /* NextEntryOffset */ + xstream_seek(s_in, 4); /* FileIndex */ + xstream_seek(s_in, 8); /* CreationTime */ + xstream_rd_u64_le(s_in, LastAccessTime); + xstream_rd_u64_le(s_in, LastWriteTime); + xstream_seek(s_in, 8); /* ChangeTime */ + xstream_rd_u64_le(s_in, EndOfFile); + xstream_seek(s_in, 8); /* AllocationSize */ + xstream_rd_u32_le(s_in, FileAttributes); + xstream_rd_u32_le(s_in, FileNameLength); + + devredir_cvt_from_unicode_len(filename, s_in->p, FileNameLength); + + i += 64 + FileNameLength; + + //log_debug("LastAccessTime: 0x%llx", LastAccessTime); + //log_debug("LastWriteTime: 0x%llx", LastWriteTime); + //log_debug("EndOfFile: %lld", EndOfFile); + //log_debug("FileAttributes: 0x%x", FileAttributes); + //log_debug("FileNameLength: %d", FileNameLength); + log_debug("FileName: %s", filename); + + fattr.mode = WindowsToLinuxFilePerm(FileAttributes); + fattr.size = (size_t) EndOfFile; + fattr.atime = WINDOWS_TO_LINUX_TIME(LastAccessTime); + fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); + + /* add this entry to xrdp file system */ + xfuse_devredir_cb_enum_dir_add_entry(irp->fuse_info, filename, + &fattr); + } + + /* Ask for more directory entries */ + devredir_send_drive_dir_request(irp, DeviceId, 0, NULL); + } + else + { + if (IoStatus == NT_STATUS_NO_MORE_FILES) + { + IoStatus = NT_STATUS_SUCCESS; + } + xfuse_devredir_cb_enum_dir_done(irp->fuse_info, IoStatus); irp->completion_type = CID_CLOSE; - dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, - PAKID_CORE_DEVICE_IOREQUEST, - DeviceId, - irp->FileId, - irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); - free(fuse_data); - return; + devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); } - - /* TODO check status for errors */ - - /* process FILE_DIRECTORY_INFORMATION structures */ - while (i < Length) - { - log_debug("processing FILE_DIRECTORY_INFORMATION structs"); - - xstream_seek(s_in, 4); /* NextEntryOffset */ - xstream_seek(s_in, 4); /* FileIndex */ - 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_u64_le(s_in, EndOfFile); - xstream_seek(s_in, 8); /* AllocationSize */ - xstream_rd_u32_le(s_in, FileAttributes); - xstream_rd_u32_le(s_in, FileNameLength); - -#ifdef USE_SHORT_NAMES_IN_DIR_LISTING - xstream_seek(s_in, 4); /* EaSize */ - xstream_seek(s_in, 1); /* ShortNameLength */ - xstream_seek(s_in, 1); /* Reserved */ - xstream_seek(s_in, 23); /* ShortName in Unicode */ -#endif - devredir_cvt_from_unicode_len(filename, s_in->p, FileNameLength); - -#ifdef USE_SHORT_NAMES_IN_DIR_LISTING - i += 70 + 23 + FileNameLength; -#else - i += 64 + FileNameLength; -#endif - //log_debug("CreationTime: 0x%llx", CreationTime); - //log_debug("LastAccessTime: 0x%llx", LastAccessTime); - //log_debug("LastWriteTime: 0x%llx", LastWriteTime); - //log_debug("EndOfFile: %lld", EndOfFile); - //log_debug("FileAttributes: 0x%x", FileAttributes); -#ifdef USE_SHORT_NAMES_IN_DIR_LISTING - //log_debug("ShortNameLength: %d", ShortNameLength); -#endif - //log_debug("FileNameLength: %d", FileNameLength); - log_debug("FileName: %s", filename); - - 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); - (void)xfuse_devredir_add_file_or_dir(fuse_data->data_ptr, &fattr); - } - - dev_redir_send_drive_dir_request(irp, DeviceId, 0, NULL); } /** @@ -1041,49 +1152,54 @@ dev_redir_proc_query_dir_response(IRP *irp, *****************************************************************************/ int -dev_redir_get_dir_listing(void *fusep, tui32 device_id, const char *path) +devredir_get_dir_listing(struct state_dirscan *fusep, tui32 device_id, + const char *path) { tui32 DesiredAccess; tui32 CreateOptions; tui32 CreateDisposition; - int rval; + int rval = -1; IRP *irp; - log_debug("fusep=%p", fusep); + /* + * We need to be able to append two additional characters to the + * path after we create the IRP + */ + if ((irp = devredir_irp_with_pathnamelen_new(strlen(path) + 2)) != NULL) + { + /* convert / to windows compatible \ */ + strcpy(irp->pathname, path); + devredir_cvt_slash(irp->pathname); - if ((irp = devredir_irp_new()) == NULL) - return -1; + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_CREATE_DIR_REQ; + irp->DeviceId = device_id; + irp->fuse_info = fusep; - strncpy(irp->pathname, path, 255); + DesiredAccess = DA_FILE_READ_DATA | DA_SYNCHRONIZE; + CreateOptions = CO_FILE_DIRECTORY_FILE | + CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateDisposition = CD_FILE_OPEN; - /* convert / to windows compatible \ */ - devredir_cvt_slash(irp->pathname); + rval = devredir_send_drive_create_request(device_id, irp->pathname, + DesiredAccess, CreateOptions, + 0, CreateDisposition, + irp->CompletionId); - irp->CompletionId = g_completion_id++; - irp->completion_type = CID_CREATE_DIR_REQ; - irp->DeviceId = device_id; - - devredir_fuse_data_enqueue(irp, fusep); - - DesiredAccess = DA_FILE_READ_DATA | DA_SYNCHRONIZE; - CreateOptions = CO_FILE_DIRECTORY_FILE | CO_FILE_SYNCHRONOUS_IO_NONALERT; - CreateDisposition = CD_FILE_OPEN; - - rval = dev_redir_send_drive_create_request(device_id, irp->pathname, - DesiredAccess, CreateOptions, - CreateDisposition, - irp->CompletionId); - - log_debug("looking for device_id=%d path=%s", device_id, irp->pathname); - - /* when we get a response to dev_redir_send_drive_create_request(), we */ - /* call dev_redir_send_drive_dir_request(), which needs the following */ - /* at the end of the path argument */ - if (dev_redir_string_ends_with(irp->pathname, '\\')) - strcat(irp->pathname, "*"); - else - strcat(irp->pathname, "\\*"); + log_debug("looking for device_id=%d path=%s", device_id, irp->pathname); + /* when we get a response to devredir_send_drive_create_request(), we + * call devredir_send_drive_dir_request(), which needs the following + * at the end of the path argument */ + if (devredir_string_ends_with(irp->pathname, '\\')) + { + strcat(irp->pathname, "*"); + } + else + { + strcat(irp->pathname, "\\*"); + } + } return rval; } @@ -1099,121 +1215,195 @@ 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) +devredir_lookup_entry(struct state_lookup *fusep, tui32 device_id, + const char *path) { 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,'/')) + if ((irp = devredir_irp_with_pathname_new(path)) != NULL) { - ++pathlen; + /* 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; + irp->DeviceId = device_id; + irp->gen.lookup.state = E_LOOKUP_GET_FH; + irp->fuse_info = fusep; + + DesiredAccess = DA_FILE_READ_ATTRIBUTES | DA_SYNCHRONIZE; + CreateOptions = 0; + CreateDisposition = CD_FILE_OPEN; + + log_debug("lookup for device_id=%d path=%s CompletionId=%d", + device_id, irp->pathname, irp->CompletionId); + + rval = devredir_send_drive_create_request(device_id, + irp->pathname, + DesiredAccess, CreateOptions, + 0, CreateDisposition, + irp->CompletionId); } - if (pathlen < sizeof(irp->pathname)) + + return rval; +} + +/** + * FUSE calls this function whenever it wants us to set the attributes for + * 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 filename the name of the file + * @param fattr the file attributes to set for the file + * @param to_set Which bits of the file attributes have changed + * + * @return 0 on success, -1 on failure + *****************************************************************************/ +int +devredir_setattr_for_entry(struct state_setattr *fusep, tui32 device_id, + const char *filename, + const struct file_attr *fattr, + tui32 to_set) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval = -1; + IRP *irp; + + log_debug("fusep=%p", fusep); + + if ((irp = devredir_irp_with_pathname_new(filename)) != NULL) { - if ((irp = devredir_irp_new()) != NULL) + /* convert / to windows compatible \ */ + devredir_cvt_slash(irp->pathname); + + /* + * Allocate an IRP to open the file, update the attributes + * and close the file. + */ + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_SETATTR; + irp->DeviceId = device_id; + irp->fuse_info = fusep; + + irp->gen.setattr.state = E_SETATTR_GET_FH; + irp->gen.setattr.to_set = to_set; + irp->gen.setattr.fattr = *fattr; + + /* + * Don't set DA_FILE_WRITE_DATA unless we're changing the + * EndOfFile pointer. Otherwise we can't change the attributes + * of read-only files! */ + DesiredAccess = DA_FILE_WRITE_ATTRIBUTES; + if (to_set & TO_SET_SIZE) { - 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); + DesiredAccess |= DA_FILE_WRITE_DATA; } + CreateOptions = 0; + CreateDisposition = CD_FILE_OPEN; + + log_debug("lookup for device_id=%d path=%s", + device_id, irp->pathname); + + rval = devredir_send_drive_create_request(device_id, + irp->pathname, + DesiredAccess, CreateOptions, + 0, 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) +devredir_file_create(struct state_create *fusep, tui32 device_id, + const char *path, int mode) { tui32 DesiredAccess; tui32 CreateOptions; + tui32 FileAttributes = 0; tui32 CreateDisposition; - int rval; + int rval = -1; IRP *irp; - log_debug("device_id=%d path=%s mode=0x%x", device_id, path, mode); + log_debug("device_id=%d path=%s mode=0%o", device_id, path, mode); - if ((irp = devredir_irp_new()) == NULL) - return -1; - - if (type & OP_RENAME_FILE) + if ((irp = devredir_irp_with_pathname_new(path)) != NULL) { - irp->completion_type = CID_RENAME_FILE; - strncpy(irp->gen.buf, gen_buf, 1023); - } - else - { - irp->completion_type = CID_CREATE_OPEN_REQ; - } + /* convert / to windows compatible \ */ + devredir_cvt_slash(irp->pathname); - irp->CompletionId = g_completion_id++; - irp->DeviceId = device_id; + irp->completion_type = CID_CREATE_REQ; + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + irp->fuse_info = fusep; - strncpy(irp->pathname, path, 255); - devredir_fuse_data_enqueue(irp, fusep); - - if (mode & O_CREAT) - { - log_debug("open file in O_CREAT"); DesiredAccess = 0x0016019f; /* got this value from windows */ - - if (type & S_IFDIR) + FileAttributes = LinuxToWindowsFilePerm(mode); + if (mode & S_IFDIR) { log_debug("creating dir"); CreateOptions = CO_FILE_DIRECTORY_FILE | CO_FILE_SYNCHRONOUS_IO_NONALERT; - irp->type = S_IFDIR; + irp->gen.create.creating_dir = 1; } else { log_debug("creating file"); CreateOptions = 0x44; /* got this value from windows */ + irp->gen.create.creating_dir = 0; } //CreateDisposition = CD_FILE_CREATE; CreateDisposition = 0x02; /* got this value from windows */ + + rval = devredir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + FileAttributes, + CreateDisposition, + irp->CompletionId); } - else + + return rval; +} + +int +devredir_file_open(struct state_open *fusep, tui32 device_id, + const char *path, int flags) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 FileAttributes = 0; + tui32 CreateDisposition; + int rval = -1; + IRP *irp; + + log_debug("device_id=%d path=%s flags=0%x", + device_id, path, flags); + + if ((irp = devredir_irp_with_pathname_new(path)) != NULL) { -#if 1 - switch(mode & O_ACCMODE) + /* convert / to windows compatible \ */ + devredir_cvt_slash(irp->pathname); + + irp->completion_type = CID_OPEN_REQ; + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + + irp->fuse_info = fusep; + + switch(flags & O_ACCMODE) { case O_RDONLY: log_debug("open file in O_RDONLY"); @@ -1239,25 +1429,19 @@ dev_redir_file_open(void *fusep, tui32 device_id, const char *path, CreateOptions = CO_FILE_SYNCHRONOUS_IO_NONALERT; CreateDisposition = CD_FILE_OPEN; // WAS 1 -#else - /* got this value from windows; the 0x00000010 was added by LK; */ - /* without this rdesktop opens files in O_RDONLY instead of */ - /* O_RDWR mode */ - DesiredAccess = 0x00120089 | 0x00000010; - CreateOptions = 0x20060; - CreateDisposition = 0x01; -#endif - } - rval = dev_redir_send_drive_create_request(device_id, path, - DesiredAccess, CreateOptions, - CreateDisposition, - irp->CompletionId); + rval = devredir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + FileAttributes, + CreateDisposition, + irp->CompletionId); + } return rval; } -int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId) +int devredir_file_close(struct state_close *fusep, tui32 device_id, + tui32 FileId) { IRP *irp; @@ -1278,15 +1462,15 @@ int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId) #endif irp->completion_type = CID_FILE_CLOSE; irp->DeviceId = device_id; - devredir_fuse_data_enqueue(irp, fusep); + irp->fuse_info = fusep; - return dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, - PAKID_CORE_DEVICE_IOREQUEST, - device_id, - FileId, - irp->CompletionId, - IRP_MJ_CLOSE, - 0, 32); + return devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + device_id, + FileId, + irp->CompletionId, + IRP_MJ_CLOSE, + 0, 32); } /** @@ -1294,38 +1478,41 @@ int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId) *****************************************************************************/ int -devredir_rmdir_or_file(void *fusep, tui32 device_id, const char *path, int mode) +devredir_rmdir_or_file(struct state_remove *fusep, tui32 device_id, + const char *path) { tui32 DesiredAccess; tui32 CreateOptions; tui32 CreateDisposition; - int rval; + int rval = -1; IRP *irp; - if ((irp = devredir_irp_new()) == NULL) - return -1; + if ((irp = devredir_irp_with_pathname_new(path)) != NULL) + { + /* convert / to windows compatible \ */ + devredir_cvt_slash(irp->pathname); - irp->CompletionId = g_completion_id++; - irp->completion_type = CID_RMDIR_OR_FILE; - irp->DeviceId = device_id; + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_RMDIR_OR_FILE; + irp->DeviceId = device_id; - strncpy(irp->pathname, path, 255); - devredir_fuse_data_enqueue(irp, fusep); + irp->fuse_info = fusep; - //DesiredAccess = DA_DELETE | DA_FILE_READ_ATTRIBUTES | DA_SYNCHRONIZE; - DesiredAccess = 0x00100080; /* got this value from windows */ + //DesiredAccess = DA_DELETE | DA_FILE_READ_ATTRIBUTES | DA_SYNCHRONIZE; + DesiredAccess = 0x00100080; /* got this value from windows */ - //CreateOptions = CO_FILE_DELETE_ON_CLOSE | CO_FILE_DIRECTORY_FILE | - // CO_FILE_SYNCHRONOUS_IO_NONALERT; - CreateOptions = 0x020; /* got this value from windows */ + //CreateOptions = CO_FILE_DELETE_ON_CLOSE | CO_FILE_DIRECTORY_FILE | + // CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateOptions = 0x020; /* got this value from windows */ - //CreateDisposition = CD_FILE_OPEN; // WAS 1 - CreateDisposition = 0x01; /* got this value from windows */ + //CreateDisposition = CD_FILE_OPEN; // WAS 1 + CreateDisposition = 0x01; /* got this value from windows */ - rval = dev_redir_send_drive_create_request(device_id, path, - DesiredAccess, CreateOptions, - CreateDisposition, - irp->CompletionId); + rval = devredir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + 0, CreateDisposition, + irp->CompletionId); + } return rval; } @@ -1337,64 +1524,69 @@ devredir_rmdir_or_file(void *fusep, tui32 device_id, const char *path, int mode) *****************************************************************************/ int -devredir_file_read(void *fusep, tui32 DeviceId, tui32 FileId, +devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, tui32 Length, tui64 Offset) { struct stream *s; IRP *irp; IRP *new_irp; int bytes; + int rval = -1; xstream_new(s, 1024); + /* Check we've got an open IRP for this file already */ if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL) { log_error("no IRP found with FileId = %d", FileId); xfuse_devredir_cb_read_file(fusep, NULL, 0); xstream_free(s); - return -1; } - /* create a new IRP for this request */ - if ((new_irp = devredir_irp_clone(irp)) == NULL) + else if ((new_irp = devredir_irp_new()) == NULL) { /* system out of memory */ xfuse_devredir_cb_read_file(fusep, NULL, 0); xstream_free(s); - return -1; } - new_irp->FileId = 0; - new_irp->completion_type = CID_READ; - new_irp->CompletionId = g_completion_id++; - devredir_fuse_data_enqueue(new_irp, fusep); + else + { + new_irp->DeviceId = DeviceId; + new_irp->FileId = FileId; + new_irp->completion_type = CID_READ; + new_irp->CompletionId = g_completion_id++; + new_irp->fuse_info = fusep; - devredir_insert_DeviceIoRequest(s, - DeviceId, - FileId, - new_irp->CompletionId, - IRP_MJ_READ, - 0); + devredir_insert_DeviceIoRequest(s, + DeviceId, + FileId, + new_irp->CompletionId, + IRP_MJ_READ, + 0); - xstream_wr_u32_le(s, Length); - xstream_wr_u64_le(s, Offset); - xstream_seek(s, 20); + xstream_wr_u32_le(s, Length); + xstream_wr_u64_le(s, Offset); + xstream_seek(s, 20); - /* send to client */ - bytes = xstream_len(s); - send_channel_data(g_rdpdr_chan_id, s->data, bytes); - xstream_free(s); + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + rval = 0; + } - return 0; + return rval; } int -dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId, - const char *buf, int Length, tui64 Offset) +devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, + const char *buf, int Length, tui64 Offset) { struct stream *s; IRP *irp; IRP *new_irp; int bytes; + int rval = -1; log_debug("DeviceId=%d FileId=%d Length=%d Offset=%lld", DeviceId, FileId, Length, (long long)Offset); @@ -1404,140 +1596,108 @@ dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId, if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL) { log_error("no IRP found with FileId = %d", FileId); - xfuse_devredir_cb_write_file(fusep, NT_STATUS_UNSUCCESSFUL, NULL, 0); + xfuse_devredir_cb_write_file(fusep, NT_STATUS_UNSUCCESSFUL, 0, 0); xstream_free(s); - return -1; } - /* create a new IRP for this request */ - if ((new_irp = devredir_irp_clone(irp)) == NULL) + else if ((new_irp = devredir_irp_new()) == NULL) { /* system out of memory */ - xfuse_devredir_cb_write_file(fusep, NT_STATUS_UNSUCCESSFUL, NULL, 0); + xfuse_devredir_cb_write_file(fusep, NT_STATUS_UNSUCCESSFUL, 0, 0); xstream_free(s); - return -1; } - new_irp->FileId = 0; - new_irp->completion_type = CID_WRITE; - new_irp->CompletionId = g_completion_id++; - devredir_fuse_data_enqueue(new_irp, fusep); - - devredir_insert_DeviceIoRequest(s, - DeviceId, - FileId, - new_irp->CompletionId, - IRP_MJ_WRITE, - 0); - - xstream_wr_u32_le(s, Length); - xstream_wr_u64_le(s, Offset); - xstream_seek(s, 20); /* padding */ - - /* now insert real data */ - xstream_copyin(s, buf, Length); - - /* send to client */ - bytes = xstream_len(s); - send_channel_data(g_rdpdr_chan_id, s->data, bytes); - xstream_free(s); - - return 0; -} - -/****************************************************************************** -** FIFO for FUSE_DATA ** -******************************************************************************/ - -/** - * Return FUSE_DATA at the head of the queue without removing it - * - * @return FUSE_DATA on success, or NULL on failure - *****************************************************************************/ - -FUSE_DATA * -devredir_fuse_data_peek(IRP *irp) -{ - log_debug("returning %p", irp->fd_head); - return irp->fd_head; -} - -/** - * Return oldest FUSE_DATA from queue - * - * @return FUSE_DATA on success, NULL on failure - *****************************************************************************/ - -FUSE_DATA * -devredir_fuse_data_dequeue(IRP *irp) -{ - FUSE_DATA *head; - - if ((irp == NULL) || (irp->fd_head == NULL)) + else { - log_debug("+++ returning NULL"); - return NULL; + new_irp->DeviceId = DeviceId; + new_irp->FileId = FileId; + new_irp->completion_type = CID_WRITE; + new_irp->CompletionId = g_completion_id++; + new_irp->fuse_info = fusep; + /* Offset needed after write to calculate new EOF */ + new_irp->gen.write.offset = Offset; + + devredir_insert_DeviceIoRequest(s, + DeviceId, + FileId, + new_irp->CompletionId, + IRP_MJ_WRITE, + 0); + + xstream_wr_u32_le(s, Length); + xstream_wr_u64_le(s, Offset); + xstream_seek(s, 20); /* padding */ + + /* now insert real data */ + xstream_copyin(s, buf, Length); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + rval = 0; } - if (irp->fd_head->next == NULL) - { - /* only one element in queue */ - head = irp->fd_head; - irp->fd_head = NULL; - irp->fd_tail = NULL; - log_debug("+++ returning FUSE_DATA=%p containing FUSE_INFO=%p", - head, head->data_ptr); - return head; - } - - /* more than one element in queue */ - head = irp->fd_head; - irp->fd_head = head->next; - log_debug("+++ returning FUSE_DATA=%p containing FUSE_INFO=%p", - head, head->data_ptr); - return head; + return rval; } -/** - * Insert specified FUSE_DATA at the end of our queue - * - * @return 0 on success, -1 on failure - *****************************************************************************/ -int -devredir_fuse_data_enqueue(IRP *irp, void *vp) +int devredir_file_rename(struct state_rename *fusep, tui32 device_id, + const char *old_name, + const char *new_name) { - FUSE_DATA *fd; - FUSE_DATA *tail; + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 FileAttributes = 0; + tui32 CreateDisposition; + int rval = -1; + IRP *irp; + unsigned int len; + + log_debug("device_id=%d old_name=%s new_name=%s", + device_id, old_name, new_name); - if (irp == NULL) - return -1; - - fd = g_new0(FUSE_DATA, 1); - if (fd == NULL) - return -1; - - fd->data_ptr = vp; - fd->next = NULL; - - if (irp->fd_tail == NULL) + /* + * Allocate an IRP with enough space for both the old and new names. + * We'll store the new name after the old name:- + * + * | n | a | m | e | 1 | \0 | n | a | m | e | 2 | \0 | + * ^ ^ + * irp->pathname ----+ | + * irp->gen.rename.new_name ------------------+ + */ + len = strlen(old_name) + 1 + strlen(new_name); + if ((irp = devredir_irp_with_pathnamelen_new(len)) != NULL) { - /* queue is empty, insert at head */ - irp->fd_head = fd; - irp->fd_tail = fd; - log_debug("+++ inserted FUSE_DATA=%p containing FUSE_INFO=%p at head", - fd, vp); - return 0; + /* Set up pointer to new name string */ + irp->gen.rename.new_name = irp->pathname + strlen(old_name) + 1; + + /* Copy both strings, and change'/' to '\\' characters */ + strcpy(irp->pathname, old_name); + devredir_cvt_slash(irp->pathname); + strcpy(irp->gen.rename.new_name, new_name); + devredir_cvt_slash(irp->gen.rename.new_name); + + irp->completion_type = CID_RENAME_FILE; + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + + irp->fuse_info = fusep; + + DesiredAccess = DA_FILE_WRITE_ATTRIBUTES | DA_DELETE; + CreateOptions = 0; + CreateDisposition = CD_FILE_OPEN; // WAS 1 + + rval = devredir_send_drive_create_request(device_id, old_name, + DesiredAccess, CreateOptions, + FileAttributes, + CreateDisposition, + irp->CompletionId); } - /* queue is not empty, insert at tail end */ - tail = irp->fd_tail; - tail->next = fd; - irp->fd_tail = fd; - log_debug("+++ inserted FUSE_DATA=%p containing FUSE_INFO=%p at tail", - fd, vp); - return 0; + return rval; } + /****************************************************************************** ** miscellaneous stuff ** ******************************************************************************/ @@ -1547,8 +1707,8 @@ devredir_insert_DeviceIoRequest(struct stream *s, tui32 DeviceId, tui32 FileId, tui32 CompletionId, - tui32 MajorFunction, - tui32 MinorFunction) + enum IRP_MJ MajorFunction, + enum IRP_MN MinorFunction) { /* setup DR_DEVICE_IOREQUEST header */ xstream_wr_u16_le(s, RDPDR_CTYP_CORE); @@ -1564,7 +1724,7 @@ devredir_insert_DeviceIoRequest(struct stream *s, * Convert / to windows compatible \ *****************************************************************************/ -void +static void devredir_cvt_slash(char *path) { char *cptr = path; @@ -1577,7 +1737,7 @@ devredir_cvt_slash(char *path) } } -void +static void devredir_cvt_to_unicode(char *unicode, const char *path) { char *dest; @@ -1603,7 +1763,7 @@ devredir_cvt_to_unicode(char *unicode, const char *path) *dest++ = 0; } -void +static void devredir_cvt_from_unicode_len(char *path, char *unicode, int len) { char *dest; @@ -1640,8 +1800,8 @@ devredir_cvt_from_unicode_len(char *path, char *unicode, int len) g_free(dest_saved); } -int -dev_redir_string_ends_with(const char *string, char c) +static int +devredir_string_ends_with(const char *string, char c) { size_t len; @@ -1649,14 +1809,6 @@ dev_redir_string_ends_with(const char *string, char c) return (len > 0 && string[len - 1] == c) ? 1 : 0; } -void -devredir_insert_RDPDR_header(struct stream *s, tui16 Component, - tui16 PacketId) -{ - xstream_wr_u16_le(s, Component); - xstream_wr_u16_le(s, PacketId); -} - static void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) { @@ -1665,12 +1817,7 @@ devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) if (IoStatus != NT_STATUS_SUCCESS) { - FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp); - if (fuse_data) - { - xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus); - free(fuse_data); - } + xfuse_devredir_cb_rmdir_or_file(irp->fuse_info, IoStatus); devredir_irp_delete(irp); return; } @@ -1697,14 +1844,7 @@ devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) static void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus) { - FUSE_DATA *fuse_data; - - fuse_data = devredir_fuse_data_dequeue(irp); - if (fuse_data) - { - xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus); - free(fuse_data); - } + xfuse_devredir_cb_rmdir_or_file(irp->fuse_info, IoStatus); if (IoStatus != NT_STATUS_SUCCESS) { @@ -1713,12 +1853,12 @@ devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus) } 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); + devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); } static void @@ -1734,18 +1874,14 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) { log_debug("rename returned with IoStatus=0x%x", IoStatus); - FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp); - if (fuse_data) - { - xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus); - free(fuse_data); - } + xfuse_devredir_cb_rename_file(irp->fuse_info, IoStatus); devredir_irp_delete(irp); return; } /* 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.rename.new_name, 0) + * sizeof(twchar)) / 2) + 2; sblen = 6 + flen; xstream_new(s, 1024 + flen); @@ -1763,7 +1899,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.rename.new_name); /* UNICODE_TODO */ xstream_seek(s, flen); /* send to client */ @@ -1777,16 +1913,9 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) static void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) { - FUSE_DATA *fuse_data; - log_debug("entered"); - fuse_data = devredir_fuse_data_dequeue(irp); - if (fuse_data) - { - xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus); - free(fuse_data); - } + xfuse_devredir_cb_rename_file(irp->fuse_info, IoStatus); if (IoStatus != NT_STATUS_SUCCESS) { @@ -1795,7 +1924,7 @@ devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) } irp->completion_type = CID_CLOSE; - dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, + devredir_send_drive_close_request(RDPDR_CTYP_CORE, PAKID_CORE_DEVICE_IOREQUEST, irp->DeviceId, irp->FileId, @@ -1803,191 +1932,364 @@ devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) 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) + +/* + * Re-uses the specified IRP to issue a request to get file attributes + * of varying types + * + * References : [MS-RDPEFS] 2.2.3.3.9 [MS-FSCC] 2.4 + *****************************************************************************/ +static void issue_lookup(IRP *irp, int lookup_type) { struct stream *s; int bytes; + bytes = + lookup_type == FileBasicInformation ? FILE_BASIC_INFORMATION_SIZE : + lookup_type == FileStandardInformation ? FILE_STD_INFORMATION_SIZE : + 0; 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 */ + xstream_wr_u32_le(s, lookup_type); + xstream_wr_u32_le(s, bytes); /* buffer length */ + xstream_seek(s, 24); /* padding */ + xstream_seek(s, bytes); /* 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) +/* + * Parses an incoming FileBasicInformation structure + *****************************************************************************/ +static void lookup_read_basic_attributes(IRP *irp, struct stream *s_in) { - FUSE_DATA *fuse_data; - tui32 Length; - tui64 EndOfFile; - struct xrdp_inode *xinode = NULL; + tui64 LastAccessTime; + tui64 LastWriteTime; + tui32 FileAttributes; - fuse_data = devredir_fuse_data_dequeue(irp); - if (!fuse_data) + log_debug("processing FILE_BASIC_INFORMATION"); + + xstream_seek(s_in, 8); /* 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("LastAccessTime: 0x%llx", + // (unsigned long long)LastAccessTime); + //log_debug("LastWriteTime: 0x%llx", + // (unsigned long long)LastWriteTime); + //log_debug("ChangeTime: 0x%llx", + // (unsigned long long)ChangeTime); + //log_debug("FileAttributes: 0x%x", (unsigned int)FileAttributes); + + /* Save the basic attributes in the IRP */ + irp->gen.lookup.fattr.mode = WindowsToLinuxFilePerm(FileAttributes); + irp->gen.lookup.fattr.atime = WINDOWS_TO_LINUX_TIME(LastAccessTime); + irp->gen.lookup.fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); +} + +/* + * Parses an incoming FileStandardInformation structure + *****************************************************************************/ +static void lookup_read_standard_attributes(IRP *irp, struct stream *s_in) +{ + tui64 EndOfFile; + 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); + + irp->gen.lookup.fattr.size = EndOfFile; +} + +/* + * Completes a lookup request and returns status to the caller. + * + * Unless IoStatus is NT_STATUS_SUCCESS, the lookup has failed. + *****************************************************************************/ +static void lookup_done(IRP *irp, tui32 IoStatus) +{ + log_debug("Lookup with completion_id=%d returning 0x%x", + irp->CompletionId, IoStatus); + xfuse_devredir_cb_lookup_entry(irp->fuse_info, IoStatus, + &irp->gen.lookup.fattr); + + if (irp->FileId == 0) { - log_error("fuse_data unexpectedly NULL!"); + /* Open failed - no file handle */ + devredir_irp_delete(irp); } else { - if (IoStatus == NT_STATUS_SUCCESS) + /* Close the file handle */ + irp->completion_type = CID_CLOSE; + devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); + } +} + + +/* + * lookup has a mini state machine built-in, as it needs to issue + * multiple I/O requests, but unlike lookup these are not always the same. + */ +static void +devredir_proc_cid_lookup(IRP *irp, + struct stream *s_in, + tui32 IoStatus) +{ + tui32 Length; + + log_debug("entry state is %d",irp->gen.lookup.state); + if (IoStatus != NT_STATUS_SUCCESS) + { + /* This is common to all setattr states */ + log_debug("last lookup returned with IoStatus=0x%08x", IoStatus); + lookup_done(irp, IoStatus); + } + else + { + /* Read and validate any data we've got queued up */ + switch(irp->gen.lookup.state) { - /* 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); + case E_LOOKUP_GET_FH: + /* We've been sent the file ID */ + xstream_rd_u32_le(s_in, irp->FileId); + issue_lookup(irp, FileBasicInformation); + irp->gen.lookup.state = E_LOOKUP_CHECK_BASIC; + break; - /* Finish the attribute block off and add the file */ - irp->gen.fattr.size = EndOfFile; - irp->gen.fattr.name = strrchr(irp->pathname,'\\') + 1; + case E_LOOKUP_CHECK_BASIC: + /* Returned length what we expected? */ + xstream_rd_u32_le(s_in, Length); + if (Length != FILE_BASIC_INFORMATION_SIZE) + { + log_error("Expected FILE_BASIC_INFORMATION length" + "%d, got len=%d", + FILE_BASIC_INFORMATION_SIZE, Length); + IoStatus = NT_STATUS_UNSUCCESSFUL; + lookup_done(irp, IoStatus); + } + else + { + lookup_read_basic_attributes(irp, s_in); + issue_lookup(irp, FileStandardInformation); + irp->gen.lookup.state = E_LOOKUP_CHECK_EOF; + } + break; - xinode = xfuse_devredir_add_file_or_dir(fuse_data->data_ptr, - &irp->gen.fattr); - } + case E_LOOKUP_CHECK_EOF: + /* Returned length what we expected? */ + xstream_rd_u32_le(s_in, Length); + if (Length != FILE_STD_INFORMATION_SIZE) + { + log_error("Expected FILE_STD_INFORMATION length" + "%d, got len=%d", + FILE_STD_INFORMATION_SIZE, Length); + IoStatus = NT_STATUS_UNSUCCESSFUL; + } + else + { + lookup_read_standard_attributes(irp, s_in); + } + lookup_done(irp, IoStatus); + break; } + } +} - xfuse_devredir_cb_lookup_entry(fuse_data->data_ptr, IoStatus, xinode); - free(fuse_data); + +/* + * Re-uses the specified IRP to issue a request to set basic file attributes + * + * References : [MS-RDPEFS] 2.2.3.3.9 [MS-FSCC] 2.4.7 + *****************************************************************************/ +static void issue_setattr_basic(IRP *irp) +{ + struct stream *s; + int bytes; + const struct file_attr *fattr = &irp->gen.setattr.fattr; + tui32 to_set = irp->gen.setattr.to_set; + + tui32 FileAttributes = 0; + tui64 atime = 0; + tui64 mtime = 0; + + if (to_set & TO_SET_MODE) + { + FileAttributes = LinuxToWindowsFilePerm(fattr->mode); + } + if (to_set & TO_SET_ATIME) + { + atime = LINUX_TO_WINDOWS_TIME(fattr->atime); + } + if (to_set & TO_SET_MTIME) + { + mtime = LINUX_TO_WINDOWS_TIME(fattr->mtime); } - /* 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); + xstream_new(s, 1024); + devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, + irp->CompletionId, + IRP_MJ_SET_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_wr_u64_le(s, 0LL); /* CreationTime */ + xstream_wr_u64_le(s, atime); /* LastAccessTime */ + xstream_wr_u64_le(s, mtime); /* LastWriteTime */ + xstream_wr_u64_le(s, 0LL); /* ChangeTime */ + xstream_wr_u32_le(s, FileAttributes); /* FileAttributes */ + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); +} + + +/* + * Re-uses the specified IRP to issue a request to set file EOF + * + * References : [MS-RDPEFS] 2.2.3.3.9 [MS-FSCC] 2.4.13 + *****************************************************************************/ +static void issue_setattr_eof(IRP *irp) +{ + struct stream *s; + int bytes; + + xstream_new(s, 1024); + devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, + irp->CompletionId, + IRP_MJ_SET_INFORMATION, 0); + + xstream_wr_u32_le(s, FileEndOfFileInformation); + xstream_wr_u32_le(s, FILE_END_OF_FILE_INFORMATION_SIZE); + /* buffer length */ + xstream_seek(s, 24); /* padding */ + xstream_wr_u64_le(s, irp->gen.setattr.fattr.size); + /* File size */ + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); +} + +/* + * Completes a setattr request and returns status to the caller. + *****************************************************************************/ +static void setattr_done(IRP *irp, tui32 IoStatus) +{ + xfuse_devredir_cb_setattr(irp->fuse_info, IoStatus); + + if (irp->FileId == 0) + { + /* Open failed - no file handle */ + devredir_irp_delete(irp); + } + else + { + /* Close the file handle */ + irp->completion_type = CID_CLOSE; + devredir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); + } +} + + +/* + * setattr has a mini state machine built-in, as it needs to issue + * multiple I/O requests, but unlike lookup these are not always the same. + */ +static void +devredir_proc_cid_setattr(IRP *irp, + struct stream *s_in, + tui32 IoStatus) +{ +#define TO_SET_BASIC_ATTRS (TO_SET_MODE | \ + TO_SET_ATIME | TO_SET_MTIME) + tui32 Length; + + log_debug("entry state is %d",irp->gen.setattr.state); + if (IoStatus != NT_STATUS_SUCCESS) + { + /* This is common to all setattr states */ + log_debug("last setattr returned with IoStatus=0x%08x", IoStatus); + setattr_done(irp, IoStatus); + } + else + { + /* Read and validate any data we've got queued up */ + switch(irp->gen.setattr.state) + { + case E_SETATTR_GET_FH: + /* We've been sent the file ID */ + xstream_rd_u32_le(s_in, irp->FileId); + break; + + case E_SETATTR_CHECK_BASIC: + /* Returned length what we expected? */ + xstream_rd_u32_le(s_in, Length); + if (Length != FILE_BASIC_INFORMATION_SIZE) + { + log_error("Expected FILE_BASIC_INFORMATION length" + "%d, got len=%d", + FILE_BASIC_INFORMATION_SIZE, Length); + } + + /* Clear the basic bits so we don't end up in here again */ + irp->gen.setattr.to_set &= ~TO_SET_BASIC_ATTRS; + break; + + case E_SETATTR_CHECK_EOF: + /* Returned length what we expected? */ + xstream_rd_u32_le(s_in, Length); + if (Length != FILE_END_OF_FILE_INFORMATION_SIZE) + { + log_error("Expected FILE_END_OF_FILE_INFORMATION length" + "%d, got len=%d", + FILE_END_OF_FILE_INFORMATION_SIZE, Length); + } + + /* Clear the size bits so we don't end up in here again */ + irp->gen.setattr.to_set &= ~TO_SET_SIZE; + break; + } + + /* Work out the next call to issue */ + if (irp->gen.setattr.to_set & TO_SET_BASIC_ATTRS) + { + issue_setattr_basic(irp); + irp->gen.setattr.state = E_SETATTR_CHECK_BASIC; + } + else if (irp->gen.setattr.to_set & TO_SET_SIZE) + { + issue_setattr_eof(irp); + irp->gen.setattr.state = E_SETATTR_CHECK_EOF; + } + else + { + setattr_done(irp, IoStatus); + } + } +#undef TO_SET_BASIC_ATTRS } diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 8042347c..04483753 100644 --- a/sesman/chansrv/devredir.h +++ b/sesman/chansrv/devredir.h @@ -18,334 +18,80 @@ * limitations under the License. */ -// LK_TODO dev_redir_xxx should become devredir_xxx - #if !defined(DEVREDIR_H) #define DEVREDIR_H #include "irp.h" +#include "ms-rdpefs.h" -#define USE_SHORT_NAMES_IN_DIR_LISTING +int devredir_init(void); +int devredir_deinit(void); -FUSE_DATA *devredir_fuse_data_peek(IRP *irp); -FUSE_DATA *devredir_fuse_data_dequeue(IRP *irp); -int devredir_fuse_data_enqueue(IRP *irp, void *vp); +int devredir_data_in(struct stream* s, int chan_id, int chan_flags, + int length, int total_length); -int dev_redir_init(void); -int dev_redir_deinit(void); - -int dev_redir_data_in(struct stream* s, int chan_id, int chan_flags, - int length, int total_length); - -int dev_redir_get_wait_objs(tbus* objs, int* count, int* timeout); -int dev_redir_check_wait_objs(void); - -void dev_redir_send_server_core_cap_req(void); -void dev_redir_send_server_clientID_confirm(void); -void dev_redir_send_server_user_logged_on(void); -void devredir_send_server_device_announce_resp(tui32 device_id); - -void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, - tui32 InitialQuery, char *Path); - -int dev_redir_send_drive_create_request(tui32 device_id, - const char *path, - tui32 DesiredAccess, - tui32 CreateOptions, - tui32 CreateDisposition, - tui32 completion_id); - -int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId, - tui32 DeviceId, - tui32 FileId, - tui32 CompletionId, - tui32 MajorFunction, - tui32 MinorFunc, - int pad_len); - -void devredir_proc_client_devlist_announce_req(struct stream *s); -void dev_redir_proc_client_core_cap_resp(struct stream *s); -void dev_redir_proc_device_iocompletion(struct stream *s); - -void dev_redir_proc_query_dir_response(IRP *irp, - struct stream *s, - tui32 DeviceId, - tui32 CompletionId, - tui32 IoStatus); +int devredir_get_wait_objs(tbus* objs, int* count, int* timeout); +int devredir_check_wait_objs(void); /* misc stuff */ void devredir_insert_DeviceIoRequest(struct stream *s, tui32 DeviceId, tui32 FileId, tui32 CompletionId, - tui32 MajorFunction, - tui32 MinorFunction); + enum IRP_MJ MajorFunction, + enum IRP_MN MinorFunction); -void devredir_cvt_slash(char *path); -void devredir_cvt_to_unicode(char *unicode, const char *path); -void devredir_cvt_from_unicode_len(char *path, char *unicode, int len); -int dev_redir_string_ends_with(const char *string, char c); - -void devredir_insert_RDPDR_header(struct stream *s, tui16 Component, - tui16 PacketId); +/* State pointer types (opaque outside this module), used for + * callback data + */ +struct state_dirscan; +struct state_lookup; +struct state_setattr; +struct state_open; +struct state_create; +struct state_read; +struct state_write; +struct state_remove; +struct state_close; /* 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 devredir_get_dir_listing(struct state_dirscan *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 devredir_lookup_entry(struct state_lookup *fusep, tui32 device_id, + const char *path); -int devredir_file_close(void *fusep, tui32 device_id, tui32 file_id); +int devredir_setattr_for_entry( + struct state_setattr *fusep, tui32 device_id, + const char *filename, + const struct file_attr *fattr, + tui32 to_set); -int devredir_file_read(void *fusep, tui32 device_id, tui32 FileId, +int devredir_file_create( + struct state_create *fusep, tui32 device_id, + const char *path, int mode); + +int devredir_file_open(struct state_open *fusep, tui32 device_id, + const char *path, int flags); + +int devredir_file_close(struct state_close *fusep, tui32 device_id, + tui32 file_id); + +int devredir_file_read(struct state_read *fusep, tui32 device_id, tui32 FileId, tui32 Length, tui64 Offset); int -dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId, +devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, const char *buf, int Length, tui64 Offset); +int devredir_file_rename( + struct state_rename *fusep, tui32 device_id, + const char *old_name, + const char *new_name); + int -devredir_rmdir_or_file(void *fusep, tui32 device_id, const char *path, int mode); - -/* - * RDPDR_HEADER definitions - */ - -/* device redirector core component; most of the packets in this protocol */ -/* are sent under this component ID */ -#define RDPDR_CTYP_CORE 0x4472 - -/* printing component. the packets that use this ID are typically about */ -/* printer cache management and identifying XPS printers */ -#define RDPDR_CTYP_PRN 0x5052 - -/* Server Announce Request, as specified in section 2.2.2.2 */ -#define PAKID_CORE_SERVER_ANNOUNCE 0x496E - -/* Client Announce Reply and Server Client ID Confirm, as specified in */ -/* sections 2.2.2.3 and 2.2.2.6. */ -#define PAKID_CORE_CLIENTID_CONFIRM 0x4343 - -/* Client Name Request, as specified in section 2.2.2.4 */ -#define PAKID_CORE_CLIENT_NAME 0x434E - -/* Client Device List Announce Request, as specified in section 2.2.2.9 */ -#define PAKID_CORE_DEVICELIST_ANNOUNCE 0x4441 - -/* Server Device Announce Response, as specified in section 2.2.2.1 */ -#define PAKID_CORE_DEVICE_REPLY 0x6472 - -/* Device I/O Request, as specified in section 2.2.1.4 */ -#define PAKID_CORE_DEVICE_IOREQUEST 0x4952 - -/* Device I/O Response, as specified in section 2.2.1.5 */ -#define PAKID_CORE_DEVICE_IOCOMPLETION 0x4943 - -/* Server Core Capability Request, as specified in section 2.2.2.7 */ -#define PAKID_CORE_SERVER_CAPABILITY 0x5350 - -/* Client Core Capability Response, as specified in section 2.2.2.8 */ -#define PAKID_CORE_CLIENT_CAPABILITY 0x4350 - -/* Client Drive Device List Remove, as specified in section 2.2.3.2 */ -#define PAKID_CORE_DEVICELIST_REMOVE 0x444D - -/* Add Printer Cachedata, as specified in [MS-RDPEPC] section 2.2.2.3 */ -#define PAKID_PRN_CACHE_DATA 0x5043 - -/* Server User Logged On, as specified in section 2.2.2.5 */ -#define PAKID_CORE_USER_LOGGEDON 0x554C - -/* Server Printer Set XPS Mode, as specified in [MS-RDPEPC] section 2.2.2.2 */ -#define PAKID_PRN_USING_XPS 0x5543 - -/* - * Capability header definitions - */ - -#define CAP_GENERAL_TYPE 0x0001 /* General cap set - GENERAL_CAPS_SET */ -#define CAP_PRINTER_TYPE 0x0002 /* Print cap set - PRINTER_CAPS_SET */ -#define CAP_PORT_TYPE 0x0003 /* Port cap set - PORT_CAPS_SET */ -#define CAP_DRIVE_TYPE 0x0004 /* Drive cap set - DRIVE_CAPS_SET */ -#define CAP_SMARTCARD_TYPE 0x0005 /* Smart card cap set - SMARTCARD_CAPS_SET */ - -/* client minor versions */ -#define RDP_CLIENT_50 0x0002 -#define RDP_CLIENT_51 0x0005 -#define RDP_CLIENT_52 0x000a -#define RDP_CLIENT_60_61 0x000c - -/* used in device announce list */ -#define RDPDR_DTYP_SERIAL 0x0001 -#define RDPDR_DTYP_PARALLEL 0x0002 -#define RDPDR_DTYP_PRINT 0x0004 -#define RDPDR_DTYP_FILESYSTEM 0x0008 -#define RDPDR_DTYP_SMARTCARD 0x0020 - -/* - * DesiredAccess Mask [MS-SMB2] section 2.2.13.1.1 - */ - -#define DA_FILE_READ_DATA 0x00000001 -#define DA_FILE_WRITE_DATA 0x00000002 -#define DA_FILE_APPEND_DATA 0x00000004 -#define DA_FILE_READ_EA 0x00000008 /* rd extended attributes */ -#define DA_FILE_WRITE_EA 0x00000010 /* wr extended attributes */ -#define DA_FILE_EXECUTE 0x00000020 -#define DA_FILE_READ_ATTRIBUTES 0x00000080 -#define DA_FILE_WRITE_ATTRIBUTES 0x00000100 -#define DA_DELETE 0x00010000 -#define DA_READ_CONTROL 0x00020000 /* rd security descriptor */ -#define DA_WRITE_DAC 0x00040000 -#define DA_WRITE_OWNER 0x00080000 -#define DA_SYNCHRONIZE 0x00100000 -#define DA_ACCESS_SYSTEM_SECURITY 0x01000000 -#define DA_MAXIMUM_ALLOWED 0x02000000 -#define DA_GENERIC_ALL 0x10000000 -#define DA_GENERIC_EXECUTE 0x20000000 -#define DA_GENERIC_WRITE 0x40000000 -#define DA_GENERIC_READ 0x80000000 - -/* - * CreateOptions Mask [MS-SMB2] section 2.2.13 SMB2 CREATE Request - */ - -enum CREATE_OPTIONS -{ - CO_FILE_DIRECTORY_FILE = 0x00000001, - CO_FILE_WRITE_THROUGH = 0x00000002, - CO_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020, - CO_FILE_DELETE_ON_CLOSE = 0x00001000 -}; - -/* - * CreateDispositions Mask [MS-SMB2] section 2.2.13 - */ - -#define CD_FILE_SUPERSEDE 0x00000000 -#define CD_FILE_OPEN 0x00000001 -#define CD_FILE_CREATE 0x00000002 -#define CD_FILE_OPEN_IF 0x00000003 -#define CD_FILE_OVERWRITE 0x00000004 -#define CD_FILE_OVERWRITE_IF 0x00000005 - -/* - * Device I/O Request MajorFunction definitions - */ - -#define IRP_MJ_CREATE 0x00000000 -#define IRP_MJ_CLOSE 0x00000002 -#define IRP_MJ_READ 0x00000003 -#define IRP_MJ_WRITE 0x00000004 -#define IRP_MJ_DEVICE_CONTROL 0x0000000E -#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0000000A -#define IRP_MJ_SET_VOLUME_INFORMATION 0x0000000B -#define IRP_MJ_QUERY_INFORMATION 0x00000005 -#define IRP_MJ_SET_INFORMATION 0x00000006 -#define IRP_MJ_DIRECTORY_CONTROL 0x0000000C -#define IRP_MJ_LOCK_CONTROL 0x00000011 - -/* - * Device I/O Request MinorFunction definitions - * - * Only valid when MajorFunction code = IRP_MJ_DIRECTORY_CONTROL - */ - -#define IRP_MN_QUERY_DIRECTORY 0x00000001 -#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002 - -/* - * 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 - -/* - * File system ioctl codes - * MS-FSCC section 2.3 FSCTL Structures - */ -#define FSCTL_DELETE_OBJECT_ID 0x900a0 - - -/* - * CompletionID types, used in IRPs to indicate I/O operation - */ - -enum COMPLETION_ID -{ - CID_CREATE_DIR_REQ = 1, - CID_DIRECTORY_CONTROL, - CID_CREATE_OPEN_REQ, - CID_READ, - CID_WRITE, - CID_CLOSE, - CID_FILE_CLOSE, - CID_RMDIR_OR_FILE, - CID_RMDIR_OR_FILE_RESP, - CID_RENAME_FILE, - 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 */ - FileAllocationInformation = 0x00000013 /* set file allocation size */ -}; - -/* - * constants for drive dir query - */ - -/* Basic information about a file or directory. Basic information is */ -/* defined as the file's name, time stamp, and size, or its attributes */ -#define FileDirectoryInformation 0x00000001 - -/* Full information about a file or directory. Full information is defined */ -/* as all the basic information, plus extended attribute size. */ -#define FileFullDirectoryInformation 0x00000002 - -/* Basic information plus extended attribute size and short name */ -/* about a file or directory. */ -#define FileBothDirectoryInformation 0x00000003 - -/* Detailed information on the names of files in a directory. */ -#define FileNamesInformation 0x0000000C - -/* - * NTSTATUS Codes of interest to us - */ - -/* No more files were found which match the file specification */ -#define STATUS_NO_MORE_FILES 0x80000006 - -/* Windows file attributes */ -#define W_FILE_ATTRIBUTE_DIRECTORY 0x00000010 -#define W_FILE_ATTRIBUTE_READONLY 0x00000001 - -#define WINDOWS_TO_LINUX_FILE_PERM(_a) \ - (((_a) & W_FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | 0100 : S_IFREG) |\ - (((_a) & W_FILE_ATTRIBUTE_READONLY) ? 0444 : 0644) - -/* Windows time starts on Jan 1, 1601 */ -/* Linux time starts on Jan 1, 1970 */ -#define EPOCH_DIFF 11644473600LL -#define WINDOWS_TO_LINUX_TIME(_t) ((_t) / 10000000) - EPOCH_DIFF; - -#define OP_RENAME_FILE 0x01 +devredir_rmdir_or_file(struct state_remove *fusep, tui32 device_id, + const char *path); #endif diff --git a/sesman/chansrv/irp.c b/sesman/chansrv/irp.c index bd2580e1..5bebc531 100644 --- a/sesman/chansrv/irp.c +++ b/sesman/chansrv/irp.c @@ -105,32 +105,67 @@ IRP * devredir_irp_new(void) } /** - * Clone specified IRP + * Create a new IRP with a copied pathname, and append to linked list. + * + * Allocation is made in such a way that the IRP can be freed with a single + * free() operation * * @return new IRP or NULL on error *****************************************************************************/ -IRP * devredir_irp_clone(IRP *irp) +IRP * devredir_irp_with_pathname_new(const char *pathname) { - IRP *new_irp; - IRP *prev; - IRP *next; + unsigned int len = g_strlen(pathname); + IRP * irp = devredir_irp_with_pathnamelen_new(len); + if (irp != NULL) + { + g_strcpy(irp->pathname, pathname); + } - if ((new_irp = devredir_irp_new()) == NULL) + return irp; +} + +/** + * Create a new IRP with space allocated for a pathname, and append to + * linked list. + * + * Allocation is made in such a way that the IRP can be freed with a single + * free() operation + * + * @return new IRP or NULL on error + *****************************************************************************/ + +IRP * devredir_irp_with_pathnamelen_new(unsigned int pathnamelen) +{ + IRP *irp; + IRP *irp_last; + + log_debug("entered"); + + /* create new IRP with space on end for the pathname and a terminator */ + irp = (IRP *)g_malloc(sizeof(IRP) + (pathnamelen + 1), 1); + if (irp == NULL) + { + log_error("system out of memory!"); return NULL; + } - /* save link pointers */ - prev = new_irp->prev; - next = new_irp->next; + irp->pathname = (char *)irp + sizeof(IRP); /* Initialise pathname pointer */ - /* copy all members */ - g_memcpy(new_irp, irp, sizeof(IRP)); + /* insert at end of linked list */ + if ((irp_last = devredir_irp_get_last()) == NULL) + { + /* list is empty, this is the first entry */ + g_irp_head = irp; + } + else + { + irp_last->next = irp; + irp->prev = irp_last; + } - /* restore link pointers */ - new_irp->prev = prev; - new_irp->next = next; - - return new_irp; + log_debug("new IRP=%p", irp); + return irp; } /** diff --git a/sesman/chansrv/irp.h b/sesman/chansrv/irp.h index 68159297..821c4b72 100644 --- a/sesman/chansrv/irp.h +++ b/sesman/chansrv/irp.h @@ -29,11 +29,49 @@ #endif #include "chansrv_fuse.h" -typedef struct fuse_data FUSE_DATA; -struct fuse_data +/* Opaque data types to us */ +typedef struct xfuse_info XFUSE_INFO; + +enum irp_lookup_state { - void *data_ptr; - FUSE_DATA *next; + E_LOOKUP_GET_FH = 0, + E_LOOKUP_CHECK_BASIC, + E_LOOKUP_CHECK_EOF +} ; + +enum irp_setattr_state +{ + E_SETATTR_GET_FH = 0, + E_SETATTR_CHECK_BASIC, + E_SETATTR_CHECK_EOF +} ; + +struct irp_lookup +{ + enum irp_lookup_state state; /* Next state to consider */ + struct file_attr fattr; /* Attributes to get */ +}; + +struct irp_setattr +{ + enum irp_setattr_state state; /* Next state to consider */ + tui32 to_set; /* Bit mask for elements in use */ + struct file_attr fattr; /* Attributes to set */ +}; + +struct irp_write +{ + tui64 offset; /* Offset the write was made at */ +}; + +struct irp_create +{ + int creating_dir; /* We're creating a directory */ +}; + +struct irp_rename +{ + char *new_name; /* New name for file */ }; /* An I/O Resource Packet to track I/O calls */ @@ -46,15 +84,18 @@ struct irp tui32 DeviceId; /* identifies remote device */ tui32 FileId; /* RDP client provided unique number */ char completion_type; /* describes I/O type */ - char pathname[256]; /* absolute pathname */ + char *pathname; /* absolute pathname + * Allocate with + * devredir_irp_with_pathname_new() */ 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 */ + struct irp_lookup lookup; /* Used by lookup */ + struct irp_setattr setattr; /* Used by setattr */ + struct irp_write write; /* Used by write */ + struct irp_create create; /* Used by create */ + struct irp_rename rename; /* Use by rename */ + } gen; /* Additional state data for some ops */ + void *fuse_info; /* Fuse info pointer for FUSE calls */ IRP *next; /* point to next IRP */ IRP *prev; /* point to previous IRP */ int scard_index; /* used to smart card to locate dev */ @@ -65,7 +106,13 @@ struct irp }; IRP * devredir_irp_new(void); -IRP * devredir_irp_clone(IRP *irp); +/* As above, but allocates sufficent space for the specified + * pathname, and copies it in to the pathname field */ +IRP * devredir_irp_with_pathname_new(const char *pathname); +/* As above, but specifies a pathname length with pathname + * initially set to "". Use if you need to modify the pathname + * significantly */ +IRP * devredir_irp_with_pathnamelen_new(unsigned int pathnamelen); int devredir_irp_delete(IRP *irp); IRP * devredir_irp_find(tui32 completion_id); IRP * devredir_irp_find_by_fileid(tui32 FileId); diff --git a/sesman/chansrv/ms-erref.h b/sesman/chansrv/ms-erref.h new file mode 100644 index 00000000..df92cd5b --- /dev/null +++ b/sesman/chansrv/ms-erref.h @@ -0,0 +1,44 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * MS-ERREF : Definitions from [MS-ERREF] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * References to MS-ERREF are currently correct for v20180912 of that + * document + */ + +#if !defined(MS_ERREF_H) +#define MS_ERREF_H + +/* + * NTSTATUS codes (section 2.3) + */ +enum NTSTATUS +{ + NT_STATUS_SUCCESS = 0x00000000, + NT_STATUS_UNSUCCESSFUL = 0xC0000001, + NT_STATUS_NO_SUCH_FILE = 0xC000000F, + NT_STATUS_ACCESS_DENIED = 0xC0000022, + NT_STATUS_OBJECT_NAME_INVALID = 0xC0000033, + NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + NT_STATUS_SHARING_VIOLATION = 0xC0000043, + NT_STATUS_NO_MORE_FILES = 0x80000006 +}; + +#endif /* MS_ERREF_H */ + + + + diff --git a/sesman/chansrv/ms-fscc.h b/sesman/chansrv/ms-fscc.h new file mode 100644 index 00000000..2f8cc201 --- /dev/null +++ b/sesman/chansrv/ms-fscc.h @@ -0,0 +1,64 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * MS-FSCC : Definitions from [MS-FSCC] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * References to MS-FSCC are currently correct for v20190923 of that + * document + */ + +#if !defined(MS_FSCC_H) +#define MS_FSCC_H + +/* + * File system ioctl codes (section 2.3) + */ +#define FSCTL_DELETE_OBJECT_ID 0x900a0 + +/* + * File information classes (section 2.4) + */ +enum FS_INFORMATION_CLASS +{ + FileAllocationInformation = 19, /* Set */ + FileBasicInformation = 4, /* Query, Set */ + FileBothDirectoryInformation = 3, /* Query */ + FileDirectoryInformation = 1, /* Query */ + FileDispositionInformation = 13, /* Set */ + FileEndOfFileInformation = 20, /* Set */ + FileFullDirectoryInformation = 2, /* Query */ + FileNamesInformation = 12, /* Query */ + FileRenameInformation = 10, /* Set */ + FileStandardInformation = 5 /* Query */ +}; + +/* + * Size of structs above without trailing RESERVED fields (MS-RDPEFS + * 2.2.3.3.8) + */ +#define FILE_BASIC_INFORMATION_SIZE 36 +#define FILE_STD_INFORMATION_SIZE 22 +#define FILE_END_OF_FILE_INFORMATION_SIZE 8 + +/* Windows file attributes (section 2.6) */ +#define W_FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define W_FILE_ATTRIBUTE_READONLY 0x00000001 +#define W_FILE_ATTRIBUTE_SYSTEM 0x00000004 +#define W_FILE_ATTRIBUTE_NORMAL 0x00000080 + +#endif /* MS_FSCC_H */ + + + diff --git a/sesman/chansrv/ms-rdpefs.h b/sesman/chansrv/ms-rdpefs.h new file mode 100644 index 00000000..dc5fe3fe --- /dev/null +++ b/sesman/chansrv/ms-rdpefs.h @@ -0,0 +1,125 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * MS-RDPEFS : Definitions from [MS-RDPEFS] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * References to MS-RDPEFS are currently correct for v20180912 of that + * document + */ + +#if !defined(MS_RDPEFS_H) +#define MS_RDPEFS_H + +/* + * RDPDR_HEADER definitions (2.2.1.1) + */ + +/* device redirector core component; most of the packets in this protocol */ +/* are sent under this component ID */ +#define RDPDR_CTYP_CORE 0x4472 + +/* printing component. the packets that use this ID are typically about */ +/* printer cache management and identifying XPS printers */ +#define RDPDR_CTYP_PRN 0x5052 + +/* Server Announce Request, as specified in section 2.2.2.2 */ +#define PAKID_CORE_SERVER_ANNOUNCE 0x496E + +/* Client Announce Reply and Server Client ID Confirm, as specified in */ +/* sections 2.2.2.3 and 2.2.2.6. */ +#define PAKID_CORE_CLIENTID_CONFIRM 0x4343 + +/* Client Name Request, as specified in section 2.2.2.4 */ +#define PAKID_CORE_CLIENT_NAME 0x434E + +/* Client Device List Announce Request, as specified in section 2.2.2.9 */ +#define PAKID_CORE_DEVICELIST_ANNOUNCE 0x4441 + +/* Server Device Announce Response, as specified in section 2.2.2.1 */ +#define PAKID_CORE_DEVICE_REPLY 0x6472 + +/* Device I/O Request, as specified in section 2.2.1.4 */ +#define PAKID_CORE_DEVICE_IOREQUEST 0x4952 + +/* Device I/O Response, as specified in section 2.2.1.5 */ +#define PAKID_CORE_DEVICE_IOCOMPLETION 0x4943 + +/* Server Core Capability Request, as specified in section 2.2.2.7 */ +#define PAKID_CORE_SERVER_CAPABILITY 0x5350 + +/* Client Core Capability Response, as specified in section 2.2.2.8 */ +#define PAKID_CORE_CLIENT_CAPABILITY 0x4350 + +/* Client Drive Device List Remove, as specified in section 2.2.3.2 */ +#define PAKID_CORE_DEVICELIST_REMOVE 0x444D + +/* Add Printer Cachedata, as specified in [MS-RDPEPC] section 2.2.2.3 */ +#define PAKID_PRN_CACHE_DATA 0x5043 + +/* Server User Logged On, as specified in section 2.2.2.5 */ +#define PAKID_CORE_USER_LOGGEDON 0x554C + +/* Server Printer Set XPS Mode, as specified in [MS-RDPEPC] section 2.2.2.2 */ +#define PAKID_PRN_USING_XPS 0x5543 + +/* + * Capability header definitions (2.2.1.2) + */ + +#define CAP_GENERAL_TYPE 0x0001 /* General cap set - GENERAL_CAPS_SET */ +#define CAP_PRINTER_TYPE 0x0002 /* Print cap set - PRINTER_CAPS_SET */ +#define CAP_PORT_TYPE 0x0003 /* Port cap set - PORT_CAPS_SET */ +#define CAP_DRIVE_TYPE 0x0004 /* Drive cap set - DRIVE_CAPS_SET */ +#define CAP_SMARTCARD_TYPE 0x0005 /* Smart card cap set - SMARTCARD_CAPS_SET */ + +/* + * Device announce header (2.2.1.3) + */ +#define RDPDR_DTYP_SERIAL 0x0001 +#define RDPDR_DTYP_PARALLEL 0x0002 +#define RDPDR_DTYP_PRINT 0x0004 +#define RDPDR_DTYP_FILESYSTEM 0x0008 +#define RDPDR_DTYP_SMARTCARD 0x0020 + +/* Device I/O Request definitions (2.2.1.4) */ +/* MajorFunction */ +enum IRP_MJ +{ + IRP_MJ_CREATE = 0x00000000, + IRP_MJ_CLOSE = 0x00000002, + IRP_MJ_READ = 0x00000003, + IRP_MJ_WRITE = 0x00000004, + IRP_MJ_DEVICE_CONTROL = 0x0000000E, + IRP_MJ_QUERY_VOLUME_INFORMATION = 0x0000000A, + IRP_MJ_SET_VOLUME_INFORMATION = 0x0000000B, + IRP_MJ_QUERY_INFORMATION = 0x00000005, + IRP_MJ_SET_INFORMATION = 0x00000006, + IRP_MJ_DIRECTORY_CONTROL = 0x0000000C, + IRP_MJ_LOCK_CONTROL = 0x00000011 +}; + +/* MinorFunction */ +/* Set to zero unless MajorFunction code == IRP_MJ_DIRECTORY_CONTROL */ +enum IRP_MN +{ + IRP_MN_NONE = 0x00000000, /* Name not in MS docs */ + IRP_MN_QUERY_DIRECTORY = 0x00000001, + IRP_MN_NOTIFY_CHANGE_DIRECTORY = 0x00000002 +}; + + +#endif /* MS_RDPEFS_H */ + + diff --git a/sesman/chansrv/ms-smb2.h b/sesman/chansrv/ms-smb2.h new file mode 100644 index 00000000..3e776545 --- /dev/null +++ b/sesman/chansrv/ms-smb2.h @@ -0,0 +1,79 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * MS-SMB2 : Definitions from [MS-SMB2] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * References to MS-SMB2 are currently correct for v20190923 of that + * document + */ + +#if !defined(MS_SMB2_H) +#define MS_SMB2_H + +/* SMB2 CREATE request values (section 2.2.13) */ + +/* + * ShareAccess Mask. Currently, this is referred + * to in MS-RDPEFS 2.2.1.4.1 as 'SharedAccess' rather than 'ShareAccess'. + */ +#define SA_FILE_SHARE_READ 0x00000001 +#define SA_FILE_SHARE_WRITE 0x00000002 +#define SA_FILE_SHARE_DELETE 0x00000004 + +/* CreateDisposition Mask */ +#define CD_FILE_SUPERSEDE 0x00000000 +#define CD_FILE_OPEN 0x00000001 +#define CD_FILE_CREATE 0x00000002 +#define CD_FILE_OPEN_IF 0x00000003 +#define CD_FILE_OVERWRITE 0x00000004 +#define CD_FILE_OVERWRITE_IF 0x00000005 + +/* CreateOptions Mask */ +enum CREATE_OPTIONS +{ + CO_FILE_DIRECTORY_FILE = 0x00000001, + CO_FILE_WRITE_THROUGH = 0x00000002, + CO_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020, + CO_FILE_DELETE_ON_CLOSE = 0x00001000 +}; + +/* + * DesiredAccess Mask (section 2.2.13.1.1) + */ + +#define DA_FILE_READ_DATA 0x00000001 +#define DA_FILE_WRITE_DATA 0x00000002 +#define DA_FILE_APPEND_DATA 0x00000004 +#define DA_FILE_READ_EA 0x00000008 /* rd extended attributes */ +#define DA_FILE_WRITE_EA 0x00000010 /* wr extended attributes */ +#define DA_FILE_EXECUTE 0x00000020 +#define DA_FILE_READ_ATTRIBUTES 0x00000080 +#define DA_FILE_WRITE_ATTRIBUTES 0x00000100 +#define DA_DELETE 0x00010000 +#define DA_READ_CONTROL 0x00020000 /* rd security descriptor */ +#define DA_WRITE_DAC 0x00040000 +#define DA_WRITE_OWNER 0x00080000 +#define DA_SYNCHRONIZE 0x00100000 +#define DA_ACCESS_SYSTEM_SECURITY 0x01000000 +#define DA_MAXIMUM_ALLOWED 0x02000000 +#define DA_GENERIC_ALL 0x10000000 +#define DA_GENERIC_EXECUTE 0x20000000 +#define DA_GENERIC_WRITE 0x40000000 +#define DA_GENERIC_READ 0x80000000 + +#endif /* MS_SMB2_H */ + + + diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 1c3147ad..483aac0e 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -105,6 +105,9 @@ param=96 [Chansrv] ; drive redirection, defaults to xrdp_client if not set FuseMountName=thinclient_drives +; this value allows only the user to acess their own mapped drives. +; Make this more permissive (e.g. 022) if required. +FileUmask=077 [SessionVariables] PULSE_SCRIPT=@sesmansysconfdir@/pulse/default.pa From 3ef2e7ce497692e2c4213538cd17945820ca98f0 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 5 Dec 2019 16:44:52 +0000 Subject: [PATCH 2/2] CI Fixes --- sesman/chansrv/Makefile.am | 4 ++ sesman/chansrv/chansrv_fuse.c | 5 +- sesman/chansrv/chansrv_fuse.h | 2 +- sesman/chansrv/chansrv_xfs.c | 2 +- sesman/chansrv/devredir.c | 127 +++++++++++++++++++--------------- sesman/chansrv/smartcard.c | 2 +- 6 files changed, 81 insertions(+), 61 deletions(-) diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index cf1d1f96..cd55df7c 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -67,6 +67,10 @@ xrdp_chansrv_SOURCES = \ irp.c \ irp.h \ mlog.h \ + ms-erref.h \ + ms-fscc.h \ + ms-rdpefs.h \ + ms-smb2.h \ rail.c \ rail.h \ smartcard.c \ diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index c439a7ac..9d83a06c 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -1335,7 +1335,7 @@ void xfuse_devredir_cb_write_file( } else { - size_t new_size = offset + length; + off_t new_size = offset + length; fuse_reply_write(fip->req, length); /* update file size */ @@ -2535,7 +2535,8 @@ static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name) if ((result = xfs_get_full_path(g_xfs, parent)) != NULL) { - char * p = realloc(result, strlen(result) + 1 + strlen(name) + 1); + char * p = (char *) realloc(result, + strlen(result) + 1 + strlen(name) + 1); if (p == NULL) { free(result); diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index dd1b9f76..9a94900e 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -27,7 +27,7 @@ struct file_attr { tui32 mode; /* File mode. */ - size_t size; /* Size of file, in bytes. */ + off_t size; /* Size of file, in bytes. */ time_t atime; /* Time of last access. */ time_t mtime; /* Time of last modification. */ }; diff --git a/sesman/chansrv/chansrv_xfs.c b/sesman/chansrv/chansrv_xfs.c index d4985c2b..a63c1604 100644 --- a/sesman/chansrv/chansrv_xfs.c +++ b/sesman/chansrv/chansrv_xfs.c @@ -566,7 +566,7 @@ xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum) ++len; /* Allow for '/' prefix */ } - result = malloc(len + 1); + result = (char *) malloc(len + 1); if (result != NULL) { /* Construct the path from the end */ diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index ca16e2c1..22cceb5c 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -147,16 +147,18 @@ struct stream *g_input_stream = NULL; /* * Local functions called from devredir_proc_device_iocompletion() */ -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_rmdir_or_file(IRP *irp, enum NTSTATUS IoStatus); +static void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, + enum NTSTATUS IoStatus); +static void devredir_proc_cid_rename_file(IRP *irp, enum NTSTATUS IoStatus); +static void devredir_proc_cid_rename_file_resp(IRP *irp, + enum NTSTATUS IoStatus); static void devredir_proc_cid_lookup( IRP *irp, struct stream *s_in, - tui32 IoStatus); + enum NTSTATUS IoStatus); static void devredir_proc_cid_setattr( IRP *irp, struct stream *s_in, - tui32 IoStatus); + enum NTSTATUS IoStatus); /* Other local functions */ static void devredir_send_server_core_cap_req(void); static void devredir_send_server_clientID_confirm(void); @@ -166,12 +168,11 @@ static void devredir_proc_client_core_cap_resp(struct stream *s); static void devredir_proc_client_devlist_announce_req(struct stream *s); static void devredir_proc_client_devlist_remove_req(struct stream *s); static void devredir_proc_device_iocompletion(struct stream *s); -static void devredir_proc_query_dir_response( - IRP *irp, - struct stream *s_in, - tui32 DeviceId, - tui32 CompletionId, - tui32 IoStatus); +static void devredir_proc_query_dir_response(IRP *irp, + struct stream *s_in, + tui32 DeviceId, + tui32 CompletionId, + enum NTSTATUS IoStatus); static void devredir_cvt_slash(char *path); static void devredir_cvt_to_unicode(char *unicode, const char *path); @@ -639,7 +640,7 @@ devredir_send_drive_create_request(tui32 device_id, 0, completion_id, IRP_MJ_CREATE, - 0); + IRP_MN_NONE); xstream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */ xstream_wr_u32_le(s, 0); /* AllocationSize high unused */ @@ -927,12 +928,14 @@ devredir_proc_device_iocompletion(struct stream *s) tui32 DeviceId; tui32 CompletionId; - tui32 IoStatus; + tui32 IoStatus32; tui32 Length; + enum COMPLETION_TYPE comp_type; xstream_rd_u32_le(s, DeviceId); xstream_rd_u32_le(s, CompletionId); - xstream_rd_u32_le(s, IoStatus); + xstream_rd_u32_le(s, IoStatus32); + enum NTSTATUS IoStatus = (enum NTSTATUS) IoStatus32; /* Needed by C++ */ if ((irp = devredir_irp_find(CompletionId)) == NULL) { @@ -946,7 +949,7 @@ devredir_proc_device_iocompletion(struct stream *s) } else { - enum COMPLETION_TYPE comp_type = irp->completion_type; + comp_type = (enum COMPLETION_TYPE) irp->completion_type; /* Log something about the IRP */ if (IoStatus == NT_STATUS_SUCCESS || IoStatus == NT_STATUS_NO_MORE_FILES || @@ -970,7 +973,8 @@ devredir_proc_device_iocompletion(struct stream *s) case CID_CREATE_DIR_REQ: if (IoStatus != NT_STATUS_SUCCESS) { - xfuse_devredir_cb_enum_dir_done(irp->fuse_info, IoStatus); + xfuse_devredir_cb_enum_dir_done( + (struct state_dirscan *) irp->fuse_info, IoStatus); devredir_irp_delete(irp); } else @@ -984,8 +988,9 @@ devredir_proc_device_iocompletion(struct stream *s) case CID_CREATE_REQ: xstream_rd_u32_le(s, irp->FileId); - xfuse_devredir_cb_create_file(irp->fuse_info, IoStatus, - DeviceId, irp->FileId); + xfuse_devredir_cb_create_file( + (struct state_create *) irp->fuse_info, + IoStatus, DeviceId, irp->FileId); if (irp->gen.create.creating_dir || IoStatus != NT_STATUS_SUCCESS) { devredir_irp_delete(irp); @@ -995,8 +1000,8 @@ devredir_proc_device_iocompletion(struct stream *s) case CID_OPEN_REQ: xstream_rd_u32_le(s, irp->FileId); - xfuse_devredir_cb_open_file(irp->fuse_info, IoStatus, - DeviceId, irp->FileId); + xfuse_devredir_cb_open_file((struct state_open *) irp->fuse_info, + IoStatus, DeviceId, irp->FileId); if (IoStatus != NT_STATUS_SUCCESS) { devredir_irp_delete(irp); @@ -1005,13 +1010,15 @@ devredir_proc_device_iocompletion(struct stream *s) case CID_READ: xstream_rd_u32_le(s, Length); - xfuse_devredir_cb_read_file(irp->fuse_info, s->p, Length); + xfuse_devredir_cb_read_file((struct state_read *) irp->fuse_info, + s->p, Length); devredir_irp_delete(irp); break; case CID_WRITE: xstream_rd_u32_le(s, Length); - xfuse_devredir_cb_write_file(irp->fuse_info, IoStatus, + xfuse_devredir_cb_write_file((struct state_write *) irp->fuse_info, + IoStatus, irp->gen.write.offset, Length); devredir_irp_delete(irp); break; @@ -1021,7 +1028,7 @@ devredir_proc_device_iocompletion(struct stream *s) break; case CID_FILE_CLOSE: - xfuse_devredir_cb_file_close(irp->fuse_info); + xfuse_devredir_cb_file_close((struct state_close *) irp->fuse_info); devredir_irp_delete(irp); break; @@ -1070,7 +1077,7 @@ devredir_proc_query_dir_response(IRP *irp, struct stream *s_in, tui32 DeviceId, tui32 CompletionId, - tui32 IoStatus) + enum NTSTATUS IoStatus) { tui32 Length; xstream_rd_u32_le(s_in, Length); @@ -1117,8 +1124,9 @@ devredir_proc_query_dir_response(IRP *irp, fattr.mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); /* add this entry to xrdp file system */ - xfuse_devredir_cb_enum_dir_add_entry(irp->fuse_info, filename, - &fattr); + xfuse_devredir_cb_enum_dir_add_entry( + (struct state_dirscan *) irp->fuse_info, + filename, &fattr); } /* Ask for more directory entries */ @@ -1130,14 +1138,15 @@ devredir_proc_query_dir_response(IRP *irp, { IoStatus = NT_STATUS_SUCCESS; } - xfuse_devredir_cb_enum_dir_done(irp->fuse_info, IoStatus); + xfuse_devredir_cb_enum_dir_done((struct state_dirscan *)irp->fuse_info, + IoStatus); irp->completion_type = CID_CLOSE; devredir_send_drive_close_request(RDPDR_CTYP_CORE, PAKID_CORE_DEVICE_IOREQUEST, DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); + IRP_MJ_CLOSE, IRP_MN_NONE, 32); } } @@ -1470,7 +1479,7 @@ int devredir_file_close(struct state_close *fusep, tui32 device_id, FileId, irp->CompletionId, IRP_MJ_CLOSE, - 0, 32); + IRP_MN_NONE, 32); } /** @@ -1562,7 +1571,7 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId, FileId, new_irp->CompletionId, IRP_MJ_READ, - 0); + IRP_MN_NONE); xstream_wr_u32_le(s, Length); xstream_wr_u64_le(s, Offset); @@ -1621,7 +1630,7 @@ devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId, FileId, new_irp->CompletionId, IRP_MJ_WRITE, - 0); + IRP_MN_NONE); xstream_wr_u32_le(s, Length); xstream_wr_u64_le(s, Offset); @@ -1810,14 +1819,15 @@ devredir_string_ends_with(const char *string, char c) } static void -devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) +devredir_proc_cid_rmdir_or_file(IRP *irp, enum NTSTATUS IoStatus) { struct stream *s; int bytes; if (IoStatus != NT_STATUS_SUCCESS) { - xfuse_devredir_cb_rmdir_or_file(irp->fuse_info, IoStatus); + xfuse_devredir_cb_rmdir_or_file((struct state_remove *) irp->fuse_info, + IoStatus); devredir_irp_delete(irp); return; } @@ -1827,7 +1837,7 @@ devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) irp->completion_type = CID_RMDIR_OR_FILE_RESP; devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_SET_INFORMATION, 0); + IRP_MJ_SET_INFORMATION, IRP_MN_NONE); xstream_wr_u32_le(s, FileDispositionInformation); xstream_wr_u32_le(s, 0); /* length is zero */ @@ -1842,9 +1852,10 @@ devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) } static void -devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus) +devredir_proc_cid_rmdir_or_file_resp(IRP *irp, enum NTSTATUS IoStatus) { - xfuse_devredir_cb_rmdir_or_file(irp->fuse_info, IoStatus); + xfuse_devredir_cb_rmdir_or_file((struct state_remove *)irp->fuse_info, + IoStatus); if (IoStatus != NT_STATUS_SUCCESS) { @@ -1858,11 +1869,11 @@ devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus) irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); + IRP_MJ_CLOSE, IRP_MN_NONE, 32); } static void -devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) +devredir_proc_cid_rename_file(IRP *irp, enum NTSTATUS IoStatus) { struct stream *s; int bytes; @@ -1874,7 +1885,8 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) { log_debug("rename returned with IoStatus=0x%x", IoStatus); - xfuse_devredir_cb_rename_file(irp->fuse_info, IoStatus); + xfuse_devredir_cb_rename_file((struct state_rename *)irp->fuse_info, + IoStatus); devredir_irp_delete(irp); return; } @@ -1889,7 +1901,7 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) irp->completion_type = CID_RENAME_FILE_RESP; devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_SET_INFORMATION, 0); + IRP_MJ_SET_INFORMATION, IRP_MN_NONE); xstream_wr_u32_le(s, FileRenameInformation); xstream_wr_u32_le(s, sblen); /* number of bytes after padding */ @@ -1911,11 +1923,12 @@ devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) } static void -devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) +devredir_proc_cid_rename_file_resp(IRP *irp, enum NTSTATUS IoStatus) { log_debug("entered"); - xfuse_devredir_cb_rename_file(irp->fuse_info, IoStatus); + xfuse_devredir_cb_rename_file((struct state_rename *)irp->fuse_info, + IoStatus); if (IoStatus != NT_STATUS_SUCCESS) { @@ -1929,7 +1942,7 @@ devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); + IRP_MJ_CLOSE, IRP_MN_NONE, 32); } @@ -1951,7 +1964,7 @@ static void issue_lookup(IRP *irp, int lookup_type) xstream_new(s, 1024); devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_QUERY_INFORMATION, 0); + IRP_MJ_QUERY_INFORMATION, IRP_MN_NONE); xstream_wr_u32_le(s, lookup_type); xstream_wr_u32_le(s, bytes); /* buffer length */ @@ -2015,11 +2028,12 @@ static void lookup_read_standard_attributes(IRP *irp, struct stream *s_in) * * Unless IoStatus is NT_STATUS_SUCCESS, the lookup has failed. *****************************************************************************/ -static void lookup_done(IRP *irp, tui32 IoStatus) +static void lookup_done(IRP *irp, enum NTSTATUS IoStatus) { log_debug("Lookup with completion_id=%d returning 0x%x", irp->CompletionId, IoStatus); - xfuse_devredir_cb_lookup_entry(irp->fuse_info, IoStatus, + xfuse_devredir_cb_lookup_entry((struct state_lookup *)irp->fuse_info, + IoStatus, &irp->gen.lookup.fattr); if (irp->FileId == 0) @@ -2036,7 +2050,7 @@ static void lookup_done(IRP *irp, tui32 IoStatus) irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); + IRP_MJ_CLOSE, IRP_MN_NONE, 32); } } @@ -2048,7 +2062,7 @@ static void lookup_done(IRP *irp, tui32 IoStatus) static void devredir_proc_cid_lookup(IRP *irp, struct stream *s_in, - tui32 IoStatus) + enum NTSTATUS IoStatus) { tui32 Length; @@ -2143,7 +2157,7 @@ static void issue_setattr_basic(IRP *irp) xstream_new(s, 1024); devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_SET_INFORMATION, 0); + IRP_MJ_SET_INFORMATION, IRP_MN_NONE); xstream_wr_u32_le(s, FileBasicInformation); xstream_wr_u32_le(s, FILE_BASIC_INFORMATION_SIZE); @@ -2176,13 +2190,13 @@ static void issue_setattr_eof(IRP *irp) xstream_new(s, 1024); devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_SET_INFORMATION, 0); + IRP_MJ_SET_INFORMATION, IRP_MN_NONE); xstream_wr_u32_le(s, FileEndOfFileInformation); xstream_wr_u32_le(s, FILE_END_OF_FILE_INFORMATION_SIZE); /* buffer length */ xstream_seek(s, 24); /* padding */ - xstream_wr_u64_le(s, irp->gen.setattr.fattr.size); + xstream_wr_u64_le(s, (tui64)irp->gen.setattr.fattr.size); /* File size */ /* send to client */ bytes = xstream_len(s); @@ -2193,9 +2207,10 @@ static void issue_setattr_eof(IRP *irp) /* * Completes a setattr request and returns status to the caller. *****************************************************************************/ -static void setattr_done(IRP *irp, tui32 IoStatus) +static void setattr_done(IRP *irp, enum NTSTATUS IoStatus) { - xfuse_devredir_cb_setattr(irp->fuse_info, IoStatus); + xfuse_devredir_cb_setattr((struct state_setattr *) irp->fuse_info, + IoStatus); if (irp->FileId == 0) { @@ -2211,7 +2226,7 @@ static void setattr_done(IRP *irp, tui32 IoStatus) irp->DeviceId, irp->FileId, irp->CompletionId, - IRP_MJ_CLOSE, 0, 32); + IRP_MJ_CLOSE, IRP_MN_NONE, 32); } } @@ -2223,7 +2238,7 @@ static void setattr_done(IRP *irp, tui32 IoStatus) static void devredir_proc_cid_setattr(IRP *irp, struct stream *s_in, - tui32 IoStatus) + enum NTSTATUS IoStatus) { #define TO_SET_BASIC_ATTRS (TO_SET_MODE | \ TO_SET_ATIME | TO_SET_MTIME) diff --git a/sesman/chansrv/smartcard.c b/sesman/chansrv/smartcard.c index d6f23e60..b09b52da 100644 --- a/sesman/chansrv/smartcard.c +++ b/sesman/chansrv/smartcard.c @@ -871,7 +871,7 @@ scard_make_new_ioctl(IRP *irp, tui32 ioctl) irp->FileId, irp->CompletionId, IRP_MJ_DEVICE_CONTROL, - 0); + IRP_MN_NONE); xstream_wr_u32_le(s, 2048); /* OutputBufferLength */ s_push_layer(s, iso_hdr, 4); /* InputBufferLength - insert later */