Merge branch 'devel' into v0.9
This commit is contained in:
commit
0a068311b1
27
.travis.yml
27
.travis.yml
@ -9,6 +9,12 @@ addons:
|
|||||||
packages: &common_deps
|
packages: &common_deps
|
||||||
- nasm
|
- nasm
|
||||||
|
|
||||||
|
# This is required to use a version of cppcheck other than that
|
||||||
|
# suplied with the operating system
|
||||||
|
cppcheck_defs: &cppcheck_defs
|
||||||
|
- CPPCHECK_VER=1.90
|
||||||
|
- CPPCHECK_REPO=https://github.com/danmar/cppcheck.git
|
||||||
|
|
||||||
min_amd64_deps: &min_amd64_deps
|
min_amd64_deps: &min_amd64_deps
|
||||||
- *common_deps
|
- *common_deps
|
||||||
- libpam0g-dev
|
- libpam0g-dev
|
||||||
@ -81,6 +87,22 @@ max_x86_conf: &max_x86_conf
|
|||||||
packages:
|
packages:
|
||||||
- *max_x86_deps
|
- *max_x86_deps
|
||||||
|
|
||||||
|
# For cppcheck, we've got a custom script
|
||||||
|
cppcheck_conf: &cppcheck_conf
|
||||||
|
env:
|
||||||
|
- *cppcheck_defs
|
||||||
|
|
||||||
|
# addons:
|
||||||
|
# apt:
|
||||||
|
# packages:
|
||||||
|
# - cppcheck
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./bootstrap
|
||||||
|
- scripts/install_cppcheck.sh $CPPCHECK_REPO $CPPCHECK_VER
|
||||||
|
- scripts/run_cppcheck.sh -v $CPPCHECK_VER
|
||||||
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
|
||||||
@ -108,6 +130,11 @@ matrix:
|
|||||||
- compiler: clang
|
- compiler: clang
|
||||||
<< : *max_x86_conf
|
<< : *max_x86_conf
|
||||||
|
|
||||||
|
# cppcheck
|
||||||
|
- name: cppcheck
|
||||||
|
compiler: gcc
|
||||||
|
<< : *cppcheck_conf
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./bootstrap
|
- ./bootstrap
|
||||||
- ./configure $CONF_FLAGS
|
- ./configure $CONF_FLAGS
|
||||||
|
27
NEWS.md
27
NEWS.md
@ -1,3 +1,30 @@
|
|||||||
|
# Release notes for xrdp v0.9.13 (2020/03/11)
|
||||||
|
|
||||||
|
This release is an intermediate bugfix release. The previous version v0.9.12 has some regressions on drive redirection.
|
||||||
|
|
||||||
|
## Bug fixes (drive redirection related)
|
||||||
|
* Fix chansrv crashes with segmentation fault (regression in #1449) #1487
|
||||||
|
* Drive redirection now supports Guacamole client #1505 #1507
|
||||||
|
* Prevent a coredump in the event of a corrupted file system #1507
|
||||||
|
* Resolve double-free in `chansrv_fuse` #1469
|
||||||
|
|
||||||
|
## Bug fixes (other)
|
||||||
|
* Fix the issue `xrdp --version | less` will show empty output #1471 #1472
|
||||||
|
* Fix some warnings found by cppcheck #1479 #1481 #1484 #1485
|
||||||
|
|
||||||
|
## Other changes
|
||||||
|
* Add FreeBSD CI test #1466
|
||||||
|
* Move Microsoft-defined constants into separate includes #1470
|
||||||
|
* Perform cppcheck during CI test #1493
|
||||||
|
* Support mousex button 8/9 #1478
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
* FreeRDP 2.0.0-rc4 or later might not able to connect to xrdp due to
|
||||||
|
xrdp's bad-mannered behaviour, add `+glyph-cache` option to FreeRDP to connect #1266
|
||||||
|
* Audio redirection by MP3 codec doesn't sound with some client, use AAC instead #965
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
|
||||||
# Release notes for xrdp v0.9.12 (2019/12/28)
|
# Release notes for xrdp v0.9.12 (2019/12/28)
|
||||||
|
|
||||||
## Bug fixes
|
## Bug fixes
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
[![Build Status](https://travis-ci.org/neutrinolabs/xrdp.svg?branch=devel)](https://travis-ci.org/neutrinolabs/xrdp)
|
[![Build Status](https://travis-ci.org/neutrinolabs/xrdp.svg?branch=devel)](https://travis-ci.org/neutrinolabs/xrdp)
|
||||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp)
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp-questions)
|
||||||
![Apache-License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
|
![Apache-License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
|
||||||
|
|
||||||
*Current Version:* 0.9.12
|
*Current Version:* 0.9.13
|
||||||
|
|
||||||
# xrdp - an open source RDP server
|
# xrdp - an open source RDP server
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@ enum NTSTATUS
|
|||||||
STATUS_ACCESS_DENIED = 0xc0000022,
|
STATUS_ACCESS_DENIED = 0xc0000022,
|
||||||
STATUS_OBJECT_NAME_INVALID = 0xc0000033,
|
STATUS_OBJECT_NAME_INVALID = 0xc0000033,
|
||||||
STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034,
|
STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034,
|
||||||
STATUS_SHARING_VIOLATION = 0xc0000043
|
STATUS_SHARING_VIOLATION = 0xc0000043,
|
||||||
|
STATUS_NOT_SUPPORTED = 0xc00000bb
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* MS_ERREF_H */
|
#endif /* MS_ERREF_H */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Process this file with autoconf to produce a configure script
|
# Process this file with autoconf to produce a configure script
|
||||||
|
|
||||||
AC_PREREQ(2.65)
|
AC_PREREQ(2.65)
|
||||||
AC_INIT([xrdp], [0.9.12], [xrdp-devel@googlegroups.com])
|
AC_INIT([xrdp], [0.9.13], [xrdp-devel@googlegroups.com])
|
||||||
AC_CONFIG_HEADERS(config_ac.h:config_ac-h.in)
|
AC_CONFIG_HEADERS(config_ac.h:config_ac-h.in)
|
||||||
AM_INIT_AUTOMAKE([1.7.2 foreign])
|
AM_INIT_AUTOMAKE([1.7.2 foreign])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
105
scripts/install_cppcheck.sh
Executable file
105
scripts/install_cppcheck.sh
Executable file
@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Script to install a version of cppcheck in ~/cppcheck.local/
|
||||||
|
#
|
||||||
|
# Used by Travis-CI builds, until Travis supports cppcheck natively
|
||||||
|
#
|
||||||
|
# Currently only supports git repos as sources
|
||||||
|
#
|
||||||
|
# Usage: /path/to/install_cppcheck.sh <cppcheck-git-repo> <version-tag>
|
||||||
|
|
||||||
|
INSTALL_ROOT=~/cppcheck.local
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# U S A G E
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
echo "** Usage: $0 <git-repo URL> <version-tag>"
|
||||||
|
echo " e.g. $0 https://github.com/danmar/cppcheck.git 1.90"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# C A L L _ M A K E
|
||||||
|
#
|
||||||
|
# Calls make with the specified parameters, but only displays the error
|
||||||
|
# log if it fails
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
call_make()
|
||||||
|
{
|
||||||
|
# Disable set -e, if active
|
||||||
|
set_entry_opts=`set +o`
|
||||||
|
set +e
|
||||||
|
|
||||||
|
status=1
|
||||||
|
log=`mktemp /tmp/cppcheck-log.XXXXXXXXXX`
|
||||||
|
if [ -n "$log" ]; then
|
||||||
|
make "$@" >$log 2>&1
|
||||||
|
status=$?
|
||||||
|
if [ $status -ne 0 ]; then
|
||||||
|
cat $log >&2
|
||||||
|
fi
|
||||||
|
rm $log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Re-enable `set -e` if active before
|
||||||
|
$set_entry_opts
|
||||||
|
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# M A I N
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
REPO_URL="$1"
|
||||||
|
CPPCHECK_VER="$2"
|
||||||
|
|
||||||
|
# Already installed?
|
||||||
|
exe=$INSTALL_ROOT/$CPPCHECK_VER/bin/cppcheck
|
||||||
|
if [ -x "$exe" ]; then
|
||||||
|
echo "cppcheck version $CPPCHECK_VER is already installed at $exe" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
workdir=`mktemp -d /tmp/cppcheck.XXXXXXXXXX`
|
||||||
|
if [ -z "$workdir" ]; then
|
||||||
|
echo "** Unable to create temporary working directory" 2>&1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use a sub-process for the next bit to restrict the scope of 'set -e'
|
||||||
|
(
|
||||||
|
set -e ; # Exit sub-process on first error
|
||||||
|
|
||||||
|
# Put everything in this directory
|
||||||
|
FILESDIR=$INSTALL_ROOT/$CPPCHECK_VER
|
||||||
|
|
||||||
|
# CFGDIR is needed for cppcheck before 1.86
|
||||||
|
make_args="FILESDIR=$FILESDIR PREFIX=$FILESDIR CFGDIR=$FILESDIR"
|
||||||
|
|
||||||
|
# See https://stackoverflow.com/questions/
|
||||||
|
# 791959/download-a-specific-tag-with-git
|
||||||
|
git clone -b $CPPCHECK_VER --depth 1 $REPO_URL $workdir
|
||||||
|
|
||||||
|
cd $workdir
|
||||||
|
echo "Making cppcheck..."
|
||||||
|
# CFGDIR is needed for cppcheck before 1.86
|
||||||
|
call_make $make_args
|
||||||
|
|
||||||
|
echo "Installing cppcheck..."
|
||||||
|
mkdir -p $FILESDIR
|
||||||
|
call_make install $make_args
|
||||||
|
)
|
||||||
|
status=$?
|
||||||
|
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
rm -rf $workdir
|
||||||
|
else
|
||||||
|
"** Script failed. Work dir is $workdir" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $status
|
58
scripts/run_cppcheck.sh
Executable file
58
scripts/run_cppcheck.sh
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Script to run cppcheck
|
||||||
|
#
|
||||||
|
# Usage: /path/to/run_cppcheck.sh [ -v CPPCHECK_VER] [<extra_opts_and_dirs>]
|
||||||
|
#
|
||||||
|
# - If <extra_opts_and_dirs> is missing, '.' is assumed
|
||||||
|
# - If -v CPPCHECK_VER is specified, that version of cppcheck is run from
|
||||||
|
# ~/cppcheck.local (whether or not it's there!). Use install_cppcheck.sh
|
||||||
|
# to install a new version.
|
||||||
|
#
|
||||||
|
# Environment (all optional):-
|
||||||
|
#
|
||||||
|
# CPPCHECK : Override the default cppcheck command ('cppcheck').
|
||||||
|
# Ignored if -v is specified
|
||||||
|
# CPPCHECK_FLAGS : Override the default cppcheck flags
|
||||||
|
|
||||||
|
INSTALL_ROOT=~/cppcheck.local
|
||||||
|
|
||||||
|
# Figure out CPPCHECK setting, if any. Currently '-v' must be the first
|
||||||
|
# argument on the command line.
|
||||||
|
case "$1" in
|
||||||
|
-v) # Version is separate parameter
|
||||||
|
if [ $# -ge 2 ]; then
|
||||||
|
CPPCHECK="$INSTALL_ROOT/$2/bin/cppcheck"
|
||||||
|
shift 2
|
||||||
|
else
|
||||||
|
echo "** ignoring '-v' with no arg" >&2
|
||||||
|
shift 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-v*) # Version is in same parameter
|
||||||
|
# ${parameter#word} is not supported by classic Bourne shell,
|
||||||
|
# but it is on bash, dash, etc. If it doesn't work on your shell,
|
||||||
|
# don't use this form!
|
||||||
|
CPPCHECK="$INSTALL_ROOT/${1#-v}/bin/cppcheck"
|
||||||
|
shift 1
|
||||||
|
esac
|
||||||
|
if [ -z "$CPPCHECK" ]; then
|
||||||
|
CPPCHECK=cppcheck
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Supply default flags passed to cppcheck if necessary
|
||||||
|
if [ -z "$CPPCHECK_FLAGS" ]; then
|
||||||
|
CPPCHECK_FLAGS="--quiet --force --std=c11 --std=c++11 --inline-suppr \
|
||||||
|
--enable=warning --error-exitcode=1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Any options/directories specified?
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
set -- .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display the cppcheck version and command for debugging
|
||||||
|
"$CPPCHECK" --version && {
|
||||||
|
echo Command: $CPPCHECK $CPPCHECK_FLAGS "$@"
|
||||||
|
"$CPPCHECK" $CPPCHECK_FLAGS "$@"
|
||||||
|
}
|
@ -78,6 +78,7 @@ void xfuse_devredir_cb_open_file(struct state_open *fip,
|
|||||||
tui32 DeviceId, tui32 FileId)
|
tui32 DeviceId, tui32 FileId)
|
||||||
{}
|
{}
|
||||||
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
||||||
|
enum NTSTATUS IoStatus,
|
||||||
const char *buf, size_t length)
|
const char *buf, size_t length)
|
||||||
{}
|
{}
|
||||||
void xfuse_devredir_cb_write_file(
|
void xfuse_devredir_cb_write_file(
|
||||||
@ -637,6 +638,7 @@ int xfuse_create_share(tui32 device_id, const char *dirname)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
xinode->is_redirected = 1;
|
||||||
xinode->device_id = device_id;
|
xinode->device_id = device_id;
|
||||||
result = 0;
|
result = 0;
|
||||||
}
|
}
|
||||||
@ -653,7 +655,7 @@ int xfuse_create_share(tui32 device_id, const char *dirname)
|
|||||||
|
|
||||||
void xfuse_delete_share(tui32 device_id)
|
void xfuse_delete_share(tui32 device_id)
|
||||||
{
|
{
|
||||||
xfs_delete_entries_with_device_id(g_xfs, device_id);
|
xfs_delete_redirected_entries_with_device_id(g_xfs, device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1313,9 +1315,18 @@ void xfuse_devredir_cb_open_file(struct state_open *fip,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
||||||
|
enum NTSTATUS IoStatus,
|
||||||
const char *buf, size_t length)
|
const char *buf, size_t length)
|
||||||
{
|
{
|
||||||
|
if (IoStatus != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
log_error("Read NTSTATUS is %d", (int) IoStatus);
|
||||||
|
fuse_reply_err(fip->req, EIO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fuse_reply_buf(fip->req, buf, length);
|
fuse_reply_buf(fip->req, buf, length);
|
||||||
|
}
|
||||||
free(fip);
|
free(fip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1438,7 +1449,7 @@ static void xfuse_cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (parent_xinode->device_id == 0)
|
if (!parent_xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* File cannot be remote - we either know about it or we don't */
|
/* File cannot be remote - we either know about it or we don't */
|
||||||
if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL)
|
if ((xinode = xfs_lookup_in_dir(g_xfs, parent, name)) != NULL)
|
||||||
@ -1673,13 +1684,13 @@ static void xfuse_cb_unlink(fuse_req_t req, fuse_ino_t parent,
|
|||||||
fuse_reply_err(req, ENOTEMPTY);
|
fuse_reply_err(req, ENOTEMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (xinode->device_id == 0)
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* specified file is a local resource */
|
/* specified file is a local resource */
|
||||||
//XFUSE_HANDLE *fh;
|
//XFUSE_HANDLE *fh;
|
||||||
|
|
||||||
log_debug("LK_TODO: this is still a TODO");
|
log_debug("LK_TODO: this is still a TODO");
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EROFS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1744,26 +1755,27 @@ static void xfuse_cb_rename(fuse_req_t req,
|
|||||||
else if (!(new_parent_xinode = xfs_get(g_xfs, new_parent)))
|
else if (!(new_parent_xinode = xfs_get(g_xfs, new_parent)))
|
||||||
{
|
{
|
||||||
log_error("inode %ld is not valid", new_parent);
|
log_error("inode %ld is not valid", new_parent);
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (!xfs_check_move_entry(g_xfs, old_xinode->inum,
|
else if (!xfs_check_move_entry(g_xfs, old_xinode->inum,
|
||||||
new_parent, new_name))
|
new_parent, new_name))
|
||||||
{
|
{
|
||||||
|
/* Catchall -see rename(2). Fix when logging is improved */
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (new_parent_xinode->device_id != old_xinode->device_id)
|
else if (new_parent_xinode->is_redirected != old_xinode->is_redirected ||
|
||||||
|
new_parent_xinode->device_id != old_xinode->device_id)
|
||||||
{
|
{
|
||||||
log_error("rename across file systems not supported");
|
fuse_reply_err(req, EXDEV);
|
||||||
fuse_reply_err(req, EINVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (old_xinode->device_id == 0)
|
else if (!old_xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* specified file is a local resource */
|
/* specified file is a local resource */
|
||||||
log_debug("LK_TODO: this is still a TODO");
|
log_debug("LK_TODO: this is still a TODO");
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EROFS);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -1853,20 +1865,25 @@ static void xfuse_create_dir_or_file(fuse_req_t req, fuse_ino_t parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* is parent inode valid? */
|
/* is parent inode valid? */
|
||||||
if (parent == FUSE_ROOT_ID ||
|
if (parent == FUSE_ROOT_ID)
|
||||||
(xinode = xfs_get(g_xfs, parent)) == NULL ||
|
{
|
||||||
(xinode->mode & S_IFDIR) == 0)
|
fuse_reply_err(req, EROFS);
|
||||||
|
}
|
||||||
|
else if ((xinode = xfs_get(g_xfs, parent)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("inode %ld is not valid", parent);
|
|
||||||
fuse_reply_err(req, ENOENT);
|
fuse_reply_err(req, ENOENT);
|
||||||
}
|
}
|
||||||
else if (xinode->device_id == 0)
|
else if ((xinode->mode & S_IFDIR) == 0)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, ENOTDIR);
|
||||||
|
}
|
||||||
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* specified file is a local resource */
|
/* specified file is a local resource */
|
||||||
//XFUSE_HANDLE *fh;
|
//XFUSE_HANDLE *fh;
|
||||||
|
|
||||||
log_debug("LK_TODO: this is still a TODO");
|
log_debug("LK_TODO: this is still a TODO");
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EROFS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1945,14 +1962,21 @@ static void xfuse_cb_open(fuse_req_t req, fuse_ino_t ino,
|
|||||||
log_debug("Invalid access mode specified");
|
log_debug("Invalid access mode specified");
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EINVAL);
|
||||||
}
|
}
|
||||||
else if (xinode->device_id == 0)
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* specified file is a local resource */
|
/* specified file is a local resource */
|
||||||
|
if ((fi->flags & O_ACCMODE) != O_RDONLY)
|
||||||
|
{
|
||||||
|
fuse_reply_err(req, EROFS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
XFUSE_HANDLE *fh = g_new0(XFUSE_HANDLE, 1);
|
XFUSE_HANDLE *fh = g_new0(XFUSE_HANDLE, 1);
|
||||||
fh->is_loc_resource = 1;
|
fh->is_loc_resource = 1;
|
||||||
fi->fh = (tintptr) fh;
|
fi->fh = (tintptr) fh;
|
||||||
fuse_reply_open(req, fi);
|
fuse_reply_open(req, fi);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* specified file resides on redirected share */
|
/* specified file resides on redirected share */
|
||||||
@ -2017,7 +2041,7 @@ static void xfuse_cb_release(fuse_req_t req, fuse_ino_t ino, struct
|
|||||||
log_error("inode %ld is not valid", ino);
|
log_error("inode %ld is not valid", ino);
|
||||||
fuse_reply_err(req, ENOENT);
|
fuse_reply_err(req, ENOENT);
|
||||||
}
|
}
|
||||||
else if (xinode->device_id == 0)
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* specified file is a local resource */
|
/* specified file is a local resource */
|
||||||
fuse_reply_err(req, 0);
|
fuse_reply_err(req, 0);
|
||||||
@ -2153,7 +2177,7 @@ static void xfuse_cb_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
|
|||||||
{
|
{
|
||||||
/* target file is in .clipboard dir */
|
/* target file is in .clipboard dir */
|
||||||
log_debug("THIS IS STILL A TODO!");
|
log_debug("THIS IS STILL A TODO!");
|
||||||
fuse_reply_err(req, EINVAL);
|
fuse_reply_err(req, EROFS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2283,7 +2307,7 @@ static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
|||||||
/* No changes have been made */
|
/* No changes have been made */
|
||||||
make_fuse_attr_reply(req, xinode);
|
make_fuse_attr_reply(req, xinode);
|
||||||
}
|
}
|
||||||
else if (xinode->device_id == 0)
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
/* Update the local fs */
|
/* Update the local fs */
|
||||||
update_inode_file_attributes(&attrs, change_mask, xinode);
|
update_inode_file_attributes(&attrs, change_mask, xinode);
|
||||||
@ -2349,7 +2373,7 @@ static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino,
|
|||||||
log_error("inode %ld is not valid", ino);
|
log_error("inode %ld is not valid", ino);
|
||||||
fuse_reply_err(req, ENOENT);
|
fuse_reply_err(req, ENOENT);
|
||||||
}
|
}
|
||||||
else if (xinode->device_id == 0)
|
else if (!xinode->is_redirected)
|
||||||
{
|
{
|
||||||
if ((fi->fh = (tintptr) xfs_opendir(g_xfs, ino)) == 0)
|
if ((fi->fh = (tintptr) xfs_opendir(g_xfs, ino)) == 0)
|
||||||
{
|
{
|
||||||
@ -2537,6 +2561,8 @@ static char *get_name_for_entry_in_parent(fuse_ino_t parent, const char *name)
|
|||||||
strlen(result) + 1 + strlen(name) + 1);
|
strlen(result) + 1 + strlen(name) + 1);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
{
|
{
|
||||||
|
/* See cppcheck trac #9292 and #9437 */
|
||||||
|
/* cppcheck-suppress doubleFree symbolName=result */
|
||||||
free(result);
|
free(result);
|
||||||
result = NULL;
|
result = NULL;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ void xfuse_devredir_cb_open_file(struct state_open *fip,
|
|||||||
tui32 DeviceId, tui32 FileId);
|
tui32 DeviceId, tui32 FileId);
|
||||||
|
|
||||||
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
void xfuse_devredir_cb_read_file(struct state_read *fip,
|
||||||
|
enum NTSTATUS IoStatus,
|
||||||
const char *buf, size_t length);
|
const char *buf, size_t length);
|
||||||
void xfuse_devredir_cb_write_file(
|
void xfuse_devredir_cb_write_file(
|
||||||
struct state_write *fip,
|
struct state_write *fip,
|
||||||
|
@ -344,6 +344,7 @@ xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid)
|
|||||||
xino1->pub.ctime = xino1->pub.atime;
|
xino1->pub.ctime = xino1->pub.atime;
|
||||||
strcpy(xino1->pub.name, ".");
|
strcpy(xino1->pub.name, ".");
|
||||||
xino1->pub.generation = xfs->generation;
|
xino1->pub.generation = xfs->generation;
|
||||||
|
xino1->pub.is_redirected = 0;
|
||||||
xino1->pub.device_id = 0;
|
xino1->pub.device_id = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -365,6 +366,7 @@ xfs_create_xfs_fs(mode_t umask, uid_t uid, gid_t gid)
|
|||||||
xino2->pub.ctime = xino2->pub.atime;
|
xino2->pub.ctime = xino2->pub.atime;
|
||||||
strcpy(xino2->pub.name, ".delete-pending");
|
strcpy(xino2->pub.name, ".delete-pending");
|
||||||
xino2->pub.generation = xfs->generation;
|
xino2->pub.generation = xfs->generation;
|
||||||
|
xino2->pub.is_redirected = 0;
|
||||||
xino2->pub.device_id = 0;
|
xino2->pub.device_id = 0;
|
||||||
|
|
||||||
xino2->parent = NULL;
|
xino2->parent = NULL;
|
||||||
@ -469,6 +471,7 @@ xfs_add_entry(struct xfs_fs *xfs, fuse_ino_t parent_inum,
|
|||||||
xino->pub.ctime = xino->pub.atime;
|
xino->pub.ctime = xino->pub.atime;
|
||||||
strcpy(xino->pub.name, name);
|
strcpy(xino->pub.name, name);
|
||||||
xino->pub.generation = xfs->generation;
|
xino->pub.generation = xfs->generation;
|
||||||
|
xino->pub.is_redirected = parent->pub.is_redirected;
|
||||||
xino->pub.device_id = parent->pub.device_id;
|
xino->pub.device_id = parent->pub.device_id;
|
||||||
xino->pub.lindex = 0;
|
xino->pub.lindex = 0;
|
||||||
|
|
||||||
@ -565,7 +568,7 @@ xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum)
|
|||||||
*/
|
*/
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
XFS_INODE_ALL *p;
|
XFS_INODE_ALL *p;
|
||||||
for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent)
|
for (p = xino ; p && p->pub.inum != FUSE_ROOT_ID ; p = p->parent)
|
||||||
{
|
{
|
||||||
len += strlen(p->pub.name);
|
len += strlen(p->pub.name);
|
||||||
++len; /* Allow for '/' prefix */
|
++len; /* Allow for '/' prefix */
|
||||||
@ -578,7 +581,7 @@ xfs_get_full_path(struct xfs_fs *xfs, fuse_ino_t inum)
|
|||||||
char *end = result + len;
|
char *end = result + len;
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
|
|
||||||
for (p = xino ; p->pub.inum != FUSE_ROOT_ID ; p = p->parent)
|
for (p = xino ; p && p->pub.inum != FUSE_ROOT_ID ; p = p->parent)
|
||||||
{
|
{
|
||||||
len = strlen(p->pub.name);
|
len = strlen(p->pub.name);
|
||||||
end -= (len + 1);
|
end -= (len + 1);
|
||||||
@ -816,13 +819,12 @@ xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
void
|
void
|
||||||
xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id)
|
xfs_delete_redirected_entries_with_device_id(struct xfs_fs *xfs,
|
||||||
|
tui32 device_id)
|
||||||
{
|
{
|
||||||
fuse_ino_t inum;
|
fuse_ino_t inum;
|
||||||
XFS_INODE_ALL *xino;
|
XFS_INODE_ALL *xino;
|
||||||
|
|
||||||
if (device_id != 0)
|
|
||||||
{
|
|
||||||
/* Using xfs_remove_entry() is convenient, but it recurses
|
/* Using xfs_remove_entry() is convenient, but it recurses
|
||||||
* in to directories. To make sure all entries are removed, set the
|
* in to directories. To make sure all entries are removed, set the
|
||||||
* open_count of all affected files to 0 first
|
* open_count of all affected files to 0 first
|
||||||
@ -830,6 +832,7 @@ xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id)
|
|||||||
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum)
|
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum)
|
||||||
{
|
{
|
||||||
if ((xino = xfs->inode_table[inum]) != NULL &&
|
if ((xino = xfs->inode_table[inum]) != NULL &&
|
||||||
|
xino->pub.is_redirected != 0 &&
|
||||||
xino->pub.device_id == device_id &&
|
xino->pub.device_id == device_id &&
|
||||||
(xino->pub.mode & S_IFREG) != 0)
|
(xino->pub.mode & S_IFREG) != 0)
|
||||||
{
|
{
|
||||||
@ -841,12 +844,12 @@ xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id)
|
|||||||
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum)
|
for (inum = FUSE_ROOT_ID; inum < xfs->inode_count; ++inum)
|
||||||
{
|
{
|
||||||
if ((xino = xfs->inode_table[inum]) != NULL &&
|
if ((xino = xfs->inode_table[inum]) != NULL &&
|
||||||
|
xino->pub.is_redirected != 0 &&
|
||||||
xino->pub.device_id == device_id)
|
xino->pub.device_id == device_id)
|
||||||
{
|
{
|
||||||
xfs_remove_entry(xfs, xino->pub.inum);
|
xfs_remove_entry(xfs, xino->pub.inum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------------ */
|
||||||
|
@ -49,9 +49,8 @@ typedef struct xfs_inode
|
|||||||
time_t ctime; /* Time of last status change. */
|
time_t ctime; /* Time of last status change. */
|
||||||
char name[XFS_MAXFILENAMELEN + 1]; /* Short name */
|
char name[XFS_MAXFILENAMELEN + 1]; /* Short name */
|
||||||
tui32 generation; /* Changes if inode is reused */
|
tui32 generation; /* Changes if inode is reused */
|
||||||
tui32 device_id; /* for file system redirection
|
char is_redirected; /* file is on redirected device */
|
||||||
* Non-redirected devices are guaranteed
|
tui32 device_id; /* device ID of redirected device */
|
||||||
* to have a device_id or zero */
|
|
||||||
int lindex; /* used in clipboard operations */
|
int lindex; /* used in clipboard operations */
|
||||||
} XFS_INODE;
|
} XFS_INODE;
|
||||||
|
|
||||||
@ -271,17 +270,15 @@ unsigned int
|
|||||||
xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum);
|
xfs_get_file_open_count(struct xfs_fs *xfs, fuse_ino_t inum);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deletes all entries with the matching device id
|
* Deletes all redirected entries with the matching device id
|
||||||
*
|
*
|
||||||
* Files are deleted even if they are open
|
* Files are deleted even if they are open
|
||||||
*
|
*
|
||||||
* The specified device_id must be non-zero so that the root
|
|
||||||
* filesystem is not deleted!
|
|
||||||
*
|
|
||||||
* @param device_id Device ID
|
* @param device_id Device ID
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
xfs_delete_entries_with_device_id(struct xfs_fs *xfs, tui32 device_id);
|
xfs_delete_redirected_entries_with_device_id(struct xfs_fs *xfs,
|
||||||
|
tui32 device_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check an entry move will be successful
|
* Check an entry move will be successful
|
||||||
|
@ -185,28 +185,10 @@ devredir_init(void)
|
|||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
int bytes;
|
int bytes;
|
||||||
int fd;
|
|
||||||
|
|
||||||
union _u
|
|
||||||
{
|
|
||||||
tui32 clientID;
|
tui32 clientID;
|
||||||
char buf[4];
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/* get a random number that will act as a unique clientID */
|
/* get a random number that will act as a unique clientID */
|
||||||
if ((fd = open("/dev/urandom", O_RDONLY)) != -1)
|
g_random((char *) &clientID, sizeof(clientID));
|
||||||
{
|
|
||||||
if (read(fd, u.buf, 4) != 4)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* /dev/urandom did not work - use address of struct s */
|
|
||||||
tui64 u64 = (tui64) (tintptr) &s;
|
|
||||||
u.clientID = (tui32) u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup stream */
|
/* setup stream */
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
@ -216,7 +198,7 @@ devredir_init(void)
|
|||||||
xstream_wr_u16_le(s, PAKID_CORE_SERVER_ANNOUNCE);
|
xstream_wr_u16_le(s, PAKID_CORE_SERVER_ANNOUNCE);
|
||||||
xstream_wr_u16_le(s, 0x0001); /* server major ver */
|
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_u16_le(s, 0x000C); /* server minor ver - pretend 2 b Win 7 */
|
||||||
xstream_wr_u32_le(s, u.clientID); /* unique ClientID */
|
xstream_wr_u32_le(s, clientID); /* unique ClientID */
|
||||||
|
|
||||||
/* send data to client */
|
/* send data to client */
|
||||||
bytes = xstream_len(s);
|
bytes = xstream_len(s);
|
||||||
@ -578,7 +560,8 @@ devredir_send_server_user_logged_on(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
devredir_send_server_device_announce_resp(tui32 device_id)
|
devredir_send_server_device_announce_resp(tui32 device_id,
|
||||||
|
enum NTSTATUS result_code)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
int bytes;
|
int bytes;
|
||||||
@ -589,7 +572,7 @@ devredir_send_server_device_announce_resp(tui32 device_id)
|
|||||||
xstream_wr_u16_le(s, RDPDR_CTYP_CORE);
|
xstream_wr_u16_le(s, RDPDR_CTYP_CORE);
|
||||||
xstream_wr_u16_le(s, PAKID_CORE_DEVICE_REPLY);
|
xstream_wr_u16_le(s, PAKID_CORE_DEVICE_REPLY);
|
||||||
xstream_wr_u32_le(s, device_id);
|
xstream_wr_u32_le(s, device_id);
|
||||||
xstream_wr_u32_le(s, 0); /* ResultCode */
|
xstream_wr_u32_le(s, (tui32)result_code);
|
||||||
|
|
||||||
/* send to client */
|
/* send to client */
|
||||||
bytes = xstream_len(s);
|
bytes = xstream_len(s);
|
||||||
@ -829,6 +812,7 @@ devredir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
tui32 device_type;
|
tui32 device_type;
|
||||||
tui32 device_data_len;
|
tui32 device_data_len;
|
||||||
char preferred_dos_name[9];
|
char preferred_dos_name[9];
|
||||||
|
enum NTSTATUS response_status;
|
||||||
|
|
||||||
/* get number of devices being announced */
|
/* get number of devices being announced */
|
||||||
xstream_rd_u32_le(s, device_count);
|
xstream_rd_u32_le(s, device_count);
|
||||||
@ -839,19 +823,20 @@ devredir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
{
|
{
|
||||||
xstream_rd_u32_le(s, device_type);
|
xstream_rd_u32_le(s, device_type);
|
||||||
xstream_rd_u32_le(s, g_device_id);
|
xstream_rd_u32_le(s, g_device_id);
|
||||||
|
/* get preferred DOS name
|
||||||
switch (device_type)
|
* DOS names that are 8 chars long are not NULL terminated */
|
||||||
{
|
|
||||||
case RDPDR_DTYP_FILESYSTEM:
|
|
||||||
/* get preferred DOS name */
|
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
preferred_dos_name[j] = *s->p++;
|
preferred_dos_name[j] = *s->p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DOS names that are 8 chars long are not NULL terminated */
|
|
||||||
preferred_dos_name[8] = 0;
|
preferred_dos_name[8] = 0;
|
||||||
|
|
||||||
|
/* Assume this device isn't supported by us */
|
||||||
|
response_status = STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
switch (device_type)
|
||||||
|
{
|
||||||
|
case RDPDR_DTYP_FILESYSTEM:
|
||||||
/* get device data len */
|
/* get device data len */
|
||||||
xstream_rd_u32_le(s, device_data_len);
|
xstream_rd_u32_le(s, device_data_len);
|
||||||
if (device_data_len)
|
if (device_data_len)
|
||||||
@ -865,7 +850,7 @@ devredir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
preferred_dos_name,
|
preferred_dos_name,
|
||||||
device_data_len, g_full_name_for_filesystem);
|
device_data_len, g_full_name_for_filesystem);
|
||||||
|
|
||||||
devredir_send_server_device_announce_resp(g_device_id);
|
response_status = STATUS_SUCCESS;
|
||||||
|
|
||||||
/* create share directory in xrdp file system; */
|
/* create share directory in xrdp file system; */
|
||||||
/* think of this as the mount point for this share */
|
/* think of this as the mount point for this share */
|
||||||
@ -873,31 +858,44 @@ devredir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RDPDR_DTYP_SMARTCARD:
|
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 */
|
/* for smart cards, device data len always 0 */
|
||||||
|
|
||||||
log_debug("device_type=SMARTCARD device_id=0x%x dosname=%s",
|
log_debug("device_type=SMARTCARD device_id=0x%x dosname=%s",
|
||||||
g_device_id, preferred_dos_name);
|
g_device_id, preferred_dos_name);
|
||||||
|
|
||||||
devredir_send_server_device_announce_resp(g_device_id);
|
response_status = STATUS_SUCCESS;
|
||||||
|
|
||||||
scard_device_announce(g_device_id);
|
scard_device_announce(g_device_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* we don't yet support these devices */
|
|
||||||
case RDPDR_DTYP_SERIAL:
|
case RDPDR_DTYP_SERIAL:
|
||||||
|
log_debug(
|
||||||
|
"device_type=SERIAL device_id=0x%x dosname=%s",
|
||||||
|
g_device_id, preferred_dos_name);
|
||||||
|
break;
|
||||||
|
|
||||||
case RDPDR_DTYP_PARALLEL:
|
case RDPDR_DTYP_PARALLEL:
|
||||||
|
log_debug(
|
||||||
|
"device_type=PARALLEL device_id=0x%x dosname=%s",
|
||||||
|
g_device_id, preferred_dos_name);
|
||||||
|
break;
|
||||||
|
|
||||||
case RDPDR_DTYP_PRINT:
|
case RDPDR_DTYP_PRINT:
|
||||||
log_debug("unsupported dev: 0x%x", device_type);
|
log_debug(
|
||||||
|
"device_type=PRINT device_id=0x%x dosname=%s",
|
||||||
|
g_device_id, preferred_dos_name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log_debug(
|
||||||
|
"device_type=UNKNOWN device_id=0x%x dosname=%s",
|
||||||
|
g_device_id, preferred_dos_name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tell the client wheth or not we're supporting this one */
|
||||||
|
devredir_send_server_device_announce_resp(g_device_id,
|
||||||
|
response_status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,6 +1009,7 @@ devredir_proc_device_iocompletion(struct stream *s)
|
|||||||
case CID_READ:
|
case CID_READ:
|
||||||
xstream_rd_u32_le(s, Length);
|
xstream_rd_u32_le(s, Length);
|
||||||
xfuse_devredir_cb_read_file((struct state_read *) irp->fuse_info,
|
xfuse_devredir_cb_read_file((struct state_read *) irp->fuse_info,
|
||||||
|
IoStatus,
|
||||||
s->p, Length);
|
s->p, Length);
|
||||||
devredir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
break;
|
break;
|
||||||
@ -1529,10 +1528,10 @@ devredir_rmdir_or_file(struct state_remove *fusep, tui32 device_id,
|
|||||||
/**
|
/**
|
||||||
* Read data from previously opened file
|
* Read data from previously opened file
|
||||||
*
|
*
|
||||||
* @return 0 on success, -1 on failure
|
* Errors are reported via xfuse_devredir_cb_read_file()
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
int
|
void
|
||||||
devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId,
|
devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId,
|
||||||
tui32 Length, tui64 Offset)
|
tui32 Length, tui64 Offset)
|
||||||
{
|
{
|
||||||
@ -1540,7 +1539,6 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
IRP *irp;
|
IRP *irp;
|
||||||
IRP *new_irp;
|
IRP *new_irp;
|
||||||
int bytes;
|
int bytes;
|
||||||
int rval = -1;
|
|
||||||
|
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
|
|
||||||
@ -1548,14 +1546,14 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL)
|
if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("no IRP found with FileId = %d", FileId);
|
log_error("no IRP found with FileId = %d", FileId);
|
||||||
xfuse_devredir_cb_read_file(fusep, NULL, 0);
|
xfuse_devredir_cb_read_file(fusep, STATUS_UNSUCCESSFUL, NULL, 0);
|
||||||
xstream_free(s);
|
xstream_free(s);
|
||||||
}
|
}
|
||||||
/* create a new IRP for this request */
|
/* create a new IRP for this request */
|
||||||
else if ((new_irp = devredir_irp_new()) == NULL)
|
else if ((new_irp = devredir_irp_new()) == NULL)
|
||||||
{
|
{
|
||||||
/* system out of memory */
|
/* system out of memory */
|
||||||
xfuse_devredir_cb_read_file(fusep, NULL, 0);
|
xfuse_devredir_cb_read_file(fusep, STATUS_UNSUCCESSFUL, NULL, 0);
|
||||||
xstream_free(s);
|
xstream_free(s);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1581,13 +1579,16 @@ devredir_file_read(struct state_read *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
bytes = xstream_len(s);
|
bytes = xstream_len(s);
|
||||||
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
||||||
xstream_free(s);
|
xstream_free(s);
|
||||||
rval = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/**
|
||||||
|
* Read data from previously opened file
|
||||||
|
*
|
||||||
|
* Errors are reported via xfuse_devredir_cb_write_file()
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
||||||
const char *buf, int Length, tui64 Offset)
|
const char *buf, int Length, tui64 Offset)
|
||||||
{
|
{
|
||||||
@ -1595,7 +1596,6 @@ devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
IRP *irp;
|
IRP *irp;
|
||||||
IRP *new_irp;
|
IRP *new_irp;
|
||||||
int bytes;
|
int bytes;
|
||||||
int rval = -1;
|
|
||||||
|
|
||||||
log_debug("DeviceId=%d FileId=%d Length=%d Offset=%lld",
|
log_debug("DeviceId=%d FileId=%d Length=%d Offset=%lld",
|
||||||
DeviceId, FileId, Length, (long long)Offset);
|
DeviceId, FileId, Length, (long long)Offset);
|
||||||
@ -1643,10 +1643,7 @@ devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
bytes = xstream_len(s);
|
bytes = xstream_len(s);
|
||||||
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
||||||
xstream_free(s);
|
xstream_free(s);
|
||||||
rval = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,10 +78,11 @@ int devredir_file_open(struct state_open *fusep, tui32 device_id,
|
|||||||
int devredir_file_close(struct state_close *fusep, tui32 device_id,
|
int devredir_file_close(struct state_close *fusep, tui32 device_id,
|
||||||
tui32 file_id);
|
tui32 file_id);
|
||||||
|
|
||||||
int devredir_file_read(struct state_read *fusep, tui32 device_id, tui32 FileId,
|
void
|
||||||
|
devredir_file_read(struct state_read *fusep, tui32 device_id, tui32 FileId,
|
||||||
tui32 Length, tui64 Offset);
|
tui32 Length, tui64 Offset);
|
||||||
|
|
||||||
int
|
void
|
||||||
devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
devredir_file_write(struct state_write *fusep, tui32 DeviceId, tui32 FileId,
|
||||||
const char *buf, int Length, tui64 Offset);
|
const char *buf, int Length, tui64 Offset);
|
||||||
|
|
||||||
|
@ -37,8 +37,6 @@ int g_rem_io_count = 0; // bytes read from remote port
|
|||||||
static int g_terminated = 0;
|
static int g_terminated = 0;
|
||||||
static char g_buf[1024 * 32];
|
static char g_buf[1024 * 32];
|
||||||
|
|
||||||
#define
|
|
||||||
#define
|
|
||||||
|
|
||||||
typedef unsigned short tui16;
|
typedef unsigned short tui16;
|
||||||
|
|
||||||
|
@ -1,266 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
// not used
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "decoderthread.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO:
|
|
||||||
* o need to maintain aspect ratio while resizing
|
|
||||||
* o clicking in the middle of the slider bar should move the slider to the middle
|
|
||||||
* o need to be able to rewind the move when it is done playing
|
|
||||||
* o need to be able to load another move and play it w/o restarting player
|
|
||||||
* o pause button needs to work
|
|
||||||
* o need images for btns
|
|
||||||
*/
|
|
||||||
|
|
||||||
DecoderThread::DecoderThread()
|
|
||||||
{
|
|
||||||
channel = NULL;
|
|
||||||
geometry.setX(0);
|
|
||||||
geometry.setY(0);
|
|
||||||
geometry.setWidth(0);
|
|
||||||
geometry.setHeight(0);
|
|
||||||
stream_id = 101;
|
|
||||||
elapsedTime = 0;
|
|
||||||
la_seekPos = -1;
|
|
||||||
videoTimer = NULL;
|
|
||||||
audioTimer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::run()
|
|
||||||
{
|
|
||||||
/* need a media file */
|
|
||||||
if (filename.length() == 0)
|
|
||||||
{
|
|
||||||
emit on_decoderErrorMsg("No media file",
|
|
||||||
"Please select a media file to play");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::startMediaPlay()
|
|
||||||
{
|
|
||||||
MediaPacket *mediaPkt;
|
|
||||||
int is_video_frame;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
/* setup video timer; each time this timer fires, it sends */
|
|
||||||
/* one video pkt to the client then resets the callback duration */
|
|
||||||
videoTimer = new QTimer;
|
|
||||||
connect(videoTimer, SIGNAL(timeout()), this, SLOT(videoTimerCallback()));
|
|
||||||
//videoTimer->start(1500);
|
|
||||||
|
|
||||||
/* setup audio timer; does the same as above, but with audio pkts */
|
|
||||||
audioTimer = new QTimer;
|
|
||||||
connect(audioTimer, SIGNAL(timeout()), this, SLOT(audioTimerCallback()));
|
|
||||||
//audioTimer->start(500);
|
|
||||||
|
|
||||||
/* setup pktTimer; each time this timer fires, it reads AVPackets */
|
|
||||||
/* and puts them into audio/video Queues */
|
|
||||||
pktTimer = new QTimer;
|
|
||||||
connect(pktTimer, SIGNAL(timeout()), this, SLOT(pktTimerCallback()));
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* fill the audio/video queues with initial data; thereafter */
|
|
||||||
/* data will be filled by pktTimerCallback() */
|
|
||||||
if ((audioQueue.count() >= 3000) || (videoQueue.count() >= 3000))
|
|
||||||
{
|
|
||||||
//pktTimer->start(50);
|
|
||||||
|
|
||||||
//videoTimer->start(1500);
|
|
||||||
//audioTimer->start(500);
|
|
||||||
|
|
||||||
playVideo = new PlayVideo(NULL, &videoQueue, channel, 101);
|
|
||||||
playVideoThread = new QThread(this);
|
|
||||||
connect(playVideoThread, SIGNAL(started()), playVideo, SLOT(play()));
|
|
||||||
playVideo->moveToThread(playVideoThread);
|
|
||||||
playVideoThread->start();
|
|
||||||
|
|
||||||
playAudio = new PlayAudio(NULL, &audioQueue, channel, 101);
|
|
||||||
playAudioThread = new QThread(this);
|
|
||||||
connect(playAudioThread, SIGNAL(started()), playAudio, SLOT(play()));
|
|
||||||
playAudio->moveToThread(playAudioThread);
|
|
||||||
playAudioThread->start();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaPkt = new MediaPacket;
|
|
||||||
rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
|
|
||||||
&is_video_frame,
|
|
||||||
&mediaPkt->delay_in_us);
|
|
||||||
if (rv < 0)
|
|
||||||
{
|
|
||||||
/* looks like we reached end of file */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_video_frame)
|
|
||||||
videoQueue.enqueue(mediaPkt);
|
|
||||||
else
|
|
||||||
audioQueue.enqueue(mediaPkt);
|
|
||||||
|
|
||||||
} /* end while (1) */
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::on_mediaSeek(int value)
|
|
||||||
{
|
|
||||||
mutex.lock();
|
|
||||||
la_seekPos = value;
|
|
||||||
mutex.unlock();
|
|
||||||
|
|
||||||
qDebug() << "media seek value=" << value;
|
|
||||||
|
|
||||||
/* pktTimer stops at end of media; need to restart it */
|
|
||||||
if (!pktTimer->isActive())
|
|
||||||
{
|
|
||||||
updateSlider();
|
|
||||||
pktTimer->start(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::setFilename(QString filename)
|
|
||||||
{
|
|
||||||
this->filename = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::stopPlayer()
|
|
||||||
{
|
|
||||||
pktTimer->stop();
|
|
||||||
audioQueue.clear();
|
|
||||||
videoQueue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::pausePlayer()
|
|
||||||
{
|
|
||||||
pktTimer->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::resumePlayer()
|
|
||||||
{
|
|
||||||
pktTimer->start(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::close()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::audioTimerCallback()
|
|
||||||
{
|
|
||||||
MediaPacket *pkt;
|
|
||||||
int delayInMs;
|
|
||||||
|
|
||||||
if (audioQueue.isEmpty())
|
|
||||||
{
|
|
||||||
qDebug() << "audioTimerCallback: got empty";
|
|
||||||
audioTimer->setInterval(100);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pkt = audioQueue.dequeue();
|
|
||||||
delayInMs = (int) ((float) pkt->delay_in_us / 1000.0);
|
|
||||||
send_audio_pkt(channel, 101, pkt->av_pkt);
|
|
||||||
delete pkt;
|
|
||||||
|
|
||||||
//qDebug() << "audioTimerCallback: delay :" << delayInMs;
|
|
||||||
|
|
||||||
audioTimer->setInterval(delayInMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::videoTimerCallback()
|
|
||||||
{
|
|
||||||
MediaPacket *pkt;
|
|
||||||
int delayInMs;
|
|
||||||
|
|
||||||
if (videoQueue.isEmpty())
|
|
||||||
{
|
|
||||||
qDebug() << "videoTimerCallback: GOT EMPTY";
|
|
||||||
videoTimer->setInterval(100);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pkt = videoQueue.dequeue();
|
|
||||||
delayInMs = (int) 10; // ((float) pkt->delay_in_us / 1000.0);
|
|
||||||
send_video_pkt(channel, 101, pkt->av_pkt);
|
|
||||||
delete pkt;
|
|
||||||
updateSlider();
|
|
||||||
//qDebug() << "videoTimerCallback: delay :" << delayInMs;
|
|
||||||
videoTimer->setInterval(delayInMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::pktTimerCallback()
|
|
||||||
{
|
|
||||||
MediaPacket *mediaPkt;
|
|
||||||
int is_video_frame;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
qDebug() << "pktTimerCallback: audioCount=" << audioQueue.count() << "videoCount=" << videoQueue.count();
|
|
||||||
#if 1
|
|
||||||
if ((audioQueue.count() >= 20) || (videoQueue.count() >= 20))
|
|
||||||
return;
|
|
||||||
#else
|
|
||||||
if (videoQueue.count() >= 60)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
mediaPkt = new MediaPacket;
|
|
||||||
rv = xrdpvr_get_frame(&mediaPkt->av_pkt,
|
|
||||||
&is_video_frame,
|
|
||||||
&mediaPkt->delay_in_us);
|
|
||||||
if (rv < 0)
|
|
||||||
{
|
|
||||||
/* looks like we reached end of file */
|
|
||||||
qDebug() << "###### looks like we reached EOF";
|
|
||||||
pktTimer->stop();
|
|
||||||
// LK_TODO set some flag so audio/video timer also stop when q is empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_video_frame)
|
|
||||||
videoQueue.enqueue(mediaPkt);
|
|
||||||
else
|
|
||||||
audioQueue.enqueue(mediaPkt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecoderThread::updateSlider()
|
|
||||||
{
|
|
||||||
if (elapsedTime == 0)
|
|
||||||
elapsedTime = av_gettime();
|
|
||||||
|
|
||||||
/* time elapsed in 1/100th sec units since play started */
|
|
||||||
emit on_elapsedtime((av_gettime() - elapsedTime) / 10000);
|
|
||||||
|
|
||||||
mutex.lock();
|
|
||||||
if (la_seekPos >= 0)
|
|
||||||
{
|
|
||||||
qDebug() << "seeking to" << la_seekPos;
|
|
||||||
//audioTimer->stop();
|
|
||||||
//videoTimer->stop();
|
|
||||||
xrdpvr_seek_media(la_seekPos, 0);
|
|
||||||
elapsedTime = av_gettime() - la_seekPos * 1000000;
|
|
||||||
//audioTimer->start(10);
|
|
||||||
//videoTimer->start(10);
|
|
||||||
la_seekPos = -1;
|
|
||||||
}
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
#ifndef DECODERTHREAD_H
|
|
||||||
#define DECODERTHREAD_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define __STDC_CONSTANT_MACROS
|
|
||||||
#ifdef _STDINT_H
|
|
||||||
#undef _STDINT_H
|
|
||||||
#endif
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QThread>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QString>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QQueue>
|
|
||||||
|
|
||||||
#include <xrdpapi.h>
|
|
||||||
#include <xrdpvr.h>
|
|
||||||
#include <mediapacket.h>
|
|
||||||
#include <playvideo.h>
|
|
||||||
#include <playaudio.h>
|
|
||||||
|
|
||||||
/* ffmpeg related stuff */
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
class DecoderThread : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/* public methods */
|
|
||||||
DecoderThread();
|
|
||||||
|
|
||||||
void setFilename(QString filename);
|
|
||||||
void stopPlayer();
|
|
||||||
void pausePlayer();
|
|
||||||
void resumePlayer();
|
|
||||||
void oneTimeDeinit();
|
|
||||||
void close();
|
|
||||||
void run();
|
|
||||||
void startMediaPlay();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void on_mediaSeek(int value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* private variables */
|
|
||||||
QQueue<MediaPacket *> audioQueue;
|
|
||||||
QQueue<MediaPacket *> videoQueue;
|
|
||||||
|
|
||||||
QTimer *videoTimer;
|
|
||||||
QTimer *audioTimer;
|
|
||||||
QTimer *pktTimer;
|
|
||||||
QString filename;
|
|
||||||
void *channel;
|
|
||||||
int stream_id;
|
|
||||||
QRect geometry;
|
|
||||||
int64_t elapsedTime; /* elapsed time in usecs since play started */
|
|
||||||
QMutex mutex;
|
|
||||||
int64_t la_seekPos; /* locked access; must hold mutex */
|
|
||||||
|
|
||||||
PlayVideo *playVideo;
|
|
||||||
QThread *playVideoThread;
|
|
||||||
PlayAudio *playAudio;
|
|
||||||
QThread *playAudioThread;
|
|
||||||
|
|
||||||
/* private functions */
|
|
||||||
int sendMetadataFile();
|
|
||||||
int sendAudioFormat();
|
|
||||||
int sendVideoFormat();
|
|
||||||
int sendGeometry();
|
|
||||||
void updateSlider();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
/* private slots */
|
|
||||||
void audioTimerCallback();
|
|
||||||
void videoTimerCallback();
|
|
||||||
void pktTimerCallback();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
/* private signals */
|
|
||||||
void on_progressUpdate(int percent);
|
|
||||||
void on_decoderErrorMsg(QString title, QString msg);
|
|
||||||
void on_mediaDurationInSeconds(int duration);
|
|
||||||
void on_elapsedtime(int val); /* in hundredth of a sec */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DECODERTHREAD_H
|
|
@ -11,6 +11,7 @@ OurInterface::OurInterface(QObject *parent) :
|
|||||||
savedGeometry.setHeight(0);
|
savedGeometry.setHeight(0);
|
||||||
stream_id = 101;
|
stream_id = 101;
|
||||||
demuxMedia = 0;
|
demuxMedia = 0;
|
||||||
|
demuxMediaThread = NULL;
|
||||||
//elapsedTime = 0;
|
//elapsedTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@ handle_connection(int client_fd)
|
|||||||
}
|
}
|
||||||
if (retlen != sizeof(rdp_fd))
|
if (retlen != sizeof(rdp_fd))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "WTSVirtualChannelQuery() returned wrong length %d\n",
|
fprintf(stderr, "WTSVirtualChannelQuery() returned wrong length %u\n",
|
||||||
retlen);
|
retlen);
|
||||||
}
|
}
|
||||||
rdp_fd = *retdata;
|
rdp_fd = *retdata;
|
||||||
|
Loading…
Reference in New Issue
Block a user