diff options
Diffstat (limited to 'sesman/chansrv')
-rw-r--r-- | sesman/chansrv/Makefile.am | 4 | ||||
-rw-r--r-- | sesman/chansrv/chansrv.c | 19 | ||||
-rw-r--r-- | sesman/chansrv/chansrv_fuse.c | 3257 | ||||
-rw-r--r-- | sesman/chansrv/chansrv_fuse.h | 80 | ||||
-rw-r--r-- | sesman/chansrv/clipboard.c | 8 | ||||
-rw-r--r-- | sesman/chansrv/clipboard_file.c | 28 | ||||
-rw-r--r-- | sesman/chansrv/devredir.c | 1510 | ||||
-rw-r--r-- | sesman/chansrv/devredir.h | 332 | ||||
-rw-r--r-- | sesman/chansrv/irp.c | 241 | ||||
-rw-r--r-- | sesman/chansrv/irp.h | 64 | ||||
-rw-r--r-- | sesman/chansrv/pulse/Makefile | 18 | ||||
-rw-r--r-- | sesman/chansrv/pulse/module-xrdp-sink-symdef.h | 29 | ||||
-rw-r--r-- | sesman/chansrv/pulse/module-xrdp-sink.c | 581 | ||||
-rw-r--r-- | sesman/chansrv/pulse/pulse-notes.txt | 72 | ||||
-rw-r--r-- | sesman/chansrv/smartcard.c | 528 | ||||
-rw-r--r-- | sesman/chansrv/smartcard.h | 42 | ||||
-rw-r--r-- | sesman/chansrv/sound.c | 321 | ||||
-rw-r--r-- | sesman/chansrv/sound.h | 14 | ||||
-rw-r--r-- | sesman/chansrv/wave-format-server.txt | 466 |
19 files changed, 7020 insertions, 594 deletions
diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am index 39d808c9..89753430 100644 --- a/sesman/chansrv/Makefile.am +++ b/sesman/chansrv/Makefile.am @@ -45,10 +45,12 @@ xrdp_chansrv_SOURCES = \ clipboard.c \ clipboard_file.c \ devredir.c \ + smartcard.c \ rail.c \ xcommon.c \ drdynvc.c \ - chansrv_fuse.c + chansrv_fuse.c \ + irp.c xrdp_chansrv_LDFLAGS = \ $(EXTRA_FLAGS) diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 9d55388e..0f7ff042 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * Copyright (C) Jay Sorg 2009-2013 * Copyright (C) Laxmikant Rashinkar 2009-2012 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,6 +45,7 @@ static int g_rdpsnd_index = -1; static int g_rdpdr_index = -1; static int g_rail_index = -1; static int g_drdynvc_index = -1; +static int g_sent = 0; /* if sent data to xrdp, waiting response */ /* state info for dynamic virtual channels */ static struct xrdp_api_data *g_dvc_channels[MAX_DVC_CHANNELS]; @@ -149,6 +150,7 @@ send_data_from_chan_item(struct chan_item *chan_item) s_mark_end(s); LOGM((LOG_LEVEL_DEBUG, "chansrv::send_channel_data: -- " "size %d chan_flags 0x%8.8x", size, chan_flags)); + g_sent = 1; error = trans_force_write(g_con_trans); if (error != 0) @@ -214,7 +216,10 @@ send_channel_data(int chan_id, char *data, int size) if (g_chan_items[index].id == chan_id) { add_data_to_chan_item(g_chan_items + index, data, size); - check_chan_items(); + if (g_sent == 0) + { + check_chan_items(); + } return 0; } } @@ -376,7 +381,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 +392,7 @@ process_message_channel_setup(struct stream *s) if (g_rdpdr_index >= 0) { dev_redir_init(); + xfuse_init(); } if (g_rail_index >= 0) @@ -477,6 +483,7 @@ static int APP_CC process_message_channel_data_response(struct stream *s) { LOG(10, ("process_message_channel_data_response:")); + g_sent = 0; check_chan_items(); return 0; } @@ -953,7 +960,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 +972,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..70dd12af 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 LK.Rashinkar@gmail.com * * 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,74 @@ * limitations under the License. */ -#ifdef XRDP_FUSE +/* + * TODO + * o when creating dir/file, ensure it does not already exist + * o do not allow dirs to be created in ino==1 except for .clipbard and share mounts + * o fix the HACK where I have to use my own buf instead of g_buffer + * this is in func xfuse_check_wait_objs() + * o if fuse mount point is already mounted, I get segfault + * o in open, check for modes such as O_TRUNC, O_APPEND + * o copying over an existing file does not work + * o after a dir is created, the device cannot be unmounted on the client side + * so something is holding it up + * o in thunar, when I move a file by dragging to another folder, the file + * is getting copied instead of being moved + * o unable to edit files in vi + * o fuse ops to support + * o touch does not work + * o chmod must work + * o cat >> file is not working + * + */ + +//#define USE_SYNC_FLAG + +/* FUSE mount point */ +char g_fuse_root_path[256] = ""; +char g_fuse_clipboard_path[256] = ""; /* for clipboard use */ + +#ifndef XRDP_FUSE + +/****************************************************************************** +** ** +** when FUSE is NOT enabled in xrdp ** +** ** +******************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "arch.h" +#include "chansrv_fuse.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) {} +int xfuse_create_share(tui32 device_id, char *dirname) {} +void xfuse_devredir_cb_open_file(void *vp, tui32 DeviceId, tui32 FileId) {} +void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length) {} +void xfuse_devredir_cb_read_file(void *vp, char *buf, size_t length) {} +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_rmdir_or_file(void *vp, tui32 IoStatus) {} +void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) {} +void xfuse_devredir_cb_file_close(void *vp) {} + +#else + +/****************************************************************************** +** ** +** when FUSE is enabled in xrdp ** +** ** +******************************************************************************/ #define FUSE_USE_VERSION 26 #define _FILE_OFFSET_BITS 64 @@ -24,692 +91,2940 @@ #include <fuse/fuse_lowlevel.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <errno.h> -#include <fcntl.h> +#include <time.h> #include <unistd.h> +#include <string.h> +#include <pthread.h> +#include <sched.h> + #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) +#include "list.h" -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; +#define XFUSE_ATTR_TIMEOUT 1.0 +#define XFUSE_ENTRY_TIMEOUT 1.0 -/* used for file data request sent to client */ -struct req_list_item +#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); \ + } \ +} + +#define OP_RENAME_FILE 0x01 + +/* the xrdp file system in memory */ +struct xrdp_fs { - fuse_req_t req; - int stream_id; - int lindex; - int off; - int size; + 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; }; -struct xfuse_file_info +struct dirbuf1 { - int ino; + char buf[4096]; + int bytes_in_buf; + int first_time; +}; + +/* 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; + fuse_ino_t new_inode; + int invoke_fuse; + char name[1024]; + char new_name[1024]; + tui32 device_id; + int reply_type; + int mode; + int type; + size_t size; + off_t off; + struct dirbuf1 dirbuf1; +}; +typedef struct xfuse_info XFUSE_INFO; + +struct xfuse_handle +{ + tui32 DeviceId; + tui32 FileId; + int is_loc_resource; /* this is not a redirected resource */ +}; +typedef struct xfuse_handle XFUSE_HANDLE; + +/* used for file data request sent to client */ +struct req_list_item +{ + fuse_req_t req; + int stream_id; int lindex; - char pathname[256]; - char filename[256]; - int flags; + int off; int size; - tui64 time; - struct xfuse_file_info* child; - struct xfuse_file_info* parent; - struct xfuse_file_info* next; - struct xfuse_file_info* prev; }; -static struct xfuse_file_info *g_fuse_files = 0; -static struct fuse_lowlevel_ops g_xrdp_ll_oper; -static int g_ino = 2; +static struct list *g_req_list = 0; +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(struct fuse_args *args); +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 struct xfuse_file_info *APP_CC -fuse_find_file_info_by_name(struct xfuse_file_info *ffi, const char *filename) +static int xfuse_does_file_exist(int parent, char *name); +static int xfuse_delete_file(int parent, char *name); +static int xfuse_delete_file_with_xinode(XRDP_INODE *xinode); +static int xfuse_delete_dir_with_xinode(XRDP_INODE *xinode); +static void xfuse_update_xrdpfs_size(); +static void xfuse_enum_dir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi); + +/* forward declarations for calls we make into devredir */ +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, char *gen_buf); + +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); + +int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId); + +/* 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 int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *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_cb_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name); + +static void xfuse_cb_rename(fuse_req_t req, + fuse_ino_t old_parent, const char *old_name, + fuse_ino_t new_parent, const char *new_name); + +/* this is not a callback, but it is used by the above two functions */ +static void xfuse_remove_dir_or_file(fuse_req_t req, fuse_ino_t parent, + const char *name, int type); + +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_release(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 void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi); + +static void xfuse_cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi); + +/* clipboard calls */ +int clipboard_request_file_data(int stream_id, int lindex, int offset, + int request_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); + +/***************************************************************************** +** ** +** 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; + struct fuse_args args = FUSE_ARGS_INIT(0, NULL); - 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")); + g_snprintf(g_fuse_clipboard_path, 255, "%s/.clipboard", g_fuse_root_path); + + /* 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.unlink = xfuse_cb_unlink; + g_xfuse_ops.rename = xfuse_cb_rename; + g_xfuse_ops.open = xfuse_cb_open; + g_xfuse_ops.release = xfuse_cb_release; + g_xfuse_ops.read = xfuse_cb_read; + g_xfuse_ops.write = xfuse_cb_write; + g_xfuse_ops.create = xfuse_cb_create; + //g_xfuse_ops.fsync = xfuse_cb_fsync; /* LK_TODO delete this */ + g_xfuse_ops.getattr = xfuse_cb_getattr; + g_xfuse_ops.setattr = xfuse_cb_setattr; + + fuse_opt_add_arg(&args, "xrdp-chansrv"); + fuse_opt_add_arg(&args, g_fuse_root_path); + + if (xfuse_init_lib(&args)) + { + 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) + 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; + } + + if (g_req_list != 0) + { + list_delete(g_req_list); + g_req_list = 0; + } + + g_xfuse_inited = 0; + return 0; +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ +int xfuse_check_wait_objs(void) +{ + struct fuse_chan *tmpch; + int rval; + +#define HACK + +#ifdef HACK +char buf[135168]; +#endif + + if (g_ch == 0) + return 0; + + if (g_tcp_select(g_fd, 0) & 1) + { + 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 + } + + 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) +{ +#if 0 + XFUSE_INFO *fip; +#endif + 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; + } + + /* 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++; + +#if 0 + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) + { + log_error("system out of memory"); + return -1; + } + + /* enumerate root dir, do not call FUSE when done */ + fip->req = NULL; + fip->inode = 1; /* TODO saved_inode; */ + strncpy(fip->name, dirname, 1024); + fip->name[1023] = 0; + fip->device_id = device_id; + + dev_redir_get_dir_listing((void *) fip, device_id, "\\"); +#endif + + return 0; +} + +/** + * Clear all clipboard entries in xrdp_fs + * + * This function is called by clipboard code + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_clear_clip_dir(void) +{ + int i; + XRDP_INODE *xinode; + XRDP_INODE *xip; + + log_debug("entered"); + + /* xinode for .clipboard */ + xip = g_xrdp_fs.inode_table[2]; + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) + { + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + if (xinode->parent_inode == 2) { - rv1 = fuse_find_file_info_by_ino(rv->child, ino); - if (rv1 != 0) - { - return rv1; - } + g_xrdp_fs.inode_table[i] = NULL; + free(xinode); + xip->nentries--; } - rv = rv->next; } + return 0; } -/*****************************************************************************/ -static int APP_CC -xrdp_ffi2stat(struct xfuse_file_info *ffi, struct stat *stbuf) +/** + * Return clipboard data to fuse + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_file_contents_range(int stream_id, char *data, int data_bytes) { - stbuf->st_ino = ffi->ino; - if (ffi->flags & 1) + log_debug("entered: stream_id=%d data_bytes=%d", stream_id, data_bytes); + + struct req_list_item *rli; + + if ((rli = (struct req_list_item *) list_get_item(g_req_list, 0)) == NULL) { - 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; + log_error("range error!"); + return -1; } - else + + log_debug("lindex=%d off=%d size=%d", rli->lindex, rli->off, rli->size); + + fuse_reply_buf(rli->req, data, data_bytes); + + list_remove_item(g_req_list, 0); + if (g_req_list->count <= 0) { - 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_debug("completed all requests"); + return 0; } + + /* send next request */ + rli = (struct req_list_item *) list_get_item(g_req_list, 0); + if (rli == NULL) + { + log_error("range error!"); + return -1; + } + + log_debug("requesting clipboard file data"); + + clipboard_request_file_data(rli->stream_id, rli->lindex, + rli->off, rli->size); + return 0; } -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +/** + * Create a file in .clipboard dir + * + * This function is called by clipboard code + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) +{ + log_debug("entered: filename=%s flags=%d size=%d lindex=%d", + filename, flags, size, lindex); + + /* add entry to xrdp_fs */ + XRDP_INODE *xinode = xfuse_create_file_in_xrdp_fs(0, /* device id */ + 2, /* parent inode */ + filename, + S_IFREG); + xinode->size = size; + xinode->lindex = lindex; + xinode->is_loc_resource = 1; + + return 0; +} + +/** + * + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int xfuse_file_contents_size(int stream_id, int file_size) +{ + log_debug("entered: stream_id=%d file_size=%d", stream_id, file_size); + return 0; +} + +/***************************************************************************** +** ** +** 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(struct fuse_args *args) { - struct xfuse_file_info *ffi; - struct fuse_entry_param e; + if (fuse_parse_cmdline(args, &g_mount_point, 0, 0) < 0) + { + log_error("fuse_parse_cmdline() failed"); + fuse_opt_free_args(args); + return -1; + } - LLOGLN(10, ("xrdp_ll_lookup: name %s", name)); - if (parent != 1) + if ((g_ch = fuse_mount(g_mount_point, args)) == 0) { - fuse_reply_err(req, ENOENT); + log_error("fuse_mount() failed"); + fuse_opt_free_args(args); + return -1; } - else + + g_se = fuse_lowlevel_new(args, &g_xfuse_ops, sizeof(g_xfuse_ops), 0); + if (g_se == 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_lowlevel_new() failed"); + fuse_unmount(g_mount_point, g_ch); + g_ch = 0; + fuse_opt_free_args(args); + return -1; } - fuse_reply_err(req, ENOENT); + + 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); + + g_req_list = list_create(); + g_req_list->auto_free = 1; + + 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; - LLOGLN(10, ("xrdp_ll_getattr: ino %d", ino)); - g_memset(&stbuf, 0, sizeof(stbuf)); - if (ino == 1) + g_xrdp_fs.inode_table = calloc(4096, sizeof(struct xrdp_inode *)); + if (g_xrdp_fs.inode_table == NULL) { - 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); - return; + log_error("system out of memory"); + return -1; } - ffi = fuse_find_file_info_by_ino(g_fuse_files, ino); - if (ffi == 0) + + /* + * index 0 is our .. dir + */ + + if ((xino = calloc(1, sizeof(struct xrdp_inode))) == NULL) { - LLOGLN(0, ("xrdp_ll_getattr: fuse_find_file_info_by_ino failed ino %d", ino)); - fuse_reply_err(req, ENOENT); + log_error("system out of memory"); + free(g_xrdp_fs.inode_table); + return -1; } - else if (xrdp_ffi2stat(ffi, &stbuf) == -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) { - fuse_reply_err(req, ENOENT); + log_error("system out of memory"); + free(g_xrdp_fs.inode_table[0]); + free(g_xrdp_fs.inode_table); + return -1; } - else + 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) { - fuse_reply_attr(req, &stbuf, 1.0); + log_error("system out of memory"); + free(g_xrdp_fs.inode_table[0]); + free(g_xrdp_fs.inode_table[1]); + free(g_xrdp_fs.inode_table); + return -1; } + + g_xrdp_fs.inode_table[2] = xino; + xino->parent_inode = 1; + xino->inode = 2; + xino->nentries = 0; + xino->mode = S_IFDIR | 0755; + xino->uid = getuid(); + xino->gid = getgid(); + xino->size = 0; + xino->atime = time(0); + xino->mtime = time(0); + xino->ctime = time(0); + xino->is_loc_resource = 1; + strcpy(xino->name, ".clipboard"); + + g_xrdp_fs.max_entries = 4096; + g_xrdp_fs.num_entries = 3; + g_xrdp_fs.next_node = 3; + + return 0; } -/*****************************************************************************/ -static void APP_CC -dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name, fuse_ino_t ino) +/** + * zap the xrdp file system + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +static int xfuse_deinit_xrdp_fs() { - 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)); - stbuf.st_ino = ino; - fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, - &stbuf, b->size); + 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; } -#define lmin(x, y) ((x) < (y) ? (x) : (y)) +/** + * @brief Create a directory or regular file. + *****************************************************************************/ -/*****************************************************************************/ -static int APP_CC -reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, - off_t off, size_t maxsize) +// 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) { - LLOGLN(10, ("reply_buf_limited: %d", maxsize)); - if (off < bufsize) + 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)) { - return fuse_reply_buf(req, buf + off, - lmin(bufsize - off, maxsize)); + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); } - else + + if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL) { - return fuse_reply_buf(req, 0, 0); + 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; + xfuse_update_xrdpfs_size(); + 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 DEFAULT_CC -xrdp_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t off, struct fuse_file_info *fi) +static void xfuse_dump_fs() { - struct xfuse_file_info *ffi; - struct dirbuf b; + int i; + struct xrdp_inode *xinode; - LLOGLN(10, ("xrdp_ll_readdir: ino %d", ino)); - if (ino != 1) - { - fuse_reply_err(req, ENOTDIR); - } - else + log_debug("found %d entries", g_xrdp_fs.num_entries - FIRST_INODE); + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - 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); + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + log_debug("pinode=%d inode=%d nentries=%d nopen=%d is_synced=%d name=%s", + (int) xinode->parent_inode, (int) xinode->inode, + xinode->nentries, xinode->nopen, xinode->is_synced, + 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(""); } -/*****************************************************************************/ -static void DEFAULT_CC -xrdp_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +/** + * 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) { - LLOGLN(10, ("xrdp_ll_open: ino %d", (int)ino)); + tui32 parent_inode = 0; + tui32 child_inode = ino; + char reverse_path[4096]; + + /* ino == 1 is a special case; we already know that it is not */ + /* associated with any device redirection */ if (ino == 1) { - fuse_reply_err(req, EISDIR); + /* just return the device_id for the file in full_path */ + log_debug("looking for file with pinode=%d name=%s", ino, full_path); + xfuse_dump_fs(); + + XRDP_INODE *xinode = xfuse_get_inode_from_pinode_name(ino, full_path); + full_path[0] = 0; + if (xinode) + return xinode->device_id; + else + return 0; } - else if ((fi->flags & 3) != O_RDONLY) + + reverse_path[0] = 0; + full_path[0] = 0; + + while (1) { - fuse_reply_err(req, EACCES); + 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; } - else + + 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) { - fuse_reply_open(req, fi); + strcat(full_path, cptr + 1); + strcat(full_path, "/"); + cptr[0] = 0; } + strcat(full_path, reverse_path); } -/*****************************************************************************/ -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) +/** + * 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) { - char *data; - int stream_id; - struct xfuse_file_info *ffi; - struct req_list_item *rli; + int i; + struct xrdp_inode * xinode; - 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) + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - /* 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); - } - 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); + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + /* match parent inode */ + if (xinode->parent_inode != pinode) + continue; + + /* match name */ + if (strcmp(xinode->name, name) != 0) + continue; + + return xinode; + } + return NULL; } -/*****************************************************************************/ -/* returns error */ -static int APP_CC -fuse_init_lib(int argc, char **argv) +/** + * 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) { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - int error; + XRDP_INODE *xinode; + XRDP_INODE *xinodep; - error = fuse_parse_cmdline(&args, &g_mountpoint, 0, 0); - if (error == -1) + if ((name == NULL) || (strlen(name) == 0)) + return NULL; + + if ((xinode = calloc(1, sizeof(XRDP_INODE))) == NULL) { - LLOGLN(0, ("fuse_init_lib: fuse_parse_cmdline failed")); - fuse_opt_free_args(&args); - return 1; + log_error("system out of memory"); + return NULL; } - g_ch = fuse_mount(g_mountpoint, &args); - if (g_ch == 0) + + xinode->parent_inode = pinode; + xinode->inode = g_xrdp_fs.next_node++; + 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) { - LLOGLN(0, ("fuse_init_lib: fuse_mount failed")); - fuse_opt_free_args(&args); - return 1; + xinode->mode = 0755 | type; + xinode->size = 4096; } - g_se = fuse_lowlevel_new(&args, &g_xrdp_ll_oper, - sizeof(g_xrdp_ll_oper), 0); - if (g_se == 0) + else { - 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; + xinode->mode = 0644 | type; + xinode->size = 0; } - 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; + + g_xrdp_fs.inode_table[xinode->inode] = xinode; + g_xrdp_fs.num_entries++; + + /* bump up lookup count in parent dir */ + xinodep = g_xrdp_fs.inode_table[pinode]; + xinodep->nentries++; + xfuse_update_xrdpfs_size(); + + log_debug("incremented nentries; parent=%d nentries=%d", + pinode, xinodep->nentries); + + /* LK_TODO */ + xfuse_dump_fs(); + + return xinode; } -/*****************************************************************************/ -static int APP_CC -fuse_delete_dir_items(struct xfuse_file_info *ffi) +/** + * 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) { - struct xfuse_file_info *ffi1; + int i; + XRDP_INODE *xinode; - while (ffi != 0) + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - if (ffi->flags & 1) + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + if ((xinode->parent_inode == parent) && + (strcmp(xinode->name, name) == 0)) { - fuse_delete_dir_items(ffi->child); + return 1; } - ffi1 = ffi; - ffi = ffi->next; - g_free(ffi1); } + return 0; } -/*****************************************************************************/ -int APP_CC -fuse_clear_clip_dir(void) +static int xfuse_delete_file(int parent, char *name) +{ + return -1; +} + +static int xfuse_delete_file_with_xinode(XRDP_INODE *xinode) { - fuse_delete_dir_items(g_fuse_files); - g_fuse_files = 0; + /* make sure it is not a dir */ + if ((xinode == NULL) || (xinode->mode & S_IFDIR)) + return -1; + + g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--; + g_xrdp_fs.inode_table[xinode->inode] = NULL; + free(xinode); + return 0; } -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) +static int xfuse_delete_dir_with_xinode(XRDP_INODE *xinode) { - struct xfuse_file_info *ffi; - struct xfuse_file_info *ffi1; + XRDP_INODE *xip; + int i; - LLOGLN(10, ("fuse_add_clip_dir_item: adding %s ino %d", filename, g_ino)); - ffi = g_fuse_files; - if (ffi == 0) + /* make sure it is not a file */ + if ((xinode == NULL) || (xinode->mode & S_IFREG)) + return -1; + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - 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; + if ((xip = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + /* look for child inodes */ + if (xip->parent_inode == xinode->inode) + { + /* got one, delete it */ + g_xrdp_fs.inode_table[xip->inode] = NULL; + free(xip); + } } - while (ffi->next != 0) + + /* our parent will have one less dir */ + g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--; + + g_xrdp_fs.inode_table[xinode->inode] = NULL; + free(xinode); + + return 0; +} + +static void xfuse_update_xrdpfs_size() +{ + void *vp; + int diff; + + diff = g_xrdp_fs.max_entries - g_xrdp_fs.num_entries; + if (diff > 100) + return; + + /* extend memory */ + vp = realloc(g_xrdp_fs.inode_table, + (g_xrdp_fs.max_entries + 100) * sizeof(struct xrdp_inode *)); + + if (vp == NULL) { - ffi = ffi->next; + log_error("system out of memory"); + return; } - 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; + + /* zero newly added memory */ + memset(vp + g_xrdp_fs.max_entries * sizeof(struct xrdp_inode *), + 0, + 100 * sizeof(struct xrdp_inode *)); + + g_xrdp_fs.max_entries += 100; + g_xrdp_fs.inode_table = vp; } -/*****************************************************************************/ -int APP_CC -fuse_get_wait_objs(tbus *objs, int *count, int *timeout) +static void xfuse_enum_dir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) { - int lcount; + XRDP_INODE *xinode; + XRDP_INODE *xinode1; + struct dirbuf b; + int first_time = 1; + int i; - LLOGLN(10, ("fuse_get_wait_objs:")); - if (g_ch == 0) + memset(&b, 0, sizeof(struct dirbuf)); + + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - return 0; + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + /* match parent inode */ + if (xinode->parent_inode != ino) + continue; + + if (first_time) + { + first_time = 0; + if (ino == 1) + { + xfuse_dirbuf_add(req, &b, ".", 1); + xfuse_dirbuf_add(req, &b, "..", 1); + } + else + { + xinode1 = g_xrdp_fs.inode_table[ino]; + xfuse_dirbuf_add(req, &b, ".", ino); + xfuse_dirbuf_add(req, &b, "..", xinode1->parent_inode); + } + } + + xfuse_dirbuf_add(req, &b, xinode->name, xinode->inode); } - lcount = *count; - objs[lcount] = g_fd; - lcount++; - *count = lcount; - return 0; + if (!first_time) + { + if (off < b.size) + fuse_reply_buf(req, b.p + off, min(b.size - off, size)); + else + fuse_reply_buf(req, NULL, 0); + } + + if (b.p) + free(b.p); } -/*****************************************************************************/ -int APP_CC -fuse_check_wait_objs(void) +/****************************************************************************** +** ** +** callbacks for devredir ** +** ** +******************************************************************************/ + +/** + * Add a file or directory to xrdp file system + *****************************************************************************/ + +/* LK_TODO delete this after testing */ +#if 0 +void ___xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) { - struct fuse_chan *tmpch; - int res; + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + XRDP_INODE *xip = NULL; - LLOGLN(10, ("fuse_check_wait_objs:")); - if (g_ch == 0) + log_debug("<<<<<< entered"); + + if ((fip == NULL) || (xinode == NULL)) { - return 0; + log_error("fip or xinode are NULL"); + printf("RASH_TODO: fip or xinode are NULL - leaving\n"); + return; } - if (g_tcp_select(g_fd, 0) & 1) + + log_debug("req=%p", fip->req); + + /* do we have a valid inode? */ + if (!xfuse_is_inode_valid(fip->inode)) { - LLOGLN(10, ("fuse_check_wait_objs: fd is set")); - tmpch = g_ch; - res = fuse_chan_recv(&tmpch, g_buffer, g_bufsize); - if (res == -EINTR) + log_error("inode %d is not valid", fip->inode); + printf("RASH_TODO: inode %d is not valid - leaving\n", (tui32) fip->inode); + return; + } + + /* if filename is . or .. don't add it */ + if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0)) + { + free(xinode); + printf("RASH_TODO: not adding ./.. - leaving\n"); + return; + } + +// LK_TODO +#if 0 + /* 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) + { + log_debug("did not find entry with inode=%d name=%s", + fip->inode, fip->name); + return; + } +#endif + + if ((xip = xfuse_get_inode_from_pinode_name(fip->inode, xinode->name)) != NULL) + { + log_debug("inode=%d name=%s already exists in xrdp_fs; not adding it", + fip->inode, xinode->name); + free(xinode); + xinode = xip; + goto update_fuse; + } + + xinode->parent_inode = fip->inode; + xinode->inode = g_xrdp_fs.next_node++; + xinode->uid = getuid(); + xinode->gid = getgid(); + xinode->device_id = fip->device_id; + + g_xrdp_fs.num_entries++; + + /* insert it in xrdp fs and update lookup count */ + g_xrdp_fs.inode_table[xinode->inode] = xinode; + g_xrdp_fs.inode_table[fip->inode]->nentries; + xfuse_update_xrdpfs_size(); + +update_fuse: + +#if 1 + /* let FUSE know about this entry */ + if (fip->invoke_fuse) + { + struct dirbuf b; + + memset(&b, 0, sizeof(struct dirbuf)); + + /* RASH_TODO if we are not using dirbuf, change this code */ + if (fip->dirbuf == NULL) { - return 0; + fip->dirbuf = calloc(1, sizeof(struct dirbuf)); + xfuse_dirbuf_add(fip->req, &b, ".", xinode->inode); + xfuse_dirbuf_add(fip->req, &b, "..", xinode->parent_inode); } - if (res <= 0) + + xfuse_dirbuf_add(fip->req, &b, xinode->name, xinode->inode); + + if (fip->off < b.size) { - return 1; + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): req=%p off=%d\n", + fip->req, (tui32) fip->off); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): dumping req b4\n\n"); + g_hexdump((char *) fip->req, 128); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): dumping buf b4\n\n"); + g_hexdump(b.p, b.size); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): calling fuse\n"); + + fuse_reply_buf(fip->req, b.p, b.size); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): calling fuse...done\n"); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): dumping req aft\n\n"); + g_hexdump((char *) fip->req, 128); + + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): dumping buf aft\n\n"); + g_hexdump(b.p, b.size); } - fuse_session_process(g_se, g_buffer, res, tmpch); + else + { + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): calling fuse with NULL\n"); + fuse_reply_buf(fip->req, NULL, 0); + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): calling fuse with NULL...done\n"); + } + + log_debug("added inode=%d name=%s to FUSE", (tui32) xinode->inode, xinode->name); } - return 0; +#endif + + log_debug("leaving"); + printf("RASH_TODO: xfuse_devredir_cb_enum_dir(): leaving\n"); } +#endif -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_init(void) +void xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) { - char *param0 = "xrdp-chansrv"; - char *argv[4]; + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + XRDP_INODE *xip = NULL; - if (g_ch != 0) + if ((fip == NULL) || (xinode == NULL)) { - return 0; + log_error("fip or xinode are NULL"); + 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)) + + log_debug("fip->req=%p", fip->req); + + if (!xfuse_is_inode_valid(fip->inode)) { - if (!g_create_dir(g_fuse_root_path)) - { - LLOGLN(0, ("fuse_init: g_create_dir failed [%s]", - g_fuse_root_path)); - return 1; - } + log_error("inode %d is not valid", fip->inode); + 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; + /* if filename is . or .. don't add it */ + if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0)) + { + free(xinode); + return; + } - g_req_list = list_create(); - g_req_list->auto_free = 1; + if ((xip = xfuse_get_inode_from_pinode_name(fip->inode, xinode->name)) != NULL) + { + log_debug("inode=%d name=%s already exists in xrdp_fs; not adding it", + fip->inode, xinode->name); + free(xinode); + xinode = xip; + return; + } + + xinode->parent_inode = fip->inode; + xinode->inode = g_xrdp_fs.next_node++; + xinode->uid = getuid(); + xinode->gid = getgid(); + xinode->device_id = fip->device_id; - return fuse_init_lib(2, argv); + g_xrdp_fs.num_entries++; + + /* insert it in xrdp fs and update lookup count */ + g_xrdp_fs.inode_table[xinode->inode] = xinode; + g_xrdp_fs.inode_table[fip->inode]->nentries++; /* this was missing */ + xfuse_update_xrdpfs_size(); } -/*****************************************************************************/ -/* returns error */ -int APP_CC -fuse_deinit(void) +/** + *****************************************************************************/ + +void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus) { - LLOGLN(0, ("fuse_deinit:")); - if (g_ch != 0) + log_debug(">>>>>> vp=%p IoStatus=0x%x", vp, IoStatus); + + if (vp == NULL) + return; + + XRDP_INODE *xinode; + XRDP_INODE *ti; + struct dirbuf1 b; + int i; + int first_time = 1; + + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + + if (fip == NULL) { - LLOGLN(0, ("fuse_deinit: calling fuse_unmount")); - fuse_session_remove_chan(g_ch); - fuse_unmount(g_mountpoint, g_ch); - g_ch = 0; + log_debug("fip is NULL"); + goto done; } - if (g_se != 0) + + log_debug("fip->req=%p", fip->req); + + if (IoStatus != 0) { - LLOGLN(0, ("fuse_deinit: calling fuse_session_destroy")); - fuse_session_destroy(g_se); - g_se = 0; + /* command failed */ + if (fip->invoke_fuse) + fuse_reply_err(fip->req, ENOENT); + goto done; } - if (g_buffer != 0) + + /* do we have a valid inode? */ + if (!xfuse_is_inode_valid(fip->inode)) { - g_free(g_buffer); - g_buffer = 0; + log_error("inode %d is not valid", fip->inode); + if (fip->invoke_fuse) + fuse_reply_err(fip->req, EBADF); + goto done; } - if (g_req_list != 0) +#if 0 + memset(&b, 0, sizeof(struct dirbuf)); +#else + b.bytes_in_buf = 0; +#endif + for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++) { - list_delete(g_req_list); - g_req_list = 0; + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + /* match parent inode */ + if (xinode->parent_inode != fip->inode) + continue; + + xinode->is_synced = 1; + + if (first_time) + { + first_time = 0; + ti = g_xrdp_fs.inode_table[fip->inode]; +#if 0 + xfuse_dirbuf_add(fip->req, &b, ".", fip->inode); + xfuse_dirbuf_add(fip->req, &b, "..", ti->parent_inode); +#else + xfuse_dirbuf_add1(fip->req, &b, ".", fip->inode); + xfuse_dirbuf_add1(fip->req, &b, "..", ti->parent_inode); +#endif + } +#if 0 + xfuse_dirbuf_add(fip->req, &b, xinode->name, xinode->inode); +#else + xfuse_dirbuf_add1(fip->req, &b, xinode->name, xinode->inode); +#endif } - return 0; + + if ((first_time == 0) && (fip->invoke_fuse)) + { + if (fip->off < b.bytes_in_buf) + { +#if 0 + fuse_reply_buf(fip->req, b.p + fip->off, + min(b.size - fip->off, fip->size)); +#else + log_debug("calling fuse_reply_buf() with data..."); + fuse_reply_buf(fip->req, b.buf, b.bytes_in_buf); + log_debug("calling fuse_reply_buf() with data...done"); +#endif + } + else + { + log_debug("calling fuse_reply_buf() with NULL..."); + fuse_reply_buf(fip->req, NULL, 0); + log_debug("calling fuse_reply_buf() with NULL...done"); + } + } + else + { + log_debug("calling fuse_reply_err()..."); + fuse_reply_err(fip->req, ENOENT); + log_debug("calling fuse_reply_err()...done"); + } + +done: + +#if 0 + if (b.p) + free(b.p); +#endif + + if (!fip) + printf("###### %s : %s : %d: fip is NULL\n", __FILE__, __func__, __LINE__); + + if (fip) + free(fip); } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_size(int stream_id, int file_size) +void xfuse_devredir_cb_enum_dir_done_TODO(void *vp, tui32 IoStatus) { - LLOGLN(10, ("fuse_file_contents_size: file_size %d", file_size)); - return 0; + struct xrdp_inode *xinode; + struct fuse_entry_param e; + int i; + + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + + printf("--------- xfuse_devredir_cb_enum_dir_done() entered\n"); + + 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++) + { + if ((xinode = g_xrdp_fs.inode_table[i]) == NULL) + continue; + + /* 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); } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_range(int stream_id, char *data, int data_bytes) +void xfuse_devredir_cb_open_file(void *vp, tui32 DeviceId, tui32 FileId) { - struct req_list_item *rli; + XFUSE_HANDLE *fh; + XRDP_INODE *xinode; + + XFUSE_INFO *fip = (XFUSE_INFO *) vp; + if (fip == NULL) + { + log_debug("fip is NULL"); + goto done; + } + + log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p DeviceId=%d FileId=%d", + fip, fip->fi, DeviceId, FileId); - 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) + if (fip->fi != NULL) { - fuse_reply_buf(rli->req, data, data_bytes); - list_remove_item(g_req_list, 0); - if (g_req_list->count > 0) + if ((fh = calloc(1, sizeof(XFUSE_HANDLE))) == NULL) { - /* send next request */ - rli = (struct req_list_item *)list_get_item(g_req_list, 0); - if (rli != 0) + 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) ((long) fh); + log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=%p", + fip, fip->fi, fip->fi->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); + + /* update open count */ + if ((xinode = g_xrdp_fs.inode_table[fip->inode]) != NULL) + xinode->nopen++; + + fuse_reply_open(fip->req, fip->fi); + } + else if (fip->reply_type == RT_FUSE_REPLY_CREATE) + { + struct fuse_entry_param e; + +// LK_TODO +#if 0 + if ((xinode = g_xrdp_fs.inode_table[fip->inode]) == NULL) { - clipboard_request_file_data(rli->stream_id, rli->lindex, - rli->off, rli->size); + 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 { - LLOGLN(0, ("fuse_file_contents_range: error")); + xinode->nopen++; + 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_INFO *fip; + + fip = (XFUSE_INFO *) vp; + if ((fip == NULL) || (fip->req == NULL)) + { + log_error("fip for fip->req is NULL"); + return; } + + fuse_reply_buf(fip->req, buf, length); + free(fip); +} + +void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length) +{ + XRDP_INODE *xinode; + XFUSE_INFO *fip; + + fip = (XFUSE_INFO *) vp; + if ((fip == NULL) || (fip->req == NULL) || (fip->fi == NULL)) + { + log_error("fip, fip->req or fip->fi is NULL"); + return; + } + + log_debug("+++ XFUSE_INFO=%p, XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=%p", + fip, fip->fi, fip->fi->fh); + + 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); + + free(fip); +} + +void xfuse_devredir_cb_rmdir_or_file(void *vp, tui32 IoStatus) +{ + XFUSE_INFO *fip; + XRDP_INODE *xinode; + + fip = (XFUSE_INFO *) vp; + if (fip == NULL) + return; + + if (IoStatus != 0) { - LLOGLN(0, ("fuse_file_contents_range: error")); + fuse_reply_err(fip->req, EBADF); + free(fip); + return; } - return 0; + + /* now delete the item in xrdp fs */ + xinode = xfuse_get_inode_from_pinode_name(fip->inode, fip->name); + if (xinode == NULL) + { + fuse_reply_err(fip->req, EBADF); + free(fip); + return; + } + + g_xrdp_fs.inode_table[xinode->inode] = NULL; + free(xinode); + + /* update parent */ + xinode = g_xrdp_fs.inode_table[fip->inode]; + xinode->nentries--; + + fuse_reply_err(fip->req, 0); + free(fip); } -#else +void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus) +{ + XFUSE_INFO *fip; + XRDP_INODE *old_xinode; + XRDP_INODE *new_xinode; -#include "arch.h" + fip = (XFUSE_INFO *) vp; + if (fip == NULL) + return; -char g_fuse_root_path[256] = ""; + if (IoStatus != 0) + { + fuse_reply_err(fip->req, EEXIST); + free(fip); + return; + } + + /* + * update xrdp file system + */ + + /* if destination dir/file exists, delete it */ + if (xfuse_does_file_exist(fip->new_inode, fip->new_name)) + { + new_xinode = xfuse_get_inode_from_pinode_name(fip->new_inode, + fip->new_name); + + if (new_xinode->mode & S_IFREG) + xfuse_delete_file_with_xinode(new_xinode); + else + xfuse_delete_dir_with_xinode(new_xinode); + + new_xinode = NULL; + } + + old_xinode = xfuse_get_inode_from_pinode_name(fip->inode, fip->name); + if (old_xinode == NULL) + { + fuse_reply_err(fip->req, EBADF); + free(fip); + return; + } -/*****************************************************************************/ -int APP_CC -fuse_get_wait_objs(tbus *objs, int *count, int *timeout) + old_xinode->parent_inode = fip->new_inode; + strcpy(old_xinode->name, fip->new_name); + + if (fip->inode != fip->new_inode) + { + /* file has been moved to a different dir */ + old_xinode->is_synced = 1; + g_xrdp_fs.inode_table[fip->inode]->nentries--; + g_xrdp_fs.inode_table[fip->new_inode]->nentries++; + } + + fuse_reply_err(fip->req, 0); + free(fip); +} + +void xfuse_devredir_cb_file_close(void *vp) { - return 0; + XFUSE_INFO *fip; + XRDP_INODE *xinode; + + fip = (XFUSE_INFO *) vp; + if (fip == NULL) + { + log_error("fip is NULL"); + return; + } + + if (fip->fi == NULL) + { + log_error("fip->fi is NULL"); + return; + } + + log_debug("+++ XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=%p", + fip, fip->fi, fip->fi->fh); + + if ((xinode = g_xrdp_fs.inode_table[fip->inode]) == NULL) + { + log_debug("inode_table[%d] is NULL", fip->inode); + fuse_reply_err(fip->req, EBADF); + return; + } + + log_debug("before: inode=%d nopen=%d", xinode->inode, xinode->nopen); + + if (xinode->nopen > 0) + xinode->nopen--; + + /* LK_TODO */ +#if 0 + if ((xinode->nopen == 0) && fip->fi && fip->fi->fh) + { + printf("LK_TODO: ################################ fi=%p fi->fh=%p\n", + fip->fi, fip->fi->fh); + + free((char *) (tintptr) (fip->fi->fh)); + fip->fi->fh = NULL; + } +#endif + + fuse_reply_err(fip->req, 0); } -/*****************************************************************************/ -int APP_CC -fuse_check_wait_objs(void) +/****************************************************************************** +** ** +** 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) { - return 0; + XRDP_INODE *xinode; + struct fuse_entry_param e; + + log_debug("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; + } + + xinode = xfuse_get_inode_from_pinode_name(parent, name); + if (xinode == NULL) + { + log_debug("did not find entry for parent=%d name=%s", parent, name); + fuse_reply_err(req, ENOENT); + return; + } + + 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 for parent=%d name=%s uid=%d gid=%d", + parent, name, xinode->uid, xinode->gid); + return; } -/*****************************************************************************/ -int APP_CC -fuse_init(void) +/** + * Get file attributes + *****************************************************************************/ + +static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) { - return 0; + struct xrdp_inode *xino; + struct stat stbuf; + + (void) fi; + + log_debug("req=%p ino=%d", req, (int) ino); + + /* if ino is not valid, just return */ + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + xino = g_xrdp_fs.inode_table[ino]; + + 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); + log_debug("exiting"); } -/*****************************************************************************/ -int APP_CC -fuse_deinit(void) +/** + * + *****************************************************************************/ + +static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b, + const char *name, fuse_ino_t ino) { - return 0; + struct stat 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); } -/*****************************************************************************/ -int APP_CC -fuse_clear_clip_dir(void) +static int xfuse_dirbuf_add1(fuse_req_t req, struct dirbuf1 *b, + const char *name, fuse_ino_t ino) { + struct stat stbuf; + int len; + + len = fuse_add_direntry(req, NULL, 0, name, NULL, 0); + if (b->bytes_in_buf + len > 4096) + { + log_debug("not adding entry because dirbuf overflow would occur"); + return -1; + } + + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_ino = ino; + + fuse_add_direntry(req, + &b->buf[b->bytes_in_buf], /* index where new entry will be added to buf */ + 4096 - len, /* remaining size of buf */ + name, /* name of entry */ + &stbuf, /* file attributes */ + b->bytes_in_buf + len /* offset of next entry */ + ); + + b->bytes_in_buf += len; return 0; } -/*****************************************************************************/ -int APP_CC -fuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) +/** + * + *****************************************************************************/ + +static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) { - return 0; + XRDP_INODE *xinode; + XFUSE_INFO *fip; + tui32 device_id; + char full_path[4096]; + char *cptr; + + log_debug("req=%p inode=%d size=%d offset=%d", req, ino, size, off); + + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + if (ino == 1) + { + /* special case; enumerate top level dir */ + log_debug("enumerating top level dir"); + xfuse_enum_dir(req, ino, size, off, fi); + return; + } + + xinode = g_xrdp_fs.inode_table[ino]; + if (xinode->is_loc_resource) + { + /* enumerate local resources */ + xfuse_enum_dir(req, ino, size, off, fi); + return; + } + + /* enumerate resources on a remote device */ + +#ifdef USE_SYNC_FLAG + if (xinode->is_synced) + { + xfuse_enum_dir(req, ino, size, off, fi); + return; + } + else + { + goto do_remote_lookup; + } +#endif + +do_remote_lookup: + + log_debug("did not find entry; redirecting call to dev_redir"); + device_id = xfuse_get_device_id_for_inode((tui32) ino, full_path); + log_debug("dev_id=%d ino=%d full_path=%s", device_id, ino, full_path); + + 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->size = size; + fip->off = off; + fip->fi = fi; + fip->dirbuf1.first_time = 1; + fip->dirbuf1.bytes_in_buf = 0; + + fip->invoke_fuse = 1; + fip->device_id = device_id; + + log_debug("fip->req=%p", fip->req); + + /* 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 + { + 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); + } + } } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_size(int stream_id, int file_size) +/** + * Create a directory + *****************************************************************************/ + +static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode) { - return 0; + XRDP_INODE *xinode; + struct fuse_entry_param e; + + log_debug("entered: parent_inode=%d name=%s", (int) parent, name); + + if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) != NULL) + { + /* dir already exists, just return it */ + memset(&e, 0, sizeof(struct fuse_entry_param)); + + e.ino = xinode->inode; + e.attr_timeout = XFUSE_ATTR_TIMEOUT; + e.entry_timeout = XFUSE_ENTRY_TIMEOUT; + e.attr.st_ino = xinode->inode; + e.attr.st_mode = xinode->mode; + e.attr.st_nlink = xinode->nlink; + e.attr.st_uid = xinode->uid; + e.attr.st_gid = xinode->gid; + e.attr.st_size = xinode->size; + e.attr.st_atime = xinode->atime; + e.attr.st_mtime = xinode->mtime; + e.attr.st_ctime = xinode->ctime; + e.generation = 1; + + fuse_reply_entry(req, &e); + return; + } + + /* dir does not exist, create it */ + xfuse_create_dir_or_file(req, parent, name, mode, NULL, S_IFDIR); } -/*****************************************************************************/ -int APP_CC -fuse_file_contents_range(int stream_id, char *data, int data_bytes) +/** + * Remove specified dir + *****************************************************************************/ + +static void xfuse_cb_rmdir(fuse_req_t req, fuse_ino_t parent, + const char *name) { - return 0; + xfuse_remove_dir_or_file(req, parent, name, 1); } -#endif +static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent, + const char *name) +{ + xfuse_remove_dir_or_file(req, parent, name, 2); +} + +/** + * Remove a dir or file + * + * @param type 1=dir, 2=file + *****************************************************************************/ + +static void xfuse_remove_dir_or_file(fuse_req_t req, fuse_ino_t parent, + const char *name, int type) +{ + XFUSE_INFO *fip; + XRDP_INODE *xinode; + char *cptr; + char full_path[4096]; + tui32 device_id; + + log_debug("entered: parent=%d name=%s", parent, name); + + /* is parent inode valid? */ + if (!xfuse_is_inode_valid(parent)) + { + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + return; + } + + if ((xinode = xfuse_get_inode_from_pinode_name(parent, name)) == NULL) + { + log_error("did not find file with pinode=%d name=%s", parent, name); + fuse_reply_err(req, EBADF); + return; + } + + device_id = xfuse_get_device_id_for_inode(parent, full_path); + + log_debug("path=%s nentries=%d", full_path, xinode->nentries); + + if ((type == 1) && (xinode->nentries != 0)) + { + log_debug("cannot rmdir; lookup count is %d", xinode->nentries); + fuse_reply_err(req, ENOTEMPTY); + return; + } + else if (type == 2) + { + if ((xinode->nopen > 1) || ((xinode->nopen == 1) && + (xinode->close_in_progress == 0))) + { + log_debug("cannot unlink; open count is %d", xinode->nopen); + fuse_reply_err(req, EBUSY); + return; + } + } + + strcat(full_path, "/"); + strcat(full_path, name); + + if (xinode->is_loc_resource) + { + /* specified file is a local resource */ + //XFUSE_HANDLE *fh; + + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + return; + } + + /* specified file resides on redirected share */ + + 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; + fip->invoke_fuse = 1; + fip->device_id = device_id; + strncpy(fip->name, name, 1024); + fip->name[1023] = 0; + fip->type = type; + + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) + { + /* get dev_redir to open the remote file */ + if (devredir_rmdir_or_file((void *) fip, device_id, "\\", O_RDWR)) + { + log_error("failed to send dev_redir_open_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + return; + } + } + else + { + if (devredir_rmdir_or_file((void *) fip, device_id, cptr, O_RDWR)) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + return; + } + } +} + +static void xfuse_cb_rename(fuse_req_t req, + fuse_ino_t old_parent, const char *old_name, + fuse_ino_t new_parent, const char *new_name) +{ + XRDP_INODE *old_xinode; + XFUSE_INFO *fip; + tui32 new_device_id; + char *cptr; + char old_full_path[1024]; + char new_full_path[1024]; + char *cp; + + tui32 device_id; + + log_debug("entered: old_parent=%d old_name=%s new_parent=%d new_name=%s", + old_parent, old_name, new_parent, new_name); + xfuse_dump_fs(); + + /* is old_parent inode valid? */ + if (!xfuse_is_inode_valid(old_parent)) + { + log_error("inode %d is not valid", old_parent); + fuse_reply_err(req, EINVAL); + return; + } + + /* is new_parent inode valid? */ + if (!xfuse_is_inode_valid(new_parent)) + { + log_error("inode %d is not valid", new_parent); + fuse_reply_err(req, EINVAL); + return; + } + + if ((old_name == NULL) || (strlen(old_name) == 0)) + { + fuse_reply_err(req, EINVAL); + return; + } + + if ((new_name == NULL) || (strlen(new_name) == 0)) + { + fuse_reply_err(req, EINVAL); + return; + } + + old_xinode = xfuse_get_inode_from_pinode_name(old_parent, old_name); + if (old_xinode == NULL) + { + log_error("did not find file with pinode=%d name=%s", + old_parent, old_name); + fuse_reply_err(req, EBADF); + return; + } + + /* if file is open, cannot rename */ + if (old_xinode->nopen != 0) + { + fuse_reply_err(req, EBUSY); + return; + } + + /* rename across file systems not yet supported */ + new_device_id = xfuse_get_device_id_for_inode(new_parent, new_full_path); + strcat(new_full_path, "/"); + strcat(new_full_path, new_name); + + if (new_device_id != old_xinode->device_id) + { + log_error("rename across file systems not supported"); + fuse_reply_err(req, EINVAL); + return; + } + + if (old_xinode->is_loc_resource) + { + /* specified file is a local resource */ + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + return; + } + + /* resource is on a redirected share */ + + device_id = old_xinode->device_id; + + xfuse_get_device_id_for_inode(old_parent, old_full_path); + strcat(old_full_path, "/"); + strcat(old_full_path, old_name); + + 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 = old_parent; + fip->new_inode = new_parent; + strncpy(fip->name, old_name, 1024); + strncpy(fip->new_name, new_name, 1024); + fip->name[1023] = 0; + fip->new_name[1023] = 0; + fip->invoke_fuse = 1; + fip->device_id = device_id; + + if ((cp = strchr(new_full_path, '/')) == NULL) + cp = "\\"; + + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(old_full_path, '/')) == NULL) + { + /* get dev_redir to open the remote file */ + if (dev_redir_file_open((void *) fip, device_id, "\\", + O_RDWR, S_IFREG | OP_RENAME_FILE, cp)) + { + log_error("failed to send dev_redir_file_open() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + return; + } + } + else + { + if (dev_redir_file_open((void *) fip, device_id, cptr, + O_RDWR, S_IFREG | OP_RENAME_FILE, cp)) + { + log_error("failed to send dev_redir_file_open() cmd"); + fuse_reply_err(req, EREMOTEIO); + free(fip); + return; + } + } +} + +/** + * 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) +{ + XFUSE_INFO *fip; + char *cptr; + char full_path[1024]; + tui32 device_id; + + full_path[0] = 0; + + log_debug("entered: parent_ino=%d name=%s type=%s", + (int) parent, name, (type == S_IFDIR) ? "dir" : "file"); + + /* name must be valid */ + if ((name == NULL) || (strlen(name) == 0)) + { + log_error("invalid name"); + fuse_reply_err(req, EBADF); + return; + } + + /* is parent inode valid? */ + if ((parent == 1) || (!xfuse_is_inode_valid(parent))) + { + log_error("inode %d is not valid", parent); + fuse_reply_err(req, EBADF); + return; + } + + device_id = xfuse_get_device_id_for_inode(parent, full_path); + strcat(full_path, "/"); + strcat(full_path, name); + + XRDP_INODE *xinode = g_xrdp_fs.inode_table[parent]; + if (xinode->is_loc_resource) + { + /* specified file is a local resource */ + //XFUSE_HANDLE *fh; + + log_debug("LK_TODO: this is still a TODO"); + fuse_reply_err(req, EINVAL); + return; + } + + /* specified file resides on redirected share */ + + if ((fip = calloc(1, sizeof(XFUSE_INFO))) == NULL) + { + log_error("system out of memory"); + fuse_reply_err(req, ENOMEM); + return; + } + + 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; + + log_debug("+++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p", fip, fip->fi); + + /* LK_TODO need to handle open permissions */ + + /* we want path minus 'root node of the share' */ + if ((cptr = strchr(full_path, '/')) == NULL) + { + /* get dev_redir to open the remote file */ + if (dev_redir_file_open((void *) fip, device_id, "\\", + O_CREAT, type, NULL)) + { + log_error("failed to send dev_redir_open_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + } + } + else + { + if (dev_redir_file_open((void *) fip, device_id, cptr, + O_CREAT, type, NULL)) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_err(req, EREMOTEIO); + } + } +} + +/** + * Open specified file + *****************************************************************************/ + +static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) +{ + XRDP_INODE *xinode; + XFUSE_INFO *fip; + char *cptr; + char full_path[4096]; + tui32 device_id; + + log_debug("entered: ino=%d", (int) ino); + + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + /* if ino points to a dir, fail the open request */ + xinode = g_xrdp_fs.inode_table[ino]; + if (xinode->mode & S_IFDIR) + { + log_debug("reading a dir not allowed!"); + fuse_reply_err(req, EISDIR); + return; + } + + device_id = xfuse_get_device_id_for_inode((tui32) ino, full_path); + + if (xinode->is_loc_resource) + { + /* specified file is a local resource */ + XFUSE_HANDLE *fh = calloc(1, sizeof(XFUSE_HANDLE)); + fh->is_loc_resource = 1; + fi->fh = (uint64_t) ((long) fh); + fuse_reply_open(req, fi); + return; + } + + /* specified file resides on redirected share */ + + 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; + + log_debug("LK_TODO: fip->fi = %p", fip->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) + { + /* get dev_redir to open the remote file */ + if (dev_redir_file_open((void *) fip, device_id, "\\", + fi->flags, S_IFREG, NULL)) + { + log_error("failed to send dev_redir_open_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + } + } + else + { + if (dev_redir_file_open((void *) fip, device_id, cptr, + fi->flags, S_IFREG, NULL)) + { + log_error("failed to send dev_redir_get_dir_listing() cmd"); + fuse_reply_err(req, EREMOTEIO); + } + } +} + +static void xfuse_cb_release(fuse_req_t req, fuse_ino_t ino, struct + fuse_file_info *fi) +{ + XFUSE_INFO *fip = NULL; + XFUSE_HANDLE *handle = (XFUSE_HANDLE *) (tintptr) (fi->fh); + tui32 FileId; + + log_debug("entered: ino=%d fi=%p fi->fh=%p", (int) ino, fi, fi->fh); + + if (!xfuse_is_inode_valid(ino)) + { + log_error("inode %d is not valid", ino); + fuse_reply_err(req, EBADF); + return; + } + + XRDP_INODE *xinode = g_xrdp_fs.inode_table[ino]; + if (xinode->is_loc_resource) + { + /* specified file is a local resource */ + fuse_reply_err(req, 0); + return; + } + + /* specified file resides on redirected share */ + + log_debug("nopen=%d", xinode->nopen); + + /* if file is not opened, just return */ + if (xinode->nopen == 0) + { + log_debug("cannot close because file not opened"); + fuse_reply_err(req, 0); + return; + } + + 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 = handle->DeviceId; + fip->fi = fi; + + log_debug(" +++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=%p", + fip, fip->fi, fip->fi->fh); + + FileId = handle->FileId; + free(handle); + fip->fi->fh = NULL; + xinode->close_in_progress = 1; + + if (devredir_file_close((void *) fip, fip->device_id, handle->FileId)) + { + log_error("failed to send devredir_close_file() cmd"); + fuse_reply_err(req, EREMOTEIO); + } +} + +/** + *****************************************************************************/ + +static void xfuse_cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t off, struct fuse_file_info *fi) +{ + XFUSE_HANDLE *fh; + XFUSE_INFO *fusep; + XRDP_INODE *xinode; + struct req_list_item *rli; + long handle; + + log_debug("want_bytes %ld bytes at off %ld", size, off); + + if (fi->fh == 0) + { + fuse_reply_err(req, EINVAL); + return; + } + + handle = fi->fh; + fh = (XFUSE_HANDLE *) handle; + + if (fh->is_loc_resource) + { + /* target file is in .clipboard dir */ + + log_debug("target file is in .clipboard dir"); + + if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL) + { + log_error("ino does not exist in xrdp_fs"); + fuse_reply_buf(req, 0, 0); + return; + } + + rli = (struct req_list_item *) + g_malloc(sizeof(struct req_list_item), 1); + + rli->stream_id = 0; + rli->req = req; + rli->lindex = xinode->lindex; + rli->off = off; + rli->size = size; + list_add_item(g_req_list, (tbus) rli); + + if (g_req_list->count == 1) + { + log_debug("requesting clipboard file data lindex = %d off = %d size = %d", + rli->lindex, (int) off, (int) size); + + clipboard_request_file_data(rli->stream_id, rli->lindex, + (int) off, (int) size); + } + + return; + } + + /* target file is on a remote device */ + + 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; + + dev_redir_file_read(fusep, fh->DeviceId, fh->FileId, size, off); +} + +/** + *****************************************************************************/ + +static void xfuse_cb_write(fuse_req_t req, fuse_ino_t ino, const char *buf, + size_t size, off_t off, struct fuse_file_info *fi) +{ + XFUSE_HANDLE *fh; + XFUSE_INFO *fusep; + long handle; + + log_debug("write %d bytes at off %d to inode=%d", + (int) size, (int) off, (int) ino); + + if (fi->fh == 0) + { + log_error("file handle fi->fh is NULL"); + fuse_reply_err(req, EINVAL); + return; + } + + handle = fi->fh; + fh = (XFUSE_HANDLE *) handle; + + if (fh->is_loc_resource) + { + /* target file is in .clipboard dir */ + log_debug("THIS IS STILL A TODO!"); + return; + } + + /* target file is on a remote device */ + + 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; + + log_debug("+++ created XFUSE_INFO=%p XFUSE_INFO->fi=%p XFUSE_INFO->fi->fh=%p", + fusep, fusep->fi, fusep->fi->fh); + + dev_redir_file_write(fusep, fh->DeviceId, fh->FileId, buf, size, off); + log_debug("exiting"); +} + +/** + *****************************************************************************/ + +static void xfuse_cb_create(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, + struct fuse_file_info *fi) +{ + log_debug("entered: parent_inode=%d, name=%s fi=%p", + (int) parent, name, fi); + + xfuse_create_dir_or_file(req, parent, name, mode, fi, S_IFREG); +} + +/** + *****************************************************************************/ + +static void xfuse_cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) +{ + log_debug("#################### entered: ino=%d datasync=%d", (int) ino, datasync); + log_debug("function not required"); + fuse_reply_err(req, EINVAL); +} + +/** + *****************************************************************************/ + +static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int to_set, struct fuse_file_info *fi) +{ + XRDP_INODE *xinode; + struct stat st; + + log_debug("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("FUSE_SET_ATTR_MODE"); + + } + + if (to_set & FUSE_SET_ATTR_UID) + { + xinode->uid = attr->st_uid; + log_debug("FUSE_SET_ATTR_UID"); + } + + if (to_set & FUSE_SET_ATTR_GID) + { + xinode->gid = attr->st_gid; + log_debug("FUSE_SET_ATTR_GID"); + } + + if (to_set & FUSE_SET_ATTR_SIZE) + { + log_debug("previous file size: %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("FUSE_SET_ATTR_ATIME"); + } + + if (to_set & FUSE_SET_ATTR_MTIME) + { + xinode->mtime = attr->st_mtime; + log_debug("FUSE_SET_ATTR_MTIME"); + } + + if (to_set & FUSE_SET_ATTR_ATIME_NOW) + { + xinode->atime = attr->st_atime; + log_debug("FUSE_SET_ATTR_ATIME_NOW"); + } + + if (to_set & FUSE_SET_ATTR_MTIME_NOW) + { + xinode->mtime = attr->st_mtime; + log_debug("FUSE_SET_ATTR_MTIME_NOW"); + } + + memset(&st, 0, sizeof(st)); + st.st_ino = xinode->inode; + st.st_mode = xinode->mode; + st.st_size = xinode->size; + st.st_uid = xinode->uid; + st.st_gid = xinode->gid; + st.st_atime = xinode->atime; + st.st_mtime = xinode->mtime; + st.st_ctime = xinode->ctime; + + fuse_reply_attr(req, &st, 1.0); /* LK_TODO just faking for now */ +} + +#endif /* end else #ifndef XRDP_FUSE */ diff --git a/sesman/chansrv/chansrv_fuse.h b/sesman/chansrv/chansrv_fuse.h index 0ccde368..912a0b22 100644 --- a/sesman/chansrv/chansrv_fuse.h +++ b/sesman/chansrv/chansrv_fuse.h @@ -1,23 +1,67 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com + * + * 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 nopen; /* number of simultaneous opens */ + tui32 uid; /* User ID of the file's owner. */ + tui32 gid; /* Group ID of the file's group. */ + size_t size; /* Size of file, in bytes. */ + time_t atime; /* Time of last access. */ + time_t mtime; /* Time of last modification. */ + time_t ctime; /* Time of last status change. */ + char name[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 */ + int lindex; /* used in clipboard operations */ + int is_loc_resource; /* this is not a redirected resource */ + int close_in_progress; /* close cmd sent to client */ +}; +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 invoked 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); +void xfuse_devredir_cb_rmdir_or_file(void *vp, tui32 IoStatus); +void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus); +void xfuse_devredir_cb_file_close(void *vp); #endif diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index 67afbd49..27c45fff 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -247,7 +247,7 @@ static int g_cliprdr_flags = CB_USE_LONG_FORMAT_NAMES | static int g_formatIds[16]; static int g_num_formatIds = 0; -static int g_file_format_id = 0; +static int g_file_format_id = -1; /*****************************************************************************/ /* this is one way to get the current time from the x server */ @@ -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 54a7b46a..42b5d4a3 100644 --- a/sesman/chansrv/clipboard_file.c +++ b/sesman/chansrv/clipboard_file.c @@ -53,7 +53,7 @@ extern int g_cliprdr_chan_id; /* in chansrv.c */ extern struct clip_s2c g_clip_s2c; /* in clipboard.c */ extern struct clip_c2s g_clip_c2s; /* in clipboard.c */ -extern char g_fuse_root_path[]; /* in chansrv_fuse.c */ +extern char g_fuse_clipboard_path[]; struct cb_file_info { @@ -454,7 +454,9 @@ clipboard_request_file_data(int stream_id, int lindex, int offset, int size; int rv; - LLOGLN(10, ("clipboard_request_file_data:")); + LLOGLN(10, ("clipboard_request_file_data: stream_id=%d lindex=%d off=%d request_bytes=%d", + stream_id, lindex, offset, request_bytes)); + if (g_file_request_sent_type != 0) { LLOGLN(0, ("clipboard_request_file_data: warning, still waiting " @@ -534,13 +536,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 { @@ -593,8 +595,18 @@ clipboard_c2s_in_files(struct stream *s, char *file_list) struct clip_file_desc *cfd; char *ptr; + if (!s_check_rem(s, 4)) + { + LLOGLN(0, ("clipboard_c2s_in_files: parse error")); + return 1; + } in_uint32_le(s, cItems); - fuse_clear_clip_dir(); + if (cItems > 64 * 1024) /* sanity check */ + { + LLOGLN(0, ("clipboard_c2s_in_files: error cItems %d too big", cItems)); + return 1; + } + 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); @@ -610,13 +622,13 @@ 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; - str_len = g_strlen(g_fuse_root_path); - g_strcpy(ptr, g_fuse_root_path); + str_len = g_strlen(g_fuse_clipboard_path); + g_strcpy(ptr, g_fuse_clipboard_path); ptr += str_len; *ptr = '/'; diff --git a/sesman/chansrv/devredir.c b/sesman/chansrv/devredir.c index e6407211..08f28a0d 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 LK.Rashinkar@gmail.com * * 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,133 @@ * limitations under the License. */ +/* + * 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> + #include "arch.h" #include "parse.h" #include "os_calls.h" +#include "log.h" +#include "chansrv_fuse.h" +#include "devredir.h" +#include "smartcard.h" + +/* 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); \ + } \ +} + +/* 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_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 */ +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 */ + xstream_new(s, 1024); + + /* initiate drive redirection protocol by sending Server Announce Req */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_SERVER_ANNOUNCE); + xstream_wr_u16_le(s, 0x0001); /* server major ver */ + xstream_wr_u16_le(s, 0x000C); /* server minor ver - pretend 2 b Win 7 */ + xstream_wr_u32_le(s, u.clientID); /* unique ClientID */ + + /* send data to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_free(s); return 0; } @@ -36,12 +155,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) + xstream_new(g_input_stream, total_length); + + xstream_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 */ + xstream_rd_u16_le(ls, comp_type); + xstream_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: + xstream_seek(ls, 2); /* major version, we ignore it */ + xstream_rd_u16_le(ls, minor_ver); + xstream_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: + devredir_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) + { + xstream_free(g_input_stream); + g_input_stream = NULL; + } + + return rv; } /*****************************************************************************/ @@ -57,3 +291,1273 @@ 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; + + xstream_new(s, 1024); + + /* setup header */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_SERVER_CAPABILITY); + + xstream_wr_u16_le(s, 5); /* num of caps we are sending */ + xstream_wr_u16_le(s, 0x0000); /* padding */ + + /* setup general capability */ + xstream_wr_u16_le(s, CAP_GENERAL_TYPE); /* CapabilityType */ + xstream_wr_u16_le(s, 44); /* CapabilityLength - len of this */ + /* CAPABILITY_SET in bytes, inc */ + /* the header */ + xstream_wr_u32_le(s, 2); /* Version */ + xstream_wr_u32_le(s, 2); /* O.S type */ + xstream_wr_u32_le(s, 0); /* O.S version */ + xstream_wr_u16_le(s, 1); /* protocol major version */ + xstream_wr_u16_le(s, g_client_rdp_version); /* protocol minor version */ + xstream_wr_u32_le(s, 0xffff); /* I/O code 1 */ + xstream_wr_u32_le(s, 0); /* I/O code 2 */ + xstream_wr_u32_le(s, 7); /* Extended PDU */ + xstream_wr_u32_le(s, 0); /* extra flags 1 */ + xstream_wr_u32_le(s, 0); /* extra flags 2 */ + xstream_wr_u32_le(s, 2); /* special type device cap */ + + /* setup printer capability */ + xstream_wr_u16_le(s, CAP_PRINTER_TYPE); + xstream_wr_u16_le(s, 8); + xstream_wr_u32_le(s, 1); + + /* setup serial port capability */ + xstream_wr_u16_le(s, CAP_PORT_TYPE); + xstream_wr_u16_le(s, 8); + xstream_wr_u32_le(s, 1); + + /* setup file system capability */ + xstream_wr_u16_le(s, CAP_DRIVE_TYPE); /* CapabilityType */ + xstream_wr_u16_le(s, 8); /* CapabilityLength - len of this */ + /* CAPABILITY_SET in bytes, inc */ + /* the header */ + xstream_wr_u32_le(s, 2); /* Version */ + + /* setup smart card capability */ + xstream_wr_u16_le(s, CAP_SMARTCARD_TYPE); + xstream_wr_u16_le(s, 8); + xstream_wr_u32_le(s, 1); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_free(s); +} + +void dev_redir_send_server_clientID_confirm() +{ + struct stream *s; + int bytes; + + xstream_new(s, 1024); + + /* setup stream */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_CLIENTID_CONFIRM); + xstream_wr_u16_le(s, 0x0001); + xstream_wr_u16_le(s, g_client_rdp_version); + xstream_wr_u32_le(s, g_clientID); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_free(s); +} + +void dev_redir_send_server_user_logged_on() +{ + struct stream *s; + int bytes; + + xstream_new(s, 1024); + + /* setup stream */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_USER_LOGGEDON); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_free(s); +} + +void devredir_send_server_device_announce_resp(tui32 device_id) +{ + struct stream *s; + int bytes; + + xstream_new(s, 1024); + + /* setup stream */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_DEVICE_REPLY); + xstream_wr_u32_le(s, device_id); + xstream_wr_u32_le(s, 0); /* ResultCode */ + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_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("DesiredAccess=0x%x CreateDisposition=0x%x CreateOptions=0x%x", + DesiredAccess, CreateDisposition, CreateOptions); + + /* to store path as unicode */ + len = strlen(path) * 2 + 2; + + xstream_new(s, 1024 + len); + + devredir_insert_DeviceIoRequest(s, + device_id, + 0, + completion_id, + IRP_MJ_CREATE, + 0); + + xstream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */ + xstream_wr_u32_le(s, 0); /* AllocationSize high unused */ + xstream_wr_u32_le(s, 0); /* AllocationSize low unused */ + xstream_wr_u32_le(s, 0); /* FileAttributes */ + xstream_wr_u32_le(s, 3); /* SharedAccess LK_TODO */ + xstream_wr_u32_le(s, CreateDisposition); /* CreateDisposition */ + xstream_wr_u32_le(s, CreateOptions); /* CreateOptions */ + xstream_wr_u32_le(s, len); /* PathLength */ + devredir_cvt_to_unicode(s->p, path); /* path in unicode */ + xstream_seek(s, len); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_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; + + xstream_new(s, 1024); + + devredir_insert_DeviceIoRequest(s, DeviceId, FileId, CompletionId, + MajorFunction, MinorFunc); + + if (pad_len) + xstream_seek(s, pad_len); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_free(s); + log_debug("sent close request; expect CID_FILE_CLOSE"); + 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); + } + + xstream_new(s, 1024 + path_len); + + irp->completion_type = CID_DIRECTORY_CONTROL; + devredir_insert_DeviceIoRequest(s, + device_id, + irp->FileId, + irp->CompletionId, + IRP_MJ_DIRECTORY_CONTROL, + IRP_MN_QUERY_DIRECTORY); + +#ifdef USE_SHORT_NAMES_IN_DIR_LISTING + xstream_wr_u32_le(s, FileBothDirectoryInformation); /* FsInformationClass */ +#else + xstream_wr_u32_le(s, FileDirectoryInformation); /* FsInformationClass */ +#endif + xstream_wr_u8(s, InitialQuery); /* InitialQuery */ + + if (!InitialQuery) + { + xstream_wr_u32_le(s, 0); /* PathLength */ + xstream_seek(s, 23); + } + else + { + xstream_wr_u32_le(s, path_len); /* PathLength */ + xstream_seek(s, 23); /* Padding */ + xstream_wr_string(s, upath, path_len); + } + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + + xstream_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; + + xstream_rd_u16_le(s, num_caps); + xstream_seek(s, 2); /* padding */ + + for (i = 0; i < num_caps; i++) + { + xstream_rd_u16_le(s, cap_type); + xstream_rd_u16_le(s, cap_len); + xstream_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"); + xstream_seek(s, cap_len); + break; + + case CAP_PRINTER_TYPE: + log_debug("got CAP_PRINTER_TYPE"); + g_is_printer_redir_supported = 1; + xstream_seek(s, cap_len); + break; + + case CAP_PORT_TYPE: + log_debug("got CAP_PORT_TYPE"); + g_is_port_redir_supported = 1; + xstream_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; + xstream_seek(s, cap_len); + break; + + case CAP_SMARTCARD_TYPE: + log_debug("got CAP_SMARTCARD_TYPE"); + g_is_smartcard_redir_supported = 1; + xstream_seek(s, cap_len); + break; + } + } +} + +void devredir_proc_client_devlist_announce_req(struct stream *s) +{ + int i; + int j; + tui32 device_count; + tui32 device_type; + tui32 device_data_len; + char preferred_dos_name[9]; + + /* get number of devices being announced */ + xstream_rd_u32_le(s, device_count); + + log_debug("num of devices announced: %d", device_count); + + for (i = 0; i < device_count; i++) + { + xstream_rd_u32_le(s, device_type); + xstream_rd_u32_le(s, g_device_id); + + switch (device_type) + { + case RDPDR_DTYP_FILESYSTEM: + /* get preferred DOS name */ + for (j = 0; j < 8; j++) + { + preferred_dos_name[j] = *s->p++; + } + + /* DOS names that are 8 chars long are not NULL terminated */ + preferred_dos_name[8] = 0; + + /* get device data len */ + xstream_rd_u32_le(s, device_data_len); + if (device_data_len) + { + xstream_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, + preferred_dos_name, + device_data_len, g_full_name_for_filesystem); + + devredir_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, preferred_dos_name); + break; + + case RDPDR_DTYP_SMARTCARD: + /* get preferred DOS name */ + for (j = 0; j < 8; j++) + { + preferred_dos_name[j] = *s->p++; + } + + /* DOS names that are 8 chars long are not NULL terminated */ + preferred_dos_name[8] = 0; + + /* for smart cards, device data len always 0 */ + + log_debug("device_type=SMARTCARD device_id=0x%x dosname=%s " + "device_data_len=%d", + g_device_id, preferred_dos_name, device_data_len); + + devredir_send_server_device_announce_resp(g_device_id); + scard_device_announce(g_device_id); + break; + + /* we don't yet support these devices */ + case RDPDR_DTYP_SERIAL: + case RDPDR_DTYP_PARALLEL: + case RDPDR_DTYP_PRINT: + 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; + + xstream_rd_u32_le(s, DeviceId); + xstream_rd_u32_le(s, CompletionId); + xstream_rd_u32_le(s, IoStatus); + + /* LK_TODO need to check for IoStatus */ + + log_debug("entered: IoStatus=0x%x CompletionId=%d", IoStatus, CompletionId); + + if ((irp = devredir_irp_find(CompletionId)) == NULL) + { + log_error("IRP with completion ID %d not found", CompletionId); + return; + } + + /* if callback has been set, call it */ + if (irp->callback) + { + (*irp->callback)(s, irp, DeviceId, CompletionId, IoStatus); + goto done; + } + + switch (irp->completion_type) + { + case CID_CREATE_DIR_REQ: + log_debug("got 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 = devredir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_enum_dir_done(fuse_data->data_ptr, + IoStatus); + free(fuse_data); + } + devredir_irp_delete(irp); + return; + } + + xstream_rd_u32_le(s, irp->FileId); + log_debug("got CID_CREATE_DIR_REQ IoStatus=0x%x FileId=%d", + IoStatus, irp->FileId); + + dev_redir_send_drive_dir_request(irp, DeviceId, 1, irp->pathname); + break; + + case CID_CREATE_OPEN_REQ: + xstream_rd_u32_le(s, irp->FileId); + log_debug("got CID_CREATE_OPEN_REQ IoStatus=0x%x FileId=%d", + IoStatus, irp->FileId); + fuse_data = devredir_fuse_data_dequeue(irp); + xfuse_devredir_cb_open_file(fuse_data->data_ptr, + DeviceId, irp->FileId); + if (irp->type == S_IFDIR) + devredir_irp_delete(irp); + break; + + case CID_READ: + log_debug("got CID_READ"); + xstream_rd_u32_le(s, Length); + fuse_data = devredir_fuse_data_dequeue(irp); + if (fuse_data == NULL) + log_error("fuse_data is NULL"); + xfuse_devredir_cb_read_file(fuse_data->data_ptr, s->p, Length); + break; + + case CID_WRITE: + log_debug("got CID_WRITE"); + xstream_rd_u32_le(s, Length); + fuse_data = devredir_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"); + log_debug("deleting irp with completion_id=%d comp_type=%d", + irp->CompletionId, irp->completion_type); + devredir_irp_delete(irp); + break; + + case CID_FILE_CLOSE: + log_debug("got CID_FILE_CLOSE"); + fuse_data = devredir_fuse_data_dequeue(irp); + xfuse_devredir_cb_file_close(fuse_data->data_ptr); + devredir_irp_delete(irp); + break; + + case CID_DIRECTORY_CONTROL: + log_debug("got CID_DIRECTORY_CONTROL"); + + dev_redir_proc_query_dir_response(irp, s, DeviceId, + CompletionId, IoStatus); + break; + + case CID_RMDIR_OR_FILE: + log_debug("got CID_RMDIR_OR_FILE"); + xstream_rd_u32_le(s, irp->FileId); + devredir_proc_cid_rmdir_or_file(irp, IoStatus); + return; + break; + + case CID_RMDIR_OR_FILE_RESP: + log_debug("got CID_RMDIR_OR_FILE_RESP"); + devredir_proc_cid_rmdir_or_file_resp(irp, IoStatus); + break; + + case CID_RENAME_FILE: + log_debug("got CID_RENAME_FILE"); + xstream_rd_u32_le(s, irp->FileId); + devredir_proc_cid_rename_file(irp, IoStatus); + return; + break; + + case CID_RENAME_FILE_RESP: + log_debug("got CID_RENAME_FILE_RESP"); + devredir_proc_cid_rename_file_resp(irp, IoStatus); + break; + + default: + log_error("got unknown CompletionID: DeviceId=0x%x " + "CompletionId=0x%x IoStatus=0x%x", + DeviceId, CompletionId, IoStatus); + break; + } + +done: + + if (fuse_data) + { + log_debug("free FUSE_DATA=%p", fuse_data); + free(fuse_data); + } + + log_debug("exiting"); +} + +void dev_redir_proc_query_dir_response(IRP *irp, + struct stream *s_in, + tui32 DeviceId, + tui32 CompletionId, + tui32 IoStatus) +{ + 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; + +#ifdef USE_SHORT_NAMES_IN_DIR_LISTING + tui32 EaSize; + tui8 ShortNameLength; + tui8 Reserved; +#endif + + char filename[256]; + int i = 0; + + xstream_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 = devredir_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->CompletionId, + 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"); + + xstream_rd_u32_le(s_in, NextEntryOffset); + xstream_seek(s_in, 4); /* FileIndex */ + xstream_rd_u64_le(s_in, CreationTime); + xstream_rd_u64_le(s_in, LastAccessTime); + xstream_rd_u64_le(s_in, LastWriteTime); + xstream_rd_u64_le(s_in, ChangeTime); + xstream_rd_u64_le(s_in, EndOfFile); + xstream_seek(s_in, 8); /* AllocationSize */ + xstream_rd_u32_le(s_in, FileAttributes); + xstream_rd_u32_le(s_in, FileNameLength); + +#ifdef USE_SHORT_NAMES_IN_DIR_LISTING + xstream_rd_u32_le(s_in, EaSize); + xstream_rd_u8(s_in, ShortNameLength); + xstream_rd_u8(s_in, Reserved); + xstream_seek(s_in, 23); /* ShortName in Unicode */ +#endif + devredir_cvt_from_unicode_len(filename, s_in->p, FileNameLength); + +#ifdef USE_SHORT_NAMES_IN_DIR_LISTING + i += 70 + 23 + FileNameLength; +#else + i += 64 + FileNameLength; +#endif + //log_debug("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); +#ifdef USE_SHORT_NAMES_IN_DIR_LISTING + //log_debug("ShortNameLength: %d", ShortNameLength); +#endif + //log_debug("FileNameLength: %d", FileNameLength); + log_debug("FileName: %s", filename); + + if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL) + { + log_error("system out of memory"); + fuse_data = devredir_fuse_data_peek(irp); + xfuse_devredir_cb_enum_dir(fuse_data->data_ptr, NULL); + return; + } + + strcpy(xinode->name, filename); + xinode->size = (size_t) EndOfFile; + xinode->mode = WINDOWS_TO_LINUX_FILE_PERM(FileAttributes); + xinode->atime = WINDOWS_TO_LINUX_TIME(LastAccessTime); + xinode->mtime = WINDOWS_TO_LINUX_TIME(LastWriteTime); + xinode->ctime = WINDOWS_TO_LINUX_TIME(CreationTime); + + /* add this entry to xrdp file system */ + fuse_data = devredir_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; + + log_debug("fusep=%p", fusep); + + if ((irp = devredir_irp_new()) == NULL) + return -1; + + /* cvt / to windows compatible \ */ + devredir_cvt_slash(path); + + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_CREATE_DIR_REQ; + irp->DeviceId = device_id; + strcpy(irp->pathname, path); + devredir_fuse_data_enqueue(irp, fusep); + + DesiredAccess = DA_FILE_READ_DATA | DA_SYNCHRONIZE; + CreateOptions = CO_FILE_DIRECTORY_FILE | CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateDisposition = CD_FILE_OPEN; + + rval = dev_redir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + CreateDisposition, + irp->CompletionId); + + 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, char *gen_buf) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval; + IRP *irp; + + log_debug("device_id=%d path=%s mode=0x%x", device_id, path, mode); + + if ((irp = devredir_irp_new()) == NULL) + return -1; + + if (type & OP_RENAME_FILE) + { + irp->completion_type = CID_RENAME_FILE; + strcpy(irp->gen_buf, gen_buf); + } + else + { + irp->completion_type = CID_CREATE_OPEN_REQ; + } + + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + strcpy(irp->pathname, path); + devredir_fuse_data_enqueue(irp, fusep); + + if (mode & O_CREAT) + { + log_debug("open file in O_CREAT"); + DesiredAccess = 0x0016019f; /* got this value from windows */ + + if (type & S_IFDIR) + { + log_debug("creating dir"); + CreateOptions = CO_FILE_DIRECTORY_FILE | CO_FILE_SYNCHRONOUS_IO_NONALERT; + irp->type = S_IFDIR; + } + else + { + log_debug("creating file"); + CreateOptions = 0x44; /* got this value from windows */ + } + + //CreateDisposition = CD_FILE_CREATE; + CreateDisposition = 0x02; /* got this value from windows */ + } + else + { + log_debug("open file in O_RDWR"); +#if 1 + /* without the 0x00000010 rdesktop opens files in */ + /* O_RDONLY instead of O_RDWR mode */ + DesiredAccess = DA_FILE_READ_DATA | DA_FILE_WRITE_DATA | DA_SYNCHRONIZE | 0x00000010; + CreateOptions = CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateDisposition = CD_FILE_OPEN; // WAS 1 +#else + /* got this value from windows; the 0x00000010 was added by LK; */ + /* without this rdesktop opens files in O_RDONLY instead of */ + /* O_RDWR mode */ + DesiredAccess = 0x00120089 | 0x00000010; + CreateOptions = 0x20060; + CreateDisposition = 0x01; +#endif + } + + rval = dev_redir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + CreateDisposition, + irp->CompletionId); + + return rval; +} + +int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId) +{ + IRP *irp; + + log_debug("entered: fusep=%p device_id=%d FileId=%d", + fusep, device_id, FileId); + +#if 0 + if ((irp = devredir_irp_new()) == NULL) + return -1; + + irp->CompletionId = g_completion_id++; +#else + if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL) + { + log_error("no IRP found with FileId = %d", FileId); + return -1; + } +#endif + irp->completion_type = CID_FILE_CLOSE; + irp->DeviceId = device_id; + devredir_fuse_data_enqueue(irp, fusep); + + return dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + device_id, + FileId, + irp->CompletionId, + IRP_MJ_CLOSE, + 0, 32); +} + +/** + * Remove (delete) a directory or file + *****************************************************************************/ + +int devredir_rmdir_or_file(void *fusep, tui32 device_id, char *path, int mode) +{ + tui32 DesiredAccess; + tui32 CreateOptions; + tui32 CreateDisposition; + int rval; + IRP *irp; + + if ((irp = devredir_irp_new()) == NULL) + return -1; + + irp->CompletionId = g_completion_id++; + irp->completion_type = CID_RMDIR_OR_FILE; + irp->DeviceId = device_id; + strcpy(irp->pathname, path); + devredir_fuse_data_enqueue(irp, fusep); + + //DesiredAccess = DA_DELETE | DA_FILE_READ_ATTRIBUTES | DA_SYNCHRONIZE; + DesiredAccess = 0x00100080; /* got this value from windows */ + + //CreateOptions = CO_FILE_DELETE_ON_CLOSE | CO_FILE_DIRECTORY_FILE | + // CO_FILE_SYNCHRONOUS_IO_NONALERT; + CreateOptions = 0x020; /* got this value from windows */ + + //CreateDisposition = CD_FILE_OPEN; // WAS 1 + CreateDisposition = 0x01; /* got this value from windows */ + + rval = dev_redir_send_drive_create_request(device_id, path, + DesiredAccess, CreateOptions, + CreateDisposition, + irp->CompletionId); + + 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; + + xstream_new(s, 1024); + + if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL) + { + log_error("no IRP found with FileId = %d", FileId); + xfuse_devredir_cb_read_file(fusep, NULL, 0); + return -1; + } + + irp->completion_type = CID_READ; + devredir_fuse_data_enqueue(irp, fusep); + devredir_insert_DeviceIoRequest(s, + DeviceId, + FileId, + irp->CompletionId, + IRP_MJ_READ, + 0); + + xstream_wr_u32_le(s, Length); + xstream_wr_u64_le(s, Offset); + xstream_seek(s, 20); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + 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; + + log_debug("DeviceId=%d FileId=%d Length=%d Offset=%lld", + DeviceId, FileId, Length, Offset); + + xstream_new(s, 1024 + Length); + + if ((irp = devredir_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; + devredir_fuse_data_enqueue(irp, fusep); + devredir_insert_DeviceIoRequest(s, + DeviceId, + FileId, + irp->CompletionId, + IRP_MJ_WRITE, + 0); + + xstream_wr_u32_le(s, Length); + xstream_wr_u64_le(s, Offset); + xstream_seek(s, 20); /* padding */ + + /* now insert real data */ + xstream_copyin(s, buf, Length); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + return 0; +} + +/****************************************************************************** +** FIFO for FUSE_DATA ** +******************************************************************************/ + +/** + * Return FUSE_DATA at the head of the queue without removing it + * + * @return FUSE_DATA on success, or NULL on failure + *****************************************************************************/ + +void *devredir_fuse_data_peek(IRP *irp) +{ + log_debug("returning %p", irp->fd_head); + return irp->fd_head; +} + +/** + * Return oldest FUSE_DATA from queue + * + * @return FUSE_DATA on success, NULL on failure + *****************************************************************************/ + +void *devredir_fuse_data_dequeue(IRP *irp) +{ + FUSE_DATA *head; + + if ((irp == NULL) || (irp->fd_head == NULL)) + { + log_debug("+++ returning 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; + log_debug("+++ returning FUSE_DATA=%p containing FUSE_INFO=%p", + head, head->data_ptr); + return head; + } + + /* more than one element in queue */ + head = irp->fd_head; + irp->fd_head = head->next; + log_debug("+++ returning FUSE_DATA=%p containing FUSE_INFO=%p", + head, head->data_ptr); + return head; +} + +/** + * Insert specified FUSE_DATA at the end of our queue + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int devredir_fuse_data_enqueue(IRP *irp, void *vp) +{ + 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; + log_debug("+++ inserted FUSE_DATA=%p containing FUSE_INFO=%p at head", + fd, vp); + return 0; + } + + /* queue is not empty, insert at tail end */ + tail = irp->fd_tail; + tail->next = fd; + irp->fd_tail = fd; + log_debug("+++ inserted FUSE_DATA=%p containing FUSE_INFO=%p at tail", + fd, vp); + return 0; +} + +/****************************************************************************** +** miscellaneous stuff ** +******************************************************************************/ + +void devredir_insert_DeviceIoRequest(struct stream *s, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + tui32 MajorFunction, + tui32 MinorFunction) +{ + /* setup DR_DEVICE_IOREQUEST header */ + xstream_wr_u16_le(s, RDPDR_CTYP_CORE); + xstream_wr_u16_le(s, PAKID_CORE_DEVICE_IOREQUEST); + xstream_wr_u32_le(s, DeviceId); + xstream_wr_u32_le(s, FileId); + xstream_wr_u32_le(s, CompletionId); + xstream_wr_u32_le(s, MajorFunction); + xstream_wr_u32_le(s, MinorFunction); +} + +/** + * Convert / to windows compatible \ + *****************************************************************************/ + +void devredir_cvt_slash(char *path) +{ + char *cptr = path; + + while (*cptr != 0) + { + if (*cptr == '/') + *cptr = '\\'; + cptr++; + } +} + +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 devredir_insert_RDPDR_header(struct stream *s, tui16 Component, + tui16 PacketId) +{ + xstream_wr_u16_le(s, Component); + xstream_wr_u16_le(s, PacketId); +} + +void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus) +{ + struct stream *s; + int bytes; + + if (IoStatus != NT_STATUS_SUCCESS) + { + FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus); + free(fuse_data); + } + devredir_irp_delete(irp); + return; + } + + xstream_new(s, 1024); + + irp->completion_type = CID_RMDIR_OR_FILE_RESP; + devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, + irp->CompletionId, + IRP_MJ_SET_INFORMATION, 0); + + xstream_wr_u32_le(s, FileDispositionInformation); + xstream_wr_u32_le(s, 0); /* length is zero */ + xstream_seek(s, 24); /* padding */ + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + return; +} + +void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus) +{ + FUSE_DATA *fuse_data; + + fuse_data = devredir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus); + free(fuse_data); + } + + if (IoStatus != NT_STATUS_SUCCESS) + { + devredir_irp_delete(irp); + return; + } + + irp->completion_type = CID_CLOSE; + dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); +} + +void devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus) +{ + struct stream *s; + int bytes; + int sblen; /* SetBuffer length */ + int flen; /*FileNameLength */ + + + if (IoStatus != NT_STATUS_SUCCESS) + { + log_debug("rename returned with IoStatus=0x%x", IoStatus); + + FUSE_DATA *fuse_data = devredir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus); + free(fuse_data); + } + devredir_irp_delete(irp); + return; + } + + xstream_new(s, 1024); + + irp->completion_type = CID_RENAME_FILE_RESP; + devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId, + irp->CompletionId, + IRP_MJ_SET_INFORMATION, 0); + + flen = strlen(irp->gen_buf) * 2 + 2; + sblen = 6 + flen; + + xstream_wr_u32_le(s, FileRenameInformation); + xstream_wr_u32_le(s, sblen); /* Length */ + xstream_seek(s, 24); /* padding */ + xstream_wr_u8(s, 1); /* ReplaceIfExists */ + xstream_wr_u8(s, 0); /* RootDirectory */ + xstream_wr_u32_le(s, flen); /* FileNameLength */ + + /* filename in unicode */ + devredir_cvt_to_unicode(s->p, irp->gen_buf); + xstream_seek(s, flen); + + /* send to client */ + bytes = xstream_len(s); + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + return; +} + +void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus) +{ + FUSE_DATA *fuse_data; + + log_debug("entered"); + + fuse_data = devredir_fuse_data_dequeue(irp); + if (fuse_data) + { + xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus); + free(fuse_data); + } + + if (IoStatus != NT_STATUS_SUCCESS) + { + devredir_irp_delete(irp); + return; + } + + irp->completion_type = CID_CLOSE; + dev_redir_send_drive_close_request(RDPDR_CTYP_CORE, + PAKID_CORE_DEVICE_IOREQUEST, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_CLOSE, 0, 32); +} diff --git a/sesman/chansrv/devredir.h b/sesman/chansrv/devredir.h index 3ec08a51..da49350b 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 LK.Rashinkar@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,22 +18,322 @@ * limitations under the License. */ +// LK_TODO dev_redir_xxx should become devredir_xxx + #if !defined(DEVREDIR_H) #define DEVREDIR_H -#include "arch.h" -#include "parse.h" - -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); +#include "irp.h" + +#define USE_SHORT_NAMES_IN_DIR_LISTING + +void *devredir_fuse_data_peek(IRP *irp); +void *devredir_fuse_data_dequeue(IRP *irp); +int devredir_fuse_data_enqueue(IRP *irp, void *vp); + +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 devredir_send_server_device_announce_resp(tui32 device_id); + +void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id, + tui32 InitialQuery, char *Path); + +int dev_redir_send_drive_create_request(tui32 device_id, char *path, + tui32 DesiredAccess, + tui32 CreateOptions, + tui32 CreateDisposition, + tui32 completion_id); + +int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + tui32 MajorFunction, + tui32 MinorFunc, + int pad_len); + +void devredir_proc_client_devlist_announce_req(struct stream *s); +void dev_redir_proc_client_core_cap_resp(struct stream *s); +void dev_redir_proc_device_iocompletion(struct stream *s); + +void dev_redir_proc_query_dir_response(IRP *irp, + struct stream *s, + tui32 DeviceId, + tui32 CompletionId, + tui32 IoStatus); + +/* misc stuff */ +void devredir_insert_DeviceIoRequest(struct stream *s, + tui32 DeviceId, + tui32 FileId, + tui32 CompletionId, + tui32 MajorFunction, + tui32 MinorFunction); + +void devredir_cvt_slash(char *path); +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 devredir_insert_RDPDR_header(struct stream *s, tui16 Component, + tui16 PacketId); + +void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus); +void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus); +void devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus); +void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus); + +/* 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, char *gen_buf); + +int devredir_file_close(void *fusep, tui32 device_id, tui32 file_id); + +int dev_redir_file_read(void *fusep, tui32 device_id, tui32 FileId, + tui32 Length, tui64 Offset); + +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 + */ + +enum CREATE_OPTIONS +{ + CO_FILE_DIRECTORY_FILE = 0x00000001, + CO_FILE_WRITE_THROUGH = 0x00000002, + CO_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020, + CO_FILE_DELETE_ON_CLOSE = 0x00001000 +}; + +/* + * CreateDispositions Mask [MS-SMB2] section 2.2.13 + */ + +#define CD_FILE_SUPERSEDE 0x00000000 +#define CD_FILE_OPEN 0x00000001 +#define CD_FILE_CREATE 0x00000002 +#define CD_FILE_OPEN_IF 0x00000003 +#define CD_FILE_OVERWRITE 0x00000004 +#define CD_FILE_OVERWRITE_IF 0x00000005 + +/* + * Device I/O Request MajorFunction definitions + */ + +#define IRP_MJ_CREATE 0x00000000 +#define IRP_MJ_CLOSE 0x00000002 +#define IRP_MJ_READ 0x00000003 +#define IRP_MJ_WRITE 0x00000004 +#define IRP_MJ_DEVICE_CONTROL 0x0000000E +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0000000A +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0000000B +#define IRP_MJ_QUERY_INFORMATION 0x00000005 +#define IRP_MJ_SET_INFORMATION 0x00000006 +#define IRP_MJ_DIRECTORY_CONTROL 0x0000000C +#define IRP_MJ_LOCK_CONTROL 0x00000011 + +/* + * Device I/O Request MinorFunction definitions + * + * Only valid when MajorFunction code = IRP_MJ_DIRECTORY_CONTROL + */ + +#define IRP_MN_QUERY_DIRECTORY 0x00000001 +#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x00000002 + +/* + * NTSTATUS codes (used by IoStatus) + */ + +#define NT_STATUS_SUCCESS 0x00000000 +#define NT_STATUS_UNSUCCESSFUL 0xC0000001 + +/* + * File system ioctl codes + * MS-FSCC section 2.3 FSCTL Structures + */ +#define FSCTL_DELETE_OBJECT_ID 0x900a0 + + +/* + * CompletionID types, used in IRPs to indicate I/O operation + */ + +enum COMPLETION_ID +{ + CID_CREATE_DIR_REQ = 1, + CID_DIRECTORY_CONTROL, + CID_CREATE_OPEN_REQ, + CID_READ, + CID_WRITE, + CID_CLOSE, + CID_FILE_CLOSE, + CID_RMDIR_OR_FILE, + CID_RMDIR_OR_FILE_RESP, + CID_RENAME_FILE, + CID_RENAME_FILE_RESP +}; + +enum FS_INFORMATION_CLASS +{ + FileBasicInformation = 0x00000004, /* set atime, mtime, ctime etc */ + FileEndOfFileInformation = 0x00000014, /* set EOF info */ + FileDispositionInformation = 0x0000000D, /* mark a file for deletion */ + FileRenameInformation = 0x0000000A, /* rename a file */ + FileAllocationInformation = 0x00000013 /* set file allocation size */ +}; + +/* + * constants for drive dir query + */ + +/* Basic information about a file or directory. Basic information is */ +/* defined as the file's name, time stamp, and size, or its attributes */ +#define FileDirectoryInformation 0x00000001 + +/* Full information about a file or directory. Full information is defined */ +/* as all the basic information, plus extended attribute size. */ +#define FileFullDirectoryInformation 0x00000002 + +/* Basic information plus extended attribute size and short name */ +/* about a file or directory. */ +#define FileBothDirectoryInformation 0x00000003 + +/* Detailed information on the names of files in a directory. */ +#define FileNamesInformation 0x0000000C + +/* + * NTSTATUS Codes of interest to us + */ + +/* No more files were found which match the file specification */ +#define STATUS_NO_MORE_FILES 0x80000006 + +/* Windows file attributes */ +#define W_FILE_ATTRIBUTE_DIRECTORY 0x00000010 +#define W_FILE_ATTRIBUTE_READONLY 0x00000001 + +#define WINDOWS_TO_LINUX_FILE_PERM(_a) \ + (((_a) & W_FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR | 0100 : S_IFREG) |\ + (((_a) & W_FILE_ATTRIBUTE_READONLY) ? 0444 : 0644) + +/* 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; + +#define OP_RENAME_FILE 0x01 #endif diff --git a/sesman/chansrv/irp.c b/sesman/chansrv/irp.c new file mode 100644 index 00000000..5c21fe0f --- /dev/null +++ b/sesman/chansrv/irp.c @@ -0,0 +1,241 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com + * + * 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. + * + */ + +/* + * manage I/O for redirected file system and devices + */ + +#include "parse.h" +#include "os_calls.h" +#include "irp.h" + +/* 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]: IRP %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ +} + +#define log_info(_params...) \ +{ \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: IRP %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +#define log_debug(_params...) \ +{ \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: IRP %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +IRP *g_irp_head = NULL; + +/** + * Create a new IRP and append to linked list + * + * @return new IRP or NULL on error + *****************************************************************************/ + +IRP * devredir_irp_new() +{ + IRP *irp; + IRP *irp_last; + + log_debug("entered"); + + /* create new IRP */ + if ((irp = g_malloc(sizeof(IRP), 1)) == NULL) + { + log_error("system out of memory!"); + return NULL; + } + + /* insert at end of linked list */ + if ((irp_last = devredir_irp_get_last()) == NULL) + { + /* list is empty, this is the first entry */ + g_irp_head = irp; + } + else + { + irp_last->next = irp; + irp->prev = irp_last; + } + + log_debug("new IRP=%p", irp); + return irp; +} + +/** + * Delete specified IRP from linked list + * + * @return 0 on success, -1 on failure + *****************************************************************************/ + +int devredir_irp_delete(IRP *irp) +{ + IRP *lirp = g_irp_head; + + if ((irp == NULL) || (lirp == NULL)) + return -1; + + log_debug("irp=%p completion_id=%d type=%d", + irp, irp->CompletionId, irp->completion_type); + + devredir_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 */ + g_free(lirp); + g_irp_head = NULL; + devredir_irp_dump(); // LK_TODO + return 0; + } + + lirp->next->prev = NULL; + g_irp_head = lirp->next; + g_free(lirp); + } + else if (lirp->next == NULL) + { + /* we are at tail of linked list */ + lirp->prev->next = NULL; + g_free(lirp); + } + else + { + /* we are in between */ + lirp->prev->next = lirp->next; + lirp->next->prev = lirp->prev; + g_free(lirp); + } + + devredir_irp_dump(); // LK_TODO + + return 0; +} + +/** + * Return IRP containing specified completion_id + *****************************************************************************/ + +IRP *devredir_irp_find(tui32 completion_id) +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->CompletionId == completion_id) + { + log_debug("returning irp=%p", irp); + return irp; + } + + irp = irp->next; + } + + log_debug("returning irp=NULL"); + return NULL; +} + +IRP * devredir_irp_find_by_fileid(tui32 FileId) +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->FileId == FileId) + { + log_debug("returning irp=%p", irp); + return irp; + } + + irp = irp->next; + } + + log_debug("returning irp=NULL"); + return NULL; +} + +/** + * Return last IRP in linked list + *****************************************************************************/ + +IRP * devredir_irp_get_last() +{ + IRP *irp = g_irp_head; + + while (irp) + { + if (irp->next == NULL) + break; + + irp = irp->next; + } + + log_debug("returning irp=%p", irp); + return irp; +} + +void devredir_irp_dump() +{ + IRP *irp = g_irp_head; + + log_debug("------- dumping IRPs --------"); + while (irp) + { + log_debug(" completion_id=%d\tcompletion_type=%d\tFileId=%d", + irp->CompletionId, irp->completion_type, irp->FileId); + + irp = irp->next; + } + log_debug("------- dumping IRPs done ---"); +} diff --git a/sesman/chansrv/irp.h b/sesman/chansrv/irp.h new file mode 100644 index 00000000..ccab5801 --- /dev/null +++ b/sesman/chansrv/irp.h @@ -0,0 +1,64 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com + * + * 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. + * + */ + +/* + * manage I/O for redirected file system and devices + */ + +#ifndef __IRP_H +#define __IRP_H + +typedef struct fuse_data FUSE_DATA; +struct fuse_data +{ + void *data_ptr; + FUSE_DATA *next; +}; + +/* An I/O Resource Packet to track I/O calls */ + +typedef struct irp IRP; + +struct irp +{ + tui32 CompletionId; /* unique number */ + tui32 DeviceId; /* identifies remote device */ + tui32 FileId; /* RDP client provided unique number */ + char completion_type; /* describes I/O type */ + char pathname[256]; /* absolute pathname */ + char gen_buf[1024]; /* for general use */ + int type; + FUSE_DATA *fd_head; /* point to first FUSE opaque object */ + FUSE_DATA *fd_tail; /* point to last FUSE opaque object */ + IRP *next; /* point to next IRP */ + IRP *prev; /* point to previous IRP */ + int scard_index; /* used to smart card to locate dev */ + + void (*callback)(struct stream *s, IRP *irp, tui32 DeviceId, + tui32 CompletionId, tui32 IoStatus); +}; + +IRP * devredir_irp_new(); +int devredir_irp_delete(IRP *irp); +IRP * devredir_irp_find(tui32 completion_id); +IRP * devredir_irp_find_by_fileid(tui32 FileId); +IRP * devredir_irp_get_last(); +void devredir_irp_dump(); + +#endif /* end ifndef __IRP_H */ diff --git a/sesman/chansrv/pulse/Makefile b/sesman/chansrv/pulse/Makefile new file mode 100644 index 00000000..6efbfc16 --- /dev/null +++ b/sesman/chansrv/pulse/Makefile @@ -0,0 +1,18 @@ + + +#PULSE_DIR=/home/jay/temp/pulseaudio-0.9.21 +#PULSE_DIR=/home/jay/pulseaudio-0.9.22 +#PULSE_DIR=/home/jay/pulseaudio-0.9.21 +PULSE_DIR=/home/jay/pulseaudio-2.0 + +OBJS = module-xrdp-sink.o + +CFLAGS = -Wall -O2 -I$(PULSE_DIR) -I$(PULSE_DIR)/src -DHAVE_CONFIG_H -fPIC + +all: module-xrdp-sink.so + +module-xrdp-sink.so: $(OBJS) + $(CC) $(LDFLAGS) -shared -o module-xrdp-sink.so $(OBJS) + +clean: + rm -f $(OBJS) module-xrdp-sink.so diff --git a/sesman/chansrv/pulse/module-xrdp-sink-symdef.h b/sesman/chansrv/pulse/module-xrdp-sink-symdef.h new file mode 100644 index 00000000..14443d31 --- /dev/null +++ b/sesman/chansrv/pulse/module-xrdp-sink-symdef.h @@ -0,0 +1,29 @@ +#ifndef foomodulexrdpsinksymdeffoo +#define foomodulexrdpsinksymdeffoo + +#include <pulsecore/core.h> +#include <pulsecore/module.h> +#include <pulsecore/macro.h> + +#define pa__init module_xrdp_sink_LTX_pa__init +#define pa__done module_xrdp_sink_LTX_pa__done +#define pa__get_author module_xrdp_sink_LTX_pa__get_author +#define pa__get_description module_xrdp_sink_LTX_pa__get_description +#define pa__get_usage module_xrdp_sink_LTX_pa__get_usage +#define pa__get_version module_xrdp_sink_LTX_pa__get_version +#define pa__get_deprecated module_xrdp_sink_LTX_pa__get_deprecated +#define pa__load_once module_xrdp_sink_LTX_pa__load_once +#define pa__get_n_used module_xrdp_sink_LTX_pa__get_n_used + +int pa__init(pa_module*m); +void pa__done(pa_module*m); +int pa__get_n_used(pa_module*m); + +const char* pa__get_author(void); +const char* pa__get_description(void); +const char* pa__get_usage(void); +const char* pa__get_version(void); +const char* pa__get_deprecated(void); +pa_bool_t pa__load_once(void); + +#endif diff --git a/sesman/chansrv/pulse/module-xrdp-sink.c b/sesman/chansrv/pulse/module-xrdp-sink.c new file mode 100644 index 00000000..f6650635 --- /dev/null +++ b/sesman/chansrv/pulse/module-xrdp-sink.c @@ -0,0 +1,581 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * pulse sink + * + * Copyright (C) Jay Sorg 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. + */ + +/* + * see pulse-notes.txt +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <sys/ioctl.h> +#include <poll.h> + +#include <pulse/rtclock.h> +#include <pulse/timeval.h> +#include <pulse/xmalloc.h> +//#include <pulse/i18n.h> + +#include <pulsecore/core-error.h> +#include <pulsecore/sink.h> +#include <pulsecore/module.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> +#include <pulsecore/log.h> +#include <pulsecore/thread.h> +#include <pulsecore/thread-mq.h> +#include <pulsecore/rtpoll.h> + +#include "module-xrdp-sink-symdef.h" + +PA_MODULE_AUTHOR("Jay Sorg"); +PA_MODULE_DESCRIPTION("xrdp sink"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "sink_name=<name for the sink> " + "sink_properties=<properties for the sink> " + "format=<sample format> " + "rate=<sample rate>" + "channels=<number of channels> " + "channel_map=<channel map>"); + +#define DEFAULT_SINK_NAME "xrdp" +#define BLOCK_USEC 30000 +//#define BLOCK_USEC (PA_USEC_PER_SEC * 2) +#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_socket_%d" + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink; + + pa_thread *thread; + pa_thread_mq thread_mq; + pa_rtpoll *rtpoll; + + pa_usec_t block_usec; + pa_usec_t timestamp; + pa_usec_t failed_connect_time; + pa_usec_t last_send_time; + + int fd; /* unix domain socket connection to xrdp chansrv */ + int display_num; + int skip_bytes; + int got_max_latency; + +}; + +static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", + "format", + "rate", + "channels", + "channel_map", + NULL +}; + +static int close_send(struct userdata *u); + +static int sink_process_msg(pa_msgobject *o, int code, void *data, + int64_t offset, pa_memchunk *chunk) { + + struct userdata *u = PA_SINK(o)->userdata; + pa_usec_t now; + long lat; + + pa_log_debug("sink_process_msg: code %d", code); + + switch (code) { + + case PA_SINK_MESSAGE_SET_VOLUME: /* 3 */ + break; + + case PA_SINK_MESSAGE_SET_MUTE: /* 6 */ + break; + + case PA_SINK_MESSAGE_GET_LATENCY: /* 7 */ + now = pa_rtclock_now(); + lat = u->timestamp > now ? u->timestamp - now : 0ULL; + pa_log_debug("sink_process_msg: lat %ld", lat); + *((pa_usec_t*) data) = lat; + return 0; + + case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: /* 8 */ + break; + + case PA_SINK_MESSAGE_SET_STATE: /* 9 */ + if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) /* 0 */ { + pa_log("sink_process_msg: running"); + + u->timestamp = pa_rtclock_now(); + } else { + pa_log("sink_process_msg: not running"); + close_send(u); + } + break; + + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + +static void sink_update_requested_latency_cb(pa_sink *s) { + struct userdata *u; + size_t nbytes; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + u->block_usec = BLOCK_USEC; + //u->block_usec = pa_sink_get_requested_latency_within_thread(s); + pa_log("1 block_usec %d", u->block_usec); + + u->got_max_latency = 0; + if (u->block_usec == (pa_usec_t) -1) { + u->block_usec = s->thread_info.max_latency; + pa_log("2 block_usec %d", u->block_usec); + u->got_max_latency = 1; + } + + nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec); + pa_sink_set_max_rewind_within_thread(s, nbytes); + pa_sink_set_max_request_within_thread(s, nbytes); +} + +static void process_rewind(struct userdata *u, pa_usec_t now) { + size_t rewind_nbytes, in_buffer; + pa_usec_t delay; + + pa_assert(u); + + /* Figure out how much we shall rewind and reset the counter */ + rewind_nbytes = u->sink->thread_info.rewind_nbytes; + u->sink->thread_info.rewind_nbytes = 0; + + pa_assert(rewind_nbytes > 0); + pa_log_debug("Requested to rewind %lu bytes.", + (unsigned long) rewind_nbytes); + + if (u->timestamp <= now) + goto do_nothing; + + delay = u->timestamp - now; + in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); + + if (in_buffer <= 0) + goto do_nothing; + + if (rewind_nbytes > in_buffer) + rewind_nbytes = in_buffer; + + pa_sink_process_rewind(u->sink, rewind_nbytes); + u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); + u->skip_bytes += rewind_nbytes; + + pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); + return; + +do_nothing: + + pa_sink_process_rewind(u->sink, 0); +} + +struct header { + int code; + int bytes; +}; + +static int get_display_num_from_display(char *display_text) { + int index; + int mode; + int host_index; + int disp_index; + int scre_index; + int display_num; + char host[256]; + char disp[256]; + char scre[256]; + + if (display_text == NULL) { + return 0; + } + memset(host, 0, 256); + memset(disp, 0, 256); + memset(scre, 0, 256); + + index = 0; + host_index = 0; + disp_index = 0; + scre_index = 0; + mode = 0; + + while (display_text[index] != 0) { + if (display_text[index] == ':') { + mode = 1; + } else if (display_text[index] == '.') { + mode = 2; + } else if (mode == 0) { + host[host_index] = display_text[index]; + host_index++; + } else if (mode == 1) { + disp[disp_index] = display_text[index]; + disp_index++; + } else if (mode == 2) { + scre[scre_index] = display_text[index]; + scre_index++; + } + index++; + } + + host[host_index] = 0; + disp[disp_index] = 0; + scre[scre_index] = 0; + display_num = atoi(disp); + return display_num; +} + +static int data_send(struct userdata *u, pa_memchunk *chunk) { + char *data; + int bytes; + int sent; + int fd; + struct header h; + struct sockaddr_un s; + + if (u->fd == 0) { + if (u->failed_connect_time != 0) { + if (pa_rtclock_now() - u->failed_connect_time < 1000000) { + return 0; + } + } + fd = socket(PF_LOCAL, SOCK_STREAM, 0); + memset(&s, 0, sizeof(s)); + s.sun_family = AF_UNIX; + bytes = sizeof(s.sun_path) - 1; + snprintf(s.sun_path, bytes, CHANSRV_PORT_STR, u->display_num); + pa_log_debug("trying to conenct to %s", s.sun_path); + if (connect(fd, (struct sockaddr *)&s, + sizeof(struct sockaddr_un)) != 0) { + u->failed_connect_time = pa_rtclock_now(); + pa_log_debug("Connected failed"); + close(fd); + return 0; + } + u->failed_connect_time = 0; + pa_log("Connected ok fd %d", fd); + u->fd = fd; + } + + bytes = chunk->length; + pa_log_debug("bytes %d", bytes); + + /* from rewind */ + if (u->skip_bytes > 0) { + if (bytes > u->skip_bytes) { + bytes -= u->skip_bytes; + u->skip_bytes = 0; + } else { + u->skip_bytes -= bytes; + return bytes; + } + } + + h.code = 0; + h.bytes = bytes + 8; + if (send(u->fd, &h, 8, 0) != 8) { + pa_log("data_send: send failed"); + close(u->fd); + u->fd = 0; + return 0; + } else { + pa_log_debug("data_send: sent header ok bytes %d", bytes); + } + + data = (char*)pa_memblock_acquire(chunk->memblock); + data += chunk->index; + sent = send(u->fd, data, bytes, 0); + pa_memblock_release(chunk->memblock); + + if (sent != bytes) { + pa_log("data_send: send failed sent %d bytes %d", sent, bytes); + close(u->fd); + u->fd = 0; + return 0; + } + + return sent; +} + +static int close_send(struct userdata *u) { + struct header h; + + pa_log("close_send:"); + if (u->fd == 0) { + return 0; + } + h.code = 1; + h.bytes = 8; + if (send(u->fd, &h, 8, 0) != 8) { + pa_log("close_send: send failed"); + close(u->fd); + u->fd = 0; + return 0; + } else { + pa_log_debug("close_send: sent header ok"); + } + return 8; +} + +static void process_render(struct userdata *u, pa_usec_t now) { + pa_memchunk chunk; + int request_bytes; + + pa_assert(u); + if (u->got_max_latency) { + return; + } + pa_log_debug("process_render: u->block_usec %d", u->block_usec); + while (u->timestamp < now + u->block_usec) { + request_bytes = u->sink->thread_info.max_request; + request_bytes = MIN(request_bytes, 16 * 1024); + pa_sink_render(u->sink, request_bytes, &chunk); + data_send(u, &chunk); + pa_memblock_unref(chunk.memblock); + u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); + } +} + +static void thread_func(void *userdata) { + + struct userdata *u = userdata; + int ret; + pa_usec_t now; + + pa_assert(u); + + pa_log_debug("Thread starting up"); + + pa_thread_mq_install(&u->thread_mq); + + u->timestamp = pa_rtclock_now(); + + for (;;) { + + if (u->sink->thread_info.state == PA_SINK_RUNNING) { + + now = pa_rtclock_now(); + + if (u->sink->thread_info.rewind_requested) { + if (u->sink->thread_info.rewind_nbytes > 0) { + process_rewind(u, now); + } else { + pa_sink_process_rewind(u->sink, 0); + } + } + + if (u->timestamp <= now) { + pa_log_debug("thread_func: calling process_render"); + process_render(u, now); + } + + pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); + + } else { + pa_rtpoll_set_timer_disabled(u->rtpoll); + } + + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) { + goto fail; + } + + if (ret == 0) { + goto finish; + } + } + +fail: + /* If this was no regular exit from the loop we have to continue + * processing messages until we received PA_MESSAGE_SHUTDOWN */ + pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), + PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, + NULL, NULL); + pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); + +finish: + pa_log_debug("Thread shutting down"); +} + +int pa__init(pa_module*m) { + struct userdata *u = NULL; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma = NULL; + pa_sink_new_data data; + size_t nbytes; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, + PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, + pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "xrdp sink"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, + PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&data); + goto fail; + } + + u->sink = pa_sink_new(m->core, &data, + PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { + pa_log("Failed to create sink object."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->userdata = u; + + pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); + pa_sink_set_rtpoll(u->sink, u->rtpoll); + + u->block_usec = BLOCK_USEC; + pa_log("3 block_usec %d", u->block_usec); + nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); + pa_sink_set_max_rewind(u->sink, nbytes); + pa_sink_set_max_request(u->sink, nbytes); + + u->display_num = get_display_num_from_display(getenv("DISPLAY")); + +#if defined(PA_CHECK_VERSION) +#if PA_CHECK_VERSION(0, 9, 22) + if (!(u->thread = pa_thread_new("xrdp-sink", thread_func, u))) { +#else + if (!(u->thread = pa_thread_new(thread_func, u))) { +#endif +#else + if (!(u->thread = pa_thread_new(thread_func, u))) { +#endif + pa_log("Failed to create thread."); + goto fail; + } + + pa_sink_put(u->sink); + + pa_modargs_free(ma); + + return 0; + +fail: + if (ma) { + pa_modargs_free(ma); + } + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) { + return; + } + + if (u->sink) { + pa_sink_unlink(u->sink); + } + + if (u->thread) { + pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, + NULL, 0, NULL); + pa_thread_free(u->thread); + } + + pa_thread_mq_done(&u->thread_mq); + + if (u->sink) { + pa_sink_unref(u->sink); + } + + if (u->rtpoll) { + pa_rtpoll_free(u->rtpoll); + } + + pa_xfree(u); +} diff --git a/sesman/chansrv/pulse/pulse-notes.txt b/sesman/chansrv/pulse/pulse-notes.txt new file mode 100644 index 00000000..b5d51572 --- /dev/null +++ b/sesman/chansrv/pulse/pulse-notes.txt @@ -0,0 +1,72 @@ + +Pulse audio notes. + +to see what version of PA is on your machine +pulseaudio --version + +IMA ADPCM + +To build xrdp pulse sink, +get the pulse source that most closely matches your version on +your machine. Get the source from +http://freedesktop.org/software/pulseaudio/releases/ +run ./configure after extracting. I don't think you need to build it. +edit Makefile to point to your pulse source directory. + + + +PA always respawning + + To stop its respawning habit, open /etc/pulse/client.conf, change + autospawn = yes to autospawn = no, and set daemon-binary to /bin/true. + Make sure these lines are uncommented, like this: + +autospawn = no +daemon-binary = /bin/true + +xfreerdp -a 24 -z --plugin rdpsnd --data alsa:hw:0,0 -- 127.0.0.1 + + +to get ./configure on pulse source to run +apt-get install libsndfile1-dev +apt-get install libspeex-dev +apt-get install libspeexdsp-dev + +alsamixer +apt-get install alsa-utils + + +/etc/asound.conf +--------------------------------- +pcm.pulse { + type pulse +} + +ctl.pulse { + type pulse +} + +pcm.!default { + type pulse +} + +ctl.!default { + type pulse +} +--------------------------------- + + +/etc/pulse/default.pa +--------------------------------- +.nofail +.fail +load-module module-augment-properties +#load-module module-alsa-sink device=hw:0 +#load-module module-alsa-source device=hw:0 +#load-module module-pipe-sink +#load-module module-pipe-source +#load-module module-null-sink +load-module module-xrdp-sink +load-module module-native-protocol-unix +#load-module module-udev-detect tsched=0 +--------------------------------- diff --git a/sesman/chansrv/smartcard.c b/sesman/chansrv/smartcard.c new file mode 100644 index 00000000..3fdb6723 --- /dev/null +++ b/sesman/chansrv/smartcard.c @@ -0,0 +1,528 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com + * + * 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. + * + */ + +/* + * smartcard redirection support + */ + +#include "os_calls.h" +#include "smartcard.h" +#include "log.h" +#include "irp.h" +#include "devredir.h" + +/* + * TODO + * + * o need to query client for build number and determine whether we should use + * SCREDIR_VERSION_XP or SCREDIR_VERSION_LONGHORN + * + * o need to call scard_release_resources() + * + * o why is win 7 sending SCARD_IOCTL_ACCESS_STARTED_EVENT first + * 0000 00 01 00 00 04 00 00 00 e0 00 09 00 00 00 00 00 ................ + * 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + * 0020 28 b7 9d 02 + */ + +/* + * Notes: + * + * XP and Server 2003 use version SCREDIR_VERSION_XP functions 5 - 58 + * Vista and Server 2008 use version SCREDIR_VERSION_LONGHORN functions 5 - 64 + * if TS Client's build number is >= 4,034 use SCREDIR_VERSION_LONGHORN + */ + +/* module based logging */ +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_DEBUG +#endif + +#define log_error(_params...) \ +{ \ + g_write("[%10.10u]: SMART_CARD %s: %d : ERROR: ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ +} + +#define log_info(_params...) \ +{ \ + if (LOG_INFO <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: SMART_CARD %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +#define log_debug(_params...) \ +{ \ + if (LOG_DEBUG <= LOG_LEVEL) \ + { \ + g_write("[%10.10u]: SMART_CARD %s: %d : ", \ + g_time3(), __func__, __LINE__); \ + g_writeln (_params); \ + } \ +} + +/* [MS-RDPESC] 3.1.4 */ +#define SCARD_IOCTL_ESTABLISH_CONTEXT 0x00090014 /* EstablishContext */ +#define SCARD_IOCTL_RELEASE_CONTEXT 0x00090018 /* ReleaseContext */ +#define SCARD_IOCTL_IS_VALID_CONTEXT 0x0009001C /* IsValidContext */ +#define SCARD_IOCTL_LIST_READER_GROUPS 0x00090020 /* ListReaderGroups */ +#define SCARD_IOCTL_LIST_READERS_A 0x00090028 /* ListReaders ASCII */ +#define SCARD_IOCTL_LIST_READERS_W 0x0009002C /* ListReaders Wide */ +#define SCARD_IOCTL_INTRODUCE_READER_GROUP 0x00090050 /* IntroduceReaderGroup */ +#define SCARD_IOCTL_FORGET_READER_GROUP 0x00090058 /* ForgetReader */ +#define SCARD_IOCTL_INTRODUCE_READER 0x00090060 /* IntroduceReader */ +#define SCARD_IOCTL_FORGET_READER 0x00090068 /* IntroduceReader */ +#define SCARD_IOCTL_ADD_READER_TO_GROUP 0x00090070 /* AddReaderToGroup */ +#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup */ +#define SCARD_IOCTL_GET_STATUS_CHANGE 0x000900A0 /* GetStatusChangeA */ +#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */ +#define SCARD_IOCTL_CONNECT 0x000900AC /* ConnectA */ +#define SCARD_IOCTL_RECONNECT 0x000900B4 /* Reconnect */ +#define SCARD_IOCTL_DISCONNECT 0x000900B8 /* Disconnect */ +#define SCARD_IOCTL_BEGIN_TRANSACTION 0x000900BC /* BeginTransaction */ +#define SCARD_IOCTL_END_TRANSACTION 0x000900C0 /* EndTransaction */ +#define SCARD_IOCTL_STATE 0x000900C4 /* State */ +#define SCARD_IOCTL_STATUS 0x000900C8 /* StatusA */ +#define SCARD_IOCTL_TRANSMIT 0x000900D0 /* Transmit */ +#define SCARD_IOCTL_CONTROL 0x000900D4 /* Control */ +#define SCARD_IOCTL_GETATTRIB 0x000900D8 /* GetAttrib */ +#define SCARD_IOCTL_SETATTRIB 0x000900DC /* SetAttrib */ +#define SCARD_IOCTL_ACCESS_STARTED_EVENT 0x000900E0 /* SCardAccessStartedEvent */ +#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR 0x000900E8 /* LocateCardsByATR */ + +/* scope used in EstablishContextCall */ +#define SCARD_SCOPE_USER 0x00000000 +#define SCARD_SCOPE_TERMINAL 0x00000001 +#define SCARD_SCOPE_SYSTEM 0x00000002 + +#define MAX_SMARTCARDS 16 + +/* stores info about a smart card */ +typedef struct smartcard +{ + tui32 DeviceId; + char Context[16]; /* opaque context; save as passed to us */ + int Context_len; /* Context len in bytes */ +} SMARTCARD; + +SMARTCARD *smartcards[MAX_SMARTCARDS]; +int g_smartcards_inited = 0; + +extern tui32 g_completion_id; +extern int g_rdpdr_chan_id; /* in chansrv.c */ + +/* forward declarations specific to this file */ +static void scard_send_EstablishContext(IRP *irp); +static void scard_send_ListReaders(IRP *irp, int wide); +static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl); +static int scard_add_new_device(tui32 device_id); +static int scard_get_free_slot(); +static void scard_release_resources(); + +/****************************************************************************** +** non static functions ** +******************************************************************************/ + +void scard_device_announce(tui32 device_id) +{ + IRP *irp; + + log_debug("entered: device_id=%d", device_id); + + if (!g_smartcards_inited) + { + g_memset(&smartcards, 0, sizeof(smartcards)); + g_smartcards_inited = 1; + } + + if ((irp = devredir_irp_new()) == NULL) + { + log_error("system out of memory"); + return; + } + + irp->scard_index = scard_add_new_device(device_id); + if (irp->scard_index < 0) + { + log_debug("NOT adding smartcard with DeviceId=%d to list", device_id); + devredir_irp_delete(irp); + return; + } + + log_debug("added smartcard with DeviceId=%d to list", device_id); + + irp->CompletionId = g_completion_id++; + irp->DeviceId = device_id; + irp->callback = scard_handle_EstablishContext_Return; + + scard_send_EstablishContext(irp); + log_debug("leaving"); +} + +/****************************************************************************** +** callbacks into this module ** +******************************************************************************/ + +void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus) +{ + tui32 len; + int tmp; + SMARTCARD *sc; + + log_debug("entered"); + + /* sanity check */ + if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) + { + log_error("DeviceId/CompletionId do not match those in IRP"); + return; + } + + if (IoStatus != 0) + { + log_error("failed to establish context - device not usable"); + /* LK_TODO delete irp and smartcard entry */ + return; + } + + sc = smartcards[irp->scard_index]; + + /* get OutputBufferLen */ + xstream_rd_u32_le(s, len); + + /* LK_TODO */ + g_hexdump(s->p, len); + + xstream_rd_u32_le(s, tmp); /* should be len 8, LE, V1 */ + xstream_rd_u32_le(s, tmp); /* marshalling flag */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, tmp); /* ?? */ + xstream_rd_u32_le(s, len); /* len of context in bytes */ + sc->Context_len = len; + xstream_copyout(sc->Context, s, len); + + if (LOG_LEVEL == LOG_DEBUG) + { + log_debug("dumping context (%d bytes)", sc->Context_len); + g_hexdump(sc->Context, sc->Context_len); + } + + irp->callback = scard_handle_ListReaders_Return; + scard_send_ListReaders(irp, 1); + + /* LK_TODO need to delete IRP */ + log_debug("leaving"); +} + +void scard_handle_ListReaders_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus) +{ + tui32 len; + + log_debug("entered"); + + /* sanity check */ + if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId)) + { + log_error("DeviceId/CompletionId do not match those in IRP"); + return; + } + + if (IoStatus != 0) + { + log_error("failed to list readers - device not usable"); + /* LK_TODO delete irp and smartcard entry */ + return; + } + + /* get OutputBufferLen */ + xstream_rd_u32_le(s, len); + + /* LK_TODO */ + log_debug("dumping %d bytes", len); + g_hexdump(s->p, len); + + log_debug("leaving"); +} + +/****************************************************************************** +** static functions local to this file ** +******************************************************************************/ + +/** + * + *****************************************************************************/ + +static void scard_send_EstablishContext(IRP *irp) +{ + struct stream *s; + int bytes; + + if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL) + return; + + xstream_wr_u32_le(s, 0x08); /* len */ + xstream_wr_u32_le(s, 0); /* unused */ + xstream_wr_u32_le(s, SCARD_SCOPE_SYSTEM); /* Ioctl specific data */ + xstream_wr_u32_le(s, 0); /* don't know what this is, */ + /* but Win7 is sending it */ + /* get stream len */ + bytes = xstream_len(s); + + /* InputBufferLength is number of bytes AFTER 20 byte padding */ + *(s->data + 28) = bytes - 56; + + /* send to client */ + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); +} + +/** + * + *****************************************************************************/ + +static void scard_send_ListReaders(IRP *irp, int wide) +{ + /* see [MS-RDPESC] 2.2.2.4 */ + + SMARTCARD *sc; + struct stream *s; + int bytes; + tui32 ioctl; + + if ((sc = smartcards[irp->scard_index]) == NULL) + { + log_error("smartcards[%d] is NULL", irp->scard_index); + return; + } + + ioctl = (wide > 0) ? SCARD_IOCTL_LIST_READERS_W : + SCARD_IOCTL_LIST_READERS_A; + + if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL) + return; + + xstream_wr_u32_le(s, 72); /* number of bytes to follow */ + xstream_seek(s, 0x1c); /* freerdp does not use this */ + + /* insert context */ + xstream_wr_u32_le(s, sc->Context_len); + xstream_copyin(s, sc->Context, sc->Context_len); + + xstream_wr_u32_le(s, 36); /* length of mszGroups */ + xstream_wr_u16_le(s, 0x0053); + xstream_wr_u16_le(s, 0x0043); + xstream_wr_u16_le(s, 0x0061); + xstream_wr_u16_le(s, 0x0072); + xstream_wr_u16_le(s, 0x0064); + xstream_wr_u16_le(s, 0x0024); + xstream_wr_u16_le(s, 0x0041); + xstream_wr_u16_le(s, 0x006c); + xstream_wr_u16_le(s, 0x006c); + xstream_wr_u16_le(s, 0x0052); + xstream_wr_u16_le(s, 0x0065); + xstream_wr_u16_le(s, 0x0061); + xstream_wr_u16_le(s, 0x0064); + xstream_wr_u16_le(s, 0x0065); + xstream_wr_u16_le(s, 0x0072); + xstream_wr_u16_le(s, 0x0073); + + xstream_wr_u32_le(s, 0x00); + + /* get stream len */ + bytes = xstream_len(s); + + /* InputBufferLength is number of bytes AFTER 20 byte padding */ + *(s->data + 28) = bytes - 56; + + /* send to client */ + send_channel_data(g_rdpdr_chan_id, s->data, bytes); + xstream_free(s); + + /* + scard_device_control: dumping 120 bytes of data + 0000 00 08 00 00 58 00 00 00 2c 00 09 00 00 00 00 00 ....X...,....... + 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H....... + 0030 04 00 00 00 00 00 02 00 24 00 00 00 04 00 02 00 ........$....... + 0040 00 00 00 00 ff ff ff ff 04 00 00 00 84 db 03 01 ................ + 0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$. + 0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e. + 0070 72 00 73 00 00 00 00 00 r.s..... + scard_device_control: output_len=2048 input_len=88 ioctl_code=0x9002c + */ + + /* + scard_device_control: dumping 120 bytes of data + 0000 00 08 00 00 80 00 00 00 14 00 09 00 00 00 00 00 ................ + 0010 2e 2e 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H....... + 0030 02 00 00 00 00 00 00 00 72 64 00 00 00 00 00 00 ........rd...... + 0040 81 27 00 00 00 00 00 00 04 00 00 00 84 b3 03 01 .'.............. + 0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$. + 0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e. + 0070 72 00 73 00 00 00 00 00 r.s..... + scard_device_control: output_len=2048 input_len=128 ioctl_code=0x90014 + */ +} + +/** + * Crate a new stream and insert specified IOCTL + * + * @param irp information about the I/O + * @param ioctl the IOCTL code + * + * @return stream with IOCTL inserted in it, NULL on error + *****************************************************************************/ + +static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl) +{ + /* + * format of device control request + * + * DeviceIoRequest + * u16 RDPDR_CTYP_CORE + * u16 PAKID_CORE_DEVICE_IOREQUEST + * u32 DeviceId + * u32 FileId + * u32 CompletionId + * u32 MajorFunction + * u32 MinorFunction + * + * u32 OutputBufferLength SHOULD be 2048 + * u32 InputBufferLength + * u32 IoControlCode + * 20 bytes padding + * xx bytes InputBuffer (variable) + */ + + struct stream *s; + + xstream_new(s, 1024 * 3); + if (s == NULL) + { + log_error("system out of memory"); + return s; + } + + devredir_insert_DeviceIoRequest(s, + irp->DeviceId, + irp->FileId, + irp->CompletionId, + IRP_MJ_DEVICE_CONTROL, + 0); + + xstream_wr_u32_le(s, 2048); /* OutputBufferLength */ + xstream_wr_u32_le(s, 0); /* InputBufferLength - insert later */ + xstream_wr_u32_le(s, ioctl); /* Ioctl Code */ + xstream_seek(s, 20); /* padding */ + + /* [MS-RPCE] 2.2.6.1 */ + xstream_wr_u32_le(s, 0x00081001); /* len 8, LE, v1 */ + xstream_wr_u32_le(s, 0xcccccccc); /* filler */ + + return s; +} + +/** + * Create a new smart card device entry and insert it into smartcards[] + * + * @param device_id DeviceId of new card + * + * @return index into smartcards[] on success, -1 on failure + *****************************************************************************/ + +static int scard_add_new_device(tui32 device_id) +{ + int index; + SMARTCARD *sc; + + if ((index = scard_get_free_slot()) < 0) + return -1; + + if ((sc = g_malloc(sizeof(SMARTCARD), 1)) == NULL) + { + log_error("system out of memory"); + return -1; + } + + sc->DeviceId = device_id; + smartcards[index] = sc; + + return index; +} + +/** + * Find first unused entry in smartcards + * + * @return index of first unused entry in smartcards or -1 if smartcards is full + *****************************************************************************/ + +static int scard_get_free_slot() +{ + int i; + + for (i = 0; i < MAX_SMARTCARDS; i++) + { + if (smartcards[i] == NULL) + { + log_debug("found free slot at index %d", i); + return i; + } + } + + log_error("too many smart card devices; rejecting this one"); + return -1; +} + +/** + * Release resources prior to shutting down + *****************************************************************************/ + +static void scard_release_resources() +{ + int i; + + for (i = 0; i < MAX_SMARTCARDS; i++) + { + if (smartcards[i] != NULL) + { + g_free(smartcards[i]); + smartcards[i] = NULL; + } + } +} + +/** + * + *****************************************************************************/ diff --git a/sesman/chansrv/smartcard.h b/sesman/chansrv/smartcard.h new file mode 100644 index 00000000..88f31369 --- /dev/null +++ b/sesman/chansrv/smartcard.h @@ -0,0 +1,42 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com + * + * 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. + * + */ + +/* + * smartcard redirection support + */ + +#ifndef _SMARTCARD_C +#define _SMARTCARD_C + +#include "parse.h" +#include "irp.h" + +/* forward declarations */ +void scard_device_announce(tui32 device_id); + +/* callbacks into this module */ +void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus); + +void scard_handle_ListReaders_Return(struct stream *s, IRP *irp, + tui32 DeviceId, tui32 CompletionId, + tui32 IoStatus); + +#endif /* end #ifndef _SMARTCARD_C */ diff --git a/sesman/chansrv/sound.c b/sesman/chansrv/sound.c index e8b801c6..55f6f88d 100644 --- a/sesman/chansrv/sound.c +++ b/sesman/chansrv/sound.c @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * Copyright (C) Jay Sorg 2009-2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,43 +18,100 @@ #include "sound.h" #include "thread_calls.h" +#include "defines.h" extern int g_rdpsnd_chan_id; /* in chansrv.c */ extern int g_display_num; /* in chansrv.c */ -static struct trans *g_audio_l_trans = 0; // listener -static struct trans *g_audio_c_trans = 0; // connection +static struct trans *g_audio_l_trans = 0; /* listener */ +static struct trans *g_audio_c_trans = 0; /* connection */ static int g_training_sent_time = 0; static int g_cBlockNo = 0; +#define BBUF_SIZE (1024 * 8) +char g_buffer[BBUF_SIZE]; +int g_buf_index = 0; +int g_sent_time[256]; + #if defined(XRDP_SIMPLESOUND) static void *DEFAULT_CC read_raw_audio_data(void *arg); #endif +#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_socket_%d" + +struct xr_wave_format_ex +{ + int wFormatTag; + int nChannels; + int nSamplesPerSec; + int nAvgBytesPerSec; + int nBlockAlign; + int wBitsPerSample; + int cbSize; + char *data; +}; + +static char g_pmc_22050_data[] = { 0 }; +static struct xr_wave_format_ex g_pmc_22050 = +{ + 1, /* wFormatTag - WAVE_FORMAT_PCM */ + 2, /* num of channels */ + 22050, /* samples per sec */ + 88200, /* avg bytes per sec */ + 4, /* block align */ + 16, /* bits per sample */ + 0, /* data size */ + g_pmc_22050_data /* data */ +}; + +static char g_pmc_44100_data[] = { 0 }; +static struct xr_wave_format_ex g_pmc_44100 = +{ + 1, /* wFormatTag - WAVE_FORMAT_PCM */ + 2, /* num of channels */ + 44100, /* samples per sec */ + 176400, /* avg bytes per sec */ + 4, /* block align */ + 16, /* bits per sample */ + 0, /* data size */ + g_pmc_44100_data /* data */ +}; + +#define NUM_BUILT_IN 2 +static struct xr_wave_format_ex *g_wave_formats[NUM_BUILT_IN] = +{ + &g_pmc_44100, + &g_pmc_22050 +}; + +/* index into list from client */ +static int g_current_client_format_index = 0; +/* index into list from server */ +static int g_current_server_format_index = 0; + /*****************************************************************************/ static int APP_CC sound_send_server_formats(void) { struct stream *s; int bytes; + int index; char *size_ptr; - print_got_here(); - make_stream(s); init_stream(s, 8182); out_uint16_le(s, SNDC_FORMATS); size_ptr = s->p; - out_uint16_le(s, 0); /* size, set later */ - out_uint32_le(s, 0); /* dwFlags */ - out_uint32_le(s, 0); /* dwVolume */ - out_uint32_le(s, 0); /* dwPitch */ - out_uint16_le(s, 0); /* wDGramPort */ - out_uint16_le(s, 1); /* wNumberOfFormats */ - out_uint8(s, g_cBlockNo); /* cLastBlockConfirmed */ - out_uint16_le(s, 2); /* wVersion */ - out_uint8(s, 0); /* bPad */ + out_uint16_le(s, 0); /* size, set later */ + out_uint32_le(s, 0); /* dwFlags */ + out_uint32_le(s, 0); /* dwVolume */ + out_uint32_le(s, 0); /* dwPitch */ + out_uint16_le(s, 0); /* wDGramPort */ + out_uint16_le(s, NUM_BUILT_IN); /* wNumberOfFormats */ + out_uint8(s, g_cBlockNo); /* cLastBlockConfirmed */ + out_uint16_le(s, 2); /* wVersion */ + out_uint8(s, 0); /* bPad */ /* sndFormats */ /* @@ -75,13 +132,21 @@ sound_send_server_formats(void) 00 00 */ - out_uint16_le(s, 1); // wFormatTag - WAVE_FORMAT_PCM - out_uint16_le(s, 2); // num of channels - out_uint32_le(s, 44100); // samples per sec - out_uint32_le(s, 176400); // avg bytes per sec - out_uint16_le(s, 4); // block align - out_uint16_le(s, 16); // bits per sample - out_uint16_le(s, 0); // size + for (index = 0; index < NUM_BUILT_IN; index++) + { + out_uint16_le(s, g_wave_formats[index]->wFormatTag); + out_uint16_le(s, g_wave_formats[index]->nChannels); + out_uint32_le(s, g_wave_formats[index]->nSamplesPerSec); + out_uint32_le(s, g_wave_formats[index]->nAvgBytesPerSec); + out_uint16_le(s, g_wave_formats[index]->nBlockAlign); + out_uint16_le(s, g_wave_formats[index]->wBitsPerSample); + bytes = g_wave_formats[index]->cbSize; + out_uint16_le(s, bytes); + if (bytes > 0) + { + out_uint8p(s, g_wave_formats[index]->data, bytes); + } + } s_mark_end(s); bytes = (int)((s->end - s->data) - 4); @@ -102,8 +167,6 @@ sound_send_training(void) int time; char *size_ptr; - print_got_here(); - make_stream(s); init_stream(s, 8182); out_uint16_le(s, SNDC_TRAINING); @@ -125,6 +188,52 @@ sound_send_training(void) } /*****************************************************************************/ +static int APP_CC +sound_process_format(int aindex, int wFormatTag, int nChannels, + int nSamplesPerSec, int nAvgBytesPerSec, + int nBlockAlign, int wBitsPerSample, + int cbSize, char *data) +{ + int lindex; + + LOG(0, ("sound_process_format:")); + LOG(0, (" wFormatTag %d", wFormatTag)); + LOG(0, (" nChannels %d", nChannels)); + LOG(0, (" nSamplesPerSec %d", nSamplesPerSec)); + LOG(0, (" nAvgBytesPerSec %d", nAvgBytesPerSec)); + LOG(0, (" nBlockAlign %d", nBlockAlign)); + LOG(0, (" wBitsPerSample %d", wBitsPerSample)); + LOG(0, (" cbSize %d", cbSize)); + g_hexdump(data, cbSize); + if (wFormatTag == g_pmc_44100.wFormatTag && + nChannels == g_pmc_44100.nChannels && + nSamplesPerSec == g_pmc_44100.nSamplesPerSec && + nAvgBytesPerSec == g_pmc_44100.nAvgBytesPerSec && + nBlockAlign == g_pmc_44100.nBlockAlign && + wBitsPerSample == g_pmc_44100.wBitsPerSample) + { + g_current_client_format_index = aindex; + g_current_server_format_index = 0; + } +#if 0 + for (lindex = 0; lindex < NUM_BUILT_IN; lindex++) + { + if (wFormatTag == g_wave_formats[lindex]->wFormatTag && + nChannels == g_wave_formats[lindex]->nChannels && + nSamplesPerSec == g_wave_formats[lindex]->nSamplesPerSec && + nAvgBytesPerSec == g_wave_formats[lindex]->nAvgBytesPerSec && + nBlockAlign == g_wave_formats[lindex]->nBlockAlign && + wBitsPerSample == g_wave_formats[lindex]->wBitsPerSample) + { + g_current_client_format_index = aindex; + g_current_server_format_index = lindex; + } + } +#endif + return 0; +} + +/*****************************************************************************/ /* 0000 07 02 26 00 03 00 80 00 ff ff ff ff 00 00 00 00 ..&............. 0010 00 00 01 00 00 02 00 00 01 00 02 00 44 ac 00 00 ............D... @@ -135,8 +244,15 @@ static int APP_CC sound_process_formats(struct stream *s, int size) { int num_formats; - - print_got_here(); + int index; + int wFormatTag; + int nChannels; + int nSamplesPerSec; + int nAvgBytesPerSec; + int nBlockAlign; + int wBitsPerSample; + int cbSize; + char *data; LOG(0, ("sound_process_formats:")); @@ -147,9 +263,24 @@ sound_process_formats(struct stream *s, int size) in_uint8s(s, 14); in_uint16_le(s, num_formats); + in_uint8s(s, 4); if (num_formats > 0) { + for (index = 0; index < num_formats; index++) + { + in_uint16_le(s, wFormatTag); + in_uint16_le(s, nChannels); + in_uint32_le(s, nSamplesPerSec); + in_uint32_le(s, nAvgBytesPerSec); + in_uint16_le(s, nBlockAlign); + in_uint16_le(s, wBitsPerSample); + in_uint16_le(s, cbSize); + in_uint8p(s, data, cbSize); + sound_process_format(index, wFormatTag, nChannels, nSamplesPerSec, + nAvgBytesPerSec, nBlockAlign, wBitsPerSample, + cbSize, data); + } sound_send_training(); } @@ -157,37 +288,39 @@ sound_process_formats(struct stream *s, int size) } /*****************************************************************************/ +/* send wave message to client */ static int -sound_send_wave_data(char *data, int data_bytes) +sound_send_wave_data_chunk(char *data, int data_bytes) { struct stream *s; int bytes; int time; char *size_ptr; - print_got_here(); + LOG(10, ("sound_send_wave_data_chunk: data_bytes %d", data_bytes)); - if ((data_bytes < 4) || (data_bytes > 32 * 1024)) + if ((data_bytes < 4) || (data_bytes > 128 * 1024)) { - LOG(0, ("sound_send_wave_data: bad data_bytes %d", data_bytes)); + LOG(0, ("sound_send_wave_data_chunk: bad data_bytes %d", data_bytes)); } /* part one of 2 PDU wave info */ - LOG(10, ("sound_send_wave_data: sending %d bytes", data_bytes)); + LOG(10, ("sound_send_wave_data_chunk: sending %d bytes", data_bytes)); make_stream(s); - init_stream(s, data_bytes); + init_stream(s, 16 + data_bytes); /* some extra space */ out_uint16_le(s, SNDC_WAVE); size_ptr = s->p; out_uint16_le(s, 0); /* size, set later */ time = g_time2(); out_uint16_le(s, time); - out_uint16_le(s, 0); /* wFormatNo */ + out_uint16_le(s, g_current_client_format_index); /* wFormatNo */ g_cBlockNo++; out_uint8(s, g_cBlockNo); + g_sent_time[g_cBlockNo & 0xff] = time; - LOG(10, ("sound_send_wave_data: sending time %d, g_cBlockNo %d", + LOG(10, ("sound_send_wave_data_chunk: sending time %d, g_cBlockNo %d", time & 0xffff, g_cBlockNo & 0xff)); out_uint8s(s, 3); @@ -201,60 +334,139 @@ sound_send_wave_data(char *data, int data_bytes) bytes = (int)(s->end - s->data); send_channel_data(g_rdpsnd_chan_id, s->data, bytes); - /* part two of 2 PDU wave info */ + /* part two of 2 PDU wave info + even is zero, we have to send this */ init_stream(s, data_bytes); out_uint32_le(s, 0); out_uint8a(s, data + 4, data_bytes - 4); s_mark_end(s); bytes = (int)(s->end - s->data); send_channel_data(g_rdpsnd_chan_id, s->data, bytes); + + free_stream(s); + return 0; +} + +/*****************************************************************************/ +/* send wave message to client, buffer first */ +static int +sound_send_wave_data(char *data, int data_bytes) +{ + int space_left; + int chunk_bytes; + int data_index; + + LOG(10, ("sound_send_wave_data: sending %d bytes", data_bytes)); + data_index = 0; + while (data_bytes > 0) + { + space_left = BBUF_SIZE - g_buf_index; + chunk_bytes = MIN(space_left, data_bytes); + if (chunk_bytes < 1) + { + LOG(10, ("sound_send_wave_data: error")); + break; + } + g_memcpy(g_buffer + g_buf_index, data + data_index, chunk_bytes); + g_buf_index += chunk_bytes; + if (g_buf_index >= BBUF_SIZE) + { + sound_send_wave_data_chunk(g_buffer, BBUF_SIZE); + g_buf_index = 0; + } + data_bytes -= chunk_bytes; + data_index += chunk_bytes; + } + return 0; +} + +/*****************************************************************************/ +/* send close message to client */ +static int +sound_send_close(void) +{ + struct stream *s; + int bytes; + char *size_ptr; + + LOG(10, ("sound_send_close:")); + + /* send any left over data */ + sound_send_wave_data_chunk(g_buffer, g_buf_index); + g_buf_index = 0; + + make_stream(s); + init_stream(s, 8182); + out_uint16_le(s, SNDC_CLOSE); + size_ptr = s->p; + out_uint16_le(s, 0); /* size, set later */ + s_mark_end(s); + bytes = (int)((s->end - s->data) - 4); + size_ptr[0] = bytes; + size_ptr[1] = bytes >> 8; + bytes = (int)(s->end - s->data); + send_channel_data(g_rdpsnd_chan_id, s->data, bytes); free_stream(s); return 0; } /*****************************************************************************/ +/* from client */ static int APP_CC sound_process_training(struct stream *s, int size) { int time_diff; - print_got_here(); - time_diff = g_time2() - g_training_sent_time; LOG(0, ("sound_process_training: round trip time %u", time_diff)); return 0; } /*****************************************************************************/ +/* from client */ static int APP_CC sound_process_wave_confirm(struct stream *s, int size) { int wTimeStamp; int cConfirmedBlockNo; + int time; + int time_diff; - print_got_here(); - + time = g_time2(); in_uint16_le(s, wTimeStamp); in_uint8(s, cConfirmedBlockNo); + time_diff = time - g_sent_time[cConfirmedBlockNo]; - LOG(10, ("sound_process_wave_confirm: wTimeStamp %d, cConfirmedBlockNo %d", - wTimeStamp, cConfirmedBlockNo)); + LOG(10, ("sound_process_wave_confirm: wTimeStamp %d, " + "cConfirmedBlockNo %d time diff %d", + wTimeStamp, cConfirmedBlockNo, time_diff)); return 0; } /*****************************************************************************/ +/* process message in from the audio source, eg pulse, alsa + on it's way to the client */ static int APP_CC process_pcm_message(int id, int size, struct stream *s) { - print_got_here(); - - sound_send_wave_data(s->p, size); + switch (id) + { + case 0: + sound_send_wave_data(s->p, size); + break; + case 1: + sound_send_close(); + break; + default: + LOG(0, ("process_pcm_message: unknown id %d", id)); + break; + } return 0; } /*****************************************************************************/ -/* data coming in from audio source, eg pulse, alsa */ +/* data in from audio source, eg pulse, alsa */ static int DEFAULT_CC sound_trans_audio_data_in(struct trans *trans) { @@ -277,11 +489,12 @@ sound_trans_audio_data_in(struct trans *trans) in_uint32_le(s, id); in_uint32_le(s, size); - if ((id != 0) || (size > 32 * 1024 + 8) || (size < 1)) + if ((id & ~3) || (size > 128 * 1024 + 8) || (size < 8)) { LOG(0, ("sound_trans_audio_data_in: bad message id %d size %d", id, size)); return 1; } + LOG(10, ("sound_trans_audio_data_in: good message id %d size %d", id, size)); error = trans_force_read(trans, size - 8); @@ -295,10 +508,11 @@ sound_trans_audio_data_in(struct trans *trans) } /*****************************************************************************/ +/* this is a connection in on the unix domain socket */ static int DEFAULT_CC sound_trans_audio_conn_in(struct trans *trans, struct trans *new_trans) { - print_got_here(); + LOG(0, ("sound_trans_audio_conn_in:")); if (trans == 0) { @@ -335,12 +549,11 @@ sound_init(void) char port[256]; int error; - print_got_here(); LOG(0, ("sound_init:")); sound_send_server_formats(); - g_audio_l_trans = trans_create(2, 33 * 1024, 8192); - g_snprintf(port, 255, "/tmp/xrdp_chansrv_audio_socket_%d", g_display_num); + g_audio_l_trans = trans_create(2, 128 * 1024, 8192); + g_snprintf(port, 255, CHANSRV_PORT_STR, g_display_num); g_audio_l_trans->trans_conn_in = sound_trans_audio_conn_in; error = trans_listen(g_audio_l_trans, port); @@ -363,8 +576,6 @@ sound_init(void) int APP_CC sound_deinit(void) { - print_got_here(); - if (g_audio_l_trans != 0) { trans_delete(g_audio_l_trans); @@ -374,14 +585,14 @@ sound_deinit(void) if (g_audio_c_trans != 0) { trans_delete(g_audio_c_trans); - g_audio_l_trans = 0; + g_audio_c_trans = 0; } return 0; } /*****************************************************************************/ -/* data in from client ( clinet -> xrdp -> chansrv ) */ +/* data in from client ( client -> xrdp -> chansrv ) */ int APP_CC sound_data_in(struct stream *s, int chan_id, int chan_flags, int length, int total_length) @@ -389,8 +600,6 @@ sound_data_in(struct stream *s, int chan_id, int chan_flags, int length, int code; int size; - print_got_here(); - in_uint8(s, code); in_uint8s(s, 1); in_uint16_le(s, size); @@ -460,6 +669,8 @@ sound_check_wait_objs(void) #if defined(XRDP_SIMPLESOUND) +#define AUDIO_BUF_SIZE 2048 + static int DEFAULT_CC sttrans_data_in(struct trans *self) { diff --git a/sesman/chansrv/sound.h b/sesman/chansrv/sound.h index 8171680d..0666ea00 100644 --- a/sesman/chansrv/sound.h +++ b/sesman/chansrv/sound.h @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2009-2012 + * Copyright (C) Jay Sorg 2009-2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,18 +30,6 @@ #include "chansrv.h" #include "trans.h" -#define _DEBUG_RDPSND - -#ifdef DEBUG_RDPSND -#include <stdio.h> -#define print_got_here() printf("****** got here: %s : %s : %d\n", \ - __FILE__, __func__, __LINE__); -#else -#define print_got_here() -#endif - -#define AUDIO_BUF_SIZE 2048 - #define SNDC_CLOSE 0x01 #define SNDC_WAVE 0x02 #define SNDC_SETVOLUME 0x03 diff --git a/sesman/chansrv/wave-format-server.txt b/sesman/chansrv/wave-format-server.txt new file mode 100644 index 00000000..1d0bb91e --- /dev/null +++ b/sesman/chansrv/wave-format-server.txt @@ -0,0 +1,466 @@ + + +Example wave formats from some servers + +see MMREG.H for defines + +Windows 7 SP1 + +example wave data sizes coming from server 8820 + +wFormatTag=1 nChannels=2 nSamplesPerSec=44100 nAvgBytesPerSec=176400 nBlockAlign=4 wBitsPerSample=16 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=44100 nAvgBytesPerSec=88200 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=44100 nAvgBytesPerSec=88200 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=44100 nAvgBytesPerSec=44359 nBlockAlign=2048 wBitsPerSample=4 cbSize=32 + 0000 f4 07 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=44100 nAvgBytesPerSec=44251 nBlockAlign=2048 wBitsPerSample=4 cbSize=2 + 0000 f9 07 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=44100 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=44100 nAvgBytesPerSec=44100 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=44100 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=44100 nAvgBytesPerSec=44100 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=22311 nBlockAlign=1024 wBitsPerSample=4 cbSize=32 + 0000 f4 03 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=22201 nBlockAlign=1024 wBitsPerSample=4 cbSize=2 + 0000 f9 03 .. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=44100 nAvgBytesPerSec=22179 nBlockAlign=1024 wBitsPerSample=4 cbSize=32 + 0000 f4 07 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=44100 nAvgBytesPerSec=22125 nBlockAlign=1024 wBitsPerSample=4 cbSize=2 + 0000 f9 07 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=22050 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=22050 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=22050 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=22050 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=16000 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=16000 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=11289 nBlockAlign=512 wBitsPerSample=4 cbSize=32 + 0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=11177 nBlockAlign=512 wBitsPerSample=4 cbSize=2 + 0000 f9 01 .. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=11155 nBlockAlign=512 wBitsPerSample=4 cbSize=32 + 0000 f4 03 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=11100 nBlockAlign=512 wBitsPerSample=4 cbSize=2 + 0000 f9 03 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=11025 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=11025 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +wFormatTag=49 nChannels=1 nSamplesPerSec=44100 nAvgBytesPerSec=8957 nBlockAlign=65 wBitsPerSample=0 cbSize=2 + 0000 40 01 @. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=8192 nBlockAlign=512 wBitsPerSample=4 cbSize=32 + 0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=8110 nBlockAlign=512 wBitsPerSample=4 cbSize=2 + 0000 f9 01 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=8000 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=8000 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +wFormatTag=2 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=5644 nBlockAlign=256 wBitsPerSample=4 cbSize=32 + 0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +wFormatTag=17 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=5588 nBlockAlign=256 wBitsPerSample=4 cbSize=2 + 0000 f9 01 .. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=4478 nBlockAlign=65 wBitsPerSample=0 cbSize=2 + 0000 40 01 @. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=4096 nBlockAlign=256 wBitsPerSample=4 cbSize=32 + 0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ + 0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=4055 nBlockAlign=256 wBitsPerSample=4 cbSize=2 + 0000 f9 01 .. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=2239 nBlockAlign=65 wBitsPerSample=0 cbSize=2 + 0000 40 01 @. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=1625 nBlockAlign=65 wBitsPerSample=0 cbSize=2 + 0000 40 01 @. + + + + +Windows XP SP3 + +example wave data sizes coming from server 32512, 16256 + +wFormatTag=1 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=88200 nBlockAlign=4 wBitsPerSample=16 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=44100 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=44100 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=22311 nBlockAlign=1024 wBitsPerSample=4 cbSize=32 +0000 f4 03 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=22201 nBlockAlign=1024 wBitsPerSample=4 cbSize=2 +0000 f9 03 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=22050 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=22050 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=22050 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=22050 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=16000 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=16000 nBlockAlign=2 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=11289 nBlockAlign=512 wBitsPerSample=4 cbSize=32 +0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=11177 nBlockAlign=512 wBitsPerSample=4 cbSize=2 +0000 f9 01 .. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=11155 nBlockAlign=512 wBitsPerSample=4 cbSize=32 +0000 f4 03 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=11100 nBlockAlign=512 wBitsPerSample=4 cbSize=2 +0000 f9 03 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=11025 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=11025 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=8192 nBlockAlign=512 wBitsPerSample=4 cbSize=32 +0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=8110 nBlockAlign=512 wBitsPerSample=4 cbSize=2 +0000 f9 01 .. + +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +wFormatTag=6 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=8000 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +wFormatTag=7 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=8000 nBlockAlign=1 wBitsPerSample=8 cbSize=0 + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=7000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 b6 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=16000 nAvgBytesPerSec=7000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 fc 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=6000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 9c 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=16000 nAvgBytesPerSec=6000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 d8 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=5644 nBlockAlign=256 wBitsPerSample=4 cbSize=32 +0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=5588 nBlockAlign=256 wBitsPerSample=4 cbSize=2 +0000 f9 01 .. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=5000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 82 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=16000 nAvgBytesPerSec=5000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 b4 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=4478 nBlockAlign=65 wBitsPerSample=0 cbSize=2 +0000 40 01 @. + +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +wFormatTag=2 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=4096 nBlockAlign=256 wBitsPerSample=4 cbSize=32 +0000 f4 01 07 00 00 01 00 00 00 02 00 ff 00 00 00 00 ................ +0010 c0 00 40 00 f0 00 00 00 cc 01 30 ff 88 01 18 ff ..@.......0..... + +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */ +wFormatTag=17 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=4055 nBlockAlign=256 wBitsPerSample=4 cbSize=2 +0000 f9 01 .. + +? +wFormatTag=353 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=4005 nBlockAlign=186 wBitsPerSample=16 cbSize=47 +0000 00 04 00 00 01 00 ba 00 00 00 46 36 44 43 39 38 ..........F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=16000 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 90 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=12000 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 c0 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 d0 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 20 01 01 00 71 05 ...... ...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 68 00 01 00 71 05 ......h...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=4000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 90 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=12000 nAvgBytesPerSec=3000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 90 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=3000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 9c 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=3000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 d8 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=3000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 4e 00 01 00 71 05 ......N...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=3000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 6c 00 01 00 71 05 ......l...q. + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=22050 nAvgBytesPerSec=2519 nBlockAlign=117 wBitsPerSample=16 cbSize=47 +0000 00 04 00 00 00 00 75 00 00 00 46 36 44 43 39 38 ......u...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +? +wFormatTag=353 nChannels=2 nSamplesPerSec=22050 nAvgBytesPerSec=2519 nBlockAlign=117 wBitsPerSample=16 cbSize=47 +0000 00 04 00 00 00 00 75 00 00 00 46 36 44 43 39 38 ......u...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=12000 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 f0 00 02 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 04 01 02 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 68 01 02 00 71 05 ......h...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 b4 00 02 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=12000 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 f0 00 02 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=2500 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 04 01 02 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=12000 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 b0 01 04 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=11025 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 d4 01 04 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 88 02 04 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 44 01 04 00 71 05 ......D...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=12000 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 b0 01 04 00 71 05 ..........q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=2250 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 d4 01 04 00 71 05 ..........q. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=2239 nBlockAlign=65 wBitsPerSample=0 cbSize=2 +0000 40 01 @. + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=2000 nBlockAlign=64 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 40 00 00 00 46 36 44 43 39 38 ......@...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=16000 nAvgBytesPerSec=2000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 48 00 01 00 71 05 ......H...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=12000 nAvgBytesPerSec=2000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 60 00 01 00 71 05 ......`...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=2000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 68 00 01 00 71 05 ......h...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=2000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 90 00 01 00 71 05 ..........q. + +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +wFormatTag=49 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=1625 nBlockAlign=65 wBitsPerSample=0 cbSize=2 +0000 40 01 @. + +? +wFormatTag=353 nChannels=2 nSamplesPerSec=8000 nAvgBytesPerSec=1500 nBlockAlign=96 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 60 00 00 00 46 36 44 43 39 38 ......`...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=1249 nBlockAlign=58 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 3a 00 00 00 46 36 44 43 39 38 ......:...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +#define WAVE_FORMAT_DSPGROUP_TRUESPEECH 0x0022 /* DSP Group, Inc */ +wFormatTag=34 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=1067 nBlockAlign=32 wBitsPerSample=1 cbSize=32 +0000 01 00 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=1012 nBlockAlign=47 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 2f 00 00 00 46 36 44 43 39 38 ....../...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=1000 nBlockAlign=64 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 40 00 00 00 46 36 44 43 39 38 ......@...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=12000 nAvgBytesPerSec=1000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 30 00 01 00 71 05 ......0...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=11025 nAvgBytesPerSec=1000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 34 00 01 00 71 05 ......4...q. + +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +wFormatTag=85 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=1000 nBlockAlign=1 wBitsPerSample=0 cbSize=12 +0000 01 00 02 00 00 00 48 00 01 00 71 05 ......H...q. + +? +wFormatTag=66 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=800 nBlockAlign=24 wBitsPerSample=0 cbSize=10 +0000 02 00 ce 9a 32 f7 a2 ae de ac ....2..... + +? +wFormatTag=353 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=750 nBlockAlign=48 wBitsPerSample=16 cbSize=47 +0000 00 02 00 00 00 00 30 00 00 00 46 36 44 43 39 38 ......0...F6DC98 +0010 33 30 2d 42 43 37 39 2d 31 31 64 32 2d 41 39 44 30-BC79-11d2-A9D +0020 30 2d 30 30 36 30 39 37 39 32 36 30 33 36 00 0-006097926036. + +? +wFormatTag=66 nChannels=1 nSamplesPerSec=8000 nAvgBytesPerSec=666 nBlockAlign=20 wBitsPerSample=0 cbSize=10 +0000 03 00 ce 9a 32 f7 a2 ae de ac ....2..... |