2013-07-25 16:48:48 +00:00
|
|
|
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (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.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is libguac-client-rdp.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Michael Jumper.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2013-10-22 06:52:15 +00:00
|
|
|
#include <unistd.h>
|
2013-07-30 00:57:24 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2013-08-02 00:44:39 +00:00
|
|
|
#include <dirent.h>
|
2013-07-30 00:57:24 +00:00
|
|
|
#include <fcntl.h>
|
2013-09-17 21:26:05 +00:00
|
|
|
#include <fnmatch.h>
|
2013-07-30 00:57:24 +00:00
|
|
|
|
2013-07-25 16:48:48 +00:00
|
|
|
#ifdef ENABLE_WINPR
|
|
|
|
#include <winpr/stream.h>
|
|
|
|
#else
|
|
|
|
#include "compat/winpr-stream.h"
|
|
|
|
#endif
|
|
|
|
|
2013-08-01 20:36:39 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2013-07-26 17:45:40 +00:00
|
|
|
#include <guacamole/pool.h>
|
|
|
|
|
2013-07-25 16:48:48 +00:00
|
|
|
#include "rdpdr_messages.h"
|
|
|
|
#include "rdpdr_fs.h"
|
|
|
|
#include "rdpdr_service.h"
|
|
|
|
#include "client.h"
|
2013-07-26 18:27:11 +00:00
|
|
|
#include "unicode.h"
|
2013-07-25 16:48:48 +00:00
|
|
|
|
|
|
|
#include <freerdp/utils/svc_plugin.h>
|
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
/**
|
2013-08-02 20:35:52 +00:00
|
|
|
* Translates an absolute Windows virtual_path to an absolute virtual_path
|
|
|
|
* which is within the "drive virtual_path" specified in the connection
|
|
|
|
* settings.
|
2013-07-30 00:57:24 +00:00
|
|
|
*/
|
2013-08-02 20:35:52 +00:00
|
|
|
static void __guac_rdpdr_fs_translate_path(guac_rdpdr_device* device,
|
|
|
|
const char* virtual_path, char* real_path) {
|
2013-07-30 00:57:24 +00:00
|
|
|
|
|
|
|
/* Get drive path */
|
|
|
|
rdp_guac_client_data* client_data = (rdp_guac_client_data*) device->rdpdr->client->data;
|
|
|
|
char* drive_path = client_data->settings.drive_path;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Start with path from settings */
|
|
|
|
for (i=0; i<GUAC_RDPDR_FS_MAX_PATH-1; i++) {
|
|
|
|
|
2013-07-30 05:01:09 +00:00
|
|
|
/* Break on end-of-string */
|
|
|
|
char c = *(drive_path++);
|
|
|
|
if (c == 0)
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
2013-07-30 05:01:09 +00:00
|
|
|
/* Copy character */
|
2013-08-02 20:35:52 +00:00
|
|
|
*(real_path++) = c;
|
2013-07-30 05:01:09 +00:00
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Translate path */
|
|
|
|
for (; i<GUAC_RDPDR_FS_MAX_PATH-1; i++) {
|
|
|
|
|
|
|
|
/* Stop at end of string */
|
2013-08-02 20:35:52 +00:00
|
|
|
char c = *(virtual_path++);
|
2013-07-30 00:57:24 +00:00
|
|
|
if (c == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Translate backslashes to forward slashes */
|
|
|
|
if (c == '\\')
|
|
|
|
c = '/';
|
|
|
|
|
|
|
|
/* Store in real path buffer */
|
2013-08-02 20:35:52 +00:00
|
|
|
*(real_path++)= c;
|
2013-07-30 00:57:24 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Null terminator */
|
2013-08-02 20:35:52 +00:00
|
|
|
*real_path = 0;
|
2013-07-30 00:57:24 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-10-22 17:56:47 +00:00
|
|
|
int guac_rdpdr_fs_open(guac_rdpdr_device* device, const char* path,
|
|
|
|
int access, int file_attributes, int create_disposition,
|
|
|
|
int create_options) {
|
2013-07-26 17:45:40 +00:00
|
|
|
|
|
|
|
guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data;
|
2013-08-02 20:35:52 +00:00
|
|
|
char real_path[GUAC_RDPDR_FS_MAX_PATH];
|
|
|
|
char normalized_path[GUAC_RDPDR_FS_MAX_PATH];
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-08-01 20:36:39 +00:00
|
|
|
struct stat file_stat;
|
2013-07-30 00:57:24 +00:00
|
|
|
int fd;
|
2013-07-29 19:47:08 +00:00
|
|
|
int file_id;
|
|
|
|
guac_rdpdr_fs_file* file;
|
2013-07-26 17:45:40 +00:00
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
int flags = 0;
|
|
|
|
|
2013-07-29 19:47:08 +00:00
|
|
|
/* If no files available, return too many open */
|
|
|
|
if (data->open_files >= GUAC_RDPDR_FS_MAX_FILES)
|
2013-07-29 19:52:04 +00:00
|
|
|
return GUAC_RDPDR_FS_ENFILE;
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-09-11 15:52:36 +00:00
|
|
|
/* If path empty, transform to root path */
|
|
|
|
if (path[0] == '\0')
|
|
|
|
path = "\\";
|
|
|
|
|
|
|
|
/* If path is relative, the file does not exist */
|
|
|
|
else if (path[0] != '\\')
|
2013-07-30 00:57:24 +00:00
|
|
|
return GUAC_RDPDR_FS_ENOENT;
|
|
|
|
|
2013-10-18 08:06:09 +00:00
|
|
|
/* Translate access into flags */
|
2013-09-11 15:52:36 +00:00
|
|
|
if (access & ACCESS_GENERIC_ALL)
|
2013-10-18 08:06:09 +00:00
|
|
|
flags = O_RDWR;
|
2013-09-18 20:58:03 +00:00
|
|
|
else if ((access & (ACCESS_GENERIC_WRITE | ACCESS_FILE_WRITE_DATA))
|
|
|
|
&& (access & (ACCESS_GENERIC_READ | ACCESS_FILE_READ_DATA)))
|
2013-10-18 08:06:09 +00:00
|
|
|
flags = O_RDWR;
|
2013-09-18 20:58:03 +00:00
|
|
|
else if (access & (ACCESS_GENERIC_WRITE | ACCESS_FILE_WRITE_DATA))
|
2013-10-18 08:06:09 +00:00
|
|
|
flags = O_WRONLY;
|
2013-07-30 00:57:24 +00:00
|
|
|
else
|
2013-10-18 08:06:09 +00:00
|
|
|
flags = O_RDONLY;
|
2013-07-30 00:57:24 +00:00
|
|
|
|
|
|
|
/* If append access requested, add appropriate option */
|
|
|
|
if (access & ACCESS_FILE_APPEND_DATA)
|
|
|
|
flags |= O_APPEND;
|
|
|
|
|
2013-10-22 06:52:15 +00:00
|
|
|
/* Normalize path, return no-such-file if invalid */
|
|
|
|
if (guac_rdpdr_fs_normalize_path(path, normalized_path))
|
|
|
|
return GUAC_RDPDR_FS_ENOENT;
|
|
|
|
|
|
|
|
/* Translate normalized path to real path */
|
|
|
|
__guac_rdpdr_fs_translate_path(device, normalized_path, real_path);
|
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
switch (create_disposition) {
|
|
|
|
|
2013-10-22 06:52:15 +00:00
|
|
|
/* Create if not exist, fail otherwise */
|
|
|
|
case DISP_FILE_CREATE:
|
|
|
|
flags |= O_CREAT | O_EXCL;
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Open file if exists and do not overwrite, fail otherwise */
|
|
|
|
case DISP_FILE_OPEN:
|
2013-10-22 06:52:15 +00:00
|
|
|
/* No flag necessary - default functionality of open */
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Open if exists, create otherwise */
|
|
|
|
case DISP_FILE_OPEN_IF:
|
2013-10-22 06:52:15 +00:00
|
|
|
flags |= O_CREAT;
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Overwrite if exists, fail otherwise */
|
|
|
|
case DISP_FILE_OVERWRITE:
|
2013-10-22 06:52:15 +00:00
|
|
|
flags |= O_TRUNC;
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Overwrite if exists, create otherwise */
|
|
|
|
case DISP_FILE_OVERWRITE_IF:
|
2013-10-22 06:52:15 +00:00
|
|
|
flags |= O_CREAT | O_TRUNC;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Supersede (replace) if exists, otherwise create */
|
|
|
|
case DISP_FILE_SUPERSEDE:
|
|
|
|
unlink(real_path);
|
|
|
|
flags |= O_CREAT | O_TRUNC;
|
2013-07-30 00:57:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Unrecognised disposition */
|
|
|
|
default:
|
|
|
|
return GUAC_RDPDR_FS_ENOENT; /* FIXME: Replace with real return value */
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-10-23 01:55:58 +00:00
|
|
|
/* Create directory first, if necessary */
|
|
|
|
if (file_attributes & FILE_ATTRIBUTE_DIRECTORY && (flags & O_CREAT))
|
|
|
|
mkdir(real_path, S_IRWXU);
|
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
/* Open file */
|
2013-10-22 17:29:32 +00:00
|
|
|
fd = open(real_path, flags, S_IRUSR | S_IWUSR);
|
2013-09-16 22:04:18 +00:00
|
|
|
if (fd == -1)
|
2013-07-30 00:57:24 +00:00
|
|
|
return GUAC_RDPDR_FS_ENOENT;
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-07-30 00:57:24 +00:00
|
|
|
/* Get file ID, init file */
|
2013-07-29 19:47:08 +00:00
|
|
|
file_id = guac_pool_next_int(data->file_id_pool);
|
|
|
|
file = &(data->files[file_id]);
|
2013-08-02 00:23:07 +00:00
|
|
|
file->fd = fd;
|
|
|
|
file->dir = NULL;
|
2013-09-17 21:26:05 +00:00
|
|
|
file->dir_pattern[0] = '\0';
|
2013-08-02 22:01:46 +00:00
|
|
|
file->absolute_path = strdup(normalized_path);
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-08-01 20:36:39 +00:00
|
|
|
/* Attempt to pull file information */
|
|
|
|
if (fstat(fd, &file_stat) == 0) {
|
2013-08-01 20:44:30 +00:00
|
|
|
|
|
|
|
/* Load size and times */
|
2013-08-01 20:36:39 +00:00
|
|
|
file->size = file_stat.st_size;
|
2013-08-03 00:23:09 +00:00
|
|
|
file->ctime = WINDOWS_TIME(file_stat.st_ctime);
|
|
|
|
file->mtime = WINDOWS_TIME(file_stat.st_mtime);
|
|
|
|
file->atime = WINDOWS_TIME(file_stat.st_atime);
|
2013-08-01 20:44:30 +00:00
|
|
|
|
|
|
|
/* Set type */
|
|
|
|
if (S_ISDIR(file_stat.st_mode))
|
2013-08-03 00:23:09 +00:00
|
|
|
file->attributes = FILE_ATTRIBUTE_DIRECTORY;
|
2013-08-01 20:44:30 +00:00
|
|
|
else
|
2013-08-03 00:23:09 +00:00
|
|
|
file->attributes = FILE_ATTRIBUTE_NORMAL;
|
2013-08-01 20:44:30 +00:00
|
|
|
|
2013-08-01 20:36:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If information cannot be retrieved, fake it */
|
|
|
|
else {
|
|
|
|
|
2013-09-16 22:04:18 +00:00
|
|
|
guac_client_log_info(device->rdpdr->client,
|
|
|
|
"Unable to read information for \"%s\"",
|
2013-08-02 20:35:52 +00:00
|
|
|
real_path);
|
2013-08-01 20:36:39 +00:00
|
|
|
|
|
|
|
/* Init information to 0, lacking any alternative */
|
|
|
|
file->size = 0;
|
|
|
|
file->ctime = 0;
|
|
|
|
file->mtime = 0;
|
|
|
|
file->atime = 0;
|
2013-08-03 00:23:09 +00:00
|
|
|
file->attributes = FILE_ATTRIBUTE_NORMAL;
|
2013-08-01 20:36:39 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-29 19:47:08 +00:00
|
|
|
data->open_files++;
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-07-29 19:47:08 +00:00
|
|
|
return file_id;
|
2013-07-26 17:45:40 +00:00
|
|
|
|
|
|
|
}
|
2013-07-25 16:48:48 +00:00
|
|
|
|
2013-07-26 17:45:40 +00:00
|
|
|
void guac_rdpdr_fs_close(guac_rdpdr_device* device, int file_id) {
|
2013-07-26 21:11:44 +00:00
|
|
|
|
2013-07-26 17:45:40 +00:00
|
|
|
guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data;
|
2013-08-02 00:44:39 +00:00
|
|
|
guac_rdpdr_fs_file* file;
|
2013-07-26 21:11:44 +00:00
|
|
|
|
|
|
|
/* Only close if file ID is valid */
|
2013-08-02 00:44:39 +00:00
|
|
|
if (file_id < 0 || file_id >= GUAC_RDPDR_FS_MAX_FILES)
|
|
|
|
return;
|
|
|
|
|
|
|
|
file = &(data->files[file_id]);
|
|
|
|
|
|
|
|
/* Close directory, if open */
|
|
|
|
if (file->dir != NULL)
|
|
|
|
closedir(file->dir);
|
2013-08-02 00:23:07 +00:00
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
/* Close file */
|
|
|
|
close(file->fd);
|
2013-08-02 00:23:07 +00:00
|
|
|
|
2013-08-02 20:35:52 +00:00
|
|
|
/* Free name */
|
|
|
|
free(file->absolute_path);
|
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
/* Free ID back to pool */
|
|
|
|
guac_pool_free_int(data->file_id_pool, file_id);
|
|
|
|
data->open_files--;
|
2013-08-02 00:23:07 +00:00
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* guac_rdpdr_fs_read_dir(guac_rdpdr_device* device, int file_id) {
|
2013-08-02 00:23:07 +00:00
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data;
|
|
|
|
guac_rdpdr_fs_file* file;
|
2013-08-02 00:23:07 +00:00
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
struct dirent* result;
|
|
|
|
|
|
|
|
/* Only read if file ID is valid */
|
|
|
|
if (file_id < 0 || file_id >= GUAC_RDPDR_FS_MAX_FILES)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
file = &(data->files[file_id]);
|
|
|
|
|
|
|
|
/* Open directory if not yet open, stop if error */
|
|
|
|
if (file->dir == NULL) {
|
|
|
|
file->dir = fdopendir(file->fd);
|
|
|
|
if (file->dir == NULL)
|
|
|
|
return NULL;
|
2013-07-26 21:11:44 +00:00
|
|
|
}
|
|
|
|
|
2013-08-02 00:44:39 +00:00
|
|
|
/* Read next entry, stop if error */
|
|
|
|
if (readdir_r(file->dir, &(file->__dirent), &result))
|
|
|
|
return NULL;
|
2013-08-06 05:52:42 +00:00
|
|
|
|
|
|
|
/* If no more entries, return NULL */
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
2013-08-02 00:44:39 +00:00
|
|
|
|
|
|
|
/* Return filename */
|
|
|
|
return file->__dirent.d_name;
|
|
|
|
|
2013-07-25 16:48:48 +00:00
|
|
|
}
|
|
|
|
|
2013-08-02 20:35:52 +00:00
|
|
|
int guac_rdpdr_fs_normalize_path(const char* path, char* abs_path) {
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int path_depth = 0;
|
|
|
|
char path_component_data[GUAC_RDPDR_FS_MAX_PATH];
|
|
|
|
const char* path_components[64];
|
|
|
|
|
|
|
|
const char** current_path_component = &(path_components[0]);
|
|
|
|
const char* current_path_component_data = &(path_component_data[0]);
|
|
|
|
|
|
|
|
/* If original path is not absolute, normalization fails */
|
|
|
|
if (path[0] != '\\' && path[0] != '/')
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Skip past leading slash */
|
|
|
|
path++;
|
|
|
|
|
|
|
|
/* Copy path into component data for parsing */
|
|
|
|
strncpy(path_component_data, path, GUAC_RDPDR_FS_MAX_PATH-1);
|
|
|
|
|
|
|
|
/* Find path components within path */
|
|
|
|
for (i=0; i<GUAC_RDPDR_FS_MAX_PATH; i++) {
|
|
|
|
|
|
|
|
/* If current character is a path separator, parse as component */
|
|
|
|
char c = path_component_data[i];
|
|
|
|
if (c == '/' || c == '\\' || c == 0) {
|
|
|
|
|
|
|
|
/* Terminate current component */
|
|
|
|
path_component_data[i] = 0;
|
|
|
|
|
|
|
|
/* If component refers to parent, just move up in depth */
|
|
|
|
if (strcmp(current_path_component_data, "..") == 0) {
|
|
|
|
if (path_depth > 0)
|
|
|
|
path_depth--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, if component not current directory, add to list */
|
2013-08-02 22:01:46 +00:00
|
|
|
else if (strcmp(current_path_component_data, ".") != 0
|
|
|
|
&& strcmp(current_path_component_data, "") != 0)
|
2013-08-02 20:35:52 +00:00
|
|
|
path_components[path_depth++] = current_path_component_data;
|
|
|
|
|
|
|
|
/* If end of string, stop */
|
|
|
|
if (c == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Update start of next component */
|
|
|
|
current_path_component_data = &(path_component_data[i+1]);
|
|
|
|
|
|
|
|
} /* end if separator */
|
|
|
|
|
|
|
|
} /* end for each character */
|
|
|
|
|
2013-08-02 22:01:46 +00:00
|
|
|
/* If no components, the path is simply root */
|
|
|
|
if (path_depth == 0) {
|
|
|
|
strcpy(abs_path, "\\");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-08-02 20:35:52 +00:00
|
|
|
|
|
|
|
/* Ensure last component is null-terminated */
|
|
|
|
path_component_data[i] = 0;
|
|
|
|
|
|
|
|
/* Convert components back into path */
|
|
|
|
for (; path_depth > 0; path_depth--) {
|
|
|
|
|
|
|
|
const char* filename = *(current_path_component++);
|
|
|
|
|
|
|
|
/* Add separator */
|
|
|
|
*(abs_path++) = '\\';
|
|
|
|
|
|
|
|
/* Copy string */
|
|
|
|
while (*filename != 0)
|
|
|
|
*(abs_path++) = *(filename++);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Terminate absolute path */
|
|
|
|
*(abs_path++) = 0;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int guac_rdpdr_fs_convert_path(const char* parent, const char* rel_path, char* abs_path) {
|
2013-08-02 22:01:46 +00:00
|
|
|
|
|
|
|
int i;
|
|
|
|
char combined_path[GUAC_RDPDR_FS_MAX_PATH];
|
|
|
|
char* current = combined_path;
|
|
|
|
|
|
|
|
/* Copy parent path */
|
|
|
|
for (i=0; i<GUAC_RDPDR_FS_MAX_PATH; i++) {
|
|
|
|
|
|
|
|
char c = *(parent++);
|
|
|
|
if (c == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
*(current++) = c;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add trailing slash */
|
|
|
|
*(current++) = '\\';
|
|
|
|
|
|
|
|
/* Copy remaining path */
|
2013-09-09 22:46:16 +00:00
|
|
|
strncpy(current, rel_path, GUAC_RDPDR_FS_MAX_PATH-i-2);
|
2013-08-02 22:01:46 +00:00
|
|
|
|
|
|
|
/* Normalize into provided buffer */
|
|
|
|
return guac_rdpdr_fs_normalize_path(combined_path, abs_path);
|
|
|
|
|
2013-08-02 20:35:52 +00:00
|
|
|
}
|
|
|
|
|
2013-09-16 21:01:08 +00:00
|
|
|
guac_rdpdr_fs_file* guac_rdpdr_fs_get_file(guac_rdpdr_device* device,
|
|
|
|
int file_id) {
|
|
|
|
|
|
|
|
/* Validate ID */
|
|
|
|
guac_rdpdr_fs_data* data = (guac_rdpdr_fs_data*) device->data;
|
|
|
|
if (file_id < 0 || file_id >= GUAC_RDPDR_FS_MAX_FILES)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Return file at given ID */
|
|
|
|
return &(data->files[file_id]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-09-17 21:26:05 +00:00
|
|
|
int guac_rdpdr_fs_matches(const char* filename, const char* pattern) {
|
|
|
|
return fnmatch(pattern, filename, FNM_NOESCAPE) != 0;
|
|
|
|
}
|
|
|
|
|