92423a466e
strncat() will copy at most the specified number of characters and append the null character on top of that. strlen() doesn't count the final null character.
3119 lines
84 KiB
C
3119 lines
84 KiB
C
/**
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* 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 .clipboard 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 <time.h>
|
|
|
|
#include "arch.h"
|
|
#include "chansrv_fuse.h"
|
|
|
|
/* dummy calls when XRDP_FUSE is not defined */
|
|
int xfuse_init(void) { return 0; }
|
|
int xfuse_deinit(void) { return 0; }
|
|
int xfuse_check_wait_objs(void) { return 0; }
|
|
int xfuse_get_wait_objs(tbus *objs, int *count, int *timeout) { return 0; }
|
|
int xfuse_clear_clip_dir(void) { return 0; }
|
|
int xfuse_file_contents_range(int stream_id, char *data, int data_bytes) { return 0; }
|
|
int xfuse_file_contents_size(int stream_id, int file_size) { return 0; }
|
|
int xfuse_add_clip_dir_item(char *filename, int flags, int size, int lindex) { return 0; }
|
|
int xfuse_create_share(tui32 device_id, char *dirname) { return 0; }
|
|
void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, 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) {}
|
|
int xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode) { return 0; }
|
|
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
|
|
|
|
#include <fuse/fuse_lowlevel.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
|
|
#include "arch.h"
|
|
#include "os_calls.h"
|
|
#include "chansrv_fuse.h"
|
|
#include "list.h"
|
|
#include "fifo.h"
|
|
|
|
#ifndef EREMOTEIO
|
|
#define EREMOTEIO EIO
|
|
#endif
|
|
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
#define XFUSE_ATTR_TIMEOUT 1.0
|
|
#define XFUSE_ENTRY_TIMEOUT 1.0
|
|
|
|
#define DOTDOT_INODE 0
|
|
#define DOT_INODE 0
|
|
#define FIRST_INODE 1
|
|
|
|
/* module based logging */
|
|
#define LOG_ERROR 0
|
|
#define LOG_INFO 1
|
|
#define LOG_DEBUG 2
|
|
#define LOG_LEVEL LOG_ERROR
|
|
|
|
#define log_error(_params...) \
|
|
{ \
|
|
g_write("[%10.10u]: FUSE %s: %d : ERROR: ", \
|
|
g_time3(), __func__, __LINE__); \
|
|
g_writeln (_params); \
|
|
}
|
|
|
|
#define log_always(_params...) \
|
|
{ \
|
|
g_write("[%10.10u]: FUSE %s: %d : ALWAYS: ", \
|
|
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
|
|
{
|
|
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 */
|
|
};
|
|
|
|
struct dirbuf
|
|
{
|
|
char *p;
|
|
size_t size;
|
|
};
|
|
|
|
struct dirbuf1
|
|
{
|
|
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;
|
|
int off;
|
|
int size;
|
|
};
|
|
|
|
struct dir_info
|
|
{
|
|
/* last index accessed in g_xrdp_fs.inode_table[] */
|
|
int index;
|
|
};
|
|
|
|
/* queue FUSE opendir commands so we run only one at a time */
|
|
struct opendir_req
|
|
{
|
|
fuse_req_t req;
|
|
fuse_ino_t ino;
|
|
struct fuse_file_info *fi;
|
|
};
|
|
|
|
static char g_fuse_mount_name[256] = "xrdp_client";
|
|
|
|
FIFO g_fifo_opendir;
|
|
|
|
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(void);
|
|
static int xfuse_deinit_xrdp_fs(void);
|
|
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(void);
|
|
static void xfuse_dump_xrdp_inode(struct xrdp_inode *xino);
|
|
static tui32 xfuse_get_device_id_for_inode(tui32 ino, char *full_path);
|
|
static void fuse_reverse_pathname(char *full_path, char *reverse_path);
|
|
|
|
static struct xrdp_inode * xfuse_get_inode_from_pinode_name(tui32 pinode,
|
|
const char *name);
|
|
|
|
static struct xrdp_inode * xfuse_create_file_in_xrdp_fs(tui32 device_id,
|
|
int pinode, char *name,
|
|
int type);
|
|
|
|
static int xfuse_does_file_exist(int parent, char *name);
|
|
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 int xfuse_recursive_delete_dir_with_xinode(XRDP_INODE *xinode);
|
|
static void xfuse_update_xrdpfs_size(void);
|
|
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 devredir_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, int 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 it'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);
|
|
|
|
static void xfuse_cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
|
int to_set, struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi);
|
|
|
|
static int xfuse_proc_opendir_req(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi);
|
|
|
|
static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi);
|
|
|
|
/* clipboard calls */
|
|
int clipboard_request_file_data(int stream_id, int lindex, int offset,
|
|
int request_bytes);
|
|
|
|
/* misc calls */
|
|
static void xfuse_mark_as_stale(int pinode);
|
|
static void xfuse_delete_stale_entries(int pinode);
|
|
|
|
/*****************************************************************************/
|
|
int APP_CC
|
|
load_fuse_config(void)
|
|
{
|
|
int index;
|
|
char cfg_file[256];
|
|
struct list *items;
|
|
struct list *values;
|
|
char *item;
|
|
char *value;
|
|
|
|
items = list_create();
|
|
items->auto_free = 1;
|
|
values = list_create();
|
|
values->auto_free = 1;
|
|
g_snprintf(cfg_file, 255, "%s/sesman.ini", XRDP_CFG_PATH);
|
|
file_by_name_read_section(cfg_file, "Chansrv", items, values);
|
|
for (index = 0; index < items->count; index++)
|
|
{
|
|
item = (char *)list_get_item(items, index);
|
|
value = (char *)list_get_item(values, index);
|
|
if (g_strcasecmp(item, "FuseMountName") == 0)
|
|
{
|
|
g_strncpy(g_fuse_mount_name, value, 255);
|
|
}
|
|
}
|
|
list_delete(items);
|
|
list_delete(values);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** **
|
|
** public functions - can be called from any code path **
|
|
** **
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Initialize FUSE subsystem
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*****************************************************************************/
|
|
|
|
int APP_CC
|
|
xfuse_init(void)
|
|
{
|
|
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
|
|
|
|
/* if already inited, just return */
|
|
if (g_xfuse_inited)
|
|
{
|
|
log_debug("already inited");
|
|
return 1;
|
|
}
|
|
|
|
if (g_ch != 0)
|
|
{
|
|
log_error("g_ch is not zero");
|
|
return -1;
|
|
}
|
|
|
|
load_fuse_config();
|
|
|
|
/* define FUSE mount point to ~/xrdp_client, ~/thinclient_drives */
|
|
g_snprintf(g_fuse_root_path, 255, "%s/%s", g_getenv("HOME"), g_fuse_mount_name);
|
|
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))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* setup xrdp file system */
|
|
if (xfuse_init_xrdp_fs())
|
|
return -1;
|
|
|
|
/* setup FIFOs */
|
|
fifo_init(&g_fifo_opendir, 30);
|
|
|
|
/* 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;
|
|
g_xfuse_ops.opendir = xfuse_cb_opendir;
|
|
g_xfuse_ops.releasedir = xfuse_cb_releasedir;
|
|
|
|
fuse_opt_add_arg(&args, "xrdp-chansrv");
|
|
fuse_opt_add_arg(&args, g_fuse_root_path);
|
|
//fuse_opt_add_arg(&args, "-s"); /* single threaded mode */
|
|
//fuse_opt_add_arg(&args, "-d"); /* debug mode */
|
|
|
|
if (xfuse_init_lib(&args))
|
|
{
|
|
xfuse_deinit();
|
|
return -1;
|
|
}
|
|
|
|
g_xfuse_inited = 1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* De-initialize FUSE subsystem
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*****************************************************************************/
|
|
|
|
int APP_CC
|
|
xfuse_deinit(void)
|
|
{
|
|
xfuse_deinit_xrdp_fs();
|
|
fifo_deinit(&g_fifo_opendir);
|
|
|
|
if (g_ch != 0)
|
|
{
|
|
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");
|
|
|
|
if (g_xrdp_fs.inode_table == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
g_xrdp_fs.inode_table[i] = NULL;
|
|
free(xinode);
|
|
xip->nentries--;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
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)
|
|
{
|
|
log_error("range error!");
|
|
return -1;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
if (xinode == NULL)
|
|
{
|
|
log_debug("failed to create file in xrdp filesystem");
|
|
return -1;
|
|
}
|
|
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)
|
|
{
|
|
if (fuse_parse_cmdline(args, &g_mount_point, 0, 0) < 0)
|
|
{
|
|
log_error("fuse_parse_cmdline() failed");
|
|
fuse_opt_free_args(args);
|
|
return -1;
|
|
}
|
|
|
|
if ((g_ch = fuse_mount(g_mount_point, args)) == 0)
|
|
{
|
|
log_error("fuse_mount() failed");
|
|
fuse_opt_free_args(args);
|
|
return -1;
|
|
}
|
|
|
|
g_se = fuse_lowlevel_new(args, &g_xfuse_ops, sizeof(g_xfuse_ops), 0);
|
|
if (g_se == 0)
|
|
{
|
|
log_error("fuse_lowlevel_new() failed");
|
|
fuse_unmount(g_mount_point, g_ch);
|
|
g_ch = 0;
|
|
fuse_opt_free_args(args);
|
|
return -1;
|
|
}
|
|
|
|
fuse_opt_free_args(args);
|
|
fuse_session_add_chan(g_se, g_ch);
|
|
g_bufsize = fuse_chan_bufsize(g_ch);
|
|
|
|
g_buffer = calloc(g_bufsize, 1);
|
|
g_fd = fuse_chan_fd(g_ch);
|
|
|
|
g_req_list = list_create();
|
|
g_req_list->auto_free = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialize xrdp file system
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int xfuse_init_xrdp_fs()
|
|
{
|
|
struct xrdp_inode *xino;
|
|
|
|
g_xrdp_fs.inode_table = calloc(4096, sizeof(struct xrdp_inode *));
|
|
if (g_xrdp_fs.inode_table == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* index 0 is our .. dir
|
|
*/
|
|
|
|
if ((xino = calloc(1, sizeof(struct xrdp_inode))) == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
free(g_xrdp_fs.inode_table);
|
|
return -1;
|
|
}
|
|
g_xrdp_fs.inode_table[0] = xino;
|
|
xino->parent_inode = 0;
|
|
xino->inode = 0;
|
|
xino->mode = S_IFDIR | 0755;
|
|
xino->nentries = 1;
|
|
xino->uid = getuid();
|
|
xino->gid = getgid();
|
|
xino->size = 0;
|
|
xino->atime = time(0);
|
|
xino->mtime = time(0);
|
|
xino->ctime = time(0);
|
|
strcpy(xino->name, "..");
|
|
|
|
/*
|
|
* index 1 is our . dir
|
|
*/
|
|
|
|
if ((xino = calloc(1, sizeof(struct xrdp_inode))) == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
free(g_xrdp_fs.inode_table[0]);
|
|
free(g_xrdp_fs.inode_table);
|
|
return -1;
|
|
}
|
|
g_xrdp_fs.inode_table[1] = xino;
|
|
xino->parent_inode = 0;
|
|
xino->inode = 1;
|
|
xino->mode = S_IFDIR | 0755;
|
|
xino->nentries = 1;
|
|
xino->uid = getuid();
|
|
xino->gid = getgid();
|
|
xino->size = 0;
|
|
xino->atime = time(0);
|
|
xino->mtime = time(0);
|
|
xino->ctime = time(0);
|
|
strcpy(xino->name, ".");
|
|
|
|
/*
|
|
* index 2 is for clipboard use
|
|
*/
|
|
|
|
if ((xino = calloc(1, sizeof(struct xrdp_inode))) == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
free(g_xrdp_fs.inode_table[0]);
|
|
free(g_xrdp_fs.inode_table[1]);
|
|
free(g_xrdp_fs.inode_table);
|
|
return -1;
|
|
}
|
|
|
|
g_xrdp_fs.inode_table[2] = xino;
|
|
xino->parent_inode = 1;
|
|
xino->inode = 2;
|
|
xino->nentries = 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;
|
|
}
|
|
|
|
/**
|
|
* zap the xrdp file system
|
|
*
|
|
* @return 0 on success, -1 on failure
|
|
*****************************************************************************/
|
|
|
|
static int xfuse_deinit_xrdp_fs()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* determine if specified ino exists in xrdp file system
|
|
*
|
|
* @return 1 if it does, 0 otherwise
|
|
*****************************************************************************/
|
|
|
|
static int xfuse_is_inode_valid(int ino)
|
|
{
|
|
/* is ino present in our table? */
|
|
if ((ino < FIRST_INODE) || (ino >= g_xrdp_fs.next_node))
|
|
return 0;
|
|
|
|
if (g_xrdp_fs.inode_table[ino] == NULL)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a directory or regular file.
|
|
*****************************************************************************/
|
|
|
|
// LK_TODO
|
|
#if 0
|
|
static void xfuse_create_file(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, mode_t mode, int type)
|
|
{
|
|
struct xrdp_inode *xinode;
|
|
struct fuse_entry_param e;
|
|
|
|
log_debug("parent=%d name=%s", (int) parent, name);
|
|
|
|
/* do we have a valid parent inode? */
|
|
if (!xfuse_is_inode_valid(parent))
|
|
{
|
|
log_error("inode %d is not valid", parent);
|
|
fuse_reply_err(req, EBADF);
|
|
}
|
|
|
|
if ((xinode = calloc(1, sizeof(struct xrdp_inode))) == NULL)
|
|
{
|
|
log_error("calloc() failed");
|
|
fuse_reply_err(req, ENOMEM);
|
|
}
|
|
|
|
/* create directory entry */
|
|
xinode->parent_inode = parent;
|
|
xinode->inode = g_xrdp_fs.next_node++; /* TODO should be thread safe */
|
|
xinode->mode = mode | type;
|
|
xinode->uid = getuid();
|
|
xinode->gid = getgid();
|
|
xinode->size = 0;
|
|
xinode->atime = time(0);
|
|
xinode->mtime = time(0);
|
|
xinode->ctime = time(0);
|
|
strcpy(xinode->name, name);
|
|
|
|
g_xrdp_fs.num_entries++;
|
|
|
|
/* insert it in xrdp fs */
|
|
g_xrdp_fs.inode_table[xinode->inode] = xinode;
|
|
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 xfuse_dump_fs()
|
|
{
|
|
int i;
|
|
struct xrdp_inode *xinode;
|
|
|
|
log_debug("found %d entries", g_xrdp_fs.num_entries - FIRST_INODE);
|
|
|
|
#if 0
|
|
log_debug("not dumping xrdp fs");
|
|
return;
|
|
#endif
|
|
|
|
for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++)
|
|
{
|
|
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("");
|
|
}
|
|
|
|
/**
|
|
* Return the device_id associated with specified inode and copy the
|
|
* full path to the specified inode into full_path
|
|
*
|
|
* @param ino the inode
|
|
* @param full_path full path to the inode
|
|
*
|
|
* @return the device_id of specified inode
|
|
*****************************************************************************/
|
|
|
|
static tui32 xfuse_get_device_id_for_inode(tui32 ino, char *full_path)
|
|
{
|
|
tui32 parent_inode = 0;
|
|
tui32 child_inode = ino;
|
|
char reverse_path[4096];
|
|
|
|
/* ino == 1 is a special case; we already know that it is not */
|
|
/* associated with any device redirection */
|
|
if (ino == 1)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
reverse_path[0] = 0;
|
|
full_path[0] = 0;
|
|
|
|
while (1)
|
|
{
|
|
strcat(reverse_path, g_xrdp_fs.inode_table[child_inode]->name);
|
|
|
|
parent_inode = g_xrdp_fs.inode_table[child_inode]->parent_inode;
|
|
if (parent_inode == 1)
|
|
break;
|
|
|
|
strcat(reverse_path, "/");
|
|
child_inode = parent_inode;
|
|
}
|
|
|
|
fuse_reverse_pathname(full_path, reverse_path);
|
|
|
|
return g_xrdp_fs.inode_table[child_inode]->device_id;
|
|
}
|
|
|
|
/**
|
|
* Reverse the pathname in 'reverse_path' and insert it into 'full_path'
|
|
*
|
|
* Example: abba/music/share1 becomes share1/music/abba
|
|
*
|
|
* @param full_path path name in the correct order
|
|
* @param reverse_path path name in the reverse order
|
|
*****************************************************************************/
|
|
|
|
static void fuse_reverse_pathname(char *full_path, char *reverse_path)
|
|
{
|
|
char *cptr;
|
|
|
|
full_path[0] = 0;
|
|
|
|
while ((cptr = strrchr(reverse_path, '/')) != NULL)
|
|
{
|
|
strcat(full_path, cptr + 1);
|
|
strcat(full_path, "/");
|
|
cptr[0] = 0;
|
|
}
|
|
strcat(full_path, reverse_path);
|
|
}
|
|
|
|
/**
|
|
* Return the inode that matches the name and parent inode
|
|
*****************************************************************************/
|
|
|
|
static struct xrdp_inode * xfuse_get_inode_from_pinode_name(tui32 pinode,
|
|
const char *name)
|
|
{
|
|
int i;
|
|
struct xrdp_inode * xinode;
|
|
|
|
for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Create file in xrdp file system
|
|
*
|
|
* @param pinode the parent inode
|
|
* @param name filename
|
|
*
|
|
* @return XRDP_INODE on success, NULL on failure
|
|
*****************************************************************************/
|
|
|
|
static struct xrdp_inode * xfuse_create_file_in_xrdp_fs(tui32 device_id,
|
|
int pinode, char *name,
|
|
int type)
|
|
{
|
|
XRDP_INODE *xinode;
|
|
XRDP_INODE *xinodep;
|
|
|
|
if ((name == NULL) || (strlen(name) == 0))
|
|
return NULL;
|
|
|
|
if ((xinode = calloc(1, sizeof(XRDP_INODE))) == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
xinode->mode = 0755 | type;
|
|
xinode->size = 4096;
|
|
}
|
|
else
|
|
{
|
|
xinode->mode = 0644 | type;
|
|
xinode->size = 0;
|
|
}
|
|
|
|
g_xrdp_fs.inode_table[xinode->inode] = xinode;
|
|
g_xrdp_fs.num_entries++;
|
|
|
|
/* 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);
|
|
|
|
return xinode;
|
|
}
|
|
|
|
/**
|
|
* Check if specified file exists
|
|
*
|
|
* @param parent parent inode of file
|
|
* @param name filename or dirname
|
|
*
|
|
* @return 1 if specified file exists, 0 otherwise
|
|
*****************************************************************************/
|
|
|
|
static int xfuse_does_file_exist(int parent, char *name)
|
|
{
|
|
int i;
|
|
XRDP_INODE *xinode;
|
|
|
|
for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++)
|
|
{
|
|
if ((xinode = g_xrdp_fs.inode_table[i]) == NULL)
|
|
continue;
|
|
|
|
if ((xinode->parent_inode == parent) &&
|
|
(strcmp(xinode->name, name) == 0))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xfuse_delete_file(int parent, char *name)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static int xfuse_delete_file_with_xinode(XRDP_INODE *xinode)
|
|
{
|
|
/* make sure it is not a dir */
|
|
if ((xinode == NULL) || (xinode->mode & S_IFDIR))
|
|
return -1;
|
|
|
|
log_always("deleting: inode=%d name=%s", xinode->inode, xinode->name);
|
|
log_debug("deleting: inode=%d name=%s", xinode->inode, xinode->name);
|
|
|
|
g_xrdp_fs.inode_table[xinode->parent_inode]->nentries--;
|
|
g_xrdp_fs.inode_table[xinode->inode] = NULL;
|
|
free(xinode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xfuse_delete_dir_with_xinode(XRDP_INODE *xinode)
|
|
{
|
|
XRDP_INODE *xip;
|
|
int i;
|
|
|
|
/* 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++)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/**
|
|
* Recursively delete dir with specified inode
|
|
*****************************************************************************/
|
|
|
|
static int xfuse_recursive_delete_dir_with_xinode(XRDP_INODE *xinode)
|
|
{
|
|
XRDP_INODE *xip;
|
|
int i;
|
|
|
|
/* make sure it is not a file */
|
|
if ((xinode == NULL) || (xinode->mode & S_IFREG))
|
|
return -1;
|
|
|
|
log_always("recursively deleting dir with inode=%d name=%s",
|
|
xinode->inode, xinode->name);
|
|
|
|
log_debug("recursively deleting dir with inode=%d name=%s",
|
|
xinode->inode, xinode->name);
|
|
|
|
for (i = FIRST_INODE; i < g_xrdp_fs.num_entries; i++)
|
|
{
|
|
if ((xip = g_xrdp_fs.inode_table[i]) == NULL)
|
|
continue;
|
|
|
|
/* look for child inodes */
|
|
if (xip->parent_inode == xinode->inode)
|
|
{
|
|
/* got one */
|
|
if (xip->mode & S_IFREG)
|
|
{
|
|
/* regular file */
|
|
g_xrdp_fs.inode_table[xip->parent_inode]->nentries--;
|
|
g_xrdp_fs.inode_table[xip->inode] = NULL;
|
|
free(xip);
|
|
}
|
|
else
|
|
{
|
|
/* got another dir */
|
|
xfuse_recursive_delete_dir_with_xinode(xip);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
log_error("system out of memory");
|
|
return;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/******************************************************************************
|
|
** **
|
|
** callbacks for devredir **
|
|
** **
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* Add a file or directory to xrdp file system
|
|
*****************************************************************************/
|
|
|
|
int xfuse_devredir_cb_enum_dir(void *vp, struct xrdp_inode *xinode)
|
|
{
|
|
XFUSE_INFO *fip = (XFUSE_INFO *) vp;
|
|
XRDP_INODE *xip = NULL;
|
|
|
|
if ((fip == NULL) || (xinode == NULL))
|
|
{
|
|
log_error("fip or xinode are NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (!xfuse_is_inode_valid(fip->inode))
|
|
{
|
|
log_error("inode %d is not valid", fip->inode);
|
|
g_free(xinode);
|
|
return -1;
|
|
}
|
|
|
|
log_debug("parent_inode=%d name=%s", fip->inode, xinode->name);
|
|
|
|
/* if filename is . or .. don't add it */
|
|
if ((strcmp(xinode->name, ".") == 0) || (strcmp(xinode->name, "..") == 0))
|
|
{
|
|
g_free(xinode);
|
|
return -1;
|
|
}
|
|
|
|
xfuse_dump_fs();
|
|
|
|
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);
|
|
g_free(xinode);
|
|
xip->stale = 0;
|
|
return -1;
|
|
}
|
|
|
|
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();
|
|
|
|
xfuse_dump_fs();
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*****************************************************************************/
|
|
|
|
void xfuse_devredir_cb_enum_dir_done(void *vp, tui32 IoStatus)
|
|
{
|
|
XFUSE_INFO *fip;
|
|
struct dir_info *di;
|
|
struct opendir_req *odreq;
|
|
|
|
log_debug("vp=%p IoStatus=0x%x", vp, IoStatus);
|
|
|
|
fip = (XFUSE_INFO *) vp;
|
|
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;
|
|
}
|
|
|
|
xfuse_delete_stale_entries(fip->inode);
|
|
|
|
/* this will be used by xfuse_cb_readdir() */
|
|
di = calloc(1, sizeof(struct dir_info));
|
|
di->index = FIRST_INODE;
|
|
fip->fi->fh = (long) di;
|
|
|
|
fuse_reply_open(fip->req, fip->fi);
|
|
|
|
done:
|
|
|
|
if (fip)
|
|
free(fip);
|
|
|
|
/* remove current request */
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
|
|
while (1)
|
|
{
|
|
/* process next request */
|
|
odreq = fifo_peek(&g_fifo_opendir);
|
|
if (!odreq)
|
|
return;
|
|
|
|
if (xfuse_proc_opendir_req(odreq->req, odreq->ino, odreq->fi))
|
|
g_free(fifo_remove(&g_fifo_opendir)); /* req failed */
|
|
else
|
|
break; /* req has been queued */
|
|
}
|
|
}
|
|
|
|
void xfuse_devredir_cb_open_file(void *vp, tui32 IoStatus, tui32 DeviceId,
|
|
tui32 FileId)
|
|
{
|
|
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);
|
|
|
|
if (IoStatus != 0)
|
|
{
|
|
if (!fip->invoke_fuse)
|
|
goto done;
|
|
|
|
switch (IoStatus)
|
|
{
|
|
case 0xC0000022:
|
|
fuse_reply_err(fip->req, EACCES);
|
|
break;
|
|
|
|
case 0xC0000033:
|
|
case 0xC0000034:
|
|
fuse_reply_err(fip->req, ENOENT);
|
|
break;
|
|
|
|
default:
|
|
fuse_reply_err(fip->req, EIO);
|
|
break;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (fip->fi != NULL)
|
|
{
|
|
if ((fh = calloc(1, sizeof(XFUSE_HANDLE))) == NULL)
|
|
{
|
|
log_error("system out of memory");
|
|
if (fip->invoke_fuse)
|
|
fuse_reply_err(fip->req, ENOMEM);
|
|
|
|
free(fip);
|
|
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("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)
|
|
{
|
|
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
|
|
{
|
|
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)
|
|
{
|
|
fuse_reply_err(fip->req, EBADF);
|
|
free(fip);
|
|
return;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void xfuse_devredir_cb_rename_file(void *vp, tui32 IoStatus)
|
|
{
|
|
XFUSE_INFO *fip;
|
|
XRDP_INODE *old_xinode;
|
|
XRDP_INODE *new_xinode;
|
|
|
|
fip = (XFUSE_INFO *) vp;
|
|
if (fip == NULL)
|
|
return;
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
old_xinode->parent_inode = fip->new_inode;
|
|
strncpy(old_xinode->name, fip->new_name, 1023);
|
|
old_xinode->name[1023] = 0;
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/******************************************************************************
|
|
** **
|
|
** 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Get file attributes
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_cb_getattr(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct xrdp_inode *xino;
|
|
struct stat stbuf;
|
|
|
|
(void) fi;
|
|
|
|
log_debug("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];
|
|
if (!xino)
|
|
{
|
|
log_debug("****** invalid ino=%d", (int) ino);
|
|
fuse_reply_err(req, EBADF);
|
|
return;
|
|
}
|
|
|
|
memset(&stbuf, 0, sizeof(stbuf));
|
|
stbuf.st_ino = ino;
|
|
stbuf.st_mode = xino->mode;
|
|
stbuf.st_nlink = xino->nlink;
|
|
stbuf.st_size = xino->size;
|
|
fuse_reply_attr(req, &stbuf, 1.0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_dirbuf_add(fuse_req_t req, struct dirbuf *b,
|
|
const char *name, fuse_ino_t ino)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
|
off_t off, struct fuse_file_info *fi)
|
|
{
|
|
XRDP_INODE *xinode;
|
|
XRDP_INODE *ti;
|
|
struct dir_info *di;
|
|
struct dirbuf1 b;
|
|
int i;
|
|
int first_time;
|
|
|
|
log_debug("req=%p inode=%d size=%d offset=%d", req, ino, size, off);
|
|
|
|
/* do we have a valid inode? */
|
|
if (!xfuse_is_inode_valid(ino))
|
|
{
|
|
log_error("inode %d is not valid", ino);
|
|
fuse_reply_err(req, EBADF);
|
|
return;
|
|
}
|
|
|
|
di = (struct dir_info *) (tintptr) (fi->fh);
|
|
if (di == NULL)
|
|
{
|
|
/* something seriously wrong somewhere! */
|
|
fuse_reply_buf(req, 0, 0);
|
|
return;
|
|
}
|
|
|
|
b.bytes_in_buf = 0;
|
|
first_time = (di->index == FIRST_INODE) ? 1 : 0;
|
|
|
|
for (i = di->index; i < g_xrdp_fs.num_entries; i++, di->index++)
|
|
{
|
|
if ((xinode = g_xrdp_fs.inode_table[i]) == NULL)
|
|
continue;
|
|
|
|
/* match parent inode */
|
|
if (xinode->parent_inode != ino)
|
|
continue;
|
|
|
|
xinode->is_synced = 1;
|
|
|
|
if (first_time)
|
|
{
|
|
first_time = 0;
|
|
ti = g_xrdp_fs.inode_table[ino];
|
|
if (!ti)
|
|
{
|
|
log_debug("****** g_xrdp_fs.inode_table[%d] is NULL", ino);
|
|
fuse_reply_buf(req, NULL, 0);
|
|
return;
|
|
}
|
|
xfuse_dirbuf_add1(req, &b, ".", ino);
|
|
xfuse_dirbuf_add1(req, &b, "..", ti->parent_inode);
|
|
}
|
|
|
|
if (xfuse_dirbuf_add1(req, &b, xinode->name, xinode->inode))
|
|
break; /* buffer is full */
|
|
}
|
|
|
|
if (b.bytes_in_buf)
|
|
fuse_reply_buf(req, b.buf, b.bytes_in_buf);
|
|
else
|
|
fuse_reply_buf(req, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* Create a directory
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_cb_mkdir(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name, mode_t mode)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Remove specified dir
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_cb_rmdir(fuse_req_t req, fuse_ino_t parent,
|
|
const char *name)
|
|
{
|
|
xfuse_remove_dir_or_file(req, parent, name, 1);
|
|
}
|
|
|
|
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, "/");
|
|
strncat(full_path, name, sizeof(full_path) - strlen(full_path) - 1);
|
|
|
|
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)
|
|
{
|
|
log_debug("****** g_xrdp_fs.inode_table[%d] is NULL", ino);
|
|
fuse_reply_err(req, EBADF);
|
|
return;
|
|
}
|
|
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;
|
|
|
|
/* 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)
|
|
{
|
|
log_debug("****** g_xrdp_fs.inode_table[%d] is NULL", ino);
|
|
fuse_reply_err(req, 0);
|
|
return;
|
|
}
|
|
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;
|
|
fip->fi->fh = 0;
|
|
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);
|
|
}
|
|
|
|
free(handle);
|
|
}
|
|
|
|
/**
|
|
*****************************************************************************/
|
|
|
|
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;
|
|
|
|
devredir_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;
|
|
}
|
|
|
|
if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL)
|
|
{
|
|
log_debug("g_xrdp_fs.inode_table[%d] is NULL", ino);
|
|
fuse_reply_err(req, EBADF);
|
|
return;
|
|
}
|
|
|
|
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 */
|
|
}
|
|
|
|
/**
|
|
* Get dir listing
|
|
*****************************************************************************/
|
|
static void xfuse_cb_opendir(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct opendir_req *odreq;
|
|
|
|
/* save request */
|
|
odreq = malloc(sizeof(struct opendir_req));
|
|
odreq->req = req;
|
|
odreq->ino = ino;
|
|
odreq->fi = fi;
|
|
|
|
if (fifo_is_empty(&g_fifo_opendir))
|
|
{
|
|
fifo_insert(&g_fifo_opendir, odreq);
|
|
xfuse_proc_opendir_req(req, ino, fi);
|
|
}
|
|
else
|
|
{
|
|
/* place req in FIFO; xfuse_devredir_cb_enum_dir_done() will handle it */
|
|
fifo_insert(&g_fifo_opendir, odreq);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process the next opendir req
|
|
*
|
|
* @return 0 of the request was sent for remote lookup, -1 otherwise
|
|
*****************************************************************************/
|
|
static int xfuse_proc_opendir_req(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct dir_info *di;
|
|
XRDP_INODE *xinode;
|
|
XFUSE_INFO *fip;
|
|
tui32 device_id;
|
|
char full_path[4096];
|
|
char *cptr;
|
|
|
|
log_debug("inode=%d", ino);
|
|
|
|
if (!xfuse_is_inode_valid(ino))
|
|
{
|
|
log_error("inode %d is not valid", ino);
|
|
fuse_reply_err(req, EBADF);
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
return -1;
|
|
}
|
|
|
|
if (ino == 1)
|
|
goto done; /* special case; enumerate top level dir */
|
|
|
|
if ((xinode = g_xrdp_fs.inode_table[ino]) == NULL)
|
|
{
|
|
log_debug("g_xrdp_fs.inode_table[%d] is NULL", ino);
|
|
fuse_reply_err(req, EBADF);
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
return -1;
|
|
}
|
|
|
|
if (xinode->is_loc_resource)
|
|
goto done;
|
|
|
|
/* enumerate resources on a remote device */
|
|
|
|
#ifdef USE_SYNC_FLAG
|
|
if (xinode->is_synced)
|
|
{
|
|
xfuse_enum_dir(req, ino, size, off, fi);
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
goto do_remote_lookup;
|
|
}
|
|
#endif
|
|
|
|
do_remote_lookup:
|
|
|
|
xfuse_mark_as_stale((int) ino);
|
|
|
|
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);
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
return -1;
|
|
}
|
|
|
|
fip->req = req;
|
|
fip->inode = ino;
|
|
fip->size = 0;
|
|
fip->off = 0;
|
|
fip->fi = fi;
|
|
fip->dirbuf1.first_time = 1;
|
|
fip->dirbuf1.bytes_in_buf = 0;
|
|
|
|
fip->invoke_fuse = 1;
|
|
fip->device_id = device_id;
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
done:
|
|
|
|
di = calloc(1, sizeof(struct dir_info));
|
|
di->index = FIRST_INODE;
|
|
fi->fh = (long) di;
|
|
fuse_reply_open(req, fi);
|
|
g_free(fifo_remove(&g_fifo_opendir));
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
*****************************************************************************/
|
|
|
|
static void xfuse_cb_releasedir(fuse_req_t req, fuse_ino_t ino,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct dir_info *di;
|
|
|
|
di = (struct dir_info *) (tintptr) fi->fh;
|
|
if (di)
|
|
free(di);
|
|
|
|
fuse_reply_err(req, 0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* miscellaneous functions
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Mark all files with matching parent inode as stale
|
|
*
|
|
* @param pinode the parent inode
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
xfuse_mark_as_stale(int pinode)
|
|
{
|
|
int i;
|
|
XRDP_INODE *xinode;
|
|
|
|
if ((pinode < FIRST_INODE) || (pinode >= g_xrdp_fs.num_entries))
|
|
return;
|
|
|
|
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 != pinode)
|
|
continue;
|
|
|
|
/* got a match */
|
|
xinode->stale = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete all files with matching parent inode that are marked as stale
|
|
*
|
|
* @param pinode the parent inode
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
xfuse_delete_stale_entries(int pinode)
|
|
{
|
|
int i;
|
|
XRDP_INODE *xinode;
|
|
|
|
if ((pinode < FIRST_INODE) || (pinode >= g_xrdp_fs.num_entries))
|
|
return;
|
|
|
|
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 != pinode)
|
|
continue;
|
|
|
|
/* got a match, but is it stale? */
|
|
if (!xinode->stale)
|
|
continue;
|
|
|
|
/* ok to delete */
|
|
if (xinode->mode & S_IFREG)
|
|
xfuse_delete_file_with_xinode(xinode);
|
|
else
|
|
xfuse_recursive_delete_dir_with_xinode(xinode);
|
|
}
|
|
}
|
|
|
|
#endif /* end else #ifndef XRDP_FUSE */
|