summaryrefslogtreecommitdiffstats
path: root/sesman/chansrv
diff options
context:
space:
mode:
Diffstat (limited to 'sesman/chansrv')
-rw-r--r--sesman/chansrv/Makefile.am4
-rw-r--r--sesman/chansrv/chansrv.c19
-rw-r--r--sesman/chansrv/chansrv_fuse.c3257
-rw-r--r--sesman/chansrv/chansrv_fuse.h80
-rw-r--r--sesman/chansrv/clipboard.c8
-rw-r--r--sesman/chansrv/clipboard_file.c28
-rw-r--r--sesman/chansrv/devredir.c1510
-rw-r--r--sesman/chansrv/devredir.h332
-rw-r--r--sesman/chansrv/irp.c241
-rw-r--r--sesman/chansrv/irp.h64
-rw-r--r--sesman/chansrv/pulse/Makefile18
-rw-r--r--sesman/chansrv/pulse/module-xrdp-sink-symdef.h29
-rw-r--r--sesman/chansrv/pulse/module-xrdp-sink.c581
-rw-r--r--sesman/chansrv/pulse/pulse-notes.txt72
-rw-r--r--sesman/chansrv/smartcard.c528
-rw-r--r--sesman/chansrv/smartcard.h42
-rw-r--r--sesman/chansrv/sound.c321
-rw-r--r--sesman/chansrv/sound.h14
-rw-r--r--sesman/chansrv/wave-format-server.txt466
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.....