diff --git a/common/parse.h b/common/parse.h index deee7845..be01a3f1 100644 --- a/common/parse.h +++ b/common/parse.h @@ -101,7 +101,6 @@ struct stream #define s_mark_end(s) \ (s)->end = (s)->p -/******************************************************************************/ #define in_sint8(s, v) do \ { \ (v) = *((signed char*)((s)->p)); \ @@ -303,4 +302,102 @@ struct stream (s)->p += (n); \ } while (0) +/* + * @brief allocate a new stream + * + * @param _s opaque handle to the new stream + * @param _l length of new stream + ******************************************************************************/ +#define stream_new(_s, _l) \ +do \ +{ \ + make_stream((_s)); \ + init_stream((_s), (_l)); \ +} while (0) + +/** + * @brief release a previously allocated stream + * + * @param _s opaque handle returned by stream_new() + *****************************************************************************/ +#define stream_free(_s) free_stream(_s) + +#define stream_rd_u8(_s, _var) in_uint8(_s, _var) +#define stream_rd_u16_le(_s, _var) in_uint16_le(_s, _var) +#define stream_rd_u32_le(_s, _var) in_uint32_le(_s, _var) + +#define stream_rd_s8_le(_s, _var) in_sint8(_s, _var) +#define stream_rd_s16_le(_s, _var) in_sint16_le(_s, _var) +#define stream_rd_s32_le(_s, _var) TODO + +#define stream_wr_u8(_s, _var) out_uint8(_s, _var) +#define stream_wr_u16_le(_s, _var) out_uint16_le(_s, _var) +#define stream_wr_u32_le(_s, _var) out_uint32_le(_s, _var) + +#define stream_wr_s8(_s, _var) TODO +#define stream_wr_s16_le(_s, _var) TODO +#define stream_wr_s32_le(_s, _var) TODO + +#define stream_rd_u64_le(_s, _v) \ +do \ +{ \ + _v = \ + (tui64)(*((unsigned char *)_s->p)) | \ + (((tui64) (*(((unsigned char *)_s->p) + 1))) << 8) | \ + (((tui64) (*(((unsigned char *)_s->p) + 2))) << 16) | \ + (((tui64) (*(((unsigned char *)_s->p) + 3))) << 24) | \ + (((tui64) (*(((unsigned char *)_s->p) + 4))) << 32) | \ + (((tui64) (*(((unsigned char *)_s->p) + 5))) << 40) | \ + (((tui64) (*(((unsigned char *)_s->p) + 6))) << 48) | \ + (((tui64) (*(((unsigned char *)_s->p) + 7))) << 56); \ + _s->p += 8; \ +} while (0) + +#define stream_wr_u64_le(_s, _v) \ +do \ +{ \ + *(((unsigned char *) _s->p) + 0) = (unsigned char) ((_v >> 0) & 0xff); \ + *(((unsigned char *) _s->p) + 1) = (unsigned char) ((_v >> 8) & 0xff); \ + *(((unsigned char *) _s->p) + 2) = (unsigned char) ((_v >> 16) & 0xff); \ + *(((unsigned char *) _s->p) + 3) = (unsigned char) ((_v >> 24) & 0xff); \ + *(((unsigned char *) _s->p) + 4) = (unsigned char) ((_v >> 32) & 0xff); \ + *(((unsigned char *) _s->p) + 5) = (unsigned char) ((_v >> 40) & 0xff); \ + *(((unsigned char *) _s->p) + 6) = (unsigned char) ((_v >> 48) & 0xff); \ + *(((unsigned char *) _s->p) + 7) = (unsigned char) ((_v >> 56) & 0xff); \ + _s->p += 8; \ +} while (0) + +/* copy data into stream */ +#define stream_copyin(_s, _dest, _len) \ +do \ +{ \ + memcpy((_s)->p, (_dest), (_len)); \ + (_s)->p += (_len); \ +} while (0) + +/* copy data out of stream */ +#define stream_copyout(_dest, _s, _len) \ +{ \ +do \ + memcpy((_dest), (_s)->p, (_len)); \ + (_s)->p += (_len); \ +} while (0) + +#define stream_rd_string(_dest, _s, _len) \ +do \ +{ \ + memcpy((_dest), (_s)->p, (_len)); \ + (_s)->p += (_len); \ +} while (0) + +#define stream_wr_string(_s, _src, _len) \ +do \ +{ \ + memcpy((_s)->p, (_src), (_len)); \ + (_s)->p += (_len); \ +} while (0) + +#define stream_len(_s) (int) ((_s)->p - (_s)->data) +#define stream_seek(_s, _len) (_s)->p += (_len) + #endif diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 9d55388e..74e2db4a 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -376,7 +376,7 @@ process_message_channel_setup(struct stream *s) if (g_cliprdr_index >= 0) { clipboard_init(); - fuse_init(); + xfuse_init(); } if (g_rdpsnd_index >= 0) @@ -387,6 +387,7 @@ process_message_channel_setup(struct stream *s) if (g_rdpdr_index >= 0) { dev_redir_init(); + xfuse_init(); } if (g_rail_index >= 0) @@ -953,7 +954,7 @@ channel_thread_loop(void *in_val) xcommon_check_wait_objs(); sound_check_wait_objs(); dev_redir_check_wait_objs(); - fuse_check_wait_objs(); + xfuse_check_wait_objs(); timeout = -1; num_objs = 0; objs[num_objs] = g_term_event; @@ -965,8 +966,8 @@ channel_thread_loop(void *in_val) 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); - fuse_get_wait_objs(objs, &num_objs, &timeout); - } + xfuse_get_wait_objs(objs, &num_objs, &timeout); + } /* end while (g_obj_wait(objs, num_objs, 0, 0, timeout) == 0) */ } trans_delete(g_lis_trans); diff --git a/sesman/chansrv/chansrv_fuse.c b/sesman/chansrv/chansrv_fuse.c index eb60f63a..c55fd57f 100644 --- a/sesman/chansrv/chansrv_fuse.c +++ b/sesman/chansrv/chansrv_fuse.c @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2012 + * Copyright (C) Laxmikant Rashinkar 2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,62 @@ * limitations under the License. */ -#ifdef XRDP_FUSE +/* + * TODO + * o need to support sym links + * o when creating dir/file, ensure it does not already exist + * o enable changing metadata for files/folders (time, owner, mode etc) + * o do not allow dirs to be created in ino==1 except for .clipbard and share mounts + * o xrdp_fs needs to grow dynamically - currently it is fixed at 1k or 4k + * 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 copying over an existing file does not work + * o are we calling close? + * o need to keep track of open files, reqd during rename + * o fuse ops to support + * o rmdir + * o rename (mv) + * o remove file + * o touch does not work + * o mknod (may not be required if create is correctly implemented) + * o symlink + * o keep track of lookup_count + * o chmod must work + * + */ + +/* FUSE mount point */ +char g_fuse_root_path[256] = ""; + +#ifndef XRDP_FUSE + +/****************************************************************************** +** ** +** when FUSE is NOT enabled in xrdp ** +** ** +******************************************************************************/ + +#include "arch.h" + +/* dummy calls when XRDP_FUSE is not defined */ +int xfuse_init() {} +int xfuse_deinit() {} +int xfuse_check_wait_objs(void) {} +int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) {} +int xfuse_clear_clip_dir(void) {} +int xfuse_file_contents_range(int stream_id, char *data, int data_bytes) {} +int xfuse_file_contents_size(int stream_id, int file_size) {} +int xfuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) {} + +#else + +/****************************************************************************** +** ** +** when FUSE is enabled in xrdp ** +** ** +******************************************************************************/ #define FUSE_USE_VERSION 26 #define _FILE_OFFSET_BITS 64 @@ -24,692 +79,2034 @@ #include #include #include -#include #include -#include +#include #include +#include +#include +#include + #include "arch.h" -#include "parse.h" -#include "list.h" #include "os_calls.h" -#include "chansrv.h" #include "chansrv_fuse.h" -#include "clipboard_file.h" - -#define LLOG_LEVEL 1 -#define LLOGLN(_level, _args) \ - do \ - { \ - if (_level < LLOG_LEVEL) \ - { \ - g_write("chansrv:fuse [%10.10u]: ", g_time3()); \ - g_writeln _args ; \ - } \ - } \ - while (0) -char g_fuse_root_path[256] = ""; +#define min(x, y) ((x) < (y) ? (x) : (y)) -static struct fuse_chan *g_ch = 0; -static struct fuse_session *g_se = 0; -static char *g_mountpoint = 0; -static tintptr g_bufsize = 0; -static char *g_buffer = 0; -static int g_fd = 0; -static time_t g_time = 0; -static int g_uid = 0; -static int g_gid = 0; - -/* used for file data request sent to client */ -struct req_list_item -{ - fuse_req_t req; - int stream_id; - int lindex; - int off; - int size; +#define XFUSE_ATTR_TIMEOUT 1.0 +#define XFUSE_ENTRY_TIMEOUT 1.0 + +#define DOTDOT_INODE 0 +#define DOT_INODE 0 +#define FIRST_INODE 1 + +/* module based logging */ +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define LOG_LEVEL LOG_ERROR + +#define log_error(_params...) \ +{ \ + g_write("[%10.10u]: FUSE %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ +} + +#define log_info(_params...) \ +{ \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: FUSE %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +#define log_debug(_params...) \ +{ \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: FUSE %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +/* 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 */ }; -static struct list *g_req_list = 0; struct dirbuf { char *p; - int size; - int alloc_bytes; + size_t size; +}; + +/* FUSE reply types */ +#define RT_FUSE_REPLY_OPEN 1 +#define RT_FUSE_REPLY_CREATE 2 + +struct xfuse_info +{ + struct fuse_file_info *fi; + fuse_req_t req; + fuse_ino_t inode; + int invoke_fuse; + char name[1024]; + tui32 device_id; + int reply_type; + int mode; }; +typedef struct xfuse_info XFUSE_INFO; -struct xfuse_file_info -{ - int ino; - int lindex; - char pathname[256]; - char filename[256]; - int flags; - int size; - tui64 time; - struct xfuse_file_info* child; - struct xfuse_file_info* parent; - struct xfuse_file_info* next; - struct xfuse_file_info* prev; +struct xfuse_handle +{ + tui32 DeviceId; + tui32 FileId; }; +typedef struct xfuse_handle XFUSE_HANDLE; + +/* globals */ + +static struct xrdp_fs g_xrdp_fs; /* an inst of xrdp file system */ +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 */ +static struct fuse_chan *g_ch = 0; +static struct fuse_session *g_se = 0; +static char *g_buffer = 0; +static int g_fd = 0; +static tintptr g_bufsize = 0; + +/* forward declarations for internal access */ +static int xfuse_init_xrdp_fs(); +static int xfuse_deinit_xrdp_fs(); +static int xfuse_init_lib(int argc, char **argv); +static int xfuse_is_inode_valid(int 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(); +static void xfuse_dump_xrdp_inode(struct xrdp_inode *xino); +static tui32 xfuse_get_device_id_for_inode(tui32 ino, char *full_path); +static void fuse_reverse_pathname(char *full_path, char *reverse_path); + +static struct xrdp_inode * xfuse_get_inode_from_pinode_name(tui32 pinode, + const char *name); + +static struct xrdp_inode * xfuse_create_file_in_xrdp_fs(tui32 device_id, + int pinode, char *name, + int type); + +static int xfuse_does_file_exist(int parent, char *name); + +/* forward declarations for calls we make into dev_redir */ +int dev_redir_get_dir_listing(void *fusep, tui32 device_id, char *path); + +int dev_redir_file_open(void *fusep, tui32 device_id, char *path, + int mode, int type); + +int dev_redir_file_read(void *fusep, tui32 device_id, tui32 FileId, + tui32 Length, tui64 Offset); + +int dev_redir_file_write(void *fusep, tui32 device_id, tui32 FileId, + const char *buf, tui32 Length, tui64 Offset); + +/* forward declarations for FUSE callbacks */ +static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, + const char *name); + +static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + +/* this is not a callback, but its's used by xfuse_cb_readdir() */ +static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b, + const char *name, fuse_ino_t ino); + +static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi); + +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_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); + +static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi); + +static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi); + +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); -static struct xfuse_file_info *g_fuse_files = 0; -static struct fuse_lowlevel_ops g_xrdp_ll_oper; -static int g_ino = 2; +static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi); -/*****************************************************************************/ -static struct xfuse_file_info *APP_CC -fuse_find_file_info_by_name(struct xfuse_file_info *ffi, const char *filename) +// LK_TODO may not need to be implemented +#if 0 +static void xfuse_cb_statfs(fuse_req_t req, fuse_ino_t ino); + +static void xfuse_cb_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags); + +static void xfuse_cb_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size); + +static void xfuse_cb_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size); + +static void xfuse_cb_access(fuse_req_t req, fuse_ino_t ino, int mask); + +static void xfuse_cb_getlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock); + +static void xfuse_cb_setlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int sleep); + +static void xfuse_cb_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, + size_t out_bufsz); + +static void xfuse_cb_poll(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, + struct fuse_pollhandle *ph); +#endif + +static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi); + +/***************************************************************************** +** ** +** public functions - can be called from any code path ** +** ** +*****************************************************************************/ + +/** + * Initialize FUSE subsystem + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_init() { - struct xfuse_file_info *rv; - struct xfuse_file_info *rv1; + char *param0 = "xrdp-chansrv"; + char *argv[4]; - rv = ffi; - while (rv != 0) + /* if already inited, just return */ + if (g_xfuse_inited) { - if (g_strcmp(rv->filename, filename) == 0) - { - return rv; - } - if (rv->flags & 1) + log_debug("already inited"); + return 1; + } + + if (g_ch != 0) + { + log_error("g_ch is not zero"); + return -1; + } + + /* define FUSE mount point to ~/xrdp_client */ + g_snprintf(g_fuse_root_path, 255, "%s/xrdp_client", g_getenv("HOME")); + + /* if FUSE mount point does not exist, create it */ + if (!g_directory_exist(g_fuse_root_path)) + { + if (!g_create_dir(g_fuse_root_path)) { - rv1 = fuse_find_file_info_by_name(rv->child, filename); - if (rv1 != 0) - { - return rv1; - } + log_error("mkdir %s failed. If %s is already mounted, you must " + "first unmount it", g_fuse_root_path, g_fuse_root_path); + return -1; } - rv = rv->next; } + + /* setup xrdp file system */ + if (xfuse_init_xrdp_fs()) + return -1; + + /* 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.open = xfuse_cb_open; + g_xfuse_ops.read = xfuse_cb_read; + g_xfuse_ops.write = xfuse_cb_write; + g_xfuse_ops.create = xfuse_cb_create; + g_xfuse_ops.getattr = xfuse_cb_getattr; + g_xfuse_ops.setattr = xfuse_cb_setattr; + +#if 0 + g_xfuse_ops.statfs = xfuse_cb_statfs; + g_xfuse_ops.listxattr = xfuse_cb_listxattr; + g_xfuse_ops.getlk = xfuse_cb_getlk; + g_xfuse_ops.setlk = xfuse_cb_setlk; + g_xfuse_ops.ioctl = xfuse_cb_ioctl; + g_xfuse_ops.poll = xfuse_cb_poll; + g_xfuse_ops.access = xfuse_cb_access; + g_xfuse_ops.setxattr = xfuse_cb_setxattr; + g_xfuse_ops.getxattr = xfuse_cb_getxattr; +#endif + + argv[0] = param0; + argv[1] = g_fuse_root_path; + argv[2] = 0; + + if (xfuse_init_lib(2, argv)) + { + xfuse_deinit(); + return -1; + } + + g_xfuse_inited = 1; return 0; } -/*****************************************************************************/ -static struct xfuse_file_info *APP_CC -fuse_find_file_info_by_ino(struct xfuse_file_info *ffi, int ino) +/** + * De-initialize FUSE subsystem + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_deinit() { - struct xfuse_file_info *rv; - struct xfuse_file_info *rv1; + xfuse_deinit_xrdp_fs(); - rv = ffi; - while (rv != 0) + if (g_ch != 0) { - if (rv->ino == ino) - { - return rv; - } - if (rv->flags & 1) - { - rv1 = fuse_find_file_info_by_ino(rv->child, ino); - if (rv1 != 0) - { - return rv1; - } - } - rv = rv->next; + fuse_session_remove_chan(g_ch); + fuse_unmount(g_mount_point, g_ch); + g_ch = 0; + } + + if (g_se != 0) + { + fuse_session_destroy(g_se); + g_se = 0; + } + + if (g_buffer != 0) + { + g_free(g_buffer); + g_buffer = 0; } + + g_xfuse_inited = 0; return 0; } -/*****************************************************************************/ -static int APP_CC -xrdp_ffi2stat(struct xfuse_file_info *ffi, struct stat *stbuf) +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ +int xfuse_check_wait_objs(void) { - stbuf->st_ino = ffi->ino; - if (ffi->flags & 1) + struct fuse_chan *tmpch; + int rval; + +#define HACK + +#ifdef HACK +char buf[135168]; +#endif + + if (g_ch == 0) + return 0; + + if (g_tcp_select(g_fd, 0) & 1) { - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 2; - stbuf->st_uid = g_uid; - stbuf->st_gid = g_gid; - stbuf->st_atime = g_time; - stbuf->st_mtime = g_time; - stbuf->st_ctime = g_time; + 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; + + if (rval == -ENODEV) + return -1; + + 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 } - else + + return 0; +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) +{ + int lcount; + + if (g_ch == 0) + return 0; + + lcount = *count; + objs[lcount] = g_fd; + lcount++; + *count = lcount; + + return 0; +} + +/** + * @brief Create specified share directory. + * + * This code gets called from devredir + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_create_share(tui32 device_id, char *dirname) +{ + /* LK_TODO need to specify parent dir, mode */ + + XFUSE_INFO *fip; + XRDP_INODE *xinode; + tui32 saved_inode; + + if (dirname == NULL || strlen(dirname) == 0) + return -1; + + if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL) + { + log_debug("calloc() failed"); + return -1; + } + + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) { - stbuf->st_mode = S_IFREG | 0664; - stbuf->st_nlink = 1; - stbuf->st_size = ffi->size; - stbuf->st_uid = g_uid; - stbuf->st_gid = g_gid; - stbuf->st_atime = g_time; - stbuf->st_mtime = g_time; - stbuf->st_ctime = g_time; + log_error("system out of memory"); + return -1; } + + /* 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; + + 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, (int) xinode->inode); + + /* update nentries in parent inode */ + xinode = g_xrdp_fs.inode_table[1]; + if (xinode == NULL) + return -1; + xinode->nentries++; + + /* enumerate root dir, do not call FUSE when done */ + fip->req = NULL; + fip->inode = 1; // LK_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, "\\"); + return 0; } -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_clear_clip_dir(void) +{ + return 0; // CLIPBOARD_TODO +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_file_contents_range(int stream_id, char *data, int data_bytes) +{ + return 0; // CLIPBOARD_TODO +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) +{ + return 0; // CLIPBOARD_TODO +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_file_contents_size(int stream_id, int file_size) +{ + return 0; // CLIPBOARD_TODO +} + +/***************************************************************************** +** ** +** private functions - can only be called from within this file ** +** ** +*****************************************************************************/ + +/** + * Initialize FUSE library + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +static int xfuse_init_lib(int argc, char **argv) { - struct xfuse_file_info *ffi; - struct fuse_entry_param e; + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - LLOGLN(10, ("xrdp_ll_lookup: name %s", name)); - if (parent != 1) + if (fuse_parse_cmdline(&args, &g_mount_point, 0, 0) < 0) { - fuse_reply_err(req, ENOENT); + log_error("fuse_parse_cmdline() failed"); + fuse_opt_free_args(&args); + return -1; } - else + + if ((g_ch = fuse_mount(g_mount_point, &args)) == 0) { - ffi = fuse_find_file_info_by_name(g_fuse_files, name); - if (ffi != 0) - { - LLOGLN(10, ("xrdp_ll_lookup: name %s ino %d", name, ffi->ino)); - g_memset(&e, 0, sizeof(e)); - e.ino = ffi->ino; - e.attr_timeout = 1.0; - e.entry_timeout = 1.0; - xrdp_ffi2stat(ffi, &e.attr); - fuse_reply_entry(req, &e); - return; - } + log_error("fuse_mount() failed"); + fuse_opt_free_args(&args); + return -1; } - fuse_reply_err(req, ENOENT); + + g_se = fuse_lowlevel_new(&args, &g_xfuse_ops, sizeof(g_xfuse_ops), 0); + if (g_se == 0) + { + log_error("fuse_lowlevel_new() failed"); + fuse_unmount(g_mount_point, g_ch); + g_ch = 0; + fuse_opt_free_args(&args); + return -1; + } + + fuse_opt_free_args(&args); + fuse_session_add_chan(g_se, g_ch); + g_bufsize = fuse_chan_bufsize(g_ch); + + g_buffer = calloc(g_bufsize, 1); + g_fd = fuse_chan_fd(g_ch); + + return 0; } -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +/** + * Initialize xrdp file system + * + * @return 0 on success, -1 on failure + * + *****************************************************************************/ + +static int xfuse_init_xrdp_fs() { - struct stat stbuf; - struct xfuse_file_info *ffi; + struct xrdp_inode *xino; + + g_xrdp_fs.inode_table = calloc(4096, sizeof(struct xrdp_inode *)); + if (g_xrdp_fs.inode_table == NULL) + { + log_error("system out of memory"); + return -1; + } + + /* + * index 0 is our .. dir + */ + + if ((xino = calloc(1, sizeof(struct xrdp_inode))) == NULL) + { + log_error("system out of memory"); + free(g_xrdp_fs.inode_table); + return -1; + } + 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 + */ + + if ((xino = calloc(1, sizeof(struct xrdp_inode))) == 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 + */ + + if ((xino = calloc(1, sizeof(struct xrdp_inode))) == 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 = 1; + 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); + strcpy(xino->name, ".clipboard"); + + g_xrdp_fs.max_entries = 1024; + g_xrdp_fs.num_entries = 3; + g_xrdp_fs.next_node = 3; + + return 0; +} + +/** + * zap the xrdp file system + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +static int xfuse_deinit_xrdp_fs() +{ + return 0; +} + +/** + * determine if specified ino exists in xrdp file system + * + * @return 1 if it does, 0 otherwise + *****************************************************************************/ + +static int xfuse_is_inode_valid(int ino) +{ + /* our lowest ino is FIRST_INODE */ + if (ino < FIRST_INODE) + return 0; + + /* is ino present in our table? */ + if (ino >= g_xrdp_fs.next_node) + 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=%d name=%s", (int) parent, name); + + /* do we have a valid parent inode? */ + if (!xfuse_is_inode_valid(parent)) + { + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + } + + if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL) + { + log_error("calloc() 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; + log_debug("inserted new dir at inode_table[%d]", (int) xinode->inode); + + xfuse_dump_fs(); + + log_debug("new inode=%d", (int) 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() +{ + int i; + struct xrdp_inode *xinode; + + log_debug("found %d entries", g_xrdp_fs.num_entries - FIRST_INODE); + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + xinode = g_xrdp_fs.inode_table[i]; + log_debug("pinode=%d inode=%d nentries=%d mode=0x%x name=%s", + (int) xinode->parent_inode, (int) xinode->inode, + xinode->nentries, xinode->mode, xinode->name); + } + log_debug(""); +} + +/** + * Dump contents of xinode structure + * + * @param xino xinode structure to dump + *****************************************************************************/ + +static void xfuse_dump_xrdp_inode(struct xrdp_inode *xino) +{ + log_debug("--- dumping struct xinode ---"); + log_debug("name: %s", xino->name); + log_debug("parent_inode: %ld", xino->parent_inode); + log_debug("inode: %ld", 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: %ld", xino->size); + log_debug("device_id: %d", xino->device_id); + log_debug(""); +} + +/** + * 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(tui32 ino, char *full_path) +{ + tui32 parent_inode = 0; + tui32 child_inode = ino; + char reverse_path[4096]; + + reverse_path[0] = 0; + full_path[0] = 0; + + /* ino == 1 is a special case; we already know that it is not */ + /* associated with any device redirection */ + if (ino == 1) + return 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, char *reverse_path) +{ + char *cptr; + + full_path[0] = 0; + + while ((cptr = strrchr(reverse_path, '/')) != NULL) + { + strcat(full_path, cptr + 1); + strcat(full_path, "/"); + cptr[0] = 0; + } + strcat(full_path, reverse_path); +} + +/** + * Return the inode that matches the name and parent inode + *****************************************************************************/ + +static struct xrdp_inode * xfuse_get_inode_from_pinode_name(tui32 pinode, + const char *name) +{ + int i; + struct xrdp_inode * xinode; + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + xinode = g_xrdp_fs.inode_table[i]; + + /* 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, char *name, + int type) +{ + XRDP_INODE *xinode; + XRDP_INODE *xinodep; + + if ((name == NULL) || (strlen(name) == 0)) + return NULL; + + if ((xinode = calloc(1, sizeof(XRDP_INODE))) == NULL) + { + log_error("system out of memory"); + return NULL; + } + + log_debug("S_IFDIR=0x%x S_IFREG=0x%x type=0x%x", S_IFDIR, S_IFREG, type); + + xinode->parent_inode = pinode; + xinode->inode = g_xrdp_fs.next_node++; /* TODO should be thread safe */ + xinode->nlink = 1; + xinode->uid = getuid(); + xinode->gid = getgid(); + xinode->atime = time(0); + xinode->mtime = time(0); + xinode->ctime = time(0); + xinode->device_id = device_id; + xinode->is_synced = 1; + 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++; /* TODO should be thread safe */ + + /* bump up lookup count in parent dir */ + xinodep = g_xrdp_fs.inode_table[pinode]; + xinodep->nentries++; + + log_debug("LK_TODO: incremented nentries; parent=%d nentries=%d", + pinode, xinodep->nentries); + + /* LK_TODO */ + xfuse_dump_fs(); + + return xinode; +} + +/** + * Check if specified file exists + * + * @param parent parent inode of file + * @param name flilename or dirname + * + * @return 1 if specified file exists, 0 otherwise + *****************************************************************************/ + +static int xfuse_does_file_exist(int parent, char *name) +{ + int i; + XRDP_INODE *xinode; + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + xinode = g_xrdp_fs.inode_table[i]; + + if ((xinode->parent_inode == parent) && + (strcmp(xinode->name, name) == 0)) + { + return 1; + } + } + + return 0; +} + +/****************************************************************************** +** ** +** callbacks for devredir ** +** ** +******************************************************************************/ + +/** + * Add a file or directory to xrdp file system + *****************************************************************************/ + +void xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) +{ + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + XRDP_INODE *target_inode; + + if ((fip == NULL) || (xinode == NULL)) + { + log_error("fip or xinode are NULL"); + return; + } + + /* do we have a valid inode? */ + if (!xfuse_is_inode_valid(fip->inode)) + { + log_error("inode %d is not valid", fip->inode); + return; + } + + /* if filename is . or .. don't add it */ + if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0)) + { + free(xinode); + return; + } + + /* we have a parent inode and a dir name; what we need is the xinode */ + /* that matches the parent inode and the dir name */ + target_inode = xfuse_get_inode_from_pinode_name(fip->inode, fip->name); + if (target_inode == 0) + return; + + xinode->parent_inode = target_inode->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 */ + g_xrdp_fs.inode_table[xinode->inode] = xinode; + + /* bump up lookup count */ + xinode = g_xrdp_fs.inode_table[target_inode->inode]; + xinode->nentries++; + + log_debug("added %s to pinode=%d, nentries=%d target_inode->inode=%d", + fip->name, fip->inode, xinode->nentries, target_inode->inode); +} + +/** + *****************************************************************************/ + +void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus) +{ + struct xrdp_inode *xinode; + struct fuse_entry_param e; + int i; + + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + + xfuse_dump_fs(); + + if (fip == NULL) + { + log_debug("fip is NULL"); + goto done; + } + + if (IoStatus != 0) + { + /* command failed */ + if (fip->invoke_fuse) + fuse_reply_err(fip->req, ENOENT); + goto done; + } + + /* do we have a valid inode? */ + if (!xfuse_is_inode_valid(fip->inode)) + { + log_error("inode %d is not valid", fip->inode); + if (fip->invoke_fuse) + fuse_reply_err(fip->req, EBADF); + goto done; + } + + log_debug("looking for parent_inode=%d name=%s", fip->inode, fip->name); + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + xinode = g_xrdp_fs.inode_table[i]; + + /* match parent inode */ + if (xinode->parent_inode != fip->inode) + continue; + + /* match name */ + if (strcmp(xinode->name, fip->name) != 0) + continue; + + 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; + + xinode->is_synced = 1; + + if (fip->invoke_fuse) + fuse_reply_entry(fip->req, &e); + + break; + } + + if (i == g_xrdp_fs.num_entries) + { + /* requested entry not found */ + log_debug("did NOT find entry"); + if (fip->invoke_fuse) + fuse_reply_err(fip->req, ENOENT); + } + +done: + + free(fip); +} + +void xfuse_devredir_cb_open_file(void *vp, tui32 DeviceId, tui32 FileId) +{ + XFUSE_HANDLE *fh; + + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + if (fip == NULL) + { + log_debug("fip is NULL"); + goto done; + } + + if (fip->fi != NULL) + { + log_debug("$$$$$$$$$$$$$$$ allocationg fh"); + + /* LK_TODO fH NEEDS TO BE RELEASED WHEN THE FILE IS CLOSED */ + if ((fh = calloc(1, sizeof(XFUSE_HANDLE))) == NULL) + { + log_error("system out of memory"); + free(fip); + if (fip->invoke_fuse) + fuse_reply_err(fip->req, ENOMEM); + return; + } + + /* save file handle for later use */ + fh->DeviceId = DeviceId; + fh->FileId = FileId; + + fip->fi->fh = (uint64_t) fh; + } + + if (fip->invoke_fuse) + { + if (fip->reply_type == RT_FUSE_REPLY_OPEN) + { + log_debug("LK_TODO sending fuse_reply_open(); " + "DeviceId=%d FileId=%d req=%p fi=%p", + fh->DeviceId, fh->FileId, fip->req, fip->fi); + + fuse_reply_open(fip->req, fip->fi); + } + else if (fip->reply_type == RT_FUSE_REPLY_CREATE) + { + XRDP_INODE *xinode; + 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[%d] 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 + fuse_reply_create(fip->req, &e, fip->fi); + } + else + { + log_error("invalid reply type: %d", fip->reply_type); + } + } + +done: + + free(fip); +} + +void xfuse_devredir_cb_read_file(void *vp, char *buf, size_t length) +{ + XFUSE_HANDLE *fh; + XFUSE_INFO *fip; + + fip = (XFUSE_INFO *) vp; + if (fip == NULL) + goto done; + + fuse_reply_buf(fip->req, buf, length); + +done: + + fh = (XFUSE_HANDLE *) fip->fi->fh; + free(fh); + free(fip); +} + +void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length) +{ + XRDP_INODE *xinode; + XFUSE_HANDLE *fh; + XFUSE_INFO *fip; + + fip = (XFUSE_INFO *) vp; + if (fip == NULL) + goto done; + + fuse_reply_write(fip->req, length); + + /* update file size */ + if ((xinode = g_xrdp_fs.inode_table[fip->inode]) != NULL) + xinode->size += length; + else + log_error("inode at inode_table[%d] is NULL", fip->inode); + +done: + + fh = (XFUSE_HANDLE *) fip->fi->fh; + free(fh); + free(fip); +} + +/****************************************************************************** +** ** +** callbacks for fuse ** +** ** +******************************************************************************/ + +/** + * Look up a directory entry by name and get its attributes + * + *****************************************************************************/ + +static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + XFUSE_INFO *fip; + XRDP_INODE *xinode; + struct fuse_entry_param e; + tui32 device_id; + char full_path[4096]; + char *cptr; + + log_debug("ENTERED: looking for parent=%d name=%s", (int) parent, name); + + xfuse_dump_fs(); + + if (!xfuse_is_inode_valid(parent)) + { + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + return; + } + +// LK_TODO +#if 0 + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + xinode = g_xrdp_fs.inode_table[i]; + + /* match parent inode */ + if (xinode->parent_inode != parent) + continue; + + /* match name */ + if (strcmp(xinode->name, name) != 0) + continue; + + /* got a full match; if this dir is located on a remote device */ + /* and is not synced, do a remote look up */ + if ((xinode->device_id != 0) && (!xinode->is_synced)) + goto do_remote_lookup; + + memset(&e, 0, sizeof(e)); + e.ino = xinode->inode; + e.attr_timeout = XFUSE_ATTR_TIMEOUT; + e.entry_timeout = XFUSE_ENTRY_TIMEOUT; + e.attr.st_ino = xinode->inode; + e.attr.st_mode = xinode->mode; + e.attr.st_nlink = xinode->nlink; + e.attr.st_uid = xinode->uid; + e.attr.st_gid = xinode->gid; + e.attr.st_size = xinode->size; + e.attr.st_atime = xinode->atime; + e.attr.st_mtime = xinode->mtime; + e.attr.st_ctime = xinode->ctime; + e.generation = 1; + + fuse_reply_entry(req, &e); + log_debug("found entry in xrdp fs; returning"); + return; + } +#else + if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) != NULL) + { + /* got a full match; if this dir is located on a remote device */ + /* and is not synced, do a remote look up */ + if ((xinode->device_id != 0) && (!xinode->is_synced)) + goto do_remote_lookup; + + memset(&e, 0, sizeof(e)); + e.ino = xinode->inode; + e.attr_timeout = XFUSE_ATTR_TIMEOUT; + e.entry_timeout = XFUSE_ENTRY_TIMEOUT; + e.attr.st_ino = xinode->inode; + e.attr.st_mode = xinode->mode; + e.attr.st_nlink = xinode->nlink; + e.attr.st_uid = xinode->uid; + e.attr.st_gid = xinode->gid; + e.attr.st_size = xinode->size; + e.attr.st_atime = xinode->atime; + e.attr.st_mtime = xinode->mtime; + e.attr.st_ctime = xinode->ctime; + e.generation = 1; + + fuse_reply_entry(req, &e); + log_debug("found entry in xrdp fs; returning"); + return; + } + +#endif + +do_remote_lookup: + + /* if ino belongs to a redirected share, pass the call to devredir; */ + /* when done, devredir will invoke xfuse_devredir_cb_enum_dir_done(...) */ + device_id = xfuse_get_device_id_for_inode((tui32) parent, full_path); + if (device_id != 0) + { + log_debug("did not find entry; redirecting call to dev_redir"); + + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; + } + fip->req = req; + fip->inode = parent; + strncpy(fip->name, name, 1024); + fip->name[1023] = 0; + fip->invoke_fuse = 1; + fip->device_id = device_id; + + strcat(full_path, "/"); + strcat(full_path, name); + + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) + { + /* enumerate root dir */ + if (dev_redir_get_dir_listing((void *) fip, device_id, "\\")) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_buf(req, NULL, 0); + } + else + { + log_debug("dev_redir_get_dir_listing() called"); + } + + } + else + { + 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); + } + } + + log_debug("cmd sent; reting"); + return; + } + + log_debug("parent=%d name=%s not found", (int) parent, name); + fuse_reply_err(req, ENOENT); +} + +/** + * Get file attributes + *****************************************************************************/ + +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; + + log_debug("ino=%d", (int) ino); - LLOGLN(10, ("xrdp_ll_getattr: ino %d", ino)); - g_memset(&stbuf, 0, sizeof(stbuf)); - if (ino == 1) + /* if ino is not valid, just return */ + if (!xfuse_is_inode_valid(ino)) { - stbuf.st_mode = S_IFDIR | 0755; - stbuf.st_nlink = 2; - stbuf.st_uid = g_uid; - stbuf.st_gid = g_gid; - stbuf.st_atime = g_time; - stbuf.st_mtime = g_time; - stbuf.st_ctime = g_time; - fuse_reply_attr(req, &stbuf, 1.0); + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); return; } - ffi = fuse_find_file_info_by_ino(g_fuse_files, ino); - if (ffi == 0) - { - LLOGLN(0, ("xrdp_ll_getattr: fuse_find_file_info_by_ino failed ino %d", ino)); - fuse_reply_err(req, ENOENT); - } - else if (xrdp_ffi2stat(ffi, &stbuf) == -1) - { - fuse_reply_err(req, ENOENT); - } - else - { - fuse_reply_attr(req, &stbuf, 1.0); - } + + xino = g_xrdp_fs.inode_table[ino]; + xfuse_dump_xrdp_inode(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); } -/*****************************************************************************/ -static void APP_CC -dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino) +/** + * + *****************************************************************************/ + +static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b, + const char *name, fuse_ino_t ino) { struct stat stbuf; - char *newp; - int oldsize; - - oldsize = b->size; - b->size += fuse_add_direntry(req, 0, 0, name, 0, 0); - LLOGLN(10, ("1 %d %d %d", b->alloc_bytes, b->size, oldsize)); - if (b->size > b->alloc_bytes) - { - b->alloc_bytes = (b->size + 1023) & (~1023); - LLOGLN(10, ("2 %d %d %d", b->alloc_bytes, b->size, oldsize)); - newp = g_malloc(b->alloc_bytes, 0); - g_memcpy(newp, b->p, oldsize); - g_free(b->p); - b->p = newp; - } - g_memset(&stbuf, 0, sizeof(stbuf)); + size_t oldsize = b->size; + + log_debug("adding ino=%d name=%s", (int) 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); + fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf, + b->size); } -#define lmin(x, y) ((x) < (y) ? (x) : (y)) +/** + * + *****************************************************************************/ -/*****************************************************************************/ -static int APP_CC -reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, - off_t off, size_t maxsize) +static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) { - LLOGLN(10, ("reply_buf_limited: %d", maxsize)); - if (off < bufsize) + struct xrdp_inode *xinode; + struct dirbuf b; + int i; + + (void) fi; + + log_debug("looking for dir with inode=%d", ino); + + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + /* does this dir have any entries? */ + xinode = g_xrdp_fs.inode_table[ino]; + memset(&b, 0, sizeof(b)); + if (ino == 1) { - return fuse_reply_buf(req, buf + off, - lmin(bufsize - off, maxsize)); + xfuse_dirbuf_add(req, &b, ".", 1); + xfuse_dirbuf_add(req, &b, "..", 1); } else { - return fuse_reply_buf(req, 0, 0); + xfuse_dirbuf_add(req, &b, ".", xinode->inode); + xfuse_dirbuf_add(req, &b, "..", xinode->parent_inode); } -} -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *fi) -{ - struct xfuse_file_info *ffi; - struct dirbuf b; - - LLOGLN(10, ("xrdp_ll_readdir: ino %d", ino)); - if (ino != 1) + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - fuse_reply_err(req, ENOTDIR); + xinode = g_xrdp_fs.inode_table[i]; + if (xinode->parent_inode == ino) + xfuse_dirbuf_add(req, &b, xinode->name, xinode->inode); } + + if (off < b.size) + fuse_reply_buf(req, b.p + off, min(b.size - off, size)); else - { - ffi = g_fuse_files; - g_memset(&b, 0, sizeof(b)); - dirbuf_add(req, &b, ".", 1); - dirbuf_add(req, &b, "..", 1); - while (ffi != 0) - { - LLOGLN(10, ("xrdp_ll_readdir: %s", ffi->filename)); - dirbuf_add(req, &b, ffi->filename, ffi->ino); - ffi = ffi->next; - } - reply_buf_limited(req, b.p, b.size, off, size); - g_free(b.p); - } + fuse_reply_buf(req, NULL, 0); + + free(b.p); } -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +/** + * Create a directory + *****************************************************************************/ + +static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode) { - LLOGLN(10, ("xrdp_ll_open: ino %d", (int)ino)); - if (ino == 1) + XRDP_INODE *xinode; + struct fuse_entry_param e; + + if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) != NULL) { - fuse_reply_err(req, EISDIR); + /* 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; } - else if ((fi->flags & 3) != O_RDONLY) + + /* 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) +{ + XRDP_INODE *xinode; + + log_debug("entered: parent=%d name=%s", parent, name); + + /* is parent inode valid? */ + if (!xfuse_is_inode_valid(parent)) { - fuse_reply_err(req, EACCES); + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + return; } - else + + if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) == NULL) { - fuse_reply_open(req, fi); + log_error("did not find file with pinode=%d name=%s", parent, name); + fuse_reply_err(req, EBADF); + return; } -} -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *fi) -{ - char *data; - int stream_id; - struct xfuse_file_info *ffi; - struct req_list_item *rli; + log_debug("nentries is %d", xinode->nentries); - LLOGLN(10, ("xrdp_ll_read: %d %d %d", (int)ino, (int)off, (int)size)); - ffi = fuse_find_file_info_by_ino(g_fuse_files, ino); - if (ffi != 0) + if (xinode->nentries != 0) { - /* reply later */ - stream_id = 0; - rli = (struct req_list_item *) - g_malloc(sizeof(struct req_list_item), 1); - rli->req = req; - rli->stream_id = stream_id; - rli->lindex = ffi->lindex; - rli->off = off; - rli->size = size; - list_add_item(g_req_list, (tbus)rli); - if (g_req_list->count == 1) - { - clipboard_request_file_data(rli->stream_id, rli->lindex, - rli->off, rli->size); - } + log_debug("cannot rmdir; lookup count is %d", xinode->nentries); + fuse_reply_err(req, ENOTEMPTY); return; } - LLOGLN(0, ("xrdp_ll_read: fuse_find_file_info_by_ino failed ino %d", (int)ino)); - data = (char *)g_malloc(size, 1); - fuse_reply_buf(req, data, size); - g_free(data); + fuse_reply_err(req, 0); } -/*****************************************************************************/ -/* returns error */ -static int APP_CC -fuse_init_lib(int argc, char **argv) +/** + * Create a directory or file + * + * @param req opaque FUSE object + * @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 + *****************************************************************************/ + +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_args args = FUSE_ARGS_INIT(argc, argv); - int error; + XFUSE_INFO *fip; // LK_TODO use only XFUSE_INFO instead of struct + char *cptr; + char full_path[1024]; + tui32 device_id; - error = fuse_parse_cmdline(&args, &g_mountpoint, 0, 0); - if (error == -1) + full_path[0] = 0; + + log_debug("entered: type = %s", (type == S_IFDIR) ? "dir" : "file"); + + /* name must be valid */ + if ((name == NULL) || (strlen(name) == 0)) { - LLOGLN(0, ("fuse_init_lib: fuse_parse_cmdline failed")); - fuse_opt_free_args(&args); - return 1; + log_error("invalid name"); + fuse_reply_err(req, EBADF); + return; } - g_ch = fuse_mount(g_mountpoint, &args); - if (g_ch == 0) + + /* is parent inode valid? */ + if (!xfuse_is_inode_valid(parent)) { - LLOGLN(0, ("fuse_init_lib: fuse_mount failed")); - fuse_opt_free_args(&args); - return 1; + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + return; } - g_se = fuse_lowlevel_new(&args, &g_xrdp_ll_oper, - sizeof(g_xrdp_ll_oper), 0); - if (g_se == 0) + + device_id = xfuse_get_device_id_for_inode(parent, full_path); + strcat(full_path, "/"); + strcat(full_path, name); + + if (device_id == 0) { - LLOGLN(0, ("fuse_init_lib: fuse_lowlevel_new failed")); - fuse_unmount(g_mountpoint, g_ch); - g_ch = 0; - fuse_opt_free_args(&args); - return 1; + /* specified file is a local resource */ + //XFUSE_HANDLE *fh; + + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + return; } - fuse_opt_free_args(&args); - fuse_session_add_chan(g_se, g_ch); - g_bufsize = fuse_chan_bufsize(g_ch); - g_buffer = g_malloc(g_bufsize, 0); - g_fd = fuse_chan_fd(g_ch); - return 0; -} -/*****************************************************************************/ -static int APP_CC -fuse_delete_dir_items(struct xfuse_file_info *ffi) -{ - struct xfuse_file_info *ffi1; + /* specified file resides on redirected share */ - while (ffi != 0) + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) { - if (ffi->flags & 1) - { - fuse_delete_dir_items(ffi->child); - } - ffi1 = ffi; - ffi = ffi->next; - g_free(ffi1); + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; } - return 0; -} -/*****************************************************************************/ -int APP_CC -fuse_clear_clip_dir(void) -{ - fuse_delete_dir_items(g_fuse_files); - g_fuse_files = 0; - return 0; -} + fip->req = req; + fip->fi = fi; + fip->inode = parent; + fip->invoke_fuse = 1; + fip->device_id = device_id; + fip->mode = type; + fip->reply_type = RT_FUSE_REPLY_CREATE; + strncpy(fip->name, name, 1024); + fip->name[1023] = 0; -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) -{ - struct xfuse_file_info *ffi; - struct xfuse_file_info *ffi1; + /* LK_TODO need to handle open permissions */ - LLOGLN(10, ("fuse_add_clip_dir_item: adding %s ino %d", filename, g_ino)); - ffi = g_fuse_files; - if (ffi == 0) + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) { - ffi1 = (struct xfuse_file_info *) - g_malloc(sizeof(struct xfuse_file_info), 1); - ffi1->flags = flags; - ffi1->ino = g_ino++; - ffi1->lindex = lindex; - ffi1->size = size; - g_strncpy(ffi1->filename, filename, 255); - g_fuse_files = ffi1; - return 0; + /* get dev_redir to open the remote file */ + if (dev_redir_file_open((void *) fip, device_id, "\\", O_CREAT, type)) + { + log_error("failed to send dev_redir_open_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + } } - while (ffi->next != 0) + else { - ffi = ffi->next; + if (dev_redir_file_open((void *) fip, device_id, cptr, O_CREAT, type)) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_err(req, EREMOTEIO); + } } - ffi1 = (struct xfuse_file_info *) - g_malloc(sizeof(struct xfuse_file_info), 1); - ffi1->flags = flags; - ffi1->ino = g_ino++; - ffi1->lindex = lindex; - ffi1->size = size; - g_strncpy(ffi1->filename, filename, 255); - ffi->next = ffi1; - return 0; } -/*****************************************************************************/ -int APP_CC -fuse_get_wait_objs(tbus *objs, int *count, int *timeout) +/** + * Open specified file + *****************************************************************************/ + +static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) { - int lcount; + XRDP_INODE *xinode; + XFUSE_INFO *fip; + char *cptr; + char full_path[4096]; + tui32 device_id; - LLOGLN(10, ("fuse_get_wait_objs:")); - if (g_ch == 0) + log_debug("LK_TODO: open_flags=0x%x req=%p fi=%p", + fi->flags, req, fi); + + if (!xfuse_is_inode_valid(ino)) { - return 0; + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; } - lcount = *count; - objs[lcount] = g_fd; - lcount++; - *count = lcount; - return 0; -} - -/*****************************************************************************/ -int APP_CC -fuse_check_wait_objs(void) -{ - struct fuse_chan *tmpch; - int res; - - LLOGLN(10, ("fuse_check_wait_objs:")); - if (g_ch == 0) + /* if ino points to a dir, fail the open request */ + xinode = g_xrdp_fs.inode_table[ino]; + if (xinode->mode & S_IFDIR) { - return 0; + log_debug("reading a dir not allowed!"); + fuse_reply_err(req, EISDIR); + return; } - if (g_tcp_select(g_fd, 0) & 1) + + device_id = xfuse_get_device_id_for_inode((tui32) ino, full_path); + if (device_id) { - LLOGLN(10, ("fuse_check_wait_objs: fd is set")); - tmpch = g_ch; - res = fuse_chan_recv(&tmpch, g_buffer, g_bufsize); - if (res == -EINTR) + /* specified file resides on redirected share */ + + log_debug("LK_TODO looking for file %s in DeviceId=%d", full_path, device_id); + + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; + } + + fip->req = req; + fip->inode = ino; + fip->invoke_fuse = 1; + fip->device_id = device_id; + fip->fi = fi; + strncpy(fip->name, full_path, 1024); + fip->name[1023] = 0; + fip->reply_type = RT_FUSE_REPLY_OPEN; + + /* LK_TODO need to handle open permissions */ + + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) { - return 0; + /* get dev_redir to open the remote file */ + if (dev_redir_file_open((void *) fip, device_id, "\\", + fi->flags, S_IFREG)) + { + log_error("failed to send dev_redir_open_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + } } - if (res <= 0) + else { - return 1; + if (dev_redir_file_open((void *) fip, device_id, cptr, + fi->flags, S_IFREG)) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_err(req, EREMOTEIO); + } } - fuse_session_process(g_se, g_buffer, res, tmpch); } - return 0; + else + { + /* specified file is a local resource */ + //XFUSE_HANDLE *fh; + + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + } } -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_init(void) +/** + *****************************************************************************/ + +static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) { - char *param0 = "xrdp-chansrv"; - char *argv[4]; + XFUSE_HANDLE *fh; + XFUSE_INFO *fusep; - if (g_ch != 0) + log_debug("want_bytes %d bytes at off %d", size, off); + + if (fi->fh == 0) { - return 0; + log_debug("LK_TODO: looks like fi->fh is corrupted"); + fuse_reply_err(req, EINVAL); + return; } - g_snprintf(g_fuse_root_path, 255, "%s/xrdp_client", g_getenv("HOME")); - LLOGLN(0, ("fuse_init: using root_path [%s]", g_fuse_root_path)); - if (!g_directory_exist(g_fuse_root_path)) + fh = (XFUSE_HANDLE *) fi->fh; + + if (fh->DeviceId == 0) { - if (!g_create_dir(g_fuse_root_path)) - { - LLOGLN(0, ("fuse_init: g_create_dir failed [%s]", - g_fuse_root_path)); - return 1; - } + /* target file is in .clipboard dir */ + log_debug(">>>>>>>>>>>>>>>>> THIS IS STILL A TODO!"); + return; } - g_time = g_time1(); - g_uid = g_getuid(); - g_gid = g_getgid(); - argv[0] = param0; - argv[1] = g_fuse_root_path; - argv[2] = 0; - g_memset(&g_xrdp_ll_oper, 0, sizeof(g_xrdp_ll_oper)); - g_xrdp_ll_oper.lookup = xrdp_ll_lookup; - g_xrdp_ll_oper.getattr = xrdp_ll_getattr; - g_xrdp_ll_oper.readdir = xrdp_ll_readdir; - g_xrdp_ll_oper.open = xrdp_ll_open; - g_xrdp_ll_oper.read = xrdp_ll_read; + /* target file is on a remote device */ - g_req_list = list_create(); - g_req_list->auto_free = 1; + if ((fusep = calloc(1, sizeof(XFUSE_INFO))) == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; + } + fusep->req = req; + fusep->inode = ino; + fusep->invoke_fuse = 1; + fusep->device_id = fh->DeviceId; + fusep->fi = fi; - return fuse_init_lib(2, argv); + dev_redir_file_read(fusep, fh->DeviceId, fh->FileId, size, off); + log_debug("exiting"); } -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_deinit(void) +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) { - LLOGLN(0, ("fuse_deinit:")); - if (g_ch != 0) - { - LLOGLN(0, ("fuse_deinit: calling fuse_unmount")); - fuse_session_remove_chan(g_ch); - fuse_unmount(g_mountpoint, g_ch); - g_ch = 0; - } - if (g_se != 0) + XFUSE_HANDLE *fh; + XFUSE_INFO *fusep; + + log_debug("write %d bytes at off %d", size, off); + + if (fi->fh == 0) { - LLOGLN(0, ("fuse_deinit: calling fuse_session_destroy")); - fuse_session_destroy(g_se); - g_se = 0; + log_debug("LK_TODO: looks like fi->fh is corrupted"); + fuse_reply_err(req, EINVAL); + return; } - if (g_buffer != 0) + fh = (XFUSE_HANDLE *) fi->fh; + + if (fh->DeviceId == 0) { - g_free(g_buffer); - g_buffer = 0; + /* target file is in .clipboard dir */ + log_debug(">>>>>>>>>>>>>>>>> THIS IS STILL A TODO!"); + return; } - if (g_req_list != 0) + + /* target file is on a remote device */ + + if ((fusep = calloc(1, sizeof(XFUSE_INFO))) == NULL) { - list_delete(g_req_list); - g_req_list = 0; + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; } - return 0; + fusep->req = req; + fusep->inode = ino; + fusep->invoke_fuse = 1; + fusep->device_id = fh->DeviceId; + fusep->fi = fi; + + dev_redir_file_write(fusep, fh->DeviceId, fh->FileId, buf, size, off); + log_debug("exiting"); } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_size(int stream_id, int file_size) +static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) { - LLOGLN(10, ("fuse_file_contents_size: file_size %d", file_size)); - return 0; + xfuse_create_dir_or_file(req, parent, name, mode, fi, S_IFREG); } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_range(int stream_id, char *data, int data_bytes) -{ - struct req_list_item *rli; +// LK_TODO may not need to implement the following funcs - LLOGLN(10, ("fuse_file_contents_range: data_bytes %d", data_bytes)); - rli = (struct req_list_item *)list_get_item(g_req_list, 0); - if (rli != 0) - { - fuse_reply_buf(rli->req, data, data_bytes); - list_remove_item(g_req_list, 0); - if (g_req_list->count > 0) - { - /* send next request */ - rli = (struct req_list_item *)list_get_item(g_req_list, 0); - if (rli != 0) - { - clipboard_request_file_data(rli->stream_id, rli->lindex, - rli->off, rli->size); - } - else - { - LLOGLN(0, ("fuse_file_contents_range: error")); - } - } - } - else - { - LLOGLN(0, ("fuse_file_contents_range: error")); - } - return 0; +#if 0 +static void xfuse_cb_statfs(fuse_req_t req, fuse_ino_t ino) +{ + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); } -#else - -#include "arch.h" +static void xfuse_cb_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); -char g_fuse_root_path[256] = ""; +} -/*****************************************************************************/ -int APP_CC -fuse_get_wait_objs(tbus *objs, int *count, int *timeout) +static void xfuse_cb_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_check_wait_objs(void) +static void xfuse_cb_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_init(void) +static void xfuse_cb_access(fuse_req_t req, fuse_ino_t ino, int mask) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_deinit(void) +static void xfuse_cb_getlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_clear_clip_dir(void) +static void xfuse_cb_setlk(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, struct flock *lock, + int sleep) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) +static void xfuse_cb_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, + size_t out_bufsz) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_size(int stream_id, int file_size) +static void xfuse_cb_poll(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, + struct fuse_pollhandle *ph) { - return 0; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered"); + fuse_reply_err(req, ENOMEM); + } +#endif -/*****************************************************************************/ -int APP_CC -fuse_file_contents_range(int stream_id, char *data, int data_bytes) +static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi) { - return 0; + XRDP_INODE *xinode; + + log_debug(">>>>>>>>>>>>>>>> LK_TODO: entered to_set=0x%x", to_set); + + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + xinode = g_xrdp_fs.inode_table[ino]; + + if (to_set & FUSE_SET_ATTR_MODE) + { + xinode->mode = attr->st_mode; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_MODE"); + + } + + if (to_set & FUSE_SET_ATTR_UID) + { + xinode->uid = attr->st_uid; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_UID"); + } + + if (to_set & FUSE_SET_ATTR_GID) + { + xinode->gid = attr->st_gid; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_GID"); + } + + if (to_set & FUSE_SET_ATTR_SIZE) + { + log_debug("previous file size: %d", attr->st_size); + xinode->size = attr->st_size; + log_debug("returning file size: %d", xinode->size); + } + + if (to_set & FUSE_SET_ATTR_ATIME) + { + xinode->atime = attr->st_atime; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_ATIME"); + } + + if (to_set & FUSE_SET_ATTR_MTIME) + { + xinode->mtime = attr->st_mtime; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_MTIME"); + } + + if (to_set & FUSE_SET_ATTR_ATIME_NOW) + { + xinode->atime = attr->st_atime; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_ATIME_NOW"); + } + + if (to_set & FUSE_SET_ATTR_MTIME_NOW) + { + xinode->mtime = attr->st_mtime; + log_debug(">>>>>>>>>>>>>>>> LK_TODO: FUSE_SET_ATTR_MTIME_NOW"); + } + + fuse_reply_attr(req, attr, 1.0); /* LK_TODO just faking for now */ } -#endif +#endif /* end else #ifndef XRDP_FUSE */ diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index 0ccde368..34c4ba7e 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -1,23 +1,60 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 + * + * 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. + */ -#if !defined(CHANSRV_FUSE_H) -#define CHANSRV_FUSE_H +#ifndef _CHANSRV_FUSE_H +#define _CHANSRV_FUSE_H -int APP_CC -fuse_clear_clip_dir(void); -int APP_CC -fuse_add_clip_dir_item(char *filename, int flags, int size, int lindex); -int APP_CC -fuse_get_wait_objs(tbus *objs, int *count, int *timeout); -int APP_CC -fuse_check_wait_objs(void); -int APP_CC -fuse_init(void); -int APP_CC -fuse_deinit(void); +/* 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 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[256]; /* Dir or filename */ + tui32 device_id; /* for file system redirection */ + char is_synced; /* dir struct has been read from */ + /* remote device, done just once */ +}; +typedef struct xrdp_inode XRDP_INODE; // LK_TODO use this instead of using struct xrdp_inode -int APP_CC -fuse_file_contents_size(int stream_id, int file_size); -int APP_CC -fuse_file_contents_range(int stream_id, char *data, int data_bytes); +int xfuse_init(); +int xfuse_deinit(); +int xfuse_check_wait_objs(void); +int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout); +int xfuse_create_share(tui32 share_id, char *dirname); + +int xfuse_clear_clip_dir(void); +int xfuse_file_contents_range(int stream_id, char *data, int data_bytes); +int xfuse_file_contents_size(int stream_id, int file_size); +int xfuse_add_clip_dir_item(char *filename, int flags, int size, int lindex); + +/* functions that are inovked from devredir */ +void xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode); +void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus); +void xfuse_devredir_cb_open_file(void *vp, tui32 DeviceId, tui32 FileId); +void xfuse_devredir_cb_read_file(void *vp, char *buf, size_t length); #endif diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index d277d474..27c45fff 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -320,7 +320,7 @@ clipboard_init(void) return 0; } - fuse_init(); + xfuse_init(); xcommon_init(); g_incr_max_req_size = XMaxRequestSize(g_display) * 4 - 24; g_memset(&g_clip_c2s, 0, sizeof(g_clip_c2s)); @@ -464,7 +464,7 @@ clipboard_deinit(void) g_wnd = 0; } - fuse_deinit(); + xfuse_deinit(); g_free(g_clip_c2s.data); g_clip_c2s.data = 0; @@ -968,7 +968,7 @@ clipboard_process_format_announce(struct stream *s, int clip_msg_status, LLOGLN(10, ("clipboard_process_format_announce %d", clip_msg_len)); clipboard_send_format_ack(); - fuse_clear_clip_dir(); + xfuse_clear_clip_dir(); g_clip_c2s.converted = 0; desc[0] = 0; diff --git a/sesman/chansrv/clipboard_file.c b/sesman/chansrv/clipboard_file.c index 121d96a7..1942999d 100644 --- a/sesman/chansrv/clipboard_file.c +++ b/sesman/chansrv/clipboard_file.c @@ -534,13 +534,13 @@ clipboard_process_file_response(struct stream *s, int clip_msg_status, in_uint32_le(s, file_size); LLOGLN(10, ("clipboard_process_file_response: streamId %d " "file_size %d", streamId, file_size)); - fuse_file_contents_size(streamId, file_size); + xfuse_file_contents_size(streamId, file_size); } else if (g_file_request_sent_type == CB_FILECONTENTS_RANGE) { g_file_request_sent_type = 0; in_uint32_le(s, streamId); - fuse_file_contents_range(streamId, s->p, clip_msg_len - 4); + xfuse_file_contents_range(streamId, s->p, clip_msg_len - 4); } else { @@ -604,7 +604,7 @@ clipboard_c2s_in_files(struct stream *s, char *file_list) LLOGLN(0, ("clipboard_c2s_in_files: error cItems %d too big", cItems)); return 1; } - fuse_clear_clip_dir(); + xfuse_clear_clip_dir(); LLOGLN(10, ("clipboard_c2s_in_files: cItems %d", cItems)); cfd = (struct clip_file_desc *) g_malloc(sizeof(struct clip_file_desc), 0); @@ -620,7 +620,7 @@ clipboard_c2s_in_files(struct stream *s, char *file_list) "supported [%s]", cfd->cFileName)); continue; } - fuse_add_clip_dir_item(cfd->cFileName, 0, cfd->fileSizeLow, lindex); + xfuse_add_clip_dir_item(cfd->cFileName, 0, cfd->fileSizeLow, lindex); g_strcpy(ptr, "file://"); ptr += 7; diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index e6407211..d9296356 100644 --- a/sesman/chansrv/devredir.c +++ b/sesman/chansrv/devredir.c @@ -1,7 +1,9 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * xrdp device redirection - only drive redirection is currently supported + * + * Copyright (C) Laxmikant Rashinkar 2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +18,85 @@ * limitations under the License. */ -#include "arch.h" -#include "parse.h" -#include "os_calls.h" +/* + * TODO + * o there is one difference in the protocol initialization + * sequence upon reconnection: if a user is already logged on, + * send a SERVER_USER_LOGGED_ON msg as per section 3.3.5.1.5 + * + * o how to announce / delete a drive after a connection has been + * established? + * + * o need to support multiple drives; + * o for multiple drives, we cannot have global device_id's and file_id's + * and all functions must be reenterant + * o replace printf's with log_xxx + * o mark local funcs with static + */ + +#include "devredir.h" +/* globals */ extern int g_rdpdr_chan_id; /* in chansrv.c */ +int g_is_printer_redir_supported = 0; +int g_is_port_redir_supported = 0; +int g_is_drive_redir_supported = 0; +int g_is_smartcard_redir_supported = 0; +int g_drive_redir_version = 1; +char g_preferred_dos_name_for_filesystem[9]; +char g_full_name_for_filesystem[1024]; +tui32 g_completion_id = 1; + +tui32 g_clientID; /* unique client ID - announced by client */ +tui32 g_device_id; /* unique device ID - announced by client */ +tui16 g_client_rdp_version; /* returned by client */ +IRP *g_irp_head = NULL; +struct stream *g_input_stream = NULL; + +void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length); /*****************************************************************************/ int APP_CC dev_redir_init(void) { + struct stream *s; + int bytes; + int fd; + + union _u + { + tui32 clientID; + char buf[4]; + } u; + + /* get a random number that will act as a unique clientID */ + if ((fd = open("/dev/urandom", O_RDONLY))) + { + read(fd, u.buf, 4); + close(fd); + } + else + { + /* /dev/urandom did not work - use address of struct s */ + tui64 u64 = (tui64) &s; + u.clientID = (tui32) u64; + } + + /* setup stream */ + stream_new(s, 1024); + + /* initiate drive redirection protocol by sending Server Announce Req */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_SERVER_ANNOUNCE); + stream_wr_u16_le(s, 0x0001); /* server major ver */ + stream_wr_u16_le(s, 0x000C); /* server minor ver - pretend 2 b Win 7 */ + stream_wr_u32_le(s, u.clientID); /* unique ClientID */ + + /* send data to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); return 0; } @@ -36,12 +107,127 @@ dev_redir_deinit(void) return 0; } -/*****************************************************************************/ +/** + * @brief process incoming data + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + int APP_CC dev_redir_data_in(struct stream *s, int chan_id, int chan_flags, int length, int total_length) { - return 0; + struct stream *ls; + tui16 comp_type; + tui16 pktID; + tui16 minor_ver; + int rv = 0; + + /* + * handle packet fragmentation + */ + if ((chan_flags & 3) == 3) + { + /* all data contained in one packet */ + ls = s; + } + else + { + /* is this is the first packet? */ + if (chan_flags & 1) + stream_new(g_input_stream, total_length); + + stream_copyin(g_input_stream, s->p, length); + + /* in last packet, chan_flags & 0x02 will be true */ + if ((chan_flags & 2) == 0) + return 0; + + g_input_stream->p = g_input_stream->data; + ls = g_input_stream; + } + + /* read header from incoming data */ + stream_rd_u16_le(ls, comp_type); + stream_rd_u16_le(ls, pktID); + + /* for now we only handle core type, not printers */ + if (comp_type != RDPDR_CTYP_CORE) + { + log_error("invalid component type in response; expected 0x%x got 0x%x", + RDPDR_CTYP_CORE, comp_type); + + rv = -1; + goto done; + } + + /* figure out what kind of response we have gotten */ + switch (pktID) + { + case PAKID_CORE_CLIENTID_CONFIRM: + stream_seek(ls, 2); /* major version, we ignore it */ + stream_rd_u16_le(ls, minor_ver); + stream_rd_u32_le(ls, g_clientID); + + g_client_rdp_version = minor_ver; + + switch (minor_ver) + { + case RDP_CLIENT_50: + break; + + case RDP_CLIENT_51: + break; + + case RDP_CLIENT_52: + break; + + case RDP_CLIENT_60_61: + break; + } + // LK_TODO dev_redir_send_server_clientID_confirm(); + break; + + case PAKID_CORE_CLIENT_NAME: + /* client is telling us its computer name; do we even care? */ + + /* let client know loggin was successful */ + dev_redir_send_server_user_logged_on(); + usleep(1000 * 100); + + /* let client know our capabilites */ + dev_redir_send_server_core_cap_req(); + + /* send confirm clientID */ + dev_redir_send_server_clientID_confirm(); + break; + + case PAKID_CORE_CLIENT_CAPABILITY: + dev_redir_proc_client_core_cap_resp(ls); + break; + + case PAKID_CORE_DEVICELIST_ANNOUNCE: + dev_redir_proc_client_devlist_announce_req(ls); + break; + + case PAKID_CORE_DEVICE_IOCOMPLETION: + dev_redir_proc_device_iocompletion(ls); + break; + + default: + log_error("got unknown response 0x%x", pktID); + break; + } + +done: + + if (g_input_stream) + { + stream_free(g_input_stream); + g_input_stream = NULL; + } + + return rv; } /*****************************************************************************/ @@ -57,3 +243,1088 @@ dev_redir_check_wait_objs(void) { return 0; } + +/** + * @brief let client know our capabilities + *****************************************************************************/ + +void dev_redir_send_server_core_cap_req() +{ + struct stream *s; + int bytes; + + stream_new(s, 1024); + + /* setup header */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_SERVER_CAPABILITY); + + stream_wr_u16_le(s, 5); /* num of caps we are sending */ + stream_wr_u16_le(s, 0x0000); /* padding */ + + /* setup general capability */ + stream_wr_u16_le(s, CAP_GENERAL_TYPE); /* CapabilityType */ + stream_wr_u16_le(s, 44); /* CapabilityLength - len of this */ + /* CAPABILITY_SET in bytes, inc */ + /* the header */ + stream_wr_u32_le(s, 2); /* Version */ + stream_wr_u32_le(s, 2); /* O.S type */ + stream_wr_u32_le(s, 0); /* O.S version */ + stream_wr_u16_le(s, 1); /* protocol major version */ + stream_wr_u16_le(s, g_client_rdp_version); /* protocol minor version */ + stream_wr_u32_le(s, 0xffff); /* I/O code 1 */ + stream_wr_u32_le(s, 0); /* I/O code 2 */ + stream_wr_u32_le(s, 7); /* Extended PDU */ + stream_wr_u32_le(s, 0); /* extra flags 1 */ + stream_wr_u32_le(s, 0); /* extra flags 2 */ + stream_wr_u32_le(s, 2); /* special type device cap */ + + /* setup printer capability */ + stream_wr_u16_le(s, CAP_PRINTER_TYPE); + stream_wr_u16_le(s, 8); + stream_wr_u32_le(s, 1); + + /* setup serial port capability */ + stream_wr_u16_le(s, CAP_PORT_TYPE); + stream_wr_u16_le(s, 8); + stream_wr_u32_le(s, 1); + + /* setup file system capability */ + stream_wr_u16_le(s, CAP_DRIVE_TYPE); /* CapabilityType */ + stream_wr_u16_le(s, 8); /* CapabilityLength - len of this */ + /* CAPABILITY_SET in bytes, inc */ + /* the header */ + stream_wr_u32_le(s, 2); /* Version */ + + /* setup smart card capability */ + stream_wr_u16_le(s, CAP_SMARTCARD_TYPE); + stream_wr_u16_le(s, 8); + stream_wr_u32_le(s, 1); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); +} + +void dev_redir_send_server_clientID_confirm() +{ + struct stream *s; + int bytes; + + stream_new(s, 1024); + + /* setup stream */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_CLIENTID_CONFIRM); + stream_wr_u16_le(s, 0x0001); + stream_wr_u16_le(s, g_client_rdp_version); + stream_wr_u32_le(s, g_clientID); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); +} + +void dev_redir_send_server_user_logged_on() +{ + struct stream *s; + int bytes; + + stream_new(s, 1024); + + /* setup stream */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_USER_LOGGEDON); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); +} + +void dev_redir_send_server_device_announce_resp(tui32 device_id) +{ + struct stream *s; + int bytes; + + stream_new(s, 1024); + + /* setup stream */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_DEVICE_REPLY); + stream_wr_u32_le(s, device_id); + stream_wr_u32_le(s, 0); /* ResultCode */ + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); +} + +/** + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int dev_redir_send_drive_create_request(tui32 device_id, char *path, + tui32 DesiredAccess, + tui32 CreateOptions, + tui32 CreateDisposition, + tui32 completion_id) +{ + struct stream *s; + int bytes; + int len; + + log_debug("LK_TODO: DesiredAccess=0x%x CreateDisposition=0x%x CreateOptions=0x%x", + DesiredAccess, CreateDisposition, CreateOptions); + + /* to store path as unicode */ + len = strlen(path) * 2 + 2; + + stream_new(s, 1024 + len); + + dev_redir_insert_dev_io_req_header(s, + device_id, + 0, + completion_id, + IRP_MJ_CREATE, + 0); + + stream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */ + stream_wr_u32_le(s, 0); /* AllocationSize high unused */ + stream_wr_u32_le(s, 0); /* AllocationSize low unused */ + stream_wr_u32_le(s, 0); /* FileAttributes */ + stream_wr_u32_le(s, 0); /* SharedAccess */ + stream_wr_u32_le(s, CreateDisposition); /* CreateDisposition */ + stream_wr_u32_le(s, CreateOptions); /* CreateOptions */ + stream_wr_u32_le(s, len); /* PathLength */ + devredir_cvt_to_unicode(s->p, path); /* path in unicode */ + stream_seek(s, len); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); + return 0; +} + +/** + * Close a request previously created by dev_redir_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) +{ + struct stream *s; + int bytes; + + stream_new(s, 1024); + + dev_redir_insert_dev_io_req_header(s, DeviceId, FileId, CompletionId, + MajorFunction, MinorFunc); + + if (pad_len) + stream_seek(s, pad_len); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); + return 0; +} + +/** + * @brief ask client to enumerate directory + * + * Server Drive Query Directory Request + * DR_DRIVE_QUERY_DIRECTORY_REQ + * + *****************************************************************************/ +// LK_TODO Path needs to be Unicode +void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, + tui32 InitialQuery, char *Path) +{ + struct stream *s; + int bytes; + char upath[4096]; // LK_TODO need to malloc this + int path_len = 0; + + /* during Initial Query, Path cannot be NULL */ + if (InitialQuery) + { + if (Path == NULL) + return; + + path_len = strlen(Path) * 2 + 2; + devredir_cvt_to_unicode(upath, Path); + } + + stream_new(s, 1024 + path_len); + + irp->completion_type = CID_DIRECTORY_CONTROL; + dev_redir_insert_dev_io_req_header(s, + device_id, + irp->FileId, + irp->completion_id, + IRP_MJ_DIRECTORY_CONTROL, + IRP_MN_QUERY_DIRECTORY); + + stream_wr_u32_le(s, FileDirectoryInformation); /* FsInformationClass */ + stream_wr_u8(s, InitialQuery); /* InitialQuery */ + + if (!InitialQuery) + { + stream_wr_u32_le(s, 0); /* PathLength */ + stream_seek(s, 23); + } + else + { + stream_wr_u32_le(s, path_len); /* PathLength */ + stream_seek(s, 23); /* Padding */ + stream_wr_string(s, upath, path_len); + } + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + stream_free(s); +} + +/****************************************************************************** +** process data received from client ** +******************************************************************************/ + +/** + * @brief process client's repsonse to our core_capability_req() msg + * + * @param s stream containing client's response + *****************************************************************************/ +void dev_redir_proc_client_core_cap_resp(struct stream *s) +{ + int i; + tui16 num_caps; + tui16 cap_type; + tui16 cap_len; + tui32 cap_version; + + stream_rd_u16_le(s, num_caps); + stream_seek(s, 2); /* padding */ + + for (i = 0; i < num_caps; i++) + { + stream_rd_u16_le(s, cap_type); + stream_rd_u16_le(s, cap_len); + stream_rd_u32_le(s, cap_version); + + /* remove header length and version */ + cap_len -= 8; + + switch (cap_type) + { + case CAP_GENERAL_TYPE: + log_debug("got CAP_GENERAL_TYPE"); + stream_seek(s, cap_len); + break; + + case CAP_PRINTER_TYPE: + log_debug("got CAP_PRINTER_TYPE"); + g_is_printer_redir_supported = 1; + stream_seek(s, cap_len); + break; + + case CAP_PORT_TYPE: + log_debug("got CAP_PORT_TYPE"); + g_is_port_redir_supported = 1; + stream_seek(s, cap_len); + break; + + case CAP_DRIVE_TYPE: + log_debug("got CAP_DRIVE_TYPE"); + g_is_drive_redir_supported = 1; + if (cap_version == 2) + g_drive_redir_version = 2; + stream_seek(s, cap_len); + break; + + case CAP_SMARTCARD_TYPE: + log_debug("got CAP_SMARTCARD_TYPE"); + g_is_smartcard_redir_supported = 1; + stream_seek(s, cap_len); + break; + } + } +} + +void dev_redir_proc_client_devlist_announce_req(struct stream *s) +{ + int i; + int j; + tui32 device_count; + tui32 device_type; + tui32 device_data_len; + + /* get number of devices being announced */ + stream_rd_u32_le(s, device_count); + + log_debug("num of devices announced: %d", device_count); + + for (i = 0; i < device_count; i++) + { + stream_rd_u32_le(s, device_type); + stream_rd_u32_le(s, g_device_id); /* LK_TODO need to support */ + /* multiple drives */ + + switch (device_type) + { + case RDPDR_DTYP_FILESYSTEM: + /* get preferred DOS name */ + for (j = 0; j < 8; j++) + { + g_preferred_dos_name_for_filesystem[j] = *s->p++; + } + + /* DOS names that are 8 chars long are not NULL terminated */ + g_preferred_dos_name_for_filesystem[8] = 0; + + /* LK_TODO need to check for invalid chars in DOS name */ + /* see section 2.2.1.3 of the protocol documentation */ + + /* get device data len */ + stream_rd_u32_le(s, device_data_len); + if (device_data_len) + { + stream_rd_string(g_full_name_for_filesystem, s, + device_data_len); + } + + log_debug("device_type=FILE_SYSTEM device_id=0x%x dosname=%s " + "device_data_len=%d full_name=%s", g_device_id, + g_preferred_dos_name_for_filesystem, + device_data_len, g_full_name_for_filesystem); + + dev_redir_send_server_device_announce_resp(g_device_id); + + /* create share directory in xrdp file system; */ + /* think of this as the mount point for this share */ + xfuse_create_share(g_device_id, + g_preferred_dos_name_for_filesystem); + break; + + /* we don't yet support these devices */ + case RDPDR_DTYP_SERIAL: + case RDPDR_DTYP_PARALLEL: + case RDPDR_DTYP_PRINT: + case RDPDR_DTYP_SMARTCARD: + log_debug("unsupported dev: 0x%x", device_type); + break; + } + } +} + +void dev_redir_proc_device_iocompletion(struct stream *s) +{ + FUSE_DATA *fuse_data = NULL; + IRP *irp = NULL; + + tui32 DeviceId; + tui32 CompletionId; + tui32 IoStatus; + tui32 Length; + + stream_rd_u32_le(s, DeviceId); + stream_rd_u32_le(s, CompletionId); + stream_rd_u32_le(s, IoStatus); + + /* LK_TODO need to check for IoStatus */ + + log_debug("entered: IoStatus=0x%x CompletionId=%d", + IoStatus, CompletionId); + + if ((irp = dev_redir_irp_find(CompletionId)) == NULL) + { + log_error("IRP with completion ID %d not found", CompletionId); + return; + } + + switch (irp->completion_type) + { + case CID_CREATE_DIR_REQ: + if (IoStatus != NT_STATUS_SUCCESS) + { + /* we were trying to create a request to enumerate a dir */ + /* that does not exist; let FUSE know */ + fuse_data = dev_redir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_enum_dir_done(fuse_data->data_ptr, + IoStatus); + free(fuse_data); + } + return; + } + + stream_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: + stream_rd_u32_le(s, irp->FileId); + log_debug("got CID_CREATE_OPEN_REQ IoStatus=0x%x FileId=%d", + IoStatus, irp->FileId); + fuse_data = dev_redir_fuse_data_dequeue(irp); + xfuse_devredir_cb_open_file(fuse_data->data_ptr, + DeviceId, irp->FileId); + break; + + case CID_READ: + stream_rd_u32_le(s, Length); + fuse_data = dev_redir_fuse_data_dequeue(irp); + xfuse_devredir_cb_read_file(fuse_data->data_ptr, s->p, Length); + break; + + case CID_WRITE: + stream_rd_u32_le(s, Length); + fuse_data = dev_redir_fuse_data_dequeue(irp); + xfuse_devredir_cb_write_file(fuse_data->data_ptr, s->p, Length); + break; + + case CID_CLOSE: + log_debug("got CID_CLOSE"); + dev_redir_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; + + default: + log_error("got unknown CompletionID: DeviceId=0x%x " + "CompletionId=0x%x IoStatus=0x%x", + DeviceId, CompletionId, IoStatus); + break; + } + + if (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) +{ + FUSE_DATA *fuse_data = NULL; + XRDP_INODE *xinode = NULL; + + tui32 Length; + tui32 NextEntryOffset; + tui64 CreationTime; + tui64 LastAccessTime; + tui64 LastWriteTime; + tui64 ChangeTime; + tui64 EndOfFile; + tui32 FileAttributes; + tui32 FileNameLength; + tui32 status; + + char filename[256]; + int i = 0; + + stream_rd_u32_le(s_in, Length); + + if ((IoStatus == NT_STATUS_UNSUCCESSFUL) || + (IoStatus == STATUS_NO_MORE_FILES)) + { + status = (IoStatus == STATUS_NO_MORE_FILES) ? 0 : IoStatus; + fuse_data = dev_redir_fuse_data_dequeue(irp); + xfuse_devredir_cb_enum_dir_done(fuse_data->data_ptr, status); + irp->completion_type = CID_CLOSE; + dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + DeviceId, + irp->FileId, + irp->completion_id, + IRP_MJ_CLOSE, 0, 32); + free(fuse_data); + return; + } + + /* TODO check status for errors */ + + /* process FILE_DIRECTORY_INFORMATION structures */ + while (i < Length) + { + log_debug("processing FILE_DIRECTORY_INFORMATION structs"); + + stream_rd_u32_le(s_in, NextEntryOffset); + stream_seek(s_in, 4); /* FileIndex */ + stream_rd_u64_le(s_in, CreationTime); + stream_rd_u64_le(s_in, LastAccessTime); + stream_rd_u64_le(s_in, LastWriteTime); + stream_rd_u64_le(s_in, ChangeTime); + stream_rd_u64_le(s_in, EndOfFile); + stream_seek(s_in, 8); /* AllocationSize */ + stream_rd_u32_le(s_in, FileAttributes); + stream_rd_u32_le(s_in, FileNameLength); + + devredir_cvt_from_unicode_len(filename, s_in->p, FileNameLength); + + i += 64 + FileNameLength; + + log_debug("NextEntryOffset: 0x%x", NextEntryOffset); + log_debug("CreationTime: 0x%llx", CreationTime); + log_debug("LastAccessTime: 0x%llx", LastAccessTime); + log_debug("LastWriteTime: 0x%llx", LastWriteTime); + log_debug("ChangeTime: 0x%llx", ChangeTime); + log_debug("EndOfFile: %lld", EndOfFile); + log_debug("FileAttributes: 0x%x", FileAttributes); + log_debug("FileNameLength: 0x%x", FileNameLength); + log_debug("FileName: %s", filename); + + if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL) + { + log_error("system out of memory"); + fuse_data = dev_redir_fuse_data_peek(irp); + xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, NULL); + return; + } + + strcpy(xinode->name, filename); + xinode->size = (size_t) EndOfFile; + xinode->mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes); + xinode->atime = WINDOWS_TO_LINUX_TIME(LastAccessTime); + xinode->mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); + xinode->ctime = WINDOWS_TO_LINUX_TIME(CreationTime); + + /* add this entry to xrdp file system */ + fuse_data = dev_redir_fuse_data_peek(irp); + xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, xinode); + } + + dev_redir_send_drive_dir_request(irp, DeviceId, 0, NULL); +} + +/** + * FUSE calls this function whenever it wants us to enumerate a dir + * + * @param fusep opaque data struct that we just pass back to FUSE when done + * @param device_id device_id of the redirected share + * @param path the dir path to enumerate + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int dev_redir_get_dir_listing(void *fusep, tui32 device_id, char *path) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval; + IRP *irp; + + if ((irp = dev_redir_irp_new()) == NULL) + return -1; + + irp->completion_id = g_completion_id++; + irp->completion_type = CID_CREATE_DIR_REQ; + irp->device_id = device_id; + strcpy(irp->pathname, path); + dev_redir_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, path, + DesiredAccess, CreateOptions, + CreateDisposition, + irp->completion_id); + + log_debug("looking for device_id=%d path=%s", device_id, path); + + /* when we get a respone 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, "\\*"); + + return rval; +} + +int dev_redir_file_open(void *fusep, tui32 device_id, char *path, + int mode, int type) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval; + IRP *irp; + + log_debug("device_id=%d path=%s mode=0x%x", device_id, path, mode); + log_debug("O_RDONLY=0x%x O_RDWR=0x%x O_CREAT=0x%x", O_RDONLY, O_RDWR, O_CREAT); + + if ((irp = dev_redir_irp_new()) == NULL) + return -1; + + irp->completion_id = g_completion_id++; + irp->completion_type = CID_CREATE_OPEN_REQ; + irp->device_id = device_id; + strcpy(irp->pathname, path); + dev_redir_fuse_data_enqueue(irp, fusep); + + if (mode & O_CREAT) + { + log_debug("LK_TODO: open file in O_CREAT"); + DesiredAccess = DA_FILE_READ_DATA | DA_FILE_WRITE_DATA | DA_SYNCHRONIZE; + + if (type == S_IFDIR) + { + log_debug("creating dir"); + CreateOptions = CO_FILE_DIRECTORY_FILE | CO_FILE_SYNCHRONOUS_IO_NONALERT; + } + else + { + log_debug("creating file"); + CreateOptions = CO_FILE_SYNCHRONOUS_IO_NONALERT; + } + + CreateDisposition = CD_FILE_CREATE; + } + else //if (mode & O_RDWR) + { + log_debug("LK_TODO: open file in O_RDWR"); + DesiredAccess = DA_FILE_READ_DATA | DA_FILE_WRITE_DATA | DA_SYNCHRONIZE; + CreateOptions = CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateDisposition = CD_FILE_OPEN; // WAS 1 + } +#if 0 + else + { + log_debug("LK_TODO: open file in O_RDONLY"); + DesiredAccess = DA_FILE_READ_DATA | DA_SYNCHRONIZE; + CreateOptions = CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateDisposition = CD_FILE_OPEN; + } +#endif + rval = dev_redir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + CreateDisposition, + irp->completion_id); + + return rval; +} + +/** + * Read data from previously opened file + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int dev_redir_file_read(void *fusep, tui32 DeviceId, tui32 FileId, + tui32 Length, tui64 Offset) +{ + struct stream *s; + IRP *irp; + int bytes; + + stream_new(s, 1024); + + if ((irp = dev_redir_irp_find_by_fileid(FileId)) == NULL) + { + log_error("no IRP found with FileId = %d", FileId); + xfuse_devredir_cb_read_file(fusep, NULL, 0); + return -1; + } + + irp->completion_type = CID_READ; + dev_redir_fuse_data_enqueue(irp, fusep); + dev_redir_insert_dev_io_req_header(s, + DeviceId, + FileId, + irp->completion_id, + IRP_MJ_READ, + 0); + + stream_wr_u32_le(s, Length); + stream_wr_u64_le(s, Offset); + stream_seek(s, 20); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + stream_free(s); + + return 0; +} + +int dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId, + const char *buf, tui32 Length, tui64 Offset) +{ + struct stream *s; + IRP *irp; + int bytes; + + stream_new(s, 1024 + Length); + + if ((irp = dev_redir_irp_find_by_fileid(FileId)) == NULL) + { + log_error("no IRP found with FileId = %d", FileId); + xfuse_devredir_cb_write_file(fusep, NULL, 0); + return -1; + } + + irp->completion_type = CID_WRITE; + dev_redir_fuse_data_enqueue(irp, fusep); + dev_redir_insert_dev_io_req_header(s, + DeviceId, + FileId, + irp->completion_id, + IRP_MJ_WRITE, + 0); + + stream_wr_u32_le(s, Length); + stream_wr_u64_le(s, Offset); + stream_seek(s, 20); /* padding */ + + /* now insert real data */ + stream_copyin(s, buf, Length); + + /* send to client */ + bytes = stream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + stream_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 + *****************************************************************************/ + +void *dev_redir_fuse_data_peek(IRP *irp) +{ + return irp->fd_head; +} + +/** + * Return oldest FUSE_DATA from queue + * + * @return FUSE_DATA on success, NULL on failure + *****************************************************************************/ + +void *dev_redir_fuse_data_dequeue(IRP *irp) +{ + FUSE_DATA *head; + + if ((irp == NULL) || (irp->fd_head == NULL)) + return NULL; + + if (irp->fd_head->next == NULL) + { + /* only one element in queue */ + head = irp->fd_head; + irp->fd_head = NULL; + irp->fd_tail = NULL; + return head; + } + + /* more than one element in queue */ + head = irp->fd_head; + irp->fd_head = head->next; + + return head; +} + +/** + * Insert specified FUSE_DATA at the end of our queue + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int dev_redir_fuse_data_enqueue(IRP *irp, void *vp) +{ + FUSE_DATA *fd; + FUSE_DATA *tail; + + if (irp == NULL) + return -1; + + if ((fd = calloc(1, sizeof(FUSE_DATA))) == NULL) + return -1; + + fd->data_ptr = vp; + fd->next = NULL; + + if (irp->fd_tail == NULL) + { + /* queue is empty, insert at head */ + irp->fd_head = fd; + irp->fd_tail = fd; + return 0; + } + + /* queue is not empty, insert at tail end */ + tail = irp->fd_tail; + tail->next = fd; + irp->fd_tail = fd; + return 0; +} + +/****************************************************************************** +** IRP stuff ** +******************************************************************************/ + +/** + * Create a new IRP and append to linked list + * + * @return new IRP or NULL on error + *****************************************************************************/ + +IRP * dev_redir_irp_new() +{ + IRP *irp; + IRP *irp_last; + + /* create new IRP */ + if ((irp = calloc(1, sizeof(IRP))) == NULL) + { + log_error("system out of memory!"); + return NULL; + } + + /* insert at end of linked list */ + if ((irp_last = dev_redir_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; + } + + return irp; +} + +/** + * Delete specified IRP from linked list + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int dev_redir_irp_delete(IRP *irp) +{ + IRP *lirp = g_irp_head; + + if ((irp == NULL) || (lirp == NULL)) + return -1; + + dev_redir_irp_dump(); // LK_TODO + + while (lirp) + { + if (lirp == irp) + break; + + lirp = lirp->next; + } + + if (lirp == NULL) + return -1; /* did not find specified irp */ + + if (lirp->prev == NULL) + { + /* we are at head of linked list */ + if (lirp->next == NULL) + { + /* only one element in list */ + free(lirp); + g_irp_head = NULL; + dev_redir_irp_dump(); // LK_TODO + return 0; + } + + lirp->next->prev = NULL; + g_irp_head = lirp->next; + free(lirp); + } + else if (lirp->next == NULL) + { + /* we are at tail of linked list */ + lirp->prev->next = NULL; + free(lirp); + } + else + { + /* we are in between */ + lirp->prev->next = lirp->next; + lirp->next->prev = lirp->prev; + free(lirp); + } + + dev_redir_irp_dump(); // LK_TODO + + return 0; +} + +/** + * Return IRP containing specified completion_id + *****************************************************************************/ + +IRP *dev_redir_irp_find(tui32 completion_id) +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->completion_id == completion_id) + return irp; + + irp = irp->next; + } + + return NULL; +} + +IRP * dev_redir_irp_find_by_fileid(tui32 FileId) +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->FileId == FileId) + return irp; + + irp = irp->next; + } + + return NULL; +} + +/** + * Return last IRP in linked list + *****************************************************************************/ + +IRP * dev_redir_irp_get_last() +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->next == NULL) + break; + + irp = irp->next; + } + + return irp; +} + +void dev_redir_irp_dump() +{ + IRP *irp = g_irp_head; + + log_debug("------- dumping IRPs --------"); + while (irp) + { + log_debug(" completion_id=%d\tcompletion_type=%d\tFileId=%d", + irp->completion_id, irp->completion_type, irp->FileId); + + irp = irp->next; + } + log_debug("------- dumping IRPs done ---"); +} + +/****************************************************************************** +** miscellaneous stuff ** +******************************************************************************/ + +void dev_redir_insert_dev_io_req_header(struct stream *s, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + tui32 MajorFunction, + tui32 MinorFunction) +{ + /* setup DR_DEVICE_IOREQUEST header */ + stream_wr_u16_le(s, RDPDR_CTYP_CORE); + stream_wr_u16_le(s, PAKID_CORE_DEVICE_IOREQUEST); + stream_wr_u32_le(s, DeviceId); + stream_wr_u32_le(s, FileId); + stream_wr_u32_le(s, CompletionId); + stream_wr_u32_le(s, MajorFunction); + stream_wr_u32_le(s, MinorFunction); +} + +void devredir_cvt_to_unicode(char *unicode, char *path) +{ + int len = strlen(path); + int i; + int j = 0; + + for (i = 0; i < len; i++) + { + unicode[j++] = path[i]; + unicode[j++] = 0x00; + } + unicode[j++] = 0x00; + unicode[j++] = 0x00; +} + +void devredir_cvt_from_unicode_len(char *path, char *unicode, int len) +{ + int i; + int j; + + for (i = 0, j = 0; i < len; i += 2) + { + path[j++] = unicode[i]; + } + path[j] = 0; +} + +int dev_redir_string_ends_with(char *string, char c) +{ + int len; + + len = strlen(string); + return (string[len - 1] == c) ? 1 : 0; +} + +void dev_redir_insert_rdpdr_header(struct stream *s, tui16 Component, + tui16 PacketId) +{ + stream_wr_u16_le(s, Component); + stream_wr_u16_le(s, PacketId); +} diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 3ec08a51..84ab3a86 100644 --- a/sesman/chansrv/devredir.h +++ b/sesman/chansrv/devredir.h @@ -1,7 +1,9 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * xrdp device redirection - we mainly use it for drive redirection + * + * Copyright (C) Laxmikant Rashinkar 2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,19 +21,372 @@ #if !defined(DEVREDIR_H) #define DEVREDIR_H +#include +#include +#include +#include +#include +#include + #include "arch.h" #include "parse.h" +#include "os_calls.h" +#include "log.h" +#include "chansrv_fuse.h" + +typedef struct fuse_data FUSE_DATA; +struct fuse_data +{ + void *data_ptr; + FUSE_DATA *next; +}; + +/* An I/O Resource Packet to track dev_dir I/O calls */ + +typedef struct irp IRP; + +struct irp +{ + tui32 completion_id; /* unique number */ + char completion_type; /* describes I/O type */ + tui32 FileId; /* RDP client provided unique number */ + //void *fusep; /* opaque FUSE info */ + char pathname[256]; /* absolute pathname */ + tui32 device_id; /* identifies remote device */ + FUSE_DATA *fd_head; /* point to first FUSE opaque object */ + FUSE_DATA *fd_tail; /* point to last FUSE opaque object */ + IRP *next; /* point to next IRP */ + IRP *prev; /* point to previous IRP */ +}; + +void *dev_redir_fuse_data_peek(IRP *irp); +void *dev_redir_fuse_data_dequeue(IRP *irp); +int dev_redir_fuse_data_enqueue(IRP *irp, void *vp); + +IRP * dev_redir_irp_new(); +IRP * dev_redir_irp_find(tui32 completion_id); +IRP * dev_redir_irp_find_by_fileid(tui32 FileId); +IRP * dev_redir_irp_get_last(); +int dev_redir_irp_delete(IRP *irp); +void dev_redir_irp_dump(); + +int APP_CC dev_redir_init(void); +int APP_CC dev_redir_deinit(void); + +int APP_CC dev_redir_data_in(struct stream* s, int chan_id, int chan_flags, + int length, int total_length); + +int APP_CC dev_redir_get_wait_objs(tbus* objs, int* count, int* timeout); +int APP_CC dev_redir_check_wait_objs(void); + +void dev_redir_send_server_core_cap_req(); +void dev_redir_send_server_clientID_confirm(); +void dev_redir_send_server_user_logged_on(); +void dev_redir_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, 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 dev_redir_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); + +/* misc stuff */ +void dev_redir_insert_dev_io_req_header(struct stream *s, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + tui32 MajorFunction, + tui32 MinorFunction); + +void devredir_cvt_to_unicode(char *unicode, char *path); +void devredir_cvt_from_unicode_len(char *path, char *unicode, int len); +int dev_redir_string_ends_with(char *string, char c); + +void dev_redir_insert_rdpdr_header(struct stream *s, tui16 Component, + tui16 PacketId); + +/* called from FUSE module */ +int dev_redir_get_dir_listing(void *fusep, tui32 device_id, char *path); + +int dev_redir_file_open(void *fusep, tui32 device_id, char *path, + int mode, int type); + +int dev_redir_file_read(void *fusep, tui32 device_id, tui32 FileId, + tui32 Length, tui64 Offset); + +/* 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]: DEV_REDIR %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ +} + +#define log_info(_params...) \ +{ \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: DEV_REDIR %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +#define log_debug(_params...) \ +{ \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: DEV_REDIR %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +int send_channel_data(int chan_id, char *data, int size); + +/* + * 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 + */ + +#define CO_FILE_DIRECTORY_FILE 0x00000001 +#define CO_FILE_WRITE_THROUGH 0x00000002 +#define CO_FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 + +/* + * 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) + */ + +#define NT_STATUS_SUCCESS 0x00000000 +#define NT_STATUS_UNSUCCESSFUL 0xC0000001 + +/* + * CompletionID types, used in IRPs to indicate I/O operation + */ + +enum +{ + CID_CREATE_DIR_REQ = 1, + CID_CREATE_OPEN_REQ, + CID_READ, + CID_WRITE, + CID_DIRECTORY_CONTROL, + CID_CLOSE +}; + +#if 0 +#define CID_CLOSE 0x0002 +#define CID_READ 0x0003 +#define CID_WRITE 0x0004 +#define CID_DEVICE_CONTROL 0x0005 +#define CID_QUERY_VOLUME_INFORMATION 0x0006 +#define CID_SET_VOLUME_INFORMATION 0x0007 +#define CID_QUERY_INFORMATION 0x0008 +#define CID_SET_INFORMATION 0x0009 +#define CID_DIRECTORY_CONTROL 0x000a +#define CID_LOCK_CONTROL 0x000b +#endif + +/* + * 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) -int APP_CC -dev_redir_init(void); -int APP_CC -dev_redir_deinit(void); -int APP_CC -dev_redir_data_in(struct stream* s, int chan_id, int chan_flags, int length, - int total_length); -int APP_CC -dev_redir_get_wait_objs(tbus* objs, int* count, int* timeout); -int APP_CC -dev_redir_check_wait_objs(void); +/* winodws 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; #endif diff --git a/xrdp/xrdp.ini b/xrdp/xrdp.ini index 498525bc..1c6d38f2 100644 --- a/xrdp/xrdp.ini +++ b/xrdp/xrdp.ini @@ -49,6 +49,12 @@ cliprdr=true rail=true xrdpvr=true +# for debugging xrdp, in section [xrdp1], change port=-1 to this: +# port=/tmp/.xrdp/xrdp_display_10 + +# for debugging xrdp, add following line to section [xrdp1] +# chansrvport=/tmp/.xrdp/xrdp_chansrv_socket_7210 + [xrdp1] name=sesman-X11rdp lib=libxup.so